aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoffer Dall <c.dall@virtualopensystems.com>2013-01-20 18:47:42 -0500
committerChristoffer Dall <c.dall@virtualopensystems.com>2013-01-23 13:29:12 -0500
commitf7ed45be3ba524e06a6d933f0517dc7ad2d06703 (patch)
tree9e3cc5b0441daf154bccec4e672f17522b9fe13a
parent86ce85352f0da7e1431ad8efcb04323819a620e7 (diff)
KVM: ARM: World-switch implementation
Provides complete world-switch implementation to switch to other guests running in non-secure modes. Includes Hyp exception handlers that capture necessary exception information and stores the information on the VCPU and KVM structures. The following Hyp-ABI is also documented in the code: Hyp-ABI: Calling HYP-mode functions from host (in SVC mode): Switching to Hyp mode is done through a simple HVC #0 instruction. The exception vector code will check that the HVC comes from VMID==0 and if so will push the necessary state (SPSR, lr_usr) on the Hyp stack. - r0 contains a pointer to a HYP function - r1, r2, and r3 contain arguments to the above function. - The HYP function will be called with its arguments in r0, r1 and r2. On HYP function return, we return directly to SVC. A call to a function executing in Hyp mode is performed like the following: <svc code> ldr r0, =BSYM(my_hyp_fn) ldr r1, =my_param hvc #0 ; Call my_hyp_fn(my_param) from HYP mode <svc code> Otherwise, the world-switch is pretty straight-forward. All state that can be modified by the guest is first backed up on the Hyp stack and the VCPU values is loaded onto the hardware. State, which is not loaded, but theoretically modifiable by the guest is protected through the virtualiation features to generate a trap and cause software emulation. Upon guest returns, all state is restored from hardware onto the VCPU struct and the original state is restored from the Hyp-stack onto the hardware. SMP support using the VMPIDR calculated on the basis of the host MPIDR and overriding the low bits with KVM vcpu_id contributed by Marc Zyngier. Reuse of VMIDs has been implemented by Antonios Motakis and adapated from a separate patch into the appropriate patches introducing the functionality. Note that the VMIDs are stored per VM as required by the ARM architecture reference manual. To support VFP/NEON we trap those instructions using the HPCTR. When we trap, we switch the FPU. After a guest exit, the VFP state is returned to the host. When disabling access to floating point instructions, we also mask FPEXC_EN in order to avoid the guest receiving Undefined instruction exceptions before we have a chance to switch back the floating point state. We are reusing vfp_hard_struct, so we depend on VFPv3 being enabled in the host kernel, if not, we still trap cp10 and cp11 in order to inject an undefined instruction exception whenever the guest tries to use VFP/NEON. VFP/NEON developed by Antionios Motakis and Rusty Russell. Aborts that are permission faults, and not stage-1 page table walk, do not report the faulting address in the HPFAR. We have to resolve the IPA, and store it just like the HPFAR register on the VCPU struct. If the IPA cannot be resolved, it means another CPU is playing with the page tables, and we simply restart the guest. This quirk was fixed by Marc Zyngier. Reviewed-by: Will Deacon <will.deacon@arm.com> Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
-rw-r--r--arch/arm/include/asm/kvm_arm.h51
-rw-r--r--arch/arm/include/asm/kvm_host.h13
-rw-r--r--arch/arm/kernel/asm-offsets.c25
-rw-r--r--arch/arm/kvm/arm.c200
-rw-r--r--arch/arm/kvm/interrupts.S396
-rw-r--r--arch/arm/kvm/interrupts_head.S441
6 files changed, 1122 insertions, 4 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index d64b5250ad4e..c69936b1fc53 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -98,6 +98,18 @@
98#define TTBCR_T0SZ 3 98#define TTBCR_T0SZ 3
99#define HTCR_MASK (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0) 99#define HTCR_MASK (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0)
100 100
101/* Hyp System Trap Register */
102#define HSTR_T(x) (1 << x)
103#define HSTR_TTEE (1 << 16)
104#define HSTR_TJDBX (1 << 17)
105
106/* Hyp Coprocessor Trap Register */
107#define HCPTR_TCP(x) (1 << x)
108#define HCPTR_TCP_MASK (0x3fff)
109#define HCPTR_TASE (1 << 15)
110#define HCPTR_TTA (1 << 20)
111#define HCPTR_TCPAC (1 << 31)
112
101/* Hyp Debug Configuration Register bits */ 113/* Hyp Debug Configuration Register bits */
102#define HDCR_TDRA (1 << 11) 114#define HDCR_TDRA (1 << 11)
103#define HDCR_TDOSA (1 << 10) 115#define HDCR_TDOSA (1 << 10)
@@ -144,6 +156,45 @@
144#else 156#else
145#define VTTBR_X (5 - KVM_T0SZ) 157#define VTTBR_X (5 - KVM_T0SZ)
146#endif 158#endif
159#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
160#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
161#define VTTBR_VMID_SHIFT (48LLU)
162#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
163
164/* Hyp Syndrome Register (HSR) bits */
165#define HSR_EC_SHIFT (26)
166#define HSR_EC (0x3fU << HSR_EC_SHIFT)
167#define HSR_IL (1U << 25)
168#define HSR_ISS (HSR_IL - 1)
169#define HSR_ISV_SHIFT (24)
170#define HSR_ISV (1U << HSR_ISV_SHIFT)
171#define HSR_FSC (0x3f)
172#define HSR_FSC_TYPE (0x3c)
173#define HSR_WNR (1 << 6)
174
175#define FSC_FAULT (0x04)
176#define FSC_PERM (0x0c)
177
178/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
179#define HPFAR_MASK (~0xf)
147 180
181#define HSR_EC_UNKNOWN (0x00)
182#define HSR_EC_WFI (0x01)
183#define HSR_EC_CP15_32 (0x03)
184#define HSR_EC_CP15_64 (0x04)
185#define HSR_EC_CP14_MR (0x05)
186#define HSR_EC_CP14_LS (0x06)
187#define HSR_EC_CP_0_13 (0x07)
188#define HSR_EC_CP10_ID (0x08)
189#define HSR_EC_JAZELLE (0x09)
190#define HSR_EC_BXJ (0x0A)
191#define HSR_EC_CP14_64 (0x0C)
192#define HSR_EC_SVC_HYP (0x11)
193#define HSR_EC_HVC (0x12)
194#define HSR_EC_SMC (0x13)
195#define HSR_EC_IABT (0x20)
196#define HSR_EC_IABT_HYP (0x21)
197#define HSR_EC_DABT (0x24)
198#define HSR_EC_DABT_HYP (0x25)
148 199
149#endif /* __ARM_KVM_ARM_H__ */ 200#endif /* __ARM_KVM_ARM_H__ */
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 3636c7ea4eb2..7a121089c733 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -21,6 +21,7 @@
21 21
22#include <asm/kvm.h> 22#include <asm/kvm.h>
23#include <asm/kvm_asm.h> 23#include <asm/kvm_asm.h>
24#include <asm/fpstate.h>
24 25
25#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS 26#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
26#define KVM_MEMORY_SLOTS 32 27#define KVM_MEMORY_SLOTS 32
@@ -85,6 +86,14 @@ struct kvm_vcpu_arch {
85 u32 hxfar; /* Hyp Data/Inst Fault Address Register */ 86 u32 hxfar; /* Hyp Data/Inst Fault Address Register */
86 u32 hpfar; /* Hyp IPA Fault Address Register */ 87 u32 hpfar; /* Hyp IPA Fault Address Register */
87 88
89 /* Floating point registers (VFP and Advanced SIMD/NEON) */
90 struct vfp_hard_struct vfp_guest;
91 struct vfp_hard_struct *vfp_host;
92
93 /*
94 * Anything that is not used directly from assembly code goes
95 * here.
96 */
88 /* Interrupt related fields */ 97 /* Interrupt related fields */
89 u32 irq_lines; /* IRQ and FIQ levels */ 98 u32 irq_lines; /* IRQ and FIQ levels */
90 99
@@ -93,6 +102,9 @@ struct kvm_vcpu_arch {
93 102
94 /* Cache some mmu pages needed inside spinlock regions */ 103 /* Cache some mmu pages needed inside spinlock regions */
95 struct kvm_mmu_memory_cache mmu_page_cache; 104 struct kvm_mmu_memory_cache mmu_page_cache;
105
106 /* Detect first run of a vcpu */
107 bool has_run_once;
96}; 108};
97 109
98struct kvm_vm_stat { 110struct kvm_vm_stat {
@@ -112,6 +124,7 @@ struct kvm_one_reg;
112int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); 124int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
113int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); 125int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
114u64 kvm_call_hyp(void *hypfn, ...); 126u64 kvm_call_hyp(void *hypfn, ...);
127void force_vm_exit(const cpumask_t *mask);
115 128
116#define KVM_ARCH_WANT_MMU_NOTIFIER 129#define KVM_ARCH_WANT_MMU_NOTIFIER
117struct kvm; 130struct kvm;
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index c985b481192c..c8b3272dfed1 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -13,6 +13,9 @@
13#include <linux/sched.h> 13#include <linux/sched.h>
14#include <linux/mm.h> 14#include <linux/mm.h>
15#include <linux/dma-mapping.h> 15#include <linux/dma-mapping.h>
16#ifdef CONFIG_KVM_ARM_HOST
17#include <linux/kvm_host.h>
18#endif
16#include <asm/cacheflush.h> 19#include <asm/cacheflush.h>
17#include <asm/glue-df.h> 20#include <asm/glue-df.h>
18#include <asm/glue-pf.h> 21#include <asm/glue-pf.h>
@@ -146,5 +149,27 @@ int main(void)
146 DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL); 149 DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
147 DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE); 150 DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
148 DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE); 151 DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
152#ifdef CONFIG_KVM_ARM_HOST
153 DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
154 DEFINE(VCPU_MIDR, offsetof(struct kvm_vcpu, arch.midr));
155 DEFINE(VCPU_CP15, offsetof(struct kvm_vcpu, arch.cp15));
156 DEFINE(VCPU_VFP_GUEST, offsetof(struct kvm_vcpu, arch.vfp_guest));
157 DEFINE(VCPU_VFP_HOST, offsetof(struct kvm_vcpu, arch.vfp_host));
158 DEFINE(VCPU_REGS, offsetof(struct kvm_vcpu, arch.regs));
159 DEFINE(VCPU_USR_REGS, offsetof(struct kvm_vcpu, arch.regs.usr_regs));
160 DEFINE(VCPU_SVC_REGS, offsetof(struct kvm_vcpu, arch.regs.svc_regs));
161 DEFINE(VCPU_ABT_REGS, offsetof(struct kvm_vcpu, arch.regs.abt_regs));
162 DEFINE(VCPU_UND_REGS, offsetof(struct kvm_vcpu, arch.regs.und_regs));
163 DEFINE(VCPU_IRQ_REGS, offsetof(struct kvm_vcpu, arch.regs.irq_regs));
164 DEFINE(VCPU_FIQ_REGS, offsetof(struct kvm_vcpu, arch.regs.fiq_regs));
165 DEFINE(VCPU_PC, offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_pc));
166 DEFINE(VCPU_CPSR, offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_cpsr));
167 DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
168 DEFINE(VCPU_HSR, offsetof(struct kvm_vcpu, arch.hsr));
169 DEFINE(VCPU_HxFAR, offsetof(struct kvm_vcpu, arch.hxfar));
170 DEFINE(VCPU_HPFAR, offsetof(struct kvm_vcpu, arch.hpfar));
171 DEFINE(VCPU_HYP_PC, offsetof(struct kvm_vcpu, arch.hyp_pc));
172 DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
173#endif
149 return 0; 174 return 0;
150} 175}
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 2101152c3a4b..9e9fa4477884 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -40,6 +40,7 @@
40#include <asm/kvm_arm.h> 40#include <asm/kvm_arm.h>