18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020 - Google Inc
48c2ecf20Sopenharmony_ci * Author: Andrew Scull <ascull@google.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/linkage.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/assembler.h>
108c2ecf20Sopenharmony_ci#include <asm/kvm_asm.h>
118c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci	.text
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciSYM_FUNC_START(__host_exit)
168c2ecf20Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	get_host_ctxt	x0, x1
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	/* Store the host regs x2 and x3 */
218c2ecf20Sopenharmony_ci	stp	x2, x3,   [x0, #CPU_XREG_OFFSET(2)]
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	/* Retrieve the host regs x0-x1 from the stack */
248c2ecf20Sopenharmony_ci	ldp	x2, x3, [sp], #16	// x0, x1
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* Store the host regs x0-x1 and x4-x17 */
278c2ecf20Sopenharmony_ci	stp	x2, x3,   [x0, #CPU_XREG_OFFSET(0)]
288c2ecf20Sopenharmony_ci	stp	x4, x5,   [x0, #CPU_XREG_OFFSET(4)]
298c2ecf20Sopenharmony_ci	stp	x6, x7,   [x0, #CPU_XREG_OFFSET(6)]
308c2ecf20Sopenharmony_ci	stp	x8, x9,   [x0, #CPU_XREG_OFFSET(8)]
318c2ecf20Sopenharmony_ci	stp	x10, x11, [x0, #CPU_XREG_OFFSET(10)]
328c2ecf20Sopenharmony_ci	stp	x12, x13, [x0, #CPU_XREG_OFFSET(12)]
338c2ecf20Sopenharmony_ci	stp	x14, x15, [x0, #CPU_XREG_OFFSET(14)]
348c2ecf20Sopenharmony_ci	stp	x16, x17, [x0, #CPU_XREG_OFFSET(16)]
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	/* Store the host regs x18-x29, lr */
378c2ecf20Sopenharmony_ci	save_callee_saved_regs x0
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* Save the host context pointer in x29 across the function call */
408c2ecf20Sopenharmony_ci	mov	x29, x0
418c2ecf20Sopenharmony_ci	bl	handle_trap
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Restore host regs x0-x17 */
448c2ecf20Sopenharmony_ci	ldp	x0, x1,   [x29, #CPU_XREG_OFFSET(0)]
458c2ecf20Sopenharmony_ci	ldp	x2, x3,   [x29, #CPU_XREG_OFFSET(2)]
468c2ecf20Sopenharmony_ci	ldp	x4, x5,   [x29, #CPU_XREG_OFFSET(4)]
478c2ecf20Sopenharmony_ci	ldp	x6, x7,   [x29, #CPU_XREG_OFFSET(6)]
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* x0-7 are use for panic arguments */
508c2ecf20Sopenharmony_ci__host_enter_for_panic:
518c2ecf20Sopenharmony_ci	ldp	x8, x9,   [x29, #CPU_XREG_OFFSET(8)]
528c2ecf20Sopenharmony_ci	ldp	x10, x11, [x29, #CPU_XREG_OFFSET(10)]
538c2ecf20Sopenharmony_ci	ldp	x12, x13, [x29, #CPU_XREG_OFFSET(12)]
548c2ecf20Sopenharmony_ci	ldp	x14, x15, [x29, #CPU_XREG_OFFSET(14)]
558c2ecf20Sopenharmony_ci	ldp	x16, x17, [x29, #CPU_XREG_OFFSET(16)]
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Restore host regs x18-x29, lr */
588c2ecf20Sopenharmony_ci	restore_callee_saved_regs x29
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* Do not touch any register after this! */
618c2ecf20Sopenharmony_ci__host_enter_without_restoring:
628c2ecf20Sopenharmony_ci	eret
638c2ecf20Sopenharmony_ci	sb
648c2ecf20Sopenharmony_ciSYM_FUNC_END(__host_exit)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * void __noreturn __hyp_do_panic(struct kvm_cpu_context *host_ctxt, u64 spsr,
688c2ecf20Sopenharmony_ci * 				  u64 elr, u64 par);
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ciSYM_FUNC_START(__hyp_do_panic)
718c2ecf20Sopenharmony_ci	mov	x29, x0
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Load the format string into x0 and arguments into x1-7 */
748c2ecf20Sopenharmony_ci	ldr	x0, =__hyp_panic_string
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	mov	x6, x3
778c2ecf20Sopenharmony_ci	get_vcpu_ptr x7, x3
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	mrs	x3, esr_el2
808c2ecf20Sopenharmony_ci	mrs	x4, far_el2
818c2ecf20Sopenharmony_ci	mrs	x5, hpfar_el2
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Prepare and exit to the host's panic funciton. */
848c2ecf20Sopenharmony_ci	mov	lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
858c2ecf20Sopenharmony_ci		      PSR_MODE_EL1h)
868c2ecf20Sopenharmony_ci	msr	spsr_el2, lr
878c2ecf20Sopenharmony_ci	ldr	lr, =panic
888c2ecf20Sopenharmony_ci	msr	elr_el2, lr
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Enter the host, conditionally restoring the host context. */
918c2ecf20Sopenharmony_ci	cbz	x29, __host_enter_without_restoring
928c2ecf20Sopenharmony_ci	b	__host_enter_for_panic
938c2ecf20Sopenharmony_ciSYM_FUNC_END(__hyp_do_panic)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci.macro host_el1_sync_vect
968c2ecf20Sopenharmony_ci	.align 7
978c2ecf20Sopenharmony_ci.L__vect_start\@:
988c2ecf20Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
998c2ecf20Sopenharmony_ci	mrs	x0, esr_el2
1008c2ecf20Sopenharmony_ci	ubfx	x0, x0, #ESR_ELx_EC_SHIFT, #ESR_ELx_EC_WIDTH
1018c2ecf20Sopenharmony_ci	cmp	x0, #ESR_ELx_EC_HVC64
1028c2ecf20Sopenharmony_ci	ldp	x0, x1, [sp], #16
1038c2ecf20Sopenharmony_ci	b.ne	__host_exit
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Check for a stub HVC call */
1068c2ecf20Sopenharmony_ci	cmp	x0, #HVC_STUB_HCALL_NR
1078c2ecf20Sopenharmony_ci	b.hs	__host_exit
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/*
1108c2ecf20Sopenharmony_ci	 * Compute the idmap address of __kvm_handle_stub_hvc and
1118c2ecf20Sopenharmony_ci	 * jump there. Since we use kimage_voffset, do not use the
1128c2ecf20Sopenharmony_ci	 * HYP VA for __kvm_handle_stub_hvc, but the kernel VA instead
1138c2ecf20Sopenharmony_ci	 * (by loading it from the constant pool).
1148c2ecf20Sopenharmony_ci	 *
1158c2ecf20Sopenharmony_ci	 * Preserve x0-x4, which may contain stub parameters.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	ldr	x5, =__kvm_handle_stub_hvc
1188c2ecf20Sopenharmony_ci	ldr_l	x6, kimage_voffset
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* x5 = __pa(x5) */
1218c2ecf20Sopenharmony_ci	sub	x5, x5, x6
1228c2ecf20Sopenharmony_ci	br	x5
1238c2ecf20Sopenharmony_ci.L__vect_end\@:
1248c2ecf20Sopenharmony_ci.if ((.L__vect_end\@ - .L__vect_start\@) > 0x80)
1258c2ecf20Sopenharmony_ci	.error "host_el1_sync_vect larger than vector entry"
1268c2ecf20Sopenharmony_ci.endif
1278c2ecf20Sopenharmony_ci.endm
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci.macro invalid_host_el2_vect
1308c2ecf20Sopenharmony_ci	.align 7
1318c2ecf20Sopenharmony_ci	/* If a guest is loaded, panic out of it. */
1328c2ecf20Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
1338c2ecf20Sopenharmony_ci	get_loaded_vcpu x0, x1
1348c2ecf20Sopenharmony_ci	cbnz	x0, __guest_exit_panic
1358c2ecf20Sopenharmony_ci	add	sp, sp, #16
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * The panic may not be clean if the exception is taken before the host
1398c2ecf20Sopenharmony_ci	 * context has been saved by __host_exit or after the hyp context has
1408c2ecf20Sopenharmony_ci	 * been partially clobbered by __host_enter.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	b	hyp_panic
1438c2ecf20Sopenharmony_ci.endm
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci.macro invalid_host_el1_vect
1468c2ecf20Sopenharmony_ci	.align 7
1478c2ecf20Sopenharmony_ci	mov	x0, xzr		/* host_ctxt = NULL */
1488c2ecf20Sopenharmony_ci	mrs	x1, spsr_el2
1498c2ecf20Sopenharmony_ci	mrs	x2, elr_el2
1508c2ecf20Sopenharmony_ci	mrs	x3, par_el1
1518c2ecf20Sopenharmony_ci	b	__hyp_do_panic
1528c2ecf20Sopenharmony_ci.endm
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/*
1558c2ecf20Sopenharmony_ci * The host vector does not use an ESB instruction in order to avoid consuming
1568c2ecf20Sopenharmony_ci * SErrors that should only be consumed by the host. Guest entry is deferred by
1578c2ecf20Sopenharmony_ci * __guest_enter if there are any pending asynchronous exceptions so hyp will
1588c2ecf20Sopenharmony_ci * always return to the host without having consumerd host SErrors.
1598c2ecf20Sopenharmony_ci *
1608c2ecf20Sopenharmony_ci * CONFIG_KVM_INDIRECT_VECTORS is not applied to the host vectors because the
1618c2ecf20Sopenharmony_ci * host knows about the EL2 vectors already, and there is no point in hiding
1628c2ecf20Sopenharmony_ci * them.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ci	.align 11
1658c2ecf20Sopenharmony_ciSYM_CODE_START(__kvm_hyp_host_vector)
1668c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// Synchronous EL2t
1678c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// IRQ EL2t
1688c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// FIQ EL2t
1698c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// Error EL2t
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// Synchronous EL2h
1728c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// IRQ EL2h
1738c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// FIQ EL2h
1748c2ecf20Sopenharmony_ci	invalid_host_el2_vect			// Error EL2h
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	host_el1_sync_vect			// Synchronous 64-bit EL1
1778c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// IRQ 64-bit EL1
1788c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// FIQ 64-bit EL1
1798c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// Error 64-bit EL1
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// Synchronous 32-bit EL1
1828c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// IRQ 32-bit EL1
1838c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// FIQ 32-bit EL1
1848c2ecf20Sopenharmony_ci	invalid_host_el1_vect			// Error 32-bit EL1
1858c2ecf20Sopenharmony_ciSYM_CODE_END(__kvm_hyp_host_vector)
186