18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012,2013 - ARM Ltd
48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
88c2ecf20Sopenharmony_ci#include <linux/linkage.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/alternative.h>
118c2ecf20Sopenharmony_ci#include <asm/assembler.h>
128c2ecf20Sopenharmony_ci#include <asm/kvm_arm.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_asm.h>
148c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
158c2ecf20Sopenharmony_ci#include <asm/pgtable-hwdef.h>
168c2ecf20Sopenharmony_ci#include <asm/sysreg.h>
178c2ecf20Sopenharmony_ci#include <asm/virt.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	.text
208c2ecf20Sopenharmony_ci	.pushsection	.hyp.idmap.text, "ax"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	.align	11
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciSYM_CODE_START(__kvm_hyp_init)
258c2ecf20Sopenharmony_ci	ventry	__invalid		// Synchronous EL2t
268c2ecf20Sopenharmony_ci	ventry	__invalid		// IRQ EL2t
278c2ecf20Sopenharmony_ci	ventry	__invalid		// FIQ EL2t
288c2ecf20Sopenharmony_ci	ventry	__invalid		// Error EL2t
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	ventry	__invalid		// Synchronous EL2h
318c2ecf20Sopenharmony_ci	ventry	__invalid		// IRQ EL2h
328c2ecf20Sopenharmony_ci	ventry	__invalid		// FIQ EL2h
338c2ecf20Sopenharmony_ci	ventry	__invalid		// Error EL2h
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ventry	__do_hyp_init		// Synchronous 64-bit EL1
368c2ecf20Sopenharmony_ci	ventry	__invalid		// IRQ 64-bit EL1
378c2ecf20Sopenharmony_ci	ventry	__invalid		// FIQ 64-bit EL1
388c2ecf20Sopenharmony_ci	ventry	__invalid		// Error 64-bit EL1
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	ventry	__invalid		// Synchronous 32-bit EL1
418c2ecf20Sopenharmony_ci	ventry	__invalid		// IRQ 32-bit EL1
428c2ecf20Sopenharmony_ci	ventry	__invalid		// FIQ 32-bit EL1
438c2ecf20Sopenharmony_ci	ventry	__invalid		// Error 32-bit EL1
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci__invalid:
468c2ecf20Sopenharmony_ci	b	.
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * x0: SMCCC function ID
508c2ecf20Sopenharmony_ci	 * x1: HYP pgd
518c2ecf20Sopenharmony_ci	 * x2: per-CPU offset
528c2ecf20Sopenharmony_ci	 * x3: HYP stack
538c2ecf20Sopenharmony_ci	 * x4: HYP vectors
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci__do_hyp_init:
568c2ecf20Sopenharmony_ci	/* Check for a stub HVC call */
578c2ecf20Sopenharmony_ci	cmp	x0, #HVC_STUB_HCALL_NR
588c2ecf20Sopenharmony_ci	b.lo	__kvm_handle_stub_hvc
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	// We only actively check bits [24:31], and everything
618c2ecf20Sopenharmony_ci	// else has to be zero, which we check at build time.
628c2ecf20Sopenharmony_ci#if (KVM_HOST_SMCCC_FUNC(__kvm_hyp_init) & 0xFFFFFFFF00FFFFFF)
638c2ecf20Sopenharmony_ci#error Unexpected __KVM_HOST_SMCCC_FUNC___kvm_hyp_init value
648c2ecf20Sopenharmony_ci#endif
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	ror	x0, x0, #24
678c2ecf20Sopenharmony_ci	eor	x0, x0, #((KVM_HOST_SMCCC_FUNC(__kvm_hyp_init) >> 24) & 0xF)
688c2ecf20Sopenharmony_ci	ror	x0, x0, #4
698c2ecf20Sopenharmony_ci	eor	x0, x0, #((KVM_HOST_SMCCC_FUNC(__kvm_hyp_init) >> 28) & 0xF)
708c2ecf20Sopenharmony_ci	cbz	x0, 1f
718c2ecf20Sopenharmony_ci	mov	x0, #SMCCC_RET_NOT_SUPPORTED
728c2ecf20Sopenharmony_ci	eret
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci1:
758c2ecf20Sopenharmony_ci	/* Set tpidr_el2 for use by HYP to free a register */
768c2ecf20Sopenharmony_ci	msr	tpidr_el2, x2
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	phys_to_ttbr x0, x1
798c2ecf20Sopenharmony_cialternative_if ARM64_HAS_CNP
808c2ecf20Sopenharmony_ci	orr	x0, x0, #TTBR_CNP_BIT
818c2ecf20Sopenharmony_cialternative_else_nop_endif
828c2ecf20Sopenharmony_ci	msr	ttbr0_el2, x0
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	mrs	x0, tcr_el1
858c2ecf20Sopenharmony_ci	mov_q	x1, TCR_EL2_MASK
868c2ecf20Sopenharmony_ci	and	x0, x0, x1
878c2ecf20Sopenharmony_ci	mov	x1, #TCR_EL2_RES1
888c2ecf20Sopenharmony_ci	orr	x0, x0, x1
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/*
918c2ecf20Sopenharmony_ci	 * The ID map may be configured to use an extended virtual address
928c2ecf20Sopenharmony_ci	 * range. This is only the case if system RAM is out of range for the
938c2ecf20Sopenharmony_ci	 * currently configured page size and VA_BITS, in which case we will
948c2ecf20Sopenharmony_ci	 * also need the extended virtual range for the HYP ID map, or we won't
958c2ecf20Sopenharmony_ci	 * be able to enable the EL2 MMU.
968c2ecf20Sopenharmony_ci	 *
978c2ecf20Sopenharmony_ci	 * However, at EL2, there is only one TTBR register, and we can't switch
988c2ecf20Sopenharmony_ci	 * between translation tables *and* update TCR_EL2.T0SZ at the same
998c2ecf20Sopenharmony_ci	 * time. Bottom line: we need to use the extended range with *both* our
1008c2ecf20Sopenharmony_ci	 * translation tables.
1018c2ecf20Sopenharmony_ci	 *
1028c2ecf20Sopenharmony_ci	 * So use the same T0SZ value we use for the ID map.
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci	ldr_l	x1, idmap_t0sz
1058c2ecf20Sopenharmony_ci	bfi	x0, x1, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/*
1088c2ecf20Sopenharmony_ci	 * Set the PS bits in TCR_EL2.
1098c2ecf20Sopenharmony_ci	 */
1108c2ecf20Sopenharmony_ci	tcr_compute_pa_size x0, #TCR_EL2_PS_SHIFT, x1, x2
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	msr	tcr_el2, x0
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	mrs	x0, mair_el1
1158c2ecf20Sopenharmony_ci	msr	mair_el2, x0
1168c2ecf20Sopenharmony_ci	isb
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Invalidate the stale TLBs from Bootloader */
1198c2ecf20Sopenharmony_ci	tlbi	alle2
1208c2ecf20Sopenharmony_ci	dsb	sy
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * Preserve all the RES1 bits while setting the default flags,
1248c2ecf20Sopenharmony_ci	 * as well as the EE bit on BE. Drop the A flag since the compiler
1258c2ecf20Sopenharmony_ci	 * is allowed to generate unaligned accesses.
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	mov_q	x0, (SCTLR_EL2_RES1 | (SCTLR_ELx_FLAGS & ~SCTLR_ELx_A))
1288c2ecf20Sopenharmony_ciCPU_BE(	orr	x0, x0, #SCTLR_ELx_EE)
1298c2ecf20Sopenharmony_cialternative_if ARM64_HAS_ADDRESS_AUTH
1308c2ecf20Sopenharmony_ci	mov_q	x1, (SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | \
1318c2ecf20Sopenharmony_ci		     SCTLR_ELx_ENDA | SCTLR_ELx_ENDB)
1328c2ecf20Sopenharmony_ci	orr	x0, x0, x1
1338c2ecf20Sopenharmony_cialternative_else_nop_endif
1348c2ecf20Sopenharmony_ci	msr	sctlr_el2, x0
1358c2ecf20Sopenharmony_ci	isb
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Set the stack and new vectors */
1388c2ecf20Sopenharmony_ci	mov	sp, x3
1398c2ecf20Sopenharmony_ci	msr	vbar_el2, x4
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Hello, World! */
1428c2ecf20Sopenharmony_ci	mov	x0, #SMCCC_RET_SUCCESS
1438c2ecf20Sopenharmony_ci	eret
1448c2ecf20Sopenharmony_ciSYM_CODE_END(__kvm_hyp_init)
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciSYM_CODE_START(__kvm_handle_stub_hvc)
1478c2ecf20Sopenharmony_ci	cmp	x0, #HVC_SOFT_RESTART
1488c2ecf20Sopenharmony_ci	b.ne	1f
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* This is where we're about to jump, staying at EL2 */
1518c2ecf20Sopenharmony_ci	msr	elr_el2, x1
1528c2ecf20Sopenharmony_ci	mov	x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT | PSR_MODE_EL2h)
1538c2ecf20Sopenharmony_ci	msr	spsr_el2, x0
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Shuffle the arguments, and don't come back */
1568c2ecf20Sopenharmony_ci	mov	x0, x2
1578c2ecf20Sopenharmony_ci	mov	x1, x3
1588c2ecf20Sopenharmony_ci	mov	x2, x4
1598c2ecf20Sopenharmony_ci	b	reset
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci1:	cmp	x0, #HVC_RESET_VECTORS
1628c2ecf20Sopenharmony_ci	b.ne	1f
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/*
1658c2ecf20Sopenharmony_ci	 * Set the HVC_RESET_VECTORS return code before entering the common
1668c2ecf20Sopenharmony_ci	 * path so that we do not clobber x0-x2 in case we are coming via
1678c2ecf20Sopenharmony_ci	 * HVC_SOFT_RESTART.
1688c2ecf20Sopenharmony_ci	 */
1698c2ecf20Sopenharmony_ci	mov	x0, xzr
1708c2ecf20Sopenharmony_cireset:
1718c2ecf20Sopenharmony_ci	/* Reset kvm back to the hyp stub. */
1728c2ecf20Sopenharmony_ci	mrs	x5, sctlr_el2
1738c2ecf20Sopenharmony_ci	mov_q	x6, SCTLR_ELx_FLAGS
1748c2ecf20Sopenharmony_ci	bic	x5, x5, x6		// Clear SCTL_M and etc
1758c2ecf20Sopenharmony_ci	pre_disable_mmu_workaround
1768c2ecf20Sopenharmony_ci	msr	sctlr_el2, x5
1778c2ecf20Sopenharmony_ci	isb
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Install stub vectors */
1808c2ecf20Sopenharmony_ci	adr_l	x5, __hyp_stub_vectors
1818c2ecf20Sopenharmony_ci	msr	vbar_el2, x5
1828c2ecf20Sopenharmony_ci	eret
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci1:	/* Bad stub call */
1858c2ecf20Sopenharmony_ci	mov_q	x0, HVC_STUB_ERR
1868c2ecf20Sopenharmony_ci	eret
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciSYM_CODE_END(__kvm_handle_stub_hvc)
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	.popsection
191