aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2014-07-08 07:09:04 -0400
committerChristoffer Dall <christoffer.dall@linaro.org>2014-09-18 21:48:57 -0400
commitc3c918361adcceb816c92b21dd95d2b46fb96a8f (patch)
tree055024f1f93ec5fce6851aac87f9f22bb79a0a4d
parentfc675e355e705a046df7b635d3f3330c0ad94569 (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.h3
-rw-r--r--virt/kvm/arm/vgic.c56
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,
895struct mmio_range { 895struct 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
1005static 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);