Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2012 - Virtual Open Systems and Columbia University * Author: Christoffer Dall <c.dall@virtualopensystems.com> */ #include <linux/arm-smccc.h> #include <linux/linkage.h> #include <asm/kvm_arm.h> #include <asm/kvm_asm.h> .arch_extension virt .text .pushsection .hyp.text, "ax" .macro load_vcpu reg mrc p15, 4, \reg, c13, c0, 2 @ HTPIDR .endm /******************************************************************** * Hypervisor exception vector and handlers * * * The KVM/ARM Hypervisor ABI is defined as follows: * * Entry to Hyp mode from the host kernel will happen _only_ when an HVC * instruction is issued since all traps are disabled when running the host * kernel as per the Hyp-mode initialization at boot time. * * HVC instructions cause a trap to the vector page + offset 0x14 (see hyp_hvc * below) when the HVC instruction is called from SVC mode (i.e. a guest or the * host kernel) and they cause a trap to the vector page + offset 0x8 when HVC * instructions are called from within Hyp-mode. * * 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. * - 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. * * Note that the above is used to execute code in Hyp-mode from a host-kernel * point of view, and is a different concept from performing a world-switch and * executing guest code SVC mode (with a VMID != 0). */ .align 5 __kvm_hyp_vector: .global __kvm_hyp_vector @ Hyp-mode exception vector W(b) hyp_reset W(b) hyp_undef W(b) hyp_svc W(b) hyp_pabt W(b) hyp_dabt W(b) hyp_hvc W(b) hyp_irq W(b) hyp_fiq #ifdef CONFIG_HARDEN_BRANCH_PREDICTOR .align 5 __kvm_hyp_vector_ic_inv: .global __kvm_hyp_vector_ic_inv /* * We encode the exception entry in the bottom 3 bits of * SP, and we have to guarantee to be 8 bytes aligned. */ W(add) sp, sp, #1 /* Reset 7 */ W(add) sp, sp, #1 /* Undef 6 */ W(add) sp, sp, #1 /* Syscall 5 */ W(add) sp, sp, #1 /* Prefetch abort 4 */ W(add) sp, sp, #1 /* Data abort 3 */ W(add) sp, sp, #1 /* HVC 2 */ W(add) sp, sp, #1 /* IRQ 1 */ W(nop) /* FIQ 0 */ mcr p15, 0, r0, c7, c5, 0 /* ICIALLU */ isb b decode_vectors .align 5 __kvm_hyp_vector_bp_inv: .global __kvm_hyp_vector_bp_inv /* * We encode the exception entry in the bottom 3 bits of * SP, and we have to guarantee to be 8 bytes aligned. */ W(add) sp, sp, #1 /* Reset 7 */ W(add) sp, sp, #1 /* Undef 6 */ W(add) sp, sp, #1 /* Syscall 5 */ W(add) sp, sp, #1 /* Prefetch abort 4 */ W(add) sp, sp, #1 /* Data abort 3 */ W(add) sp, sp, #1 /* HVC 2 */ W(add) sp, sp, #1 /* IRQ 1 */ W(nop) /* FIQ 0 */ mcr p15, 0, r0, c7, c5, 6 /* BPIALL */ isb decode_vectors: #ifdef CONFIG_THUMB2_KERNEL /* * Yet another silly hack: Use VPIDR as a temp register. * Thumb2 is really a pain, as SP cannot be used with most * of the bitwise instructions. The vect_br macro ensures * things gets cleaned-up. */ mcr p15, 4, r0, c0, c0, 0 /* VPIDR */ mov r0, sp and r0, r0, #7 sub sp, sp, r0 push {r1, r2} mov r1, r0 mrc p15, 4, r0, c0, c0, 0 /* VPIDR */ mrc p15, 0, r2, c0, c0, 0 /* MIDR */ mcr p15, 4, r2, c0, c0, 0 /* VPIDR */ #endif .macro vect_br val, targ ARM( eor sp, sp, #\val ) ARM( tst sp, #7 ) ARM( eorne sp, sp, #\val ) THUMB( cmp r1, #\val ) THUMB( popeq {r1, r2} ) beq \targ .endm vect_br 0, hyp_fiq vect_br 1, hyp_irq vect_br 2, hyp_hvc vect_br 3, hyp_dabt vect_br 4, hyp_pabt vect_br 5, hyp_svc vect_br 6, hyp_undef vect_br 7, hyp_reset #endif .macro invalid_vector label, cause .align \label: mov r0, #\cause b __hyp_panic .endm invalid_vector hyp_reset ARM_EXCEPTION_RESET invalid_vector hyp_undef ARM_EXCEPTION_UNDEFINED invalid_vector hyp_svc ARM_EXCEPTION_SOFTWARE invalid_vector hyp_pabt ARM_EXCEPTION_PREF_ABORT invalid_vector hyp_fiq ARM_EXCEPTION_FIQ ENTRY(__hyp_do_panic) mrs lr, cpsr bic lr, lr, #MODE_MASK orr lr, lr, #SVC_MODE THUMB( orr lr, lr, #PSR_T_BIT ) msr spsr_cxsf, lr ldr lr, =panic msr ELR_hyp, lr ldr lr, =__kvm_call_hyp clrex eret ENDPROC(__hyp_do_panic) hyp_hvc: /* * Getting here is either because of a trap from a guest, * or from executing HVC from the host kernel, which means * "do something in Hyp mode". */ push {r0, r1, r2} @ Check syndrome register mrc p15, 4, r1, c5, c2, 0 @ HSR lsr r0, r1, #HSR_EC_SHIFT cmp r0, #HSR_EC_HVC bne guest_trap @ Not HVC instr. /* * Let's check if the HVC came from VMID 0 and allow simple * switch to Hyp mode */ mrrc p15, 6, r0, r2, c2 lsr r2, r2, #16 and r2, r2, #0xff cmp r2, #0 bne guest_hvc_trap @ Guest called HVC /* * Getting here means host called HVC, we shift parameters and branch * to Hyp function. */ pop {r0, r1, r2} /* * Check if we have a kernel function, which is guaranteed to be * bigger than the maximum hyp stub hypercall */ cmp r0, #HVC_STUB_HCALL_NR bhs 1f /* * Not a kernel function, treat it as a stub hypercall. * Compute the physical address for __kvm_handle_stub_hvc * (as the code lives in the idmaped page) and branch there. * We hijack ip (r12) as a tmp register. */ push {r1} ldr r1, =kimage_voffset ldr r1, [r1] ldr ip, =__kvm_handle_stub_hvc sub ip, ip, r1 pop {r1} bx ip 1: /* * Pushing r2 here is just a way of keeping the stack aligned to * 8 bytes on any path that can trigger a HYP exception. Here, * we may well be about to jump into the guest, and the guest * exit would otherwise be badly decoded by our fancy * "decode-exception-without-a-branch" code... */ push {r2, lr} mov lr, r0 mov r0, r1 mov r1, r2 mov r2, r3 THUMB( orr lr, #1) blx lr @ Call the HYP function pop {r2, lr} eret guest_hvc_trap: movw r2, #:lower16:ARM_SMCCC_ARCH_WORKAROUND_1 movt r2, #:upper16:ARM_SMCCC_ARCH_WORKAROUND_1 ldr r0, [sp] @ Guest's r0 teq r0, r2 bne guest_trap add sp, sp, #12 @ Returns: @ r0 = 0 @ r1 = HSR value (perfectly predictable) @ r2 = ARM_SMCCC_ARCH_WORKAROUND_1 mov r0, #0 eret guest_trap: load_vcpu r0 @ Load VCPU pointer to r0 #ifdef CONFIG_VFPv3 @ Check for a VFP access lsr r1, r1, #HSR_EC_SHIFT cmp r1, #HSR_EC_CP_0_13 beq __vfp_guest_restore #endif mov r1, #ARM_EXCEPTION_HVC b __guest_exit hyp_irq: push {r0, r1, r2} mov r1, #ARM_EXCEPTION_IRQ load_vcpu r0 @ Load VCPU pointer to r0 b __guest_exit hyp_dabt: push {r0, r1} mrs r0, ELR_hyp ldr r1, =abort_guest_exit_start THUMB( add r1, r1, #1) cmp r0, r1 ldrne r1, =abort_guest_exit_end THUMB( addne r1, r1, #1) cmpne r0, r1 pop {r0, r1} bne __hyp_panic orr r0, r0, #(1 << ARM_EXIT_WITH_ABORT_BIT) eret .ltorg .popsection |