162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 - Google Inc
462306a36Sopenharmony_ci * Author: Andrew Scull <ascull@google.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/linkage.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/assembler.h>
1062306a36Sopenharmony_ci#include <asm/kvm_arm.h>
1162306a36Sopenharmony_ci#include <asm/kvm_asm.h>
1262306a36Sopenharmony_ci#include <asm/kvm_mmu.h>
1362306a36Sopenharmony_ci#include <asm/kvm_ptrauth.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	.text
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciSYM_FUNC_START(__host_exit)
1862306a36Sopenharmony_ci	get_host_ctxt	x0, x1
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/* Store the host regs x2 and x3 */
2162306a36Sopenharmony_ci	stp	x2, x3,   [x0, #CPU_XREG_OFFSET(2)]
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	/* Retrieve the host regs x0-x1 from the stack */
2462306a36Sopenharmony_ci	ldp	x2, x3, [sp], #16	// x0, x1
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	/* Store the host regs x0-x1 and x4-x17 */
2762306a36Sopenharmony_ci	stp	x2, x3,   [x0, #CPU_XREG_OFFSET(0)]
2862306a36Sopenharmony_ci	stp	x4, x5,   [x0, #CPU_XREG_OFFSET(4)]
2962306a36Sopenharmony_ci	stp	x6, x7,   [x0, #CPU_XREG_OFFSET(6)]
3062306a36Sopenharmony_ci	stp	x8, x9,   [x0, #CPU_XREG_OFFSET(8)]
3162306a36Sopenharmony_ci	stp	x10, x11, [x0, #CPU_XREG_OFFSET(10)]
3262306a36Sopenharmony_ci	stp	x12, x13, [x0, #CPU_XREG_OFFSET(12)]
3362306a36Sopenharmony_ci	stp	x14, x15, [x0, #CPU_XREG_OFFSET(14)]
3462306a36Sopenharmony_ci	stp	x16, x17, [x0, #CPU_XREG_OFFSET(16)]
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* Store the host regs x18-x29, lr */
3762306a36Sopenharmony_ci	save_callee_saved_regs x0
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/* Save the host context pointer in x29 across the function call */
4062306a36Sopenharmony_ci	mov	x29, x0
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
4362306a36Sopenharmony_cialternative_if_not ARM64_HAS_ADDRESS_AUTH
4462306a36Sopenharmony_cib __skip_pauth_save
4562306a36Sopenharmony_cialternative_else_nop_endif
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cialternative_if ARM64_KVM_PROTECTED_MODE
4862306a36Sopenharmony_ci	/* Save kernel ptrauth keys. */
4962306a36Sopenharmony_ci	add x18, x29, #CPU_APIAKEYLO_EL1
5062306a36Sopenharmony_ci	ptrauth_save_state x18, x19, x20
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Use hyp keys. */
5362306a36Sopenharmony_ci	adr_this_cpu x18, kvm_hyp_ctxt, x19
5462306a36Sopenharmony_ci	add x18, x18, #CPU_APIAKEYLO_EL1
5562306a36Sopenharmony_ci	ptrauth_restore_state x18, x19, x20
5662306a36Sopenharmony_ci	isb
5762306a36Sopenharmony_cialternative_else_nop_endif
5862306a36Sopenharmony_ci__skip_pauth_save:
5962306a36Sopenharmony_ci#endif /* CONFIG_ARM64_PTR_AUTH_KERNEL */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	bl	handle_trap
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci__host_enter_restore_full:
6462306a36Sopenharmony_ci	/* Restore kernel keys. */
6562306a36Sopenharmony_ci#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
6662306a36Sopenharmony_cialternative_if_not ARM64_HAS_ADDRESS_AUTH
6762306a36Sopenharmony_cib __skip_pauth_restore
6862306a36Sopenharmony_cialternative_else_nop_endif
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cialternative_if ARM64_KVM_PROTECTED_MODE
7162306a36Sopenharmony_ci	add x18, x29, #CPU_APIAKEYLO_EL1
7262306a36Sopenharmony_ci	ptrauth_restore_state x18, x19, x20
7362306a36Sopenharmony_cialternative_else_nop_endif
7462306a36Sopenharmony_ci__skip_pauth_restore:
7562306a36Sopenharmony_ci#endif /* CONFIG_ARM64_PTR_AUTH_KERNEL */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Restore host regs x0-x17 */
7862306a36Sopenharmony_ci	ldp	x0, x1,   [x29, #CPU_XREG_OFFSET(0)]
7962306a36Sopenharmony_ci	ldp	x2, x3,   [x29, #CPU_XREG_OFFSET(2)]
8062306a36Sopenharmony_ci	ldp	x4, x5,   [x29, #CPU_XREG_OFFSET(4)]
8162306a36Sopenharmony_ci	ldp	x6, x7,   [x29, #CPU_XREG_OFFSET(6)]
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* x0-7 are use for panic arguments */
8462306a36Sopenharmony_ci__host_enter_for_panic:
8562306a36Sopenharmony_ci	ldp	x8, x9,   [x29, #CPU_XREG_OFFSET(8)]
8662306a36Sopenharmony_ci	ldp	x10, x11, [x29, #CPU_XREG_OFFSET(10)]
8762306a36Sopenharmony_ci	ldp	x12, x13, [x29, #CPU_XREG_OFFSET(12)]
8862306a36Sopenharmony_ci	ldp	x14, x15, [x29, #CPU_XREG_OFFSET(14)]
8962306a36Sopenharmony_ci	ldp	x16, x17, [x29, #CPU_XREG_OFFSET(16)]
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* Restore host regs x18-x29, lr */
9262306a36Sopenharmony_ci	restore_callee_saved_regs x29
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Do not touch any register after this! */
9562306a36Sopenharmony_ci__host_enter_without_restoring:
9662306a36Sopenharmony_ci	eret
9762306a36Sopenharmony_ci	sb
9862306a36Sopenharmony_ciSYM_FUNC_END(__host_exit)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ciSYM_FUNC_START(__host_enter)
10462306a36Sopenharmony_ci	mov	x29, x0
10562306a36Sopenharmony_ci	b	__host_enter_restore_full
10662306a36Sopenharmony_ciSYM_FUNC_END(__host_enter)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * void __noreturn __hyp_do_panic(struct kvm_cpu_context *host_ctxt, u64 spsr,
11062306a36Sopenharmony_ci * 				  u64 elr, u64 par);
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ciSYM_FUNC_START(__hyp_do_panic)
11362306a36Sopenharmony_ci	/* Prepare and exit to the host's panic funciton. */
11462306a36Sopenharmony_ci	mov	lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
11562306a36Sopenharmony_ci		      PSR_MODE_EL1h)
11662306a36Sopenharmony_ci	msr	spsr_el2, lr
11762306a36Sopenharmony_ci	adr_l	lr, nvhe_hyp_panic_handler
11862306a36Sopenharmony_ci	hyp_kimg_va lr, x6
11962306a36Sopenharmony_ci	msr	elr_el2, lr
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	mov	x29, x0
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#ifdef CONFIG_NVHE_EL2_DEBUG
12462306a36Sopenharmony_ci	/* Ensure host stage-2 is disabled */
12562306a36Sopenharmony_ci	mrs	x0, hcr_el2
12662306a36Sopenharmony_ci	bic	x0, x0, #HCR_VM
12762306a36Sopenharmony_ci	msr	hcr_el2, x0
12862306a36Sopenharmony_ci	isb
12962306a36Sopenharmony_ci	tlbi	vmalls12e1
13062306a36Sopenharmony_ci	dsb	nsh
13162306a36Sopenharmony_ci#endif
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Load the panic arguments into x0-7 */
13462306a36Sopenharmony_ci	mrs	x0, esr_el2
13562306a36Sopenharmony_ci	mov	x4, x3
13662306a36Sopenharmony_ci	mov	x3, x2
13762306a36Sopenharmony_ci	hyp_pa	x3, x6
13862306a36Sopenharmony_ci	get_vcpu_ptr x5, x6
13962306a36Sopenharmony_ci	mrs	x6, far_el2
14062306a36Sopenharmony_ci	mrs	x7, hpfar_el2
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Enter the host, conditionally restoring the host context. */
14362306a36Sopenharmony_ci	cbz	x29, __host_enter_without_restoring
14462306a36Sopenharmony_ci	b	__host_enter_for_panic
14562306a36Sopenharmony_ciSYM_FUNC_END(__hyp_do_panic)
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciSYM_FUNC_START(__host_hvc)
14862306a36Sopenharmony_ci	ldp	x0, x1, [sp]		// Don't fixup the stack yet
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* No stub for you, sonny Jim */
15162306a36Sopenharmony_cialternative_if ARM64_KVM_PROTECTED_MODE
15262306a36Sopenharmony_ci	b	__host_exit
15362306a36Sopenharmony_cialternative_else_nop_endif
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Check for a stub HVC call */
15662306a36Sopenharmony_ci	cmp	x0, #HVC_STUB_HCALL_NR
15762306a36Sopenharmony_ci	b.hs	__host_exit
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	add	sp, sp, #16
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Compute the idmap address of __kvm_handle_stub_hvc and
16262306a36Sopenharmony_ci	 * jump there.
16362306a36Sopenharmony_ci	 *
16462306a36Sopenharmony_ci	 * Preserve x0-x4, which may contain stub parameters.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	adr_l	x5, __kvm_handle_stub_hvc
16762306a36Sopenharmony_ci	hyp_pa	x5, x6
16862306a36Sopenharmony_ci	br	x5
16962306a36Sopenharmony_ciSYM_FUNC_END(__host_hvc)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci.macro host_el1_sync_vect
17262306a36Sopenharmony_ci	.align 7
17362306a36Sopenharmony_ci.L__vect_start\@:
17462306a36Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
17562306a36Sopenharmony_ci	mrs	x0, esr_el2
17662306a36Sopenharmony_ci	ubfx	x0, x0, #ESR_ELx_EC_SHIFT, #ESR_ELx_EC_WIDTH
17762306a36Sopenharmony_ci	cmp	x0, #ESR_ELx_EC_HVC64
17862306a36Sopenharmony_ci	b.eq	__host_hvc
17962306a36Sopenharmony_ci	b	__host_exit
18062306a36Sopenharmony_ci.L__vect_end\@:
18162306a36Sopenharmony_ci.if ((.L__vect_end\@ - .L__vect_start\@) > 0x80)
18262306a36Sopenharmony_ci	.error "host_el1_sync_vect larger than vector entry"
18362306a36Sopenharmony_ci.endif
18462306a36Sopenharmony_ci.endm
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci.macro invalid_host_el2_vect
18762306a36Sopenharmony_ci	.align 7
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/*
19062306a36Sopenharmony_ci	 * Test whether the SP has overflowed, without corrupting a GPR.
19162306a36Sopenharmony_ci	 * nVHE hypervisor stacks are aligned so that the PAGE_SHIFT bit
19262306a36Sopenharmony_ci	 * of SP should always be 1.
19362306a36Sopenharmony_ci	 */
19462306a36Sopenharmony_ci	add	sp, sp, x0			// sp' = sp + x0
19562306a36Sopenharmony_ci	sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp
19662306a36Sopenharmony_ci	tbz	x0, #PAGE_SHIFT, .L__hyp_sp_overflow\@
19762306a36Sopenharmony_ci	sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x0
19862306a36Sopenharmony_ci	sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* If a guest is loaded, panic out of it. */
20162306a36Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
20262306a36Sopenharmony_ci	get_loaded_vcpu x0, x1
20362306a36Sopenharmony_ci	cbnz	x0, __guest_exit_panic
20462306a36Sopenharmony_ci	add	sp, sp, #16
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * The panic may not be clean if the exception is taken before the host
20862306a36Sopenharmony_ci	 * context has been saved by __host_exit or after the hyp context has
20962306a36Sopenharmony_ci	 * been partially clobbered by __host_enter.
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	b	hyp_panic
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci.L__hyp_sp_overflow\@:
21462306a36Sopenharmony_ci	/* Switch to the overflow stack */
21562306a36Sopenharmony_ci	adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	b	hyp_panic_bad_stack
21862306a36Sopenharmony_ci	ASM_BUG()
21962306a36Sopenharmony_ci.endm
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci.macro invalid_host_el1_vect
22262306a36Sopenharmony_ci	.align 7
22362306a36Sopenharmony_ci	mov	x0, xzr		/* restore_host = false */
22462306a36Sopenharmony_ci	mrs	x1, spsr_el2
22562306a36Sopenharmony_ci	mrs	x2, elr_el2
22662306a36Sopenharmony_ci	mrs	x3, par_el1
22762306a36Sopenharmony_ci	b	__hyp_do_panic
22862306a36Sopenharmony_ci.endm
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * The host vector does not use an ESB instruction in order to avoid consuming
23262306a36Sopenharmony_ci * SErrors that should only be consumed by the host. Guest entry is deferred by
23362306a36Sopenharmony_ci * __guest_enter if there are any pending asynchronous exceptions so hyp will
23462306a36Sopenharmony_ci * always return to the host without having consumerd host SErrors.
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * CONFIG_KVM_INDIRECT_VECTORS is not applied to the host vectors because the
23762306a36Sopenharmony_ci * host knows about the EL2 vectors already, and there is no point in hiding
23862306a36Sopenharmony_ci * them.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_ci	.align 11
24162306a36Sopenharmony_ciSYM_CODE_START(__kvm_hyp_host_vector)
24262306a36Sopenharmony_ci	invalid_host_el2_vect			// Synchronous EL2t
24362306a36Sopenharmony_ci	invalid_host_el2_vect			// IRQ EL2t
24462306a36Sopenharmony_ci	invalid_host_el2_vect			// FIQ EL2t
24562306a36Sopenharmony_ci	invalid_host_el2_vect			// Error EL2t
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	invalid_host_el2_vect			// Synchronous EL2h
24862306a36Sopenharmony_ci	invalid_host_el2_vect			// IRQ EL2h
24962306a36Sopenharmony_ci	invalid_host_el2_vect			// FIQ EL2h
25062306a36Sopenharmony_ci	invalid_host_el2_vect			// Error EL2h
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	host_el1_sync_vect			// Synchronous 64-bit EL1/EL0
25362306a36Sopenharmony_ci	invalid_host_el1_vect			// IRQ 64-bit EL1/EL0
25462306a36Sopenharmony_ci	invalid_host_el1_vect			// FIQ 64-bit EL1/EL0
25562306a36Sopenharmony_ci	invalid_host_el1_vect			// Error 64-bit EL1/EL0
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	host_el1_sync_vect			// Synchronous 32-bit EL1/EL0
25862306a36Sopenharmony_ci	invalid_host_el1_vect			// IRQ 32-bit EL1/EL0
25962306a36Sopenharmony_ci	invalid_host_el1_vect			// FIQ 32-bit EL1/EL0
26062306a36Sopenharmony_ci	invalid_host_el1_vect			// Error 32-bit EL1/EL0
26162306a36Sopenharmony_ciSYM_CODE_END(__kvm_hyp_host_vector)
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/*
26462306a36Sopenharmony_ci * Forward SMC with arguments in struct kvm_cpu_context, and
26562306a36Sopenharmony_ci * store the result into the same struct. Assumes SMCCC 1.2 or older.
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * x0: struct kvm_cpu_context*
26862306a36Sopenharmony_ci */
26962306a36Sopenharmony_ciSYM_CODE_START(__kvm_hyp_host_forward_smc)
27062306a36Sopenharmony_ci	/*
27162306a36Sopenharmony_ci	 * Use x18 to keep the pointer to the host context because
27262306a36Sopenharmony_ci	 * x18 is callee-saved in SMCCC but not in AAPCS64.
27362306a36Sopenharmony_ci	 */
27462306a36Sopenharmony_ci	mov	x18, x0
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	ldp	x0, x1,   [x18, #CPU_XREG_OFFSET(0)]
27762306a36Sopenharmony_ci	ldp	x2, x3,   [x18, #CPU_XREG_OFFSET(2)]
27862306a36Sopenharmony_ci	ldp	x4, x5,   [x18, #CPU_XREG_OFFSET(4)]
27962306a36Sopenharmony_ci	ldp	x6, x7,   [x18, #CPU_XREG_OFFSET(6)]
28062306a36Sopenharmony_ci	ldp	x8, x9,   [x18, #CPU_XREG_OFFSET(8)]
28162306a36Sopenharmony_ci	ldp	x10, x11, [x18, #CPU_XREG_OFFSET(10)]
28262306a36Sopenharmony_ci	ldp	x12, x13, [x18, #CPU_XREG_OFFSET(12)]
28362306a36Sopenharmony_ci	ldp	x14, x15, [x18, #CPU_XREG_OFFSET(14)]
28462306a36Sopenharmony_ci	ldp	x16, x17, [x18, #CPU_XREG_OFFSET(16)]
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	smc	#0
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	stp	x0, x1,   [x18, #CPU_XREG_OFFSET(0)]
28962306a36Sopenharmony_ci	stp	x2, x3,   [x18, #CPU_XREG_OFFSET(2)]
29062306a36Sopenharmony_ci	stp	x4, x5,   [x18, #CPU_XREG_OFFSET(4)]
29162306a36Sopenharmony_ci	stp	x6, x7,   [x18, #CPU_XREG_OFFSET(6)]
29262306a36Sopenharmony_ci	stp	x8, x9,   [x18, #CPU_XREG_OFFSET(8)]
29362306a36Sopenharmony_ci	stp	x10, x11, [x18, #CPU_XREG_OFFSET(10)]
29462306a36Sopenharmony_ci	stp	x12, x13, [x18, #CPU_XREG_OFFSET(12)]
29562306a36Sopenharmony_ci	stp	x14, x15, [x18, #CPU_XREG_OFFSET(14)]
29662306a36Sopenharmony_ci	stp	x16, x17, [x18, #CPU_XREG_OFFSET(16)]
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ret
29962306a36Sopenharmony_ciSYM_CODE_END(__kvm_hyp_host_forward_smc)
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/*
30262306a36Sopenharmony_ci * kvm_host_psci_cpu_entry is called through br instruction, which requires
30362306a36Sopenharmony_ci * bti j instruction as compilers (gcc and llvm) doesn't insert bti j for external
30462306a36Sopenharmony_ci * functions, but bti c instead.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_ciSYM_CODE_START(kvm_host_psci_cpu_entry)
30762306a36Sopenharmony_ci       bti j
30862306a36Sopenharmony_ci       b __kvm_host_psci_cpu_entry
30962306a36Sopenharmony_ciSYM_CODE_END(kvm_host_psci_cpu_entry)
310