e3d8238d7f
- CPU ops and PSCI (Power State Coordination Interface) refactoring following the merging of the arm64 ACPI support, together with handling of Trusted (secure) OS instances - Using fixmap for permanent FDT mapping, removing the initial dtb placement requirements (within 512MB from the start of the kernel image). This required moving the FDT self reservation out of the memreserve processing - Idmap (1:1 mapping used for MMU on/off) handling clean-up - Removing flush_cache_all() - not safe on ARM unless the MMU is off. Last stages of CPU power down/up are handled by firmware already - "Alternatives" (run-time code patching) refactoring and support for immediate branch patching, GICv3 CPU interface access - User faults handling clean-up And some fixes: - Fix for VDSO building with broken ELF toolchains - Fixing another case of init_mm.pgd usage for user mappings (during ASID roll-over broadcasting) - Fix for FPSIMD reloading after CPU hotplug - Fix for missing syscall trace exit - Workaround for .inst asm bug - Compat fix for switching the user tls tpidr_el0 register -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVitZgAAoJEGvWsS0AyF7x+ToP/0Yci5bNsYVwVay8N1rK6WHh SGzDMzyxcSBjQpz2IhhTJ28eTAEH+a+HWQms+0tBehjqxqkvjuzBN0okDkc/z8NB 7Z0BV2aLkQcMwTbjgIh5jm25ZpGmvmvbWPD5oBwgmgQ4v4i1OLRKgx7+YQ+z9rWZ zC70d0UwyWjs2oxmjd2ZrAkps7v/qozEFhcRHxLzCn8Mbw+3FcTQsqMbfnoWGnH0 YuGfHQQqBY4/HC7uAslMCy7tXeJXqb+NsgrnAovjfEbHGDjsg0KNl06K++LHwE37 A5Noa3M0AQEPYqx/sg0Ec8RNUUEMB4RA2DCaibp7XlVGncXOwFfiyk/M5uVrYXIO ku5QF0ytUfZKzrMq3yQKbEDuCPOFTqjjdVpkeXKFdW66zYTohKVc3vUBV5xHZ5uO 7Kr8H0ZnhAv3OxPyKdEwAuQ5sJdWwQSvZyGClxMUO4eC/UzD0Mwwf1Y8WYtiAXx+ NSTeBKw/m33W3/KhNuNH1+qGEOKhuXuKX7AcYA84Rab8ytxYWcurHCG2bmhzpEse 2DZtNMybrP/HMQPyJlYgGac8B3QbsAIAkkU1f+dJTAv9otuBDhscaDQyZ9Y6WVht /k8zJiZeMEuGAmwgTkzLmWs/8pQq42nW4J4eQdXPZAwp4ghCIypPWfaZASAwee6/ p+es3v8P4k9wkv2TFZMh =YeGl -----END PGP SIGNATURE----- Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux Pull arm64 updates from Catalin Marinas: "Mostly refactoring/clean-up: - CPU ops and PSCI (Power State Coordination Interface) refactoring following the merging of the arm64 ACPI support, together with handling of Trusted (secure) OS instances - Using fixmap for permanent FDT mapping, removing the initial dtb placement requirements (within 512MB from the start of the kernel image). This required moving the FDT self reservation out of the memreserve processing - Idmap (1:1 mapping used for MMU on/off) handling clean-up - Removing flush_cache_all() - not safe on ARM unless the MMU is off. Last stages of CPU power down/up are handled by firmware already - "Alternatives" (run-time code patching) refactoring and support for immediate branch patching, GICv3 CPU interface access - User faults handling clean-up And some fixes: - Fix for VDSO building with broken ELF toolchains - Fix another case of init_mm.pgd usage for user mappings (during ASID roll-over broadcasting) - Fix for FPSIMD reloading after CPU hotplug - Fix for missing syscall trace exit - Workaround for .inst asm bug - Compat fix for switching the user tls tpidr_el0 register" * tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (42 commits) arm64: use private ratelimit state along with show_unhandled_signals arm64: show unhandled SP/PC alignment faults arm64: vdso: work-around broken ELF toolchains in Makefile arm64: kernel: rename __cpu_suspend to keep it aligned with arm arm64: compat: print compat_sp instead of sp arm64: mm: Fix freeing of the wrong memmap entries with !SPARSEMEM_VMEMMAP arm64: entry: fix context tracking for el0_sp_pc arm64: defconfig: enable memtest arm64: mm: remove reference to tlb.S from comment block arm64: Do not attempt to use init_mm in reset_context() arm64: KVM: Switch vgic save/restore to alternative_insn arm64: alternative: Introduce feature for GICv3 CPU interface arm64: psci: fix !CONFIG_HOTPLUG_CPU build warning arm64: fix bug for reloading FPSIMD state after CPU hotplug. arm64: kernel thread don't need to save fpsimd context. arm64: fix missing syscall trace exit arm64: alternative: Work around .inst assembler bugs arm64: alternative: Merge alternative-asm.h into alternative.h arm64: alternative: Allow immediate branch as alternative instruction arm64: Rework alternate sequence for ARM erratum 845719 ...
1289 lines
27 KiB
ArmAsm
1289 lines
27 KiB
ArmAsm
/*
|
|
* Copyright (C) 2012,2013 - ARM Ltd
|
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
#include <asm/alternative.h>
|
|
#include <asm/asm-offsets.h>
|
|
#include <asm/assembler.h>
|
|
#include <asm/cpufeature.h>
|
|
#include <asm/debug-monitors.h>
|
|
#include <asm/esr.h>
|
|
#include <asm/fpsimdmacros.h>
|
|
#include <asm/kvm.h>
|
|
#include <asm/kvm_arm.h>
|
|
#include <asm/kvm_asm.h>
|
|
#include <asm/kvm_mmu.h>
|
|
#include <asm/memory.h>
|
|
|
|
#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
|
|
#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
|
|
#define CPU_SPSR_OFFSET(x) CPU_GP_REG_OFFSET(CPU_SPSR + 8*x)
|
|
#define CPU_SYSREG_OFFSET(x) (CPU_SYSREGS + 8*x)
|
|
|
|
.text
|
|
.pushsection .hyp.text, "ax"
|
|
.align PAGE_SHIFT
|
|
|
|
.macro save_common_regs
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
add x3, x2, #CPU_XREG_OFFSET(19)
|
|
stp x19, x20, [x3]
|
|
stp x21, x22, [x3, #16]
|
|
stp x23, x24, [x3, #32]
|
|
stp x25, x26, [x3, #48]
|
|
stp x27, x28, [x3, #64]
|
|
stp x29, lr, [x3, #80]
|
|
|
|
mrs x19, sp_el0
|
|
mrs x20, elr_el2 // pc before entering el2
|
|
mrs x21, spsr_el2 // pstate before entering el2
|
|
|
|
stp x19, x20, [x3, #96]
|
|
str x21, [x3, #112]
|
|
|
|
mrs x22, sp_el1
|
|
mrs x23, elr_el1
|
|
mrs x24, spsr_el1
|
|
|
|
str x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
|
|
str x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
|
|
str x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
|
|
.endm
|
|
|
|
.macro restore_common_regs
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
|
|
ldr x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
|
|
ldr x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
|
|
|
|
msr sp_el1, x22
|
|
msr elr_el1, x23
|
|
msr spsr_el1, x24
|
|
|
|
add x3, x2, #CPU_XREG_OFFSET(31) // SP_EL0
|
|
ldp x19, x20, [x3]
|
|
ldr x21, [x3, #16]
|
|
|
|
msr sp_el0, x19
|
|
msr elr_el2, x20 // pc on return from el2
|
|
msr spsr_el2, x21 // pstate on return from el2
|
|
|
|
add x3, x2, #CPU_XREG_OFFSET(19)
|
|
ldp x19, x20, [x3]
|
|
ldp x21, x22, [x3, #16]
|
|
ldp x23, x24, [x3, #32]
|
|
ldp x25, x26, [x3, #48]
|
|
ldp x27, x28, [x3, #64]
|
|
ldp x29, lr, [x3, #80]
|
|
.endm
|
|
|
|
.macro save_host_regs
|
|
save_common_regs
|
|
.endm
|
|
|
|
.macro restore_host_regs
|
|
restore_common_regs
|
|
.endm
|
|
|
|
.macro save_fpsimd
|
|
// x2: cpu context address
|
|
// x3, x4: tmp regs
|
|
add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
|
|
fpsimd_save x3, 4
|
|
.endm
|
|
|
|
.macro restore_fpsimd
|
|
// x2: cpu context address
|
|
// x3, x4: tmp regs
|
|
add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
|
|
fpsimd_restore x3, 4
|
|
.endm
|
|
|
|
.macro save_guest_regs
|
|
// x0 is the vcpu address
|
|
// x1 is the return code, do not corrupt!
|
|
// x2 is the cpu context
|
|
// x3 is a tmp register
|
|
// Guest's x0-x3 are on the stack
|
|
|
|
// Compute base to save registers
|
|
add x3, x2, #CPU_XREG_OFFSET(4)
|
|
stp x4, x5, [x3]
|
|
stp x6, x7, [x3, #16]
|
|
stp x8, x9, [x3, #32]
|
|
stp x10, x11, [x3, #48]
|
|
stp x12, x13, [x3, #64]
|
|
stp x14, x15, [x3, #80]
|
|
stp x16, x17, [x3, #96]
|
|
str x18, [x3, #112]
|
|
|
|
pop x6, x7 // x2, x3
|
|
pop x4, x5 // x0, x1
|
|
|
|
add x3, x2, #CPU_XREG_OFFSET(0)
|
|
stp x4, x5, [x3]
|
|
stp x6, x7, [x3, #16]
|
|
|
|
save_common_regs
|
|
.endm
|
|
|
|
.macro restore_guest_regs
|
|
// x0 is the vcpu address.
|
|
// x2 is the cpu context
|
|
// x3 is a tmp register
|
|
|
|
// Prepare x0-x3 for later restore
|
|
add x3, x2, #CPU_XREG_OFFSET(0)
|
|
ldp x4, x5, [x3]
|
|
ldp x6, x7, [x3, #16]
|
|
push x4, x5 // Push x0-x3 on the stack
|
|
push x6, x7
|
|
|
|
// x4-x18
|
|
ldp x4, x5, [x3, #32]
|
|
ldp x6, x7, [x3, #48]
|
|
ldp x8, x9, [x3, #64]
|
|
ldp x10, x11, [x3, #80]
|
|
ldp x12, x13, [x3, #96]
|
|
ldp x14, x15, [x3, #112]
|
|
ldp x16, x17, [x3, #128]
|
|
ldr x18, [x3, #144]
|
|
|
|
// x19-x29, lr, sp*, elr*, spsr*
|
|
restore_common_regs
|
|
|
|
// Last bits of the 64bit state
|
|
pop x2, x3
|
|
pop x0, x1
|
|
|
|
// Do not touch any register after this!
|
|
.endm
|
|
|
|
/*
|
|
* Macros to perform system register save/restore.
|
|
*
|
|
* Ordering here is absolutely critical, and must be kept consistent
|
|
* in {save,restore}_sysregs, {save,restore}_guest_32bit_state,
|
|
* and in kvm_asm.h.
|
|
*
|
|
* In other words, don't touch any of these unless you know what
|
|
* you are doing.
|
|
*/
|
|
.macro save_sysregs
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
|
|
|
|
mrs x4, vmpidr_el2
|
|
mrs x5, csselr_el1
|
|
mrs x6, sctlr_el1
|
|
mrs x7, actlr_el1
|
|
mrs x8, cpacr_el1
|
|
mrs x9, ttbr0_el1
|
|
mrs x10, ttbr1_el1
|
|
mrs x11, tcr_el1
|
|
mrs x12, esr_el1
|
|
mrs x13, afsr0_el1
|
|
mrs x14, afsr1_el1
|
|
mrs x15, far_el1
|
|
mrs x16, mair_el1
|
|
mrs x17, vbar_el1
|
|
mrs x18, contextidr_el1
|
|
mrs x19, tpidr_el0
|
|
mrs x20, tpidrro_el0
|
|
mrs x21, tpidr_el1
|
|
mrs x22, amair_el1
|
|
mrs x23, cntkctl_el1
|
|
mrs x24, par_el1
|
|
mrs x25, mdscr_el1
|
|
|
|
stp x4, x5, [x3]
|
|
stp x6, x7, [x3, #16]
|
|
stp x8, x9, [x3, #32]
|
|
stp x10, x11, [x3, #48]
|
|
stp x12, x13, [x3, #64]
|
|
stp x14, x15, [x3, #80]
|
|
stp x16, x17, [x3, #96]
|
|
stp x18, x19, [x3, #112]
|
|
stp x20, x21, [x3, #128]
|
|
stp x22, x23, [x3, #144]
|
|
stp x24, x25, [x3, #160]
|
|
.endm
|
|
|
|
.macro save_debug
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
mrs x26, id_aa64dfr0_el1
|
|
ubfx x24, x26, #12, #4 // Extract BRPs
|
|
ubfx x25, x26, #20, #4 // Extract WRPs
|
|
mov w26, #15
|
|
sub w24, w26, w24 // How many BPs to skip
|
|
sub w25, w26, w25 // How many WPs to skip
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
mrs x20, dbgbcr15_el1
|
|
mrs x19, dbgbcr14_el1
|
|
mrs x18, dbgbcr13_el1
|
|
mrs x17, dbgbcr12_el1
|
|
mrs x16, dbgbcr11_el1
|
|
mrs x15, dbgbcr10_el1
|
|
mrs x14, dbgbcr9_el1
|
|
mrs x13, dbgbcr8_el1
|
|
mrs x12, dbgbcr7_el1
|
|
mrs x11, dbgbcr6_el1
|
|
mrs x10, dbgbcr5_el1
|
|
mrs x9, dbgbcr4_el1
|
|
mrs x8, dbgbcr3_el1
|
|
mrs x7, dbgbcr2_el1
|
|
mrs x6, dbgbcr1_el1
|
|
mrs x5, dbgbcr0_el1
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
|
|
1:
|
|
str x20, [x3, #(15 * 8)]
|
|
str x19, [x3, #(14 * 8)]
|
|
str x18, [x3, #(13 * 8)]
|
|
str x17, [x3, #(12 * 8)]
|
|
str x16, [x3, #(11 * 8)]
|
|
str x15, [x3, #(10 * 8)]
|
|
str x14, [x3, #(9 * 8)]
|
|
str x13, [x3, #(8 * 8)]
|
|
str x12, [x3, #(7 * 8)]
|
|
str x11, [x3, #(6 * 8)]
|
|
str x10, [x3, #(5 * 8)]
|
|
str x9, [x3, #(4 * 8)]
|
|
str x8, [x3, #(3 * 8)]
|
|
str x7, [x3, #(2 * 8)]
|
|
str x6, [x3, #(1 * 8)]
|
|
str x5, [x3, #(0 * 8)]
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
mrs x20, dbgbvr15_el1
|
|
mrs x19, dbgbvr14_el1
|
|
mrs x18, dbgbvr13_el1
|
|
mrs x17, dbgbvr12_el1
|
|
mrs x16, dbgbvr11_el1
|
|
mrs x15, dbgbvr10_el1
|
|
mrs x14, dbgbvr9_el1
|
|
mrs x13, dbgbvr8_el1
|
|
mrs x12, dbgbvr7_el1
|
|
mrs x11, dbgbvr6_el1
|
|
mrs x10, dbgbvr5_el1
|
|
mrs x9, dbgbvr4_el1
|
|
mrs x8, dbgbvr3_el1
|
|
mrs x7, dbgbvr2_el1
|
|
mrs x6, dbgbvr1_el1
|
|
mrs x5, dbgbvr0_el1
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
|
|
1:
|
|
str x20, [x3, #(15 * 8)]
|
|
str x19, [x3, #(14 * 8)]
|
|
str x18, [x3, #(13 * 8)]
|
|
str x17, [x3, #(12 * 8)]
|
|
str x16, [x3, #(11 * 8)]
|
|
str x15, [x3, #(10 * 8)]
|
|
str x14, [x3, #(9 * 8)]
|
|
str x13, [x3, #(8 * 8)]
|
|
str x12, [x3, #(7 * 8)]
|
|
str x11, [x3, #(6 * 8)]
|
|
str x10, [x3, #(5 * 8)]
|
|
str x9, [x3, #(4 * 8)]
|
|
str x8, [x3, #(3 * 8)]
|
|
str x7, [x3, #(2 * 8)]
|
|
str x6, [x3, #(1 * 8)]
|
|
str x5, [x3, #(0 * 8)]
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
mrs x20, dbgwcr15_el1
|
|
mrs x19, dbgwcr14_el1
|
|
mrs x18, dbgwcr13_el1
|
|
mrs x17, dbgwcr12_el1
|
|
mrs x16, dbgwcr11_el1
|
|
mrs x15, dbgwcr10_el1
|
|
mrs x14, dbgwcr9_el1
|
|
mrs x13, dbgwcr8_el1
|
|
mrs x12, dbgwcr7_el1
|
|
mrs x11, dbgwcr6_el1
|
|
mrs x10, dbgwcr5_el1
|
|
mrs x9, dbgwcr4_el1
|
|
mrs x8, dbgwcr3_el1
|
|
mrs x7, dbgwcr2_el1
|
|
mrs x6, dbgwcr1_el1
|
|
mrs x5, dbgwcr0_el1
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
|
|
1:
|
|
str x20, [x3, #(15 * 8)]
|
|
str x19, [x3, #(14 * 8)]
|
|
str x18, [x3, #(13 * 8)]
|
|
str x17, [x3, #(12 * 8)]
|
|
str x16, [x3, #(11 * 8)]
|
|
str x15, [x3, #(10 * 8)]
|
|
str x14, [x3, #(9 * 8)]
|
|
str x13, [x3, #(8 * 8)]
|
|
str x12, [x3, #(7 * 8)]
|
|
str x11, [x3, #(6 * 8)]
|
|
str x10, [x3, #(5 * 8)]
|
|
str x9, [x3, #(4 * 8)]
|
|
str x8, [x3, #(3 * 8)]
|
|
str x7, [x3, #(2 * 8)]
|
|
str x6, [x3, #(1 * 8)]
|
|
str x5, [x3, #(0 * 8)]
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
mrs x20, dbgwvr15_el1
|
|
mrs x19, dbgwvr14_el1
|
|
mrs x18, dbgwvr13_el1
|
|
mrs x17, dbgwvr12_el1
|
|
mrs x16, dbgwvr11_el1
|
|
mrs x15, dbgwvr10_el1
|
|
mrs x14, dbgwvr9_el1
|
|
mrs x13, dbgwvr8_el1
|
|
mrs x12, dbgwvr7_el1
|
|
mrs x11, dbgwvr6_el1
|
|
mrs x10, dbgwvr5_el1
|
|
mrs x9, dbgwvr4_el1
|
|
mrs x8, dbgwvr3_el1
|
|
mrs x7, dbgwvr2_el1
|
|
mrs x6, dbgwvr1_el1
|
|
mrs x5, dbgwvr0_el1
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
|
|
1:
|
|
str x20, [x3, #(15 * 8)]
|
|
str x19, [x3, #(14 * 8)]
|
|
str x18, [x3, #(13 * 8)]
|
|
str x17, [x3, #(12 * 8)]
|
|
str x16, [x3, #(11 * 8)]
|
|
str x15, [x3, #(10 * 8)]
|
|
str x14, [x3, #(9 * 8)]
|
|
str x13, [x3, #(8 * 8)]
|
|
str x12, [x3, #(7 * 8)]
|
|
str x11, [x3, #(6 * 8)]
|
|
str x10, [x3, #(5 * 8)]
|
|
str x9, [x3, #(4 * 8)]
|
|
str x8, [x3, #(3 * 8)]
|
|
str x7, [x3, #(2 * 8)]
|
|
str x6, [x3, #(1 * 8)]
|
|
str x5, [x3, #(0 * 8)]
|
|
|
|
mrs x21, mdccint_el1
|
|
str x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
|
|
.endm
|
|
|
|
.macro restore_sysregs
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
|
|
|
|
ldp x4, x5, [x3]
|
|
ldp x6, x7, [x3, #16]
|
|
ldp x8, x9, [x3, #32]
|
|
ldp x10, x11, [x3, #48]
|
|
ldp x12, x13, [x3, #64]
|
|
ldp x14, x15, [x3, #80]
|
|
ldp x16, x17, [x3, #96]
|
|
ldp x18, x19, [x3, #112]
|
|
ldp x20, x21, [x3, #128]
|
|
ldp x22, x23, [x3, #144]
|
|
ldp x24, x25, [x3, #160]
|
|
|
|
msr vmpidr_el2, x4
|
|
msr csselr_el1, x5
|
|
msr sctlr_el1, x6
|
|
msr actlr_el1, x7
|
|
msr cpacr_el1, x8
|
|
msr ttbr0_el1, x9
|
|
msr ttbr1_el1, x10
|
|
msr tcr_el1, x11
|
|
msr esr_el1, x12
|
|
msr afsr0_el1, x13
|
|
msr afsr1_el1, x14
|
|
msr far_el1, x15
|
|
msr mair_el1, x16
|
|
msr vbar_el1, x17
|
|
msr contextidr_el1, x18
|
|
msr tpidr_el0, x19
|
|
msr tpidrro_el0, x20
|
|
msr tpidr_el1, x21
|
|
msr amair_el1, x22
|
|
msr cntkctl_el1, x23
|
|
msr par_el1, x24
|
|
msr mdscr_el1, x25
|
|
.endm
|
|
|
|
.macro restore_debug
|
|
// x2: base address for cpu context
|
|
// x3: tmp register
|
|
|
|
mrs x26, id_aa64dfr0_el1
|
|
ubfx x24, x26, #12, #4 // Extract BRPs
|
|
ubfx x25, x26, #20, #4 // Extract WRPs
|
|
mov w26, #15
|
|
sub w24, w26, w24 // How many BPs to skip
|
|
sub w25, w26, w25 // How many WPs to skip
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
ldr x20, [x3, #(15 * 8)]
|
|
ldr x19, [x3, #(14 * 8)]
|
|
ldr x18, [x3, #(13 * 8)]
|
|
ldr x17, [x3, #(12 * 8)]
|
|
ldr x16, [x3, #(11 * 8)]
|
|
ldr x15, [x3, #(10 * 8)]
|
|
ldr x14, [x3, #(9 * 8)]
|
|
ldr x13, [x3, #(8 * 8)]
|
|
ldr x12, [x3, #(7 * 8)]
|
|
ldr x11, [x3, #(6 * 8)]
|
|
ldr x10, [x3, #(5 * 8)]
|
|
ldr x9, [x3, #(4 * 8)]
|
|
ldr x8, [x3, #(3 * 8)]
|
|
ldr x7, [x3, #(2 * 8)]
|
|
ldr x6, [x3, #(1 * 8)]
|
|
ldr x5, [x3, #(0 * 8)]
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
msr dbgbcr15_el1, x20
|
|
msr dbgbcr14_el1, x19
|
|
msr dbgbcr13_el1, x18
|
|
msr dbgbcr12_el1, x17
|
|
msr dbgbcr11_el1, x16
|
|
msr dbgbcr10_el1, x15
|
|
msr dbgbcr9_el1, x14
|
|
msr dbgbcr8_el1, x13
|
|
msr dbgbcr7_el1, x12
|
|
msr dbgbcr6_el1, x11
|
|
msr dbgbcr5_el1, x10
|
|
msr dbgbcr4_el1, x9
|
|
msr dbgbcr3_el1, x8
|
|
msr dbgbcr2_el1, x7
|
|
msr dbgbcr1_el1, x6
|
|
msr dbgbcr0_el1, x5
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
ldr x20, [x3, #(15 * 8)]
|
|
ldr x19, [x3, #(14 * 8)]
|
|
ldr x18, [x3, #(13 * 8)]
|
|
ldr x17, [x3, #(12 * 8)]
|
|
ldr x16, [x3, #(11 * 8)]
|
|
ldr x15, [x3, #(10 * 8)]
|
|
ldr x14, [x3, #(9 * 8)]
|
|
ldr x13, [x3, #(8 * 8)]
|
|
ldr x12, [x3, #(7 * 8)]
|
|
ldr x11, [x3, #(6 * 8)]
|
|
ldr x10, [x3, #(5 * 8)]
|
|
ldr x9, [x3, #(4 * 8)]
|
|
ldr x8, [x3, #(3 * 8)]
|
|
ldr x7, [x3, #(2 * 8)]
|
|
ldr x6, [x3, #(1 * 8)]
|
|
ldr x5, [x3, #(0 * 8)]
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x24, lsl #2
|
|
br x26
|
|
1:
|
|
msr dbgbvr15_el1, x20
|
|
msr dbgbvr14_el1, x19
|
|
msr dbgbvr13_el1, x18
|
|
msr dbgbvr12_el1, x17
|
|
msr dbgbvr11_el1, x16
|
|
msr dbgbvr10_el1, x15
|
|
msr dbgbvr9_el1, x14
|
|
msr dbgbvr8_el1, x13
|
|
msr dbgbvr7_el1, x12
|
|
msr dbgbvr6_el1, x11
|
|
msr dbgbvr5_el1, x10
|
|
msr dbgbvr4_el1, x9
|
|
msr dbgbvr3_el1, x8
|
|
msr dbgbvr2_el1, x7
|
|
msr dbgbvr1_el1, x6
|
|
msr dbgbvr0_el1, x5
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
ldr x20, [x3, #(15 * 8)]
|
|
ldr x19, [x3, #(14 * 8)]
|
|
ldr x18, [x3, #(13 * 8)]
|
|
ldr x17, [x3, #(12 * 8)]
|
|
ldr x16, [x3, #(11 * 8)]
|
|
ldr x15, [x3, #(10 * 8)]
|
|
ldr x14, [x3, #(9 * 8)]
|
|
ldr x13, [x3, #(8 * 8)]
|
|
ldr x12, [x3, #(7 * 8)]
|
|
ldr x11, [x3, #(6 * 8)]
|
|
ldr x10, [x3, #(5 * 8)]
|
|
ldr x9, [x3, #(4 * 8)]
|
|
ldr x8, [x3, #(3 * 8)]
|
|
ldr x7, [x3, #(2 * 8)]
|
|
ldr x6, [x3, #(1 * 8)]
|
|
ldr x5, [x3, #(0 * 8)]
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
msr dbgwcr15_el1, x20
|
|
msr dbgwcr14_el1, x19
|
|
msr dbgwcr13_el1, x18
|
|
msr dbgwcr12_el1, x17
|
|
msr dbgwcr11_el1, x16
|
|
msr dbgwcr10_el1, x15
|
|
msr dbgwcr9_el1, x14
|
|
msr dbgwcr8_el1, x13
|
|
msr dbgwcr7_el1, x12
|
|
msr dbgwcr6_el1, x11
|
|
msr dbgwcr5_el1, x10
|
|
msr dbgwcr4_el1, x9
|
|
msr dbgwcr3_el1, x8
|
|
msr dbgwcr2_el1, x7
|
|
msr dbgwcr1_el1, x6
|
|
msr dbgwcr0_el1, x5
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
ldr x20, [x3, #(15 * 8)]
|
|
ldr x19, [x3, #(14 * 8)]
|
|
ldr x18, [x3, #(13 * 8)]
|
|
ldr x17, [x3, #(12 * 8)]
|
|
ldr x16, [x3, #(11 * 8)]
|
|
ldr x15, [x3, #(10 * 8)]
|
|
ldr x14, [x3, #(9 * 8)]
|
|
ldr x13, [x3, #(8 * 8)]
|
|
ldr x12, [x3, #(7 * 8)]
|
|
ldr x11, [x3, #(6 * 8)]
|
|
ldr x10, [x3, #(5 * 8)]
|
|
ldr x9, [x3, #(4 * 8)]
|
|
ldr x8, [x3, #(3 * 8)]
|
|
ldr x7, [x3, #(2 * 8)]
|
|
ldr x6, [x3, #(1 * 8)]
|
|
ldr x5, [x3, #(0 * 8)]
|
|
|
|
adr x26, 1f
|
|
add x26, x26, x25, lsl #2
|
|
br x26
|
|
1:
|
|
msr dbgwvr15_el1, x20
|
|
msr dbgwvr14_el1, x19
|
|
msr dbgwvr13_el1, x18
|
|
msr dbgwvr12_el1, x17
|
|
msr dbgwvr11_el1, x16
|
|
msr dbgwvr10_el1, x15
|
|
msr dbgwvr9_el1, x14
|
|
msr dbgwvr8_el1, x13
|
|
msr dbgwvr7_el1, x12
|
|
msr dbgwvr6_el1, x11
|
|
msr dbgwvr5_el1, x10
|
|
msr dbgwvr4_el1, x9
|
|
msr dbgwvr3_el1, x8
|
|
msr dbgwvr2_el1, x7
|
|
msr dbgwvr1_el1, x6
|
|
msr dbgwvr0_el1, x5
|
|
|
|
ldr x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
|
|
msr mdccint_el1, x21
|
|
.endm
|
|
|
|
.macro skip_32bit_state tmp, target
|
|
// Skip 32bit state if not needed
|
|
mrs \tmp, hcr_el2
|
|
tbnz \tmp, #HCR_RW_SHIFT, \target
|
|
.endm
|
|
|
|
.macro skip_tee_state tmp, target
|
|
// Skip ThumbEE state if not needed
|
|
mrs \tmp, id_pfr0_el1
|
|
tbz \tmp, #12, \target
|
|
.endm
|
|
|
|
.macro skip_debug_state tmp, target
|
|
ldr \tmp, [x0, #VCPU_DEBUG_FLAGS]
|
|
tbz \tmp, #KVM_ARM64_DEBUG_DIRTY_SHIFT, \target
|
|
.endm
|
|
|
|
.macro compute_debug_state target
|
|
// Compute debug state: If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY
|
|
// is set, we do a full save/restore cycle and disable trapping.
|
|
add x25, x0, #VCPU_CONTEXT
|
|
|
|
// Check the state of MDSCR_EL1
|
|
ldr x25, [x25, #CPU_SYSREG_OFFSET(MDSCR_EL1)]
|
|
and x26, x25, #DBG_MDSCR_KDE
|
|
and x25, x25, #DBG_MDSCR_MDE
|
|
adds xzr, x25, x26
|
|
b.eq 9998f // Nothing to see there
|
|
|
|
// If any interesting bits was set, we must set the flag
|
|
mov x26, #KVM_ARM64_DEBUG_DIRTY
|
|
str x26, [x0, #VCPU_DEBUG_FLAGS]
|
|
b 9999f // Don't skip restore
|
|
|
|
9998:
|
|
// Otherwise load the flags from memory in case we recently
|
|
// trapped
|
|
skip_debug_state x25, \target
|
|
9999:
|
|
.endm
|
|
|
|
.macro save_guest_32bit_state
|
|
skip_32bit_state x3, 1f
|
|
|
|
add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
|
|
mrs x4, spsr_abt
|
|
mrs x5, spsr_und
|
|
mrs x6, spsr_irq
|
|
mrs x7, spsr_fiq
|
|
stp x4, x5, [x3]
|
|
stp x6, x7, [x3, #16]
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
|
|
mrs x4, dacr32_el2
|
|
mrs x5, ifsr32_el2
|
|
mrs x6, fpexc32_el2
|
|
stp x4, x5, [x3]
|
|
str x6, [x3, #16]
|
|
|
|
skip_debug_state x8, 2f
|
|
mrs x7, dbgvcr32_el2
|
|
str x7, [x3, #24]
|
|
2:
|
|
skip_tee_state x8, 1f
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
|
mrs x4, teecr32_el1
|
|
mrs x5, teehbr32_el1
|
|
stp x4, x5, [x3]
|
|
1:
|
|
.endm
|
|
|
|
.macro restore_guest_32bit_state
|
|
skip_32bit_state x3, 1f
|
|
|
|
add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
|
|
ldp x4, x5, [x3]
|
|
ldp x6, x7, [x3, #16]
|
|
msr spsr_abt, x4
|
|
msr spsr_und, x5
|
|
msr spsr_irq, x6
|
|
msr spsr_fiq, x7
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
|
|
ldp x4, x5, [x3]
|
|
ldr x6, [x3, #16]
|
|
msr dacr32_el2, x4
|
|
msr ifsr32_el2, x5
|
|
msr fpexc32_el2, x6
|
|
|
|
skip_debug_state x8, 2f
|
|
ldr x7, [x3, #24]
|
|
msr dbgvcr32_el2, x7
|
|
2:
|
|
skip_tee_state x8, 1f
|
|
|
|
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
|
ldp x4, x5, [x3]
|
|
msr teecr32_el1, x4
|
|
msr teehbr32_el1, x5
|
|
1:
|
|
.endm
|
|
|
|
.macro activate_traps
|
|
ldr x2, [x0, #VCPU_HCR_EL2]
|
|
msr hcr_el2, x2
|
|
mov x2, #CPTR_EL2_TTA
|
|
msr cptr_el2, x2
|
|
|
|
mov x2, #(1 << 15) // Trap CP15 Cr=15
|
|
msr hstr_el2, x2
|
|
|
|
mrs x2, mdcr_el2
|
|
and x2, x2, #MDCR_EL2_HPMN_MASK
|
|
orr x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
|
|
orr x2, x2, #(MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
|
|
|
|
// Check for KVM_ARM64_DEBUG_DIRTY, and set debug to trap
|
|
// if not dirty.
|
|
ldr x3, [x0, #VCPU_DEBUG_FLAGS]
|
|
tbnz x3, #KVM_ARM64_DEBUG_DIRTY_SHIFT, 1f
|
|
orr x2, x2, #MDCR_EL2_TDA
|
|
1:
|
|
msr mdcr_el2, x2
|
|
.endm
|
|
|
|
.macro deactivate_traps
|
|
mov x2, #HCR_RW
|
|
msr hcr_el2, x2
|
|
msr cptr_el2, xzr
|
|
msr hstr_el2, xzr
|
|
|
|
mrs x2, mdcr_el2
|
|
and x2, x2, #MDCR_EL2_HPMN_MASK
|
|
msr mdcr_el2, x2
|
|
.endm
|
|
|
|
.macro activate_vm
|
|
ldr x1, [x0, #VCPU_KVM]
|
|
kern_hyp_va x1
|
|
ldr x2, [x1, #KVM_VTTBR]
|
|
msr vttbr_el2, x2
|
|
.endm
|
|
|
|
.macro deactivate_vm
|
|
msr vttbr_el2, xzr
|
|
.endm
|
|
|
|
/*
|
|
* Call into the vgic backend for state saving
|
|
*/
|
|
.macro save_vgic_state
|
|
alternative_insn "bl __save_vgic_v2_state", "bl __save_vgic_v3_state", ARM64_HAS_SYSREG_GIC_CPUIF
|
|
mrs x24, hcr_el2
|
|
mov x25, #HCR_INT_OVERRIDE
|
|
neg x25, x25
|
|
and x24, x24, x25
|
|
msr hcr_el2, x24
|
|
.endm
|
|
|
|
/*
|
|
* Call into the vgic backend for state restoring
|
|
*/
|
|
.macro restore_vgic_state
|
|
mrs x24, hcr_el2
|
|
ldr x25, [x0, #VCPU_IRQ_LINES]
|
|
orr x24, x24, #HCR_INT_OVERRIDE
|
|
orr x24, x24, x25
|
|
msr hcr_el2, x24
|
|
alternative_insn "bl __restore_vgic_v2_state", "bl __restore_vgic_v3_state", ARM64_HAS_SYSREG_GIC_CPUIF
|
|
.endm
|
|
|
|
.macro save_timer_state
|
|
// x0: vcpu pointer
|
|
ldr x2, [x0, #VCPU_KVM]
|
|
kern_hyp_va x2
|
|
ldr w3, [x2, #KVM_TIMER_ENABLED]
|
|
cbz w3, 1f
|
|
|
|
mrs x3, cntv_ctl_el0
|
|
and x3, x3, #3
|
|
str w3, [x0, #VCPU_TIMER_CNTV_CTL]
|
|
bic x3, x3, #1 // Clear Enable
|
|
msr cntv_ctl_el0, x3
|
|
|
|
isb
|
|
|
|
mrs x3, cntv_cval_el0
|
|
str x3, [x0, #VCPU_TIMER_CNTV_CVAL]
|
|
|
|
1:
|
|
// Allow physical timer/counter access for the host
|
|
mrs x2, cnthctl_el2
|
|
orr x2, x2, #3
|
|
msr cnthctl_el2, x2
|
|
|
|
// Clear cntvoff for the host
|
|
msr cntvoff_el2, xzr
|
|
.endm
|
|
|
|
.macro restore_timer_state
|
|
// x0: vcpu pointer
|
|
// Disallow physical timer access for the guest
|
|
// Physical counter access is allowed
|
|
mrs x2, cnthctl_el2
|
|
orr x2, x2, #1
|
|
bic x2, x2, #2
|
|
msr cnthctl_el2, x2
|
|
|
|
ldr x2, [x0, #VCPU_KVM]
|
|
kern_hyp_va x2
|
|
ldr w3, [x2, #KVM_TIMER_ENABLED]
|
|
cbz w3, 1f
|
|
|
|
ldr x3, [x2, #KVM_TIMER_CNTVOFF]
|
|
msr cntvoff_el2, x3
|
|
ldr x2, [x0, #VCPU_TIMER_CNTV_CVAL]
|
|
msr cntv_cval_el0, x2
|
|
isb
|
|
|
|
ldr w2, [x0, #VCPU_TIMER_CNTV_CTL]
|
|
and x2, x2, #3
|
|
msr cntv_ctl_el0, x2
|
|
1:
|
|
.endm
|
|
|
|
__save_sysregs:
|
|
save_sysregs
|
|
ret
|
|
|
|
__restore_sysregs:
|
|
restore_sysregs
|
|
ret
|
|
|
|
__save_debug:
|
|
save_debug
|
|
ret
|
|
|
|
__restore_debug:
|
|
restore_debug
|
|
ret
|
|
|
|
__save_fpsimd:
|
|
save_fpsimd
|
|
ret
|
|
|
|
__restore_fpsimd:
|
|
restore_fpsimd
|
|
ret
|
|
|
|
/*
|
|
* u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu);
|
|
*
|
|
* This is the world switch. The first half of the function
|
|
* deals with entering the guest, and anything from __kvm_vcpu_return
|
|
* to the end of the function deals with reentering the host.
|
|
* On the enter path, only x0 (vcpu pointer) must be preserved until
|
|
* the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception
|
|
* code) must both be preserved until the epilogue.
|
|
* In both cases, x2 points to the CPU context we're saving/restoring from/to.
|
|
*/
|
|
ENTRY(__kvm_vcpu_run)
|
|
kern_hyp_va x0
|
|
msr tpidr_el2, x0 // Save the vcpu register
|
|
|
|
// Host context
|
|
ldr x2, [x0, #VCPU_HOST_CONTEXT]
|
|
kern_hyp_va x2
|
|
|
|
save_host_regs
|
|
bl __save_fpsimd
|
|
bl __save_sysregs
|
|
|
|
compute_debug_state 1f
|
|
bl __save_debug
|
|
1:
|
|
activate_traps
|
|
activate_vm
|
|
|
|
restore_vgic_state
|
|
restore_timer_state
|
|
|
|
// Guest context
|
|
add x2, x0, #VCPU_CONTEXT
|
|
|
|
bl __restore_sysregs
|
|
bl __restore_fpsimd
|
|
|
|
skip_debug_state x3, 1f
|
|
bl __restore_debug
|
|
1:
|
|
restore_guest_32bit_state
|
|
restore_guest_regs
|
|
|
|
// That's it, no more messing around.
|
|
eret
|
|
|
|
__kvm_vcpu_return:
|
|
// Assume x0 is the vcpu pointer, x1 the return code
|
|
// Guest's x0-x3 are on the stack
|
|
|
|
// Guest context
|
|
add x2, x0, #VCPU_CONTEXT
|
|
|
|
save_guest_regs
|
|
bl __save_fpsimd
|
|
bl __save_sysregs
|
|
|
|
skip_debug_state x3, 1f
|
|
bl __save_debug
|
|
1:
|
|
save_guest_32bit_state
|
|
|
|
save_timer_state
|
|
save_vgic_state
|
|
|
|
deactivate_traps
|
|
deactivate_vm
|
|
|
|
// Host context
|
|
ldr x2, [x0, #VCPU_HOST_CONTEXT]
|
|
kern_hyp_va x2
|
|
|
|
bl __restore_sysregs
|
|
bl __restore_fpsimd
|
|
|
|
skip_debug_state x3, 1f
|
|
// Clear the dirty flag for the next run, as all the state has
|
|
// already been saved. Note that we nuke the whole 64bit word.
|
|
// If we ever add more flags, we'll have to be more careful...
|
|
str xzr, [x0, #VCPU_DEBUG_FLAGS]
|
|
bl __restore_debug
|
|
1:
|
|
restore_host_regs
|
|
|
|
mov x0, x1
|
|
ret
|
|
END(__kvm_vcpu_run)
|
|
|
|
// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
|
|
ENTRY(__kvm_tlb_flush_vmid_ipa)
|
|
dsb ishst
|
|
|
|
kern_hyp_va x0
|
|
ldr x2, [x0, #KVM_VTTBR]
|
|
msr vttbr_el2, x2
|
|
isb
|
|
|
|
/*
|
|
* We could do so much better if we had the VA as well.
|
|
* Instead, we invalidate Stage-2 for this IPA, and the
|
|
* whole of Stage-1. Weep...
|
|
*/
|
|
lsr x1, x1, #12
|
|
tlbi ipas2e1is, x1
|
|
/*
|
|
* We have to ensure completion of the invalidation at Stage-2,
|
|
* since a table walk on another CPU could refill a TLB with a
|
|
* complete (S1 + S2) walk based on the old Stage-2 mapping if
|
|
* the Stage-1 invalidation happened first.
|
|
*/
|
|
dsb ish
|
|
tlbi vmalle1is
|
|
dsb ish
|
|
isb
|
|
|
|
msr vttbr_el2, xzr
|
|
ret
|
|
ENDPROC(__kvm_tlb_flush_vmid_ipa)
|
|
|
|
/**
|
|
* void __kvm_tlb_flush_vmid(struct kvm *kvm) - Flush per-VMID TLBs
|
|
* @struct kvm *kvm - pointer to kvm structure
|
|
*
|
|
* Invalidates all Stage 1 and 2 TLB entries for current VMID.
|
|
*/
|
|
ENTRY(__kvm_tlb_flush_vmid)
|
|
dsb ishst
|
|
|
|
kern_hyp_va x0
|
|
ldr x2, [x0, #KVM_VTTBR]
|
|
msr vttbr_el2, x2
|
|
isb
|
|
|
|
tlbi vmalls12e1is
|
|
dsb ish
|
|
isb
|
|
|
|
msr vttbr_el2, xzr
|
|
ret
|
|
ENDPROC(__kvm_tlb_flush_vmid)
|
|
|
|
ENTRY(__kvm_flush_vm_context)
|
|
dsb ishst
|
|
tlbi alle1is
|
|
ic ialluis
|
|
dsb ish
|
|
ret
|
|
ENDPROC(__kvm_flush_vm_context)
|
|
|
|
__kvm_hyp_panic:
|
|
// Guess the context by looking at VTTBR:
|
|
// If zero, then we're already a host.
|
|
// Otherwise restore a minimal host context before panicing.
|
|
mrs x0, vttbr_el2
|
|
cbz x0, 1f
|
|
|
|
mrs x0, tpidr_el2
|
|
|
|
deactivate_traps
|
|
deactivate_vm
|
|
|
|
ldr x2, [x0, #VCPU_HOST_CONTEXT]
|
|
kern_hyp_va x2
|
|
|
|
bl __restore_sysregs
|
|
|
|
1: adr x0, __hyp_panic_str
|
|
adr x1, 2f
|
|
ldp x2, x3, [x1]
|
|
sub x0, x0, x2
|
|
add x0, x0, x3
|
|
mrs x1, spsr_el2
|
|
mrs x2, elr_el2
|
|
mrs x3, esr_el2
|
|
mrs x4, far_el2
|
|
mrs x5, hpfar_el2
|
|
mrs x6, par_el1
|
|
mrs x7, tpidr_el2
|
|
|
|
mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
|
|
PSR_MODE_EL1h)
|
|
msr spsr_el2, lr
|
|
ldr lr, =panic
|
|
msr elr_el2, lr
|
|
eret
|
|
|
|
.align 3
|
|
2: .quad HYP_PAGE_OFFSET
|
|
.quad PAGE_OFFSET
|
|
ENDPROC(__kvm_hyp_panic)
|
|
|
|
__hyp_panic_str:
|
|
.ascii "HYP panic:\nPS:%08x PC:%p ESR:%p\nFAR:%p HPFAR:%p PAR:%p\nVCPU:%p\n\0"
|
|
|
|
.align 2
|
|
|
|
/*
|
|
* u64 kvm_call_hyp(void *hypfn, ...);
|
|
*
|
|
* This is not really a variadic function in the classic C-way and care must
|
|
* be taken when calling this to ensure parameters are passed in registers
|
|
* only, since the stack will change between the caller and the callee.
|
|
*
|
|
* Call the function with the first argument containing a pointer to the
|
|
* function you wish to call in Hyp mode, and subsequent arguments will be
|
|
* passed as x0, x1, and x2 (a maximum of 3 arguments in addition to the
|
|
* function pointer can be passed). The function being called must be mapped
|
|
* in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are
|
|
* passed in r0 and r1.
|
|
*
|
|
* A function pointer with a value of 0 has a special meaning, and is
|
|
* used to implement __hyp_get_vectors in the same way as in
|
|
* arch/arm64/kernel/hyp_stub.S.
|
|
*/
|
|
ENTRY(kvm_call_hyp)
|
|
hvc #0
|
|
ret
|
|
ENDPROC(kvm_call_hyp)
|
|
|
|
.macro invalid_vector label, target
|
|
.align 2
|
|
\label:
|
|
b \target
|
|
ENDPROC(\label)
|
|
.endm
|
|
|
|
/* None of these should ever happen */
|
|
invalid_vector el2t_sync_invalid, __kvm_hyp_panic
|
|
invalid_vector el2t_irq_invalid, __kvm_hyp_panic
|
|
invalid_vector el2t_fiq_invalid, __kvm_hyp_panic
|
|
invalid_vector el2t_error_invalid, __kvm_hyp_panic
|
|
invalid_vector el2h_sync_invalid, __kvm_hyp_panic
|
|
invalid_vector el2h_irq_invalid, __kvm_hyp_panic
|
|
invalid_vector el2h_fiq_invalid, __kvm_hyp_panic
|
|
invalid_vector el2h_error_invalid, __kvm_hyp_panic
|
|
invalid_vector el1_sync_invalid, __kvm_hyp_panic
|
|
invalid_vector el1_irq_invalid, __kvm_hyp_panic
|
|
invalid_vector el1_fiq_invalid, __kvm_hyp_panic
|
|
invalid_vector el1_error_invalid, __kvm_hyp_panic
|
|
|
|
el1_sync: // Guest trapped into EL2
|
|
push x0, x1
|
|
push x2, x3
|
|
|
|
mrs x1, esr_el2
|
|
lsr x2, x1, #ESR_ELx_EC_SHIFT
|
|
|
|
cmp x2, #ESR_ELx_EC_HVC64
|
|
b.ne el1_trap
|
|
|
|
mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest
|
|
cbnz x3, el1_trap // called HVC
|
|
|
|
/* Here, we're pretty sure the host called HVC. */
|
|
pop x2, x3
|
|
pop x0, x1
|
|
|
|
/* Check for __hyp_get_vectors */
|
|
cbnz x0, 1f
|
|
mrs x0, vbar_el2
|
|
b 2f
|
|
|
|
1: push lr, xzr
|
|
|
|
/*
|
|
* Compute the function address in EL2, and shuffle the parameters.
|
|
*/
|
|
kern_hyp_va x0
|
|
mov lr, x0
|
|
mov x0, x1
|
|
mov x1, x2
|
|
mov x2, x3
|
|
blr lr
|
|
|
|
pop lr, xzr
|
|
2: eret
|
|
|
|
el1_trap:
|
|
/*
|
|
* x1: ESR
|
|
* x2: ESR_EC
|
|
*/
|
|
cmp x2, #ESR_ELx_EC_DABT_LOW
|
|
mov x0, #ESR_ELx_EC_IABT_LOW
|
|
ccmp x2, x0, #4, ne
|
|
b.ne 1f // Not an abort we care about
|
|
|
|
/* This is an abort. Check for permission fault */
|
|
and x2, x1, #ESR_ELx_FSC_TYPE
|
|
cmp x2, #FSC_PERM
|
|
b.ne 1f // Not a permission fault
|
|
|
|
/*
|
|
* Check for Stage-1 page table walk, which is guaranteed
|
|
* to give a valid HPFAR_EL2.
|
|
*/
|
|
tbnz x1, #7, 1f // S1PTW is set
|
|
|
|
/* Preserve PAR_EL1 */
|
|
mrs x3, par_el1
|
|
push x3, xzr
|
|
|
|
/*
|
|
* Permission fault, HPFAR_EL2 is invalid.
|
|
* Resolve the IPA the hard way using the guest VA.
|
|
* Stage-1 translation already validated the memory access rights.
|
|
* As such, we can use the EL1 translation regime, and don't have
|
|
* to distinguish between EL0 and EL1 access.
|
|
*/
|
|
mrs x2, far_el2
|
|
at s1e1r, x2
|
|
isb
|
|
|
|
/* Read result */
|
|
mrs x3, par_el1
|
|
pop x0, xzr // Restore PAR_EL1 from the stack
|
|
msr par_el1, x0
|
|
tbnz x3, #0, 3f // Bail out if we failed the translation
|
|
ubfx x3, x3, #12, #36 // Extract IPA
|
|
lsl x3, x3, #4 // and present it like HPFAR
|
|
b 2f
|
|
|
|
1: mrs x3, hpfar_el2
|
|
mrs x2, far_el2
|
|
|
|
2: mrs x0, tpidr_el2
|
|
str w1, [x0, #VCPU_ESR_EL2]
|
|
str x2, [x0, #VCPU_FAR_EL2]
|
|
str x3, [x0, #VCPU_HPFAR_EL2]
|
|
|
|
mov x1, #ARM_EXCEPTION_TRAP
|
|
b __kvm_vcpu_return
|
|
|
|
/*
|
|
* Translation failed. Just return to the guest and
|
|
* let it fault again. Another CPU is probably playing
|
|
* behind our back.
|
|
*/
|
|
3: pop x2, x3
|
|
pop x0, x1
|
|
|
|
eret
|
|
|
|
el1_irq:
|
|
push x0, x1
|
|
push x2, x3
|
|
mrs x0, tpidr_el2
|
|
mov x1, #ARM_EXCEPTION_IRQ
|
|
b __kvm_vcpu_return
|
|
|
|
.ltorg
|
|
|
|
.align 11
|
|
|
|
ENTRY(__kvm_hyp_vector)
|
|
ventry el2t_sync_invalid // Synchronous EL2t
|
|
ventry el2t_irq_invalid // IRQ EL2t
|
|
ventry el2t_fiq_invalid // FIQ EL2t
|
|
ventry el2t_error_invalid // Error EL2t
|
|
|
|
ventry el2h_sync_invalid // Synchronous EL2h
|
|
ventry el2h_irq_invalid // IRQ EL2h
|
|
ventry el2h_fiq_invalid // FIQ EL2h
|
|
ventry el2h_error_invalid // Error EL2h
|
|
|
|
ventry el1_sync // Synchronous 64-bit EL1
|
|
ventry el1_irq // IRQ 64-bit EL1
|
|
ventry el1_fiq_invalid // FIQ 64-bit EL1
|
|
ventry el1_error_invalid // Error 64-bit EL1
|
|
|
|
ventry el1_sync // Synchronous 32-bit EL1
|
|
ventry el1_irq // IRQ 32-bit EL1
|
|
ventry el1_fiq_invalid // FIQ 32-bit EL1
|
|
ventry el1_error_invalid // Error 32-bit EL1
|
|
ENDPROC(__kvm_hyp_vector)
|
|
|
|
.popsection
|