Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Provide an 'upgrade to VHE' stub hypercall

As we are about to change the way a VHE system boots, let's
provide the core helper, in the form of a stub hypercall that
enables VHE and replicates the full EL1 context at EL2, thanks
to EL1 and VHE-EL2 being extremely similar.

On exception return, the kernel carries on at EL2. Fancy!

Nothing calls this new hypercall yet, so no functional change.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Acked-by: David Brazdil <dbrazdil@google.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20210208095732.3267263-5-maz@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Marc Zyngier and committed by
Will Deacon
f3591822 8cc8a324

+80 -3
+6 -1
arch/arm64/include/asm/virt.h
··· 35 35 */ 36 36 #define HVC_RESET_VECTORS 2 37 37 38 + /* 39 + * HVC_VHE_RESTART - Upgrade the CPU from EL1 to EL2, if possible 40 + */ 41 + #define HVC_VHE_RESTART 3 42 + 38 43 /* Max number of HYP stub hypercalls */ 39 - #define HVC_STUB_HCALL_NR 3 44 + #define HVC_STUB_HCALL_NR 4 40 45 41 46 /* Error returned when an invalid stub number is passed into x0 */ 42 47 #define HVC_STUB_ERR 0xbadca11
+74 -2
arch/arm64/kernel/hyp-stub.S
··· 8 8 9 9 #include <linux/init.h> 10 10 #include <linux/linkage.h> 11 - #include <linux/irqchip/arm-gic-v3.h> 12 11 13 12 #include <asm/assembler.h> 13 + #include <asm/el2_setup.h> 14 14 #include <asm/kvm_arm.h> 15 15 #include <asm/kvm_asm.h> 16 16 #include <asm/ptrace.h> ··· 47 47 48 48 SYM_CODE_START_LOCAL(el1_sync) 49 49 cmp x0, #HVC_SET_VECTORS 50 - b.ne 2f 50 + b.ne 1f 51 51 msr vbar_el2, x1 52 52 b 9f 53 + 54 + 1: cmp x0, #HVC_VHE_RESTART 55 + b.eq mutate_to_vhe 53 56 54 57 2: cmp x0, #HVC_SOFT_RESTART 55 58 b.ne 3f ··· 72 69 9: mov x0, xzr 73 70 eret 74 71 SYM_CODE_END(el1_sync) 72 + 73 + // nVHE? No way! Give me the real thing! 74 + SYM_CODE_START_LOCAL(mutate_to_vhe) 75 + // Be prepared to fail 76 + mov_q x0, HVC_STUB_ERR 77 + 78 + // Sanity check: MMU *must* be off 79 + mrs x1, sctlr_el2 80 + tbnz x1, #0, 1f 81 + 82 + // Needs to be VHE capable, obviously 83 + mrs x1, id_aa64mmfr1_el1 84 + ubfx x1, x1, #ID_AA64MMFR1_VHE_SHIFT, #4 85 + cbz x1, 1f 86 + 87 + // Engage the VHE magic! 88 + mov_q x0, HCR_HOST_VHE_FLAGS 89 + msr hcr_el2, x0 90 + isb 91 + 92 + // Doesn't do much on VHE, but still, worth a shot 93 + init_el2_state vhe 94 + 95 + // Use the EL1 allocated stack, per-cpu offset 96 + mrs x0, sp_el1 97 + mov sp, x0 98 + mrs x0, tpidr_el1 99 + msr tpidr_el2, x0 100 + 101 + // FP configuration, vectors 102 + mrs_s x0, SYS_CPACR_EL12 103 + msr cpacr_el1, x0 104 + mrs_s x0, SYS_VBAR_EL12 105 + msr vbar_el1, x0 106 + 107 + // Transfer the MM state from EL1 to EL2 108 + mrs_s x0, SYS_TCR_EL12 109 + msr tcr_el1, x0 110 + mrs_s x0, SYS_TTBR0_EL12 111 + msr ttbr0_el1, x0 112 + mrs_s x0, SYS_TTBR1_EL12 113 + msr ttbr1_el1, x0 114 + mrs_s x0, SYS_MAIR_EL12 115 + msr mair_el1, x0 116 + isb 117 + 118 + // Invalidate TLBs before enabling the MMU 119 + tlbi vmalle1 120 + dsb nsh 121 + 122 + // Enable the EL2 S1 MMU, as set up from EL1 123 + mrs_s x0, SYS_SCTLR_EL12 124 + set_sctlr_el1 x0 125 + 126 + // Disable the EL1 S1 MMU for a good measure 127 + mov_q x0, INIT_SCTLR_EL1_MMU_OFF 128 + msr_s SYS_SCTLR_EL12, x0 129 + 130 + // Hack the exception return to stay at EL2 131 + mrs x0, spsr_el1 132 + and x0, x0, #~PSR_MODE_MASK 133 + mov x1, #PSR_MODE_EL2h 134 + orr x0, x0, x1 135 + msr spsr_el1, x0 136 + 137 + mov x0, xzr 138 + 139 + 1: eret 140 + SYM_CODE_END(mutate_to_vhe) 75 141 76 142 .macro invalid_vector label 77 143 SYM_CODE_START_LOCAL(\label)