diff options
Diffstat (limited to 'virt')
-rw-r--r-- | virt/kvm/arm/vgic.c | 129 | ||||
-rw-r--r-- | virt/kvm/arm/vgic.h | 7 |
2 files changed, 136 insertions, 0 deletions
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 8802ad73467f..e968179e592f 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <asm/kvm_arm.h> | 32 | #include <asm/kvm_arm.h> |
33 | #include <asm/kvm_mmu.h> | 33 | #include <asm/kvm_mmu.h> |
34 | #include <trace/events/kvm.h> | 34 | #include <trace/events/kvm.h> |
35 | #include <asm/kvm.h> | ||
36 | #include <kvm/iodev.h> | ||
35 | 37 | ||
36 | /* | 38 | /* |
37 | * How the whole thing works (courtesy of Christoffer Dall): | 39 | * How the whole thing works (courtesy of Christoffer Dall): |
@@ -837,6 +839,66 @@ bool vgic_handle_mmio_range(struct kvm_vcpu *vcpu, struct kvm_run *run, | |||
837 | } | 839 | } |
838 | 840 | ||
839 | /** | 841 | /** |
842 | * vgic_handle_mmio_access - handle an in-kernel MMIO access | ||
843 | * This is called by the read/write KVM IO device wrappers below. | ||
844 | * @vcpu: pointer to the vcpu performing the access | ||
845 | * @this: pointer to the KVM IO device in charge | ||
846 | * @addr: guest physical address of the access | ||
847 | * @len: size of the access | ||
848 | * @val: pointer to the data region | ||
849 | * @is_write: read or write access | ||
850 | * | ||
851 | * returns true if the MMIO access could be performed | ||
852 | */ | ||
853 | static int vgic_handle_mmio_access(struct kvm_vcpu *vcpu, | ||
854 | struct kvm_io_device *this, gpa_t addr, | ||
855 | int len, void *val, bool is_write) | ||
856 | { | ||
857 | struct vgic_dist *dist = &vcpu->kvm->arch.vgic; | ||
858 | struct vgic_io_device *iodev = container_of(this, | ||
859 | struct vgic_io_device, dev); | ||
860 | struct kvm_run *run = vcpu->run; | ||
861 | const struct vgic_io_range *range; | ||
862 | struct kvm_exit_mmio mmio; | ||
863 | bool updated_state; | ||
864 | gpa_t offset; | ||
865 | |||
866 | offset = addr - iodev->addr; | ||
867 | range = vgic_find_range(iodev->reg_ranges, len, offset); | ||
868 | if (unlikely(!range || !range->handle_mmio)) { | ||
869 | pr_warn("Unhandled access %d %08llx %d\n", is_write, addr, len); | ||
870 | return -ENXIO; | ||
871 | } | ||
872 | |||
873 | mmio.phys_addr = addr; | ||
874 | mmio.len = len; | ||
875 | mmio.is_write = is_write; | ||
876 | if (is_write) | ||
877 | memcpy(mmio.data, val, len); | ||
878 | mmio.private = iodev->redist_vcpu; | ||
879 | |||
880 | spin_lock(&dist->lock); | ||
881 | offset -= range->base; | ||
882 | if (vgic_validate_access(dist, range, offset)) { | ||
883 | updated_state = call_range_handler(vcpu, &mmio, offset, range); | ||
884 | if (!is_write) | ||
885 | memcpy(val, mmio.data, len); | ||
886 | } else { | ||
887 | if (!is_write) | ||
888 | memset(val, 0, len); | ||
889 | updated_state = false; | ||
890 | } | ||
891 | spin_unlock(&dist->lock); | ||
892 | kvm_prepare_mmio(run, &mmio); | ||
893 | kvm_handle_mmio_return(vcpu, run); | ||
894 | |||
895 | if (updated_state) | ||
896 | vgic_kick_vcpus(vcpu->kvm); | ||
897 | |||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | /** | ||
840 | * vgic_handle_mmio - handle an in-kernel MMIO access for the GIC emulation | 902 | * vgic_handle_mmio - handle an in-kernel MMIO access for the GIC emulation |
841 | * @vcpu: pointer to the vcpu performing the access | 903 | * @vcpu: pointer to the vcpu performing the access |
842 | * @run: pointer to the kvm_run structure | 904 | * @run: pointer to the kvm_run structure |
@@ -860,6 +922,73 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, | |||
860 | return vcpu->kvm->arch.vgic.vm_ops.handle_mmio(vcpu, run, mmio); | 922 | return vcpu->kvm->arch.vgic.vm_ops.handle_mmio(vcpu, run, mmio); |
861 | } | 923 | } |
862 | 924 | ||
925 | static int vgic_handle_mmio_read(struct kvm_vcpu *vcpu, | ||
926 | struct kvm_io_device *this, | ||
927 | gpa_t addr, int len, void *val) | ||
928 | { | ||
929 | return vgic_handle_mmio_access(vcpu, this, addr, len, val, false); | ||
930 | } | ||
931 | |||
932 | static int vgic_handle_mmio_write(struct kvm_vcpu *vcpu, | ||
933 | struct kvm_io_device *this, | ||
934 | gpa_t addr, int len, const void *val) | ||
935 | { | ||
936 | return vgic_handle_mmio_access(vcpu, this, addr, len, (void *)val, | ||
937 | true); | ||
938 | } | ||
939 | |||
940 | struct kvm_io_device_ops vgic_io_ops = { | ||
941 | .read = vgic_handle_mmio_read, | ||
942 | .write = vgic_handle_mmio_write, | ||
943 | }; | ||
944 | |||
945 | /** | ||
946 | * vgic_register_kvm_io_dev - register VGIC register frame on the KVM I/O bus | ||
947 | * @kvm: The VM structure pointer | ||
948 | * @base: The (guest) base address for the register frame | ||
949 | * @len: Length of the register frame window | ||
950 | * @ranges: Describing the handler functions for each register | ||
951 | * @redist_vcpu_id: The VCPU ID to pass on to the handlers on call | ||
952 | * @iodev: Points to memory to be passed on to the handler | ||
953 | * | ||
954 | * @iodev stores the parameters of this function to be usable by the handler | ||
955 | * respectively the dispatcher function (since the KVM I/O bus framework lacks | ||
956 | * an opaque parameter). Initialization is done in this function, but the | ||
957 | * reference should be valid and unique for the whole VGIC lifetime. | ||
958 | * If the register frame is not mapped for a specific VCPU, pass -1 to | ||
959 | * @redist_vcpu_id. | ||
960 | */ | ||
961 | int vgic_register_kvm_io_dev(struct kvm *kvm, gpa_t base, int len, | ||
962 | const struct vgic_io_range *ranges, | ||
963 | int redist_vcpu_id, | ||
964 | struct vgic_io_device *iodev) | ||
965 | { | ||
966 | struct kvm_vcpu *vcpu = NULL; | ||
967 | int ret; | ||
968 | |||
969 | if (redist_vcpu_id >= 0) | ||
970 | vcpu = kvm_get_vcpu(kvm, redist_vcpu_id); | ||
971 | |||
972 | iodev->addr = base; | ||
973 | iodev->len = len; | ||
974 | iodev->reg_ranges = ranges; | ||
975 | iodev->redist_vcpu = vcpu; | ||
976 | |||
977 | kvm_iodevice_init(&iodev->dev, &vgic_io_ops); | ||
978 | |||
979 | mutex_lock(&kvm->slots_lock); | ||
980 | |||
981 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, base, len, | ||
982 | &iodev->dev); | ||
983 | mutex_unlock(&kvm->slots_lock); | ||
984 | |||
985 | /* Mark the iodev as invalid if registration fails. */ | ||
986 | if (ret) | ||
987 | iodev->dev.ops = NULL; | ||
988 | |||
989 | return ret; | ||
990 | } | ||
991 | |||
863 | static int vgic_nr_shared_irqs(struct vgic_dist *dist) | 992 | static int vgic_nr_shared_irqs(struct vgic_dist *dist) |
864 | { | 993 | { |
865 | return dist->nr_irqs - VGIC_NR_PRIVATE_IRQS; | 994 | return dist->nr_irqs - VGIC_NR_PRIVATE_IRQS; |
diff --git a/virt/kvm/arm/vgic.h b/virt/kvm/arm/vgic.h index 01aa6228f88b..28fa3aaf6367 100644 --- a/virt/kvm/arm/vgic.h +++ b/virt/kvm/arm/vgic.h | |||
@@ -20,6 +20,8 @@ | |||
20 | #ifndef __KVM_VGIC_H__ | 20 | #ifndef __KVM_VGIC_H__ |
21 | #define __KVM_VGIC_H__ | 21 | #define __KVM_VGIC_H__ |
22 | 22 | ||
23 | #include <kvm/iodev.h> | ||
24 | |||
23 | #define VGIC_ADDR_UNDEF (-1) | 25 | #define VGIC_ADDR_UNDEF (-1) |
24 | #define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF) | 26 | #define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF) |
25 | 27 | ||
@@ -82,6 +84,11 @@ struct vgic_io_range { | |||
82 | phys_addr_t offset); | 84 | phys_addr_t offset); |
83 | }; | 85 | }; |
84 | 86 | ||
87 | int vgic_register_kvm_io_dev(struct kvm *kvm, gpa_t base, int len, | ||
88 | const struct vgic_io_range *ranges, | ||
89 | int redist_id, | ||
90 | struct vgic_io_device *iodev); | ||
91 | |||
85 | static inline bool is_in_range(phys_addr_t addr, unsigned long len, | 92 | static inline bool is_in_range(phys_addr_t addr, unsigned long len, |
86 | phys_addr_t baseaddr, unsigned long size) | 93 | phys_addr_t baseaddr, unsigned long size) |
87 | { | 94 | { |