diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2014-07-08 07:09:04 -0400 |
---|---|---|
committer | Christoffer Dall <christoffer.dall@linaro.org> | 2014-09-18 21:48:57 -0400 |
commit | c3c918361adcceb816c92b21dd95d2b46fb96a8f (patch) | |
tree | 055024f1f93ec5fce6851aac87f9f22bb79a0a4d | |
parent | fc675e355e705a046df7b635d3f3330c0ad94569 (diff) |
arm/arm64: KVM: vgic: handle out-of-range MMIO accesses
Now that we can (almost) dynamically size the number of interrupts,
we're facing an interesting issue:
We have to evaluate at runtime whether or not an access hits a valid
register, based on the sizing of this particular instance of the
distributor. Furthermore, the GIC spec says that accessing a reserved
register is RAZ/WI.
For this, add a new field to our range structure, indicating the number
of bits a single interrupts uses. That allows us to find out whether or
not the access is in range.
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r-- | include/kvm/arm_vgic.h | 3 | ||||
-rw-r--r-- | virt/kvm/arm/vgic.c | 56 |
2 files changed, 47 insertions, 12 deletions
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 3b73d7845124..2767f939f47c 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #define VGIC_V2_MAX_LRS (1 << 6) | 33 | #define VGIC_V2_MAX_LRS (1 << 6) |
34 | #define VGIC_V3_MAX_LRS 16 | 34 | #define VGIC_V3_MAX_LRS 16 |
35 | #define VGIC_MAX_IRQS 1024 | ||
35 | 36 | ||
36 | /* Sanity checks... */ | 37 | /* Sanity checks... */ |
37 | #if (KVM_MAX_VCPUS > 8) | 38 | #if (KVM_MAX_VCPUS > 8) |
@@ -42,7 +43,7 @@ | |||
42 | #error "VGIC_NR_IRQS must be a multiple of 32" | 43 | #error "VGIC_NR_IRQS must be a multiple of 32" |
43 | #endif | 44 | #endif |
44 | 45 | ||
45 | #if (VGIC_NR_IRQS > 1024) | 46 | #if (VGIC_NR_IRQS > VGIC_MAX_IRQS) |
46 | #error "VGIC_NR_IRQS must be <= 1024" | 47 | #error "VGIC_NR_IRQS must be <= 1024" |
47 | #endif | 48 | #endif |
48 | 49 | ||
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 599ad17e5436..973eaf7ebe98 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c | |||
@@ -895,6 +895,7 @@ static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu, | |||
895 | struct mmio_range { | 895 | struct mmio_range { |
896 | phys_addr_t base; | 896 | phys_addr_t base; |
897 | unsigned long len; | 897 | unsigned long len; |
898 | int bits_per_irq; | ||
898 | bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, | 899 | bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, |
899 | phys_addr_t offset); | 900 | phys_addr_t offset); |
900 | }; | 901 | }; |
@@ -903,56 +904,67 @@ static const struct mmio_range vgic_dist_ranges[] = { | |||
903 | { | 904 | { |
904 | .base = GIC_DIST_CTRL, | 905 | .base = GIC_DIST_CTRL, |
905 | .len = 12, | 906 | .len = 12, |
907 | .bits_per_irq = 0, | ||
906 | .handle_mmio = handle_mmio_misc, | 908 | .handle_mmio = handle_mmio_misc, |
907 | }, | 909 | }, |
908 | { | 910 | { |
909 | .base = GIC_DIST_IGROUP, | 911 | .base = GIC_DIST_IGROUP, |
910 | .len = VGIC_NR_IRQS / 8, | 912 | .len = VGIC_MAX_IRQS / 8, |
913 | .bits_per_irq = 1, | ||
911 | .handle_mmio = handle_mmio_raz_wi, | 914 | .handle_mmio = handle_mmio_raz_wi, |
912 | }, | 915 | }, |
913 | { | 916 | { |
914 | .base = GIC_DIST_ENABLE_SET, | 917 | .base = GIC_DIST_ENABLE_SET, |
915 | .len = VGIC_NR_IRQS / 8, | 918 | .len = VGIC_MAX_IRQS / 8, |
919 | .bits_per_irq = 1, | ||
916 | .handle_mmio = handle_mmio_set_enable_reg, | 920 | .handle_mmio = handle_mmio_set_enable_reg, |
917 | }, | 921 | }, |
918 | { | 922 | { |
919 | .base = GIC_DIST_ENABLE_CLEAR, | 923 | .base = GIC_DIST_ENABLE_CLEAR, |
920 | .len = VGIC_NR_IRQS / 8, | 924 | .len = VGIC_MAX_IRQS / 8, |
925 | .bits_per_irq = 1, | ||
921 | .handle_mmio = handle_mmio_clear_enable_reg, | 926 | .handle_mmio = handle_mmio_clear_enable_reg, |
922 | }, | 927 | }, |
923 | { | 928 | { |
924 | .base = GIC_DIST_PENDING_SET, | 929 | .base = GIC_DIST_PENDING_SET, |
925 | .len = VGIC_NR_IRQS / 8, | 930 | .len = VGIC_MAX_IRQS / 8, |
931 | .bits_per_irq = 1, | ||
926 | .handle_mmio = handle_mmio_set_pending_reg, | 932 | .handle_mmio = handle_mmio_set_pending_reg, |
927 | }, | 933 | }, |
928 | { | 934 | { |
929 | .base = GIC_DIST_PENDING_CLEAR, | 935 | .base = GIC_DIST_PENDING_CLEAR, |
930 | .len = VGIC_NR_IRQS / 8, | 936 | .len = VGIC_MAX_IRQS / 8, |
937 | .bits_per_irq = 1, | ||
931 | .handle_mmio = handle_mmio_clear_pending_reg, | 938 | .handle_mmio = handle_mmio_clear_pending_reg, |
932 | }, | 939 | }, |
933 | { | 940 | { |
934 | .base = GIC_DIST_ACTIVE_SET, | 941 | .base = GIC_DIST_ACTIVE_SET, |
935 | .len = VGIC_NR_IRQS / 8, | 942 | .len = VGIC_MAX_IRQS / 8, |
943 | .bits_per_irq = 1, | ||
936 | .handle_mmio = handle_mmio_raz_wi, | 944 | .handle_mmio = handle_mmio_raz_wi, |
937 | }, | 945 | }, |
938 | { | 946 | { |
939 | .base = GIC_DIST_ACTIVE_CLEAR, | 947 | .base = GIC_DIST_ACTIVE_CLEAR, |
940 | .len = VGIC_NR_IRQS / 8, | 948 | .len = VGIC_MAX_IRQS / 8, |
949 | .bits_per_irq = 1, | ||
941 | .handle_mmio = handle_mmio_raz_wi, | 950 | .handle_mmio = handle_mmio_raz_wi, |
942 | }, | 951 | }, |
943 | { | 952 | { |
944 | .base = GIC_DIST_PRI, | 953 | .base = GIC_DIST_PRI, |
945 | .len = VGIC_NR_IRQS, | 954 | .len = VGIC_MAX_IRQS, |
955 | .bits_per_irq = 8, | ||
946 | .handle_mmio = handle_mmio_priority_reg, | 956 | .handle_mmio = handle_mmio_priority_reg, |
947 | }, | 957 | }, |
948 | { | 958 | { |
949 | .base = GIC_DIST_TARGET, | 959 | .base = GIC_DIST_TARGET, |
950 | .len = VGIC_NR_IRQS, | 960 | .len = VGIC_MAX_IRQS, |
961 | .bits_per_irq = 8, | ||
951 | .handle_mmio = handle_mmio_target_reg, | 962 | .handle_mmio = handle_mmio_target_reg, |
952 | }, | 963 | }, |
953 | { | 964 | { |
954 | .base = GIC_DIST_CONFIG, | 965 | .base = GIC_DIST_CONFIG, |
955 | .len = VGIC_NR_IRQS / 4, | 966 | .len = VGIC_MAX_IRQS / 4, |
967 | .bits_per_irq = 2, | ||
956 | .handle_mmio = handle_mmio_cfg_reg, | 968 | .handle_mmio = handle_mmio_cfg_reg, |
957 | }, | 969 | }, |
958 | { | 970 | { |
@@ -990,6 +1002,22 @@ struct mmio_range *find_matching_range(const struct mmio_range *ranges, | |||
990 | return NULL; | 1002 | return NULL; |
991 | } | 1003 | } |
992 | 1004 | ||
1005 | static bool vgic_validate_access(const struct vgic_dist *dist, | ||
1006 | const struct mmio_range *range, | ||
1007 | unsigned long offset) | ||
1008 | { | ||
1009 | int irq; | ||
1010 | |||
1011 | if (!range->bits_per_irq) | ||
1012 | return true; /* Not an irq-based access */ | ||
1013 | |||
1014 | irq = offset * 8 / range->bits_per_irq; | ||
1015 | if (irq >= dist->nr_irqs) | ||
1016 | return false; | ||
1017 | |||
1018 | return true; | ||
1019 | } | ||
1020 | |||
993 | /** | 1021 | /** |
994 | * vgic_handle_mmio - handle an in-kernel MMIO access | 1022 | * vgic_handle_mmio - handle an in-kernel MMIO access |
995 | * @vcpu: pointer to the vcpu performing the access | 1023 | * @vcpu: pointer to the vcpu performing the access |
@@ -1029,7 +1057,13 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, | |||
1029 | 1057 | ||
1030 | spin_lock(&vcpu->kvm->arch.vgic.lock); | 1058 | spin_lock(&vcpu->kvm->arch.vgic.lock); |
1031 | offset = mmio->phys_addr - range->base - base; | 1059 | offset = mmio->phys_addr - range->base - base; |
1032 | updated_state = range->handle_mmio(vcpu, mmio, offset); | 1060 | if (vgic_validate_access(dist, range, offset)) { |
1061 | updated_state = range->handle_mmio(vcpu, mmio, offset); | ||
1062 | } else { | ||
1063 | vgic_reg_access(mmio, NULL, offset, | ||
1064 | ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); | ||
1065 | updated_state = false; | ||
1066 | } | ||
1033 | spin_unlock(&vcpu->kvm->arch.vgic.lock); | 1067 | spin_unlock(&vcpu->kvm->arch.vgic.lock); |
1034 | kvm_prepare_mmio(run, mmio); | 1068 | kvm_prepare_mmio(run, mmio); |
1035 | kvm_handle_mmio_return(vcpu, run); | 1069 | kvm_handle_mmio_return(vcpu, run); |