aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/virtual/kvm/api.txt1
-rw-r--r--arch/arm/include/asm/kvm_vgic.h9
-rw-r--r--arch/arm/include/uapi/asm/kvm.h3
-rw-r--r--arch/arm/kvm/arm.c16
-rw-r--r--arch/arm/kvm/vgic.c62
5 files changed, 90 insertions, 1 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 4505f869e450..e0fa0ea2b187 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2222,6 +2222,7 @@ Errors:
2222 ENXIO: Device not supported on current system 2222 ENXIO: Device not supported on current system
2223 EEXIST: Address already set 2223 EEXIST: Address already set
2224 E2BIG: Address outside guest physical address space 2224 E2BIG: Address outside guest physical address space
2225 EBUSY: Address overlaps with other device range
2225 2226
2226struct kvm_arm_device_addr { 2227struct kvm_arm_device_addr {
2227 __u64 id; 2228 __u64 id;
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 8f44799b8db1..b56fcf3c3575 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -22,6 +22,9 @@
22#include <linux/irqchip/arm-gic.h> 22#include <linux/irqchip/arm-gic.h>
23 23
24struct vgic_dist { 24struct vgic_dist {
25 /* Distributor and vcpu interface mapping in the guest */
26 phys_addr_t vgic_dist_base;
27 phys_addr_t vgic_cpu_base;
25}; 28};
26 29
27struct vgic_cpu { 30struct vgic_cpu {
@@ -33,6 +36,7 @@ struct kvm_run;
33struct kvm_exit_mmio; 36struct kvm_exit_mmio;
34 37
35#ifdef CONFIG_KVM_ARM_VGIC 38#ifdef CONFIG_KVM_ARM_VGIC
39int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
36bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, 40bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
37 struct kvm_exit_mmio *mmio); 41 struct kvm_exit_mmio *mmio);
38 42
@@ -42,6 +46,11 @@ static inline int kvm_vgic_hyp_init(void)
42 return 0; 46 return 0;
43} 47}
44 48
49static inline int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
50{
51 return 0;
52}
53
45static inline int kvm_vgic_init(struct kvm *kvm) 54static inline int kvm_vgic_init(struct kvm *kvm)
46{ 55{
47 return 0; 56 return 0;
diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
index 346ac3f4a2b8..023bfeb367bf 100644
--- a/arch/arm/include/uapi/asm/kvm.h
+++ b/arch/arm/include/uapi/asm/kvm.h
@@ -78,6 +78,9 @@ struct kvm_regs {
78#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 78#define KVM_VGIC_V2_ADDR_TYPE_DIST 0
79#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 79#define KVM_VGIC_V2_ADDR_TYPE_CPU 1
80 80
81#define KVM_VGIC_V2_DIST_SIZE 0x1000
82#define KVM_VGIC_V2_CPU_SIZE 0x2000
83
81#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ 84#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */
82 85
83struct kvm_vcpu_init { 86struct kvm_vcpu_init {
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 7305aef28d0e..c327fd9d8ec3 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -880,7 +880,21 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
880static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm, 880static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
881 struct kvm_arm_device_addr *dev_addr) 881 struct kvm_arm_device_addr *dev_addr)
882{ 882{
883 return -ENODEV; 883 unsigned long dev_id, type;
884
885 dev_id = (dev_addr->id & KVM_ARM_DEVICE_ID_MASK) >>
886 KVM_ARM_DEVICE_ID_SHIFT;
887 type = (dev_addr->id & KVM_ARM_DEVICE_TYPE_MASK) >>
888 KVM_ARM_DEVICE_TYPE_SHIFT;
889
890 switch (dev_id) {
891 case KVM_ARM_DEVICE_VGIC_V2:
892 if (!vgic_present)
893 return -ENXIO;
894 return kvm_vgic_set_addr(kvm, type, dev_addr->addr);
895 default:
896 return -ENODEV;
897 }
884} 898}
885 899
886long kvm_arch_vm_ioctl(struct file *filp, 900long kvm_arch_vm_ioctl(struct file *filp,
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index c400661409ab..b333b58de4cb 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -22,6 +22,9 @@
22#include <linux/io.h> 22#include <linux/io.h>
23#include <asm/kvm_emulate.h> 23#include <asm/kvm_emulate.h>
24 24
25#define VGIC_ADDR_UNDEF (-1)
26#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
27
25#define ACCESS_READ_VALUE (1 << 0) 28#define ACCESS_READ_VALUE (1 << 0)
26#define ACCESS_READ_RAZ (0 << 0) 29#define ACCESS_READ_RAZ (0 << 0)
27#define ACCESS_READ_MASK(x) ((x) & (1 << 0)) 30#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
@@ -151,3 +154,62 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
151{ 154{
152 return KVM_EXIT_MMIO; 155 return KVM_EXIT_MMIO;
153} 156}
157
158static bool vgic_ioaddr_overlap(struct kvm *kvm)
159{
160 phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
161 phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
162
163 if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
164 return 0;
165 if ((dist <= cpu && dist + KVM_VGIC_V2_DIST_SIZE > cpu) ||
166 (cpu <= dist && cpu + KVM_VGIC_V2_CPU_SIZE > dist))
167 return -EBUSY;
168 return 0;
169}
170
171static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
172 phys_addr_t addr, phys_addr_t size)
173{
174 int ret;
175
176 if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
177 return -EEXIST;
178 if (addr + size < addr)
179 return -EINVAL;
180
181 ret = vgic_ioaddr_overlap(kvm);
182 if (ret)
183 return ret;
184 *ioaddr = addr;
185 return ret;
186}
187
188int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
189{
190 int r = 0;
191 struct vgic_dist *vgic = &kvm->arch.vgic;
192
193 if (addr & ~KVM_PHYS_MASK)
194 return -E2BIG;
195
196 if (addr & ~PAGE_MASK)
197 return -EINVAL;
198
199 mutex_lock(&kvm->lock);
200 switch (type) {
201 case KVM_VGIC_V2_ADDR_TYPE_DIST:
202 r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,
203 addr, KVM_VGIC_V2_DIST_SIZE);
204 break;
205 case KVM_VGIC_V2_ADDR_TYPE_CPU:
206 r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
207 addr, KVM_VGIC_V2_CPU_SIZE);
208 break;
209 default:
210 r = -ENODEV;
211 }
212
213 mutex_unlock(&kvm->lock);
214 return r;
215}