18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Generation of main entry point for the guest, exception handling.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2012  MIPS Technologies, Inc.
98c2ecf20Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 2016 Imagination Technologies Ltd.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
158c2ecf20Sopenharmony_ci#include <linux/log2.h>
168c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
178c2ecf20Sopenharmony_ci#include <asm/msa.h>
188c2ecf20Sopenharmony_ci#include <asm/setup.h>
198c2ecf20Sopenharmony_ci#include <asm/tlbex.h>
208c2ecf20Sopenharmony_ci#include <asm/uasm.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Register names */
238c2ecf20Sopenharmony_ci#define ZERO		0
248c2ecf20Sopenharmony_ci#define AT		1
258c2ecf20Sopenharmony_ci#define V0		2
268c2ecf20Sopenharmony_ci#define V1		3
278c2ecf20Sopenharmony_ci#define A0		4
288c2ecf20Sopenharmony_ci#define A1		5
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI32
318c2ecf20Sopenharmony_ci#define T0		8
328c2ecf20Sopenharmony_ci#define T1		9
338c2ecf20Sopenharmony_ci#define T2		10
348c2ecf20Sopenharmony_ci#define T3		11
358c2ecf20Sopenharmony_ci#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
388c2ecf20Sopenharmony_ci#define T0		12
398c2ecf20Sopenharmony_ci#define T1		13
408c2ecf20Sopenharmony_ci#define T2		14
418c2ecf20Sopenharmony_ci#define T3		15
428c2ecf20Sopenharmony_ci#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define S0		16
458c2ecf20Sopenharmony_ci#define S1		17
468c2ecf20Sopenharmony_ci#define T9		25
478c2ecf20Sopenharmony_ci#define K0		26
488c2ecf20Sopenharmony_ci#define K1		27
498c2ecf20Sopenharmony_ci#define GP		28
508c2ecf20Sopenharmony_ci#define SP		29
518c2ecf20Sopenharmony_ci#define RA		31
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Some CP0 registers */
548c2ecf20Sopenharmony_ci#define C0_PWBASE	5, 5
558c2ecf20Sopenharmony_ci#define C0_HWRENA	7, 0
568c2ecf20Sopenharmony_ci#define C0_BADVADDR	8, 0
578c2ecf20Sopenharmony_ci#define C0_BADINSTR	8, 1
588c2ecf20Sopenharmony_ci#define C0_BADINSTRP	8, 2
598c2ecf20Sopenharmony_ci#define C0_PGD		9, 7
608c2ecf20Sopenharmony_ci#define C0_ENTRYHI	10, 0
618c2ecf20Sopenharmony_ci#define C0_GUESTCTL1	10, 4
628c2ecf20Sopenharmony_ci#define C0_STATUS	12, 0
638c2ecf20Sopenharmony_ci#define C0_GUESTCTL0	12, 6
648c2ecf20Sopenharmony_ci#define C0_CAUSE	13, 0
658c2ecf20Sopenharmony_ci#define C0_EPC		14, 0
668c2ecf20Sopenharmony_ci#define C0_EBASE	15, 1
678c2ecf20Sopenharmony_ci#define C0_CONFIG5	16, 5
688c2ecf20Sopenharmony_ci#define C0_DDATA_LO	28, 3
698c2ecf20Sopenharmony_ci#define C0_ERROREPC	30, 0
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define CALLFRAME_SIZ   32
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
748c2ecf20Sopenharmony_ci#define ST0_KX_IF_64	ST0_KX
758c2ecf20Sopenharmony_ci#else
768c2ecf20Sopenharmony_ci#define ST0_KX_IF_64	0
778c2ecf20Sopenharmony_ci#endif
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic unsigned int scratch_vcpu[2] = { C0_DDATA_LO };
808c2ecf20Sopenharmony_cistatic unsigned int scratch_tmp[2] = { C0_ERROREPC };
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cienum label_id {
838c2ecf20Sopenharmony_ci	label_fpu_1 = 1,
848c2ecf20Sopenharmony_ci	label_msa_1,
858c2ecf20Sopenharmony_ci	label_return_to_host,
868c2ecf20Sopenharmony_ci	label_kernel_asid,
878c2ecf20Sopenharmony_ci	label_exit_common,
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciUASM_L_LA(_fpu_1)
918c2ecf20Sopenharmony_ciUASM_L_LA(_msa_1)
928c2ecf20Sopenharmony_ciUASM_L_LA(_return_to_host)
938c2ecf20Sopenharmony_ciUASM_L_LA(_kernel_asid)
948c2ecf20Sopenharmony_ciUASM_L_LA(_exit_common)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void *kvm_mips_build_enter_guest(void *addr);
978c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_from_exit(void *addr);
988c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_to_guest(void *addr);
998c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_to_host(void *addr);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * The version of this function in tlbex.c uses current_cpu_type(), but for KVM
1038c2ecf20Sopenharmony_ci * we assume symmetry.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic int c0_kscratch(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	switch (boot_cpu_type()) {
1088c2ecf20Sopenharmony_ci	case CPU_XLP:
1098c2ecf20Sopenharmony_ci	case CPU_XLR:
1108c2ecf20Sopenharmony_ci		return 22;
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		return 31;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * kvm_mips_entry_setup() - Perform global setup for entry code.
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci * Perform global setup for entry code, such as choosing a scratch register.
1208c2ecf20Sopenharmony_ci *
1218c2ecf20Sopenharmony_ci * Returns:	0 on success.
1228c2ecf20Sopenharmony_ci *		-errno on failure.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ciint kvm_mips_entry_setup(void)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	/*
1278c2ecf20Sopenharmony_ci	 * We prefer to use KScratchN registers if they are available over the
1288c2ecf20Sopenharmony_ci	 * defaults above, which may not work on all cores.
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	unsigned int kscratch_mask = cpu_data[0].kscratch_mask;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (pgd_reg != -1)
1338c2ecf20Sopenharmony_ci		kscratch_mask &= ~BIT(pgd_reg);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Pick a scratch register for storing VCPU */
1368c2ecf20Sopenharmony_ci	if (kscratch_mask) {
1378c2ecf20Sopenharmony_ci		scratch_vcpu[0] = c0_kscratch();
1388c2ecf20Sopenharmony_ci		scratch_vcpu[1] = ffs(kscratch_mask) - 1;
1398c2ecf20Sopenharmony_ci		kscratch_mask &= ~BIT(scratch_vcpu[1]);
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Pick a scratch register to use as a temp for saving state */
1438c2ecf20Sopenharmony_ci	if (kscratch_mask) {
1448c2ecf20Sopenharmony_ci		scratch_tmp[0] = c0_kscratch();
1458c2ecf20Sopenharmony_ci		scratch_tmp[1] = ffs(kscratch_mask) - 1;
1468c2ecf20Sopenharmony_ci		kscratch_mask &= ~BIT(scratch_tmp[1]);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void kvm_mips_build_save_scratch(u32 **p, unsigned int tmp,
1538c2ecf20Sopenharmony_ci					unsigned int frame)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	/* Save the VCPU scratch register value in cp0_epc of the stack frame */
1568c2ecf20Sopenharmony_ci	UASM_i_MFC0(p, tmp, scratch_vcpu[0], scratch_vcpu[1]);
1578c2ecf20Sopenharmony_ci	UASM_i_SW(p, tmp, offsetof(struct pt_regs, cp0_epc), frame);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Save the temp scratch register value in cp0_cause of stack frame */
1608c2ecf20Sopenharmony_ci	if (scratch_tmp[0] == c0_kscratch()) {
1618c2ecf20Sopenharmony_ci		UASM_i_MFC0(p, tmp, scratch_tmp[0], scratch_tmp[1]);
1628c2ecf20Sopenharmony_ci		UASM_i_SW(p, tmp, offsetof(struct pt_regs, cp0_cause), frame);
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic void kvm_mips_build_restore_scratch(u32 **p, unsigned int tmp,
1678c2ecf20Sopenharmony_ci					   unsigned int frame)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * Restore host scratch register values saved by
1718c2ecf20Sopenharmony_ci	 * kvm_mips_build_save_scratch().
1728c2ecf20Sopenharmony_ci	 */
1738c2ecf20Sopenharmony_ci	UASM_i_LW(p, tmp, offsetof(struct pt_regs, cp0_epc), frame);
1748c2ecf20Sopenharmony_ci	UASM_i_MTC0(p, tmp, scratch_vcpu[0], scratch_vcpu[1]);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (scratch_tmp[0] == c0_kscratch()) {
1778c2ecf20Sopenharmony_ci		UASM_i_LW(p, tmp, offsetof(struct pt_regs, cp0_cause), frame);
1788c2ecf20Sopenharmony_ci		UASM_i_MTC0(p, tmp, scratch_tmp[0], scratch_tmp[1]);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * build_set_exc_base() - Assemble code to write exception base address.
1848c2ecf20Sopenharmony_ci * @p:		Code buffer pointer.
1858c2ecf20Sopenharmony_ci * @reg:	Source register (generated code may set WG bit in @reg).
1868c2ecf20Sopenharmony_ci *
1878c2ecf20Sopenharmony_ci * Assemble code to modify the exception base address in the EBase register,
1888c2ecf20Sopenharmony_ci * using the appropriately sized access and setting the WG bit if necessary.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistatic inline void build_set_exc_base(u32 **p, unsigned int reg)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	if (cpu_has_ebase_wg) {
1938c2ecf20Sopenharmony_ci		/* Set WG so that all the bits get written */
1948c2ecf20Sopenharmony_ci		uasm_i_ori(p, reg, reg, MIPS_EBASE_WG);
1958c2ecf20Sopenharmony_ci		UASM_i_MTC0(p, reg, C0_EBASE);
1968c2ecf20Sopenharmony_ci	} else {
1978c2ecf20Sopenharmony_ci		uasm_i_mtc0(p, reg, C0_EBASE);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * kvm_mips_build_vcpu_run() - Assemble function to start running a guest VCPU.
2038c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
2048c2ecf20Sopenharmony_ci *
2058c2ecf20Sopenharmony_ci * Assemble the start of the vcpu_run function to run a guest VCPU. The function
2068c2ecf20Sopenharmony_ci * conforms to the following prototype:
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * int vcpu_run(struct kvm_vcpu *vcpu);
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * The exit from the guest and return to the caller is handled by the code
2118c2ecf20Sopenharmony_ci * generated by kvm_mips_build_ret_to_host().
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_civoid *kvm_mips_build_vcpu_run(void *addr)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	u32 *p = addr;
2188c2ecf20Sopenharmony_ci	unsigned int i;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * A0: vcpu
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* k0/k1 not being used in host kernel context */
2258c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, K1, SP, -(int)sizeof(struct pt_regs));
2268c2ecf20Sopenharmony_ci	for (i = 16; i < 32; ++i) {
2278c2ecf20Sopenharmony_ci		if (i == 24)
2288c2ecf20Sopenharmony_ci			i = 28;
2298c2ecf20Sopenharmony_ci		UASM_i_SW(&p, i, offsetof(struct pt_regs, regs[i]), K1);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Save host status */
2338c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, V0, C0_STATUS);
2348c2ecf20Sopenharmony_ci	UASM_i_SW(&p, V0, offsetof(struct pt_regs, cp0_status), K1);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Save scratch registers, will be used to store pointer to vcpu etc */
2378c2ecf20Sopenharmony_ci	kvm_mips_build_save_scratch(&p, V1, K1);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* VCPU scratch register has pointer to vcpu */
2408c2ecf20Sopenharmony_ci	UASM_i_MTC0(&p, A0, scratch_vcpu[0], scratch_vcpu[1]);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Offset into vcpu->arch */
2438c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, K1, A0, offsetof(struct kvm_vcpu, arch));
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/*
2468c2ecf20Sopenharmony_ci	 * Save the host stack to VCPU, used for exception processing
2478c2ecf20Sopenharmony_ci	 * when we exit from the Guest
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	UASM_i_SW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Save the kernel gp as well */
2528c2ecf20Sopenharmony_ci	UASM_i_SW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * Setup status register for running the guest in UM, interrupts
2568c2ecf20Sopenharmony_ci	 * are disabled
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	UASM_i_LA(&p, K0, ST0_EXL | KSU_USER | ST0_BEV | ST0_KX_IF_64);
2598c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_STATUS);
2608c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* load up the new EBASE */
2638c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1);
2648c2ecf20Sopenharmony_ci	build_set_exc_base(&p, K0);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/*
2678c2ecf20Sopenharmony_ci	 * Now that the new EBASE has been loaded, unset BEV, set
2688c2ecf20Sopenharmony_ci	 * interrupt mask as it was but make sure that timer interrupts
2698c2ecf20Sopenharmony_ci	 * are enabled
2708c2ecf20Sopenharmony_ci	 */
2718c2ecf20Sopenharmony_ci	uasm_i_addiu(&p, K0, ZERO, ST0_EXL | KSU_USER | ST0_IE | ST0_KX_IF_64);
2728c2ecf20Sopenharmony_ci	uasm_i_andi(&p, V0, V0, ST0_IM);
2738c2ecf20Sopenharmony_ci	uasm_i_or(&p, K0, K0, V0);
2748c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_STATUS);
2758c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	p = kvm_mips_build_enter_guest(p);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return p;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/**
2838c2ecf20Sopenharmony_ci * kvm_mips_build_enter_guest() - Assemble code to resume guest execution.
2848c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci * Assemble the code to resume guest execution. This code is common between the
2878c2ecf20Sopenharmony_ci * initial entry into the guest from the host, and returning from the exit
2888c2ecf20Sopenharmony_ci * handler back to the guest.
2898c2ecf20Sopenharmony_ci *
2908c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_cistatic void *kvm_mips_build_enter_guest(void *addr)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	u32 *p = addr;
2958c2ecf20Sopenharmony_ci	unsigned int i;
2968c2ecf20Sopenharmony_ci	struct uasm_label labels[2];
2978c2ecf20Sopenharmony_ci	struct uasm_reloc relocs[2];
2988c2ecf20Sopenharmony_ci	struct uasm_label __maybe_unused *l = labels;
2998c2ecf20Sopenharmony_ci	struct uasm_reloc __maybe_unused *r = relocs;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	memset(labels, 0, sizeof(labels));
3028c2ecf20Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Set Guest EPC */
3058c2ecf20Sopenharmony_ci	UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, pc), K1);
3068c2ecf20Sopenharmony_ci	UASM_i_MTC0(&p, T0, C0_EPC);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_VZ
3098c2ecf20Sopenharmony_ci	/* Save normal linux process pgd (VZ guarantees pgd_reg is set) */
3108c2ecf20Sopenharmony_ci	if (cpu_has_ldpte)
3118c2ecf20Sopenharmony_ci		UASM_i_MFC0(&p, K0, C0_PWBASE);
3128c2ecf20Sopenharmony_ci	else
3138c2ecf20Sopenharmony_ci		UASM_i_MFC0(&p, K0, c0_kscratch(), pgd_reg);
3148c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_pgd), K1);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/*
3178c2ecf20Sopenharmony_ci	 * Set up KVM GPA pgd.
3188c2ecf20Sopenharmony_ci	 * This does roughly the same as TLBMISS_HANDLER_SETUP_PGD():
3198c2ecf20Sopenharmony_ci	 * - call tlbmiss_handler_setup_pgd(mm->pgd)
3208c2ecf20Sopenharmony_ci	 * - write mm->pgd into CP0_PWBase
3218c2ecf20Sopenharmony_ci	 *
3228c2ecf20Sopenharmony_ci	 * We keep S0 pointing at struct kvm so we can load the ASID below.
3238c2ecf20Sopenharmony_ci	 */
3248c2ecf20Sopenharmony_ci	UASM_i_LW(&p, S0, (int)offsetof(struct kvm_vcpu, kvm) -
3258c2ecf20Sopenharmony_ci			  (int)offsetof(struct kvm_vcpu, arch), K1);
3268c2ecf20Sopenharmony_ci	UASM_i_LW(&p, A0, offsetof(struct kvm, arch.gpa_mm.pgd), S0);
3278c2ecf20Sopenharmony_ci	UASM_i_LA(&p, T9, (unsigned long)tlbmiss_handler_setup_pgd);
3288c2ecf20Sopenharmony_ci	uasm_i_jalr(&p, RA, T9);
3298c2ecf20Sopenharmony_ci	/* delay slot */
3308c2ecf20Sopenharmony_ci	if (cpu_has_htw)
3318c2ecf20Sopenharmony_ci		UASM_i_MTC0(&p, A0, C0_PWBASE);
3328c2ecf20Sopenharmony_ci	else
3338c2ecf20Sopenharmony_ci		uasm_i_nop(&p);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Set GM bit to setup eret to VZ guest context */
3368c2ecf20Sopenharmony_ci	uasm_i_addiu(&p, V1, ZERO, 1);
3378c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, K0, C0_GUESTCTL0);
3388c2ecf20Sopenharmony_ci	uasm_i_ins(&p, K0, V1, MIPS_GCTL0_GM_SHIFT, 1);
3398c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_GUESTCTL0);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (cpu_has_guestid) {
3428c2ecf20Sopenharmony_ci		/*
3438c2ecf20Sopenharmony_ci		 * Set root mode GuestID, so that root TLB refill handler can
3448c2ecf20Sopenharmony_ci		 * use the correct GuestID in the root TLB.
3458c2ecf20Sopenharmony_ci		 */
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		/* Get current GuestID */
3488c2ecf20Sopenharmony_ci		uasm_i_mfc0(&p, T0, C0_GUESTCTL1);
3498c2ecf20Sopenharmony_ci		/* Set GuestCtl1.RID = GuestCtl1.ID */
3508c2ecf20Sopenharmony_ci		uasm_i_ext(&p, T1, T0, MIPS_GCTL1_ID_SHIFT,
3518c2ecf20Sopenharmony_ci			   MIPS_GCTL1_ID_WIDTH);
3528c2ecf20Sopenharmony_ci		uasm_i_ins(&p, T0, T1, MIPS_GCTL1_RID_SHIFT,
3538c2ecf20Sopenharmony_ci			   MIPS_GCTL1_RID_WIDTH);
3548c2ecf20Sopenharmony_ci		uasm_i_mtc0(&p, T0, C0_GUESTCTL1);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/* GuestID handles dealiasing so we don't need to touch ASID */
3578c2ecf20Sopenharmony_ci		goto skip_asid_restore;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Root ASID Dealias (RAD) */
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* Save host ASID */
3638c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K0, C0_ENTRYHI);
3648c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_entryhi),
3658c2ecf20Sopenharmony_ci		  K1);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Set the root ASID for the Guest */
3688c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, T1, S0,
3698c2ecf20Sopenharmony_ci		     offsetof(struct kvm, arch.gpa_mm.context.asid));
3708c2ecf20Sopenharmony_ci#else
3718c2ecf20Sopenharmony_ci	/* Set the ASID for the Guest Kernel or User */
3728c2ecf20Sopenharmony_ci	UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, cop0), K1);
3738c2ecf20Sopenharmony_ci	UASM_i_LW(&p, T0, offsetof(struct mips_coproc, reg[MIPS_CP0_STATUS][0]),
3748c2ecf20Sopenharmony_ci		  T0);
3758c2ecf20Sopenharmony_ci	uasm_i_andi(&p, T0, T0, KSU_USER | ST0_ERL | ST0_EXL);
3768c2ecf20Sopenharmony_ci	uasm_i_xori(&p, T0, T0, KSU_USER);
3778c2ecf20Sopenharmony_ci	uasm_il_bnez(&p, &r, T0, label_kernel_asid);
3788c2ecf20Sopenharmony_ci	 UASM_i_ADDIU(&p, T1, K1, offsetof(struct kvm_vcpu_arch,
3798c2ecf20Sopenharmony_ci					   guest_kernel_mm.context.asid));
3808c2ecf20Sopenharmony_ci	/* else user */
3818c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, T1, K1, offsetof(struct kvm_vcpu_arch,
3828c2ecf20Sopenharmony_ci					  guest_user_mm.context.asid));
3838c2ecf20Sopenharmony_ci	uasm_l_kernel_asid(&l, p);
3848c2ecf20Sopenharmony_ci#endif
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* t1: contains the base of the ASID array, need to get the cpu id  */
3878c2ecf20Sopenharmony_ci	/* smp_processor_id */
3888c2ecf20Sopenharmony_ci	uasm_i_lw(&p, T2, offsetof(struct thread_info, cpu), GP);
3898c2ecf20Sopenharmony_ci	/* index the ASID array */
3908c2ecf20Sopenharmony_ci	uasm_i_sll(&p, T2, T2, ilog2(sizeof(long)));
3918c2ecf20Sopenharmony_ci	UASM_i_ADDU(&p, T3, T1, T2);
3928c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, 0, T3);
3938c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
3948c2ecf20Sopenharmony_ci	/*
3958c2ecf20Sopenharmony_ci	 * reuse ASID array offset
3968c2ecf20Sopenharmony_ci	 * cpuinfo_mips is a multiple of sizeof(long)
3978c2ecf20Sopenharmony_ci	 */
3988c2ecf20Sopenharmony_ci	uasm_i_addiu(&p, T3, ZERO, sizeof(struct cpuinfo_mips)/sizeof(long));
3998c2ecf20Sopenharmony_ci	uasm_i_mul(&p, T2, T2, T3);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	UASM_i_LA_mostly(&p, AT, (long)&cpu_data[0].asid_mask);
4028c2ecf20Sopenharmony_ci	UASM_i_ADDU(&p, AT, AT, T2);
4038c2ecf20Sopenharmony_ci	UASM_i_LW(&p, T2, uasm_rel_lo((long)&cpu_data[0].asid_mask), AT);
4048c2ecf20Sopenharmony_ci	uasm_i_and(&p, K0, K0, T2);
4058c2ecf20Sopenharmony_ci#else
4068c2ecf20Sopenharmony_ci	uasm_i_andi(&p, K0, K0, MIPS_ENTRYHI_ASID);
4078c2ecf20Sopenharmony_ci#endif
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#ifndef CONFIG_KVM_MIPS_VZ
4108c2ecf20Sopenharmony_ci	/*
4118c2ecf20Sopenharmony_ci	 * Set up KVM T&E GVA pgd.
4128c2ecf20Sopenharmony_ci	 * This does roughly the same as TLBMISS_HANDLER_SETUP_PGD():
4138c2ecf20Sopenharmony_ci	 * - call tlbmiss_handler_setup_pgd(mm->pgd)
4148c2ecf20Sopenharmony_ci	 * - but skips write into CP0_PWBase for now
4158c2ecf20Sopenharmony_ci	 */
4168c2ecf20Sopenharmony_ci	UASM_i_LW(&p, A0, (int)offsetof(struct mm_struct, pgd) -
4178c2ecf20Sopenharmony_ci			  (int)offsetof(struct mm_struct, context.asid), T1);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	UASM_i_LA(&p, T9, (unsigned long)tlbmiss_handler_setup_pgd);
4208c2ecf20Sopenharmony_ci	uasm_i_jalr(&p, RA, T9);
4218c2ecf20Sopenharmony_ci	 uasm_i_mtc0(&p, K0, C0_ENTRYHI);
4228c2ecf20Sopenharmony_ci#else
4238c2ecf20Sopenharmony_ci	/* Set up KVM VZ root ASID (!guestid) */
4248c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_ENTRYHI);
4258c2ecf20Sopenharmony_ciskip_asid_restore:
4268c2ecf20Sopenharmony_ci#endif
4278c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* Disable RDHWR access */
4308c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, ZERO, C0_HWRENA);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* load the guest context from VCPU and return */
4338c2ecf20Sopenharmony_ci	for (i = 1; i < 32; ++i) {
4348c2ecf20Sopenharmony_ci		/* Guest k0/k1 loaded later */
4358c2ecf20Sopenharmony_ci		if (i == K0 || i == K1)
4368c2ecf20Sopenharmony_ci			continue;
4378c2ecf20Sopenharmony_ci		UASM_i_LW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
4418c2ecf20Sopenharmony_ci	/* Restore hi/lo */
4428c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, hi), K1);
4438c2ecf20Sopenharmony_ci	uasm_i_mthi(&p, K0);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, lo), K1);
4468c2ecf20Sopenharmony_ci	uasm_i_mtlo(&p, K0);
4478c2ecf20Sopenharmony_ci#endif
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Restore the guest's k0/k1 registers */
4508c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1);
4518c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Jump to guest */
4548c2ecf20Sopenharmony_ci	uasm_i_eret(&p);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return p;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/**
4628c2ecf20Sopenharmony_ci * kvm_mips_build_tlb_refill_exception() - Assemble TLB refill handler.
4638c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
4648c2ecf20Sopenharmony_ci * @handler:	Address of common handler (within range of @addr).
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * Assemble TLB refill exception fast path handler for guest execution.
4678c2ecf20Sopenharmony_ci *
4688c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
4698c2ecf20Sopenharmony_ci */
4708c2ecf20Sopenharmony_civoid *kvm_mips_build_tlb_refill_exception(void *addr, void *handler)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	u32 *p = addr;
4738c2ecf20Sopenharmony_ci	struct uasm_label labels[2];
4748c2ecf20Sopenharmony_ci	struct uasm_reloc relocs[2];
4758c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_LOONGSON64
4768c2ecf20Sopenharmony_ci	struct uasm_label *l = labels;
4778c2ecf20Sopenharmony_ci	struct uasm_reloc *r = relocs;
4788c2ecf20Sopenharmony_ci#endif
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	memset(labels, 0, sizeof(labels));
4818c2ecf20Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Save guest k1 into scratch register */
4848c2ecf20Sopenharmony_ci	UASM_i_MTC0(&p, K1, scratch_tmp[0], scratch_tmp[1]);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Get the VCPU pointer from the VCPU scratch register */
4878c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Save guest k0 into VCPU structure */
4908c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu, arch.gprs[K0]), K1);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/*
4938c2ecf20Sopenharmony_ci	 * Some of the common tlbex code uses current_cpu_type(). For KVM we
4948c2ecf20Sopenharmony_ci	 * assume symmetry and just disable preemption to silence the warning.
4958c2ecf20Sopenharmony_ci	 */
4968c2ecf20Sopenharmony_ci	preempt_disable();
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64
4998c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K1, C0_PGD);
5008c2ecf20Sopenharmony_ci	uasm_i_lddir(&p, K0, K1, 3);  /* global page dir */
5018c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
5028c2ecf20Sopenharmony_ci	uasm_i_lddir(&p, K1, K0, 1);  /* middle page dir */
5038c2ecf20Sopenharmony_ci#endif
5048c2ecf20Sopenharmony_ci	uasm_i_ldpte(&p, K1, 0);      /* even */
5058c2ecf20Sopenharmony_ci	uasm_i_ldpte(&p, K1, 1);      /* odd */
5068c2ecf20Sopenharmony_ci	uasm_i_tlbwr(&p);
5078c2ecf20Sopenharmony_ci#else
5088c2ecf20Sopenharmony_ci	/*
5098c2ecf20Sopenharmony_ci	 * Now for the actual refill bit. A lot of this can be common with the
5108c2ecf20Sopenharmony_ci	 * Linux TLB refill handler, however we don't need to handle so many
5118c2ecf20Sopenharmony_ci	 * cases. We only need to handle user mode refills, and user mode runs
5128c2ecf20Sopenharmony_ci	 * with 32-bit addressing.
5138c2ecf20Sopenharmony_ci	 *
5148c2ecf20Sopenharmony_ci	 * Therefore the branch to label_vmalloc generated by build_get_pmde64()
5158c2ecf20Sopenharmony_ci	 * that isn't resolved should never actually get taken and is harmless
5168c2ecf20Sopenharmony_ci	 * to leave in place for now.
5178c2ecf20Sopenharmony_ci	 */
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
5208c2ecf20Sopenharmony_ci	build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
5218c2ecf20Sopenharmony_ci#else
5228c2ecf20Sopenharmony_ci	build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
5238c2ecf20Sopenharmony_ci#endif
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* we don't support huge pages yet */
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	build_get_ptep(&p, K0, K1);
5288c2ecf20Sopenharmony_ci	build_update_entries(&p, K0, K1);
5298c2ecf20Sopenharmony_ci	build_tlb_write_entry(&p, &l, &r, tlb_random);
5308c2ecf20Sopenharmony_ci#endif
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	preempt_enable();
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Get the VCPU pointer from the VCPU scratch register again */
5358c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* Restore the guest's k0/k1 registers */
5388c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu, arch.gprs[K0]), K1);
5398c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
5408c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K1, scratch_tmp[0], scratch_tmp[1]);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	/* Jump to guest */
5438c2ecf20Sopenharmony_ci	uasm_i_eret(&p);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return p;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/**
5498c2ecf20Sopenharmony_ci * kvm_mips_build_exception() - Assemble first level guest exception handler.
5508c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
5518c2ecf20Sopenharmony_ci * @handler:	Address of common handler (within range of @addr).
5528c2ecf20Sopenharmony_ci *
5538c2ecf20Sopenharmony_ci * Assemble exception vector code for guest execution. The generated vector will
5548c2ecf20Sopenharmony_ci * branch to the common exception handler generated by kvm_mips_build_exit().
5558c2ecf20Sopenharmony_ci *
5568c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
5578c2ecf20Sopenharmony_ci */
5588c2ecf20Sopenharmony_civoid *kvm_mips_build_exception(void *addr, void *handler)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	u32 *p = addr;
5618c2ecf20Sopenharmony_ci	struct uasm_label labels[2];
5628c2ecf20Sopenharmony_ci	struct uasm_reloc relocs[2];
5638c2ecf20Sopenharmony_ci	struct uasm_label *l = labels;
5648c2ecf20Sopenharmony_ci	struct uasm_reloc *r = relocs;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	memset(labels, 0, sizeof(labels));
5678c2ecf20Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Save guest k1 into scratch register */
5708c2ecf20Sopenharmony_ci	UASM_i_MTC0(&p, K1, scratch_tmp[0], scratch_tmp[1]);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Get the VCPU pointer from the VCPU scratch register */
5738c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]);
5748c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, K1, K1, offsetof(struct kvm_vcpu, arch));
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* Save guest k0 into VCPU structure */
5778c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Branch to the common handler */
5808c2ecf20Sopenharmony_ci	uasm_il_b(&p, &r, label_exit_common);
5818c2ecf20Sopenharmony_ci	 uasm_i_nop(&p);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	uasm_l_exit_common(&l, handler);
5848c2ecf20Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return p;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci/**
5908c2ecf20Sopenharmony_ci * kvm_mips_build_exit() - Assemble common guest exit handler.
5918c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
5928c2ecf20Sopenharmony_ci *
5938c2ecf20Sopenharmony_ci * Assemble the generic guest exit handling code. This is called by the
5948c2ecf20Sopenharmony_ci * exception vectors (generated by kvm_mips_build_exception()), and calls
5958c2ecf20Sopenharmony_ci * kvm_mips_handle_exit(), then either resumes the guest or returns to the host
5968c2ecf20Sopenharmony_ci * depending on the return value.
5978c2ecf20Sopenharmony_ci *
5988c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
5998c2ecf20Sopenharmony_ci */
6008c2ecf20Sopenharmony_civoid *kvm_mips_build_exit(void *addr)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	u32 *p = addr;
6038c2ecf20Sopenharmony_ci	unsigned int i;
6048c2ecf20Sopenharmony_ci	struct uasm_label labels[3];
6058c2ecf20Sopenharmony_ci	struct uasm_reloc relocs[3];
6068c2ecf20Sopenharmony_ci	struct uasm_label *l = labels;
6078c2ecf20Sopenharmony_ci	struct uasm_reloc *r = relocs;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	memset(labels, 0, sizeof(labels));
6108c2ecf20Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/*
6138c2ecf20Sopenharmony_ci	 * Generic Guest exception handler. We end up here when the guest
6148c2ecf20Sopenharmony_ci	 * does something that causes a trap to kernel mode.
6158c2ecf20Sopenharmony_ci	 *
6168c2ecf20Sopenharmony_ci	 * Both k0/k1 registers will have already been saved (k0 into the vcpu
6178c2ecf20Sopenharmony_ci	 * structure, and k1 into the scratch_tmp register).
6188c2ecf20Sopenharmony_ci	 *
6198c2ecf20Sopenharmony_ci	 * The k1 register will already contain the kvm_vcpu_arch pointer.
6208c2ecf20Sopenharmony_ci	 */
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* Start saving Guest context to VCPU */
6238c2ecf20Sopenharmony_ci	for (i = 0; i < 32; ++i) {
6248c2ecf20Sopenharmony_ci		/* Guest k0/k1 saved later */
6258c2ecf20Sopenharmony_ci		if (i == K0 || i == K1)
6268c2ecf20Sopenharmony_ci			continue;
6278c2ecf20Sopenharmony_ci		UASM_i_SW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1);
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6
6318c2ecf20Sopenharmony_ci	/* We need to save hi/lo and restore them on the way out */
6328c2ecf20Sopenharmony_ci	uasm_i_mfhi(&p, T0);
6338c2ecf20Sopenharmony_ci	UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, hi), K1);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	uasm_i_mflo(&p, T0);
6368c2ecf20Sopenharmony_ci	UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, lo), K1);
6378c2ecf20Sopenharmony_ci#endif
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/* Finally save guest k1 to VCPU */
6408c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
6418c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, T0, scratch_tmp[0], scratch_tmp[1]);
6428c2ecf20Sopenharmony_ci	UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* Now that context has been saved, we can use other registers */
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Restore vcpu */
6478c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, S0, scratch_vcpu[0], scratch_vcpu[1]);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/*
6508c2ecf20Sopenharmony_ci	 * Save Host level EPC, BadVaddr and Cause to VCPU, useful to process
6518c2ecf20Sopenharmony_ci	 * the exception
6528c2ecf20Sopenharmony_ci	 */
6538c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K0, C0_EPC);
6548c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, pc), K1);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	UASM_i_MFC0(&p, K0, C0_BADVADDR);
6578c2ecf20Sopenharmony_ci	UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_badvaddr),
6588c2ecf20Sopenharmony_ci		  K1);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, K0, C0_CAUSE);
6618c2ecf20Sopenharmony_ci	uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_cause), K1);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (cpu_has_badinstr) {
6648c2ecf20Sopenharmony_ci		uasm_i_mfc0(&p, K0, C0_BADINSTR);
6658c2ecf20Sopenharmony_ci		uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch,
6668c2ecf20Sopenharmony_ci					   host_cp0_badinstr), K1);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (cpu_has_badinstrp) {
6708c2ecf20Sopenharmony_ci		uasm_i_mfc0(&p, K0, C0_BADINSTRP);
6718c2ecf20Sopenharmony_ci		uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch,
6728c2ecf20Sopenharmony_ci					   host_cp0_badinstrp), K1);
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	/* Now restore the host state just enough to run the handlers */
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Switch EBASE to the one used by Linux */
6788c2ecf20Sopenharmony_ci	/* load up the host EBASE */
6798c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, V0, C0_STATUS);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	uasm_i_lui(&p, AT, ST0_BEV >> 16);
6828c2ecf20Sopenharmony_ci	uasm_i_or(&p, K0, V0, AT);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_STATUS);
6858c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	UASM_i_LA_mostly(&p, K0, (long)&ebase);
6888c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K0, uasm_rel_lo((long)&ebase), K0);
6898c2ecf20Sopenharmony_ci	build_set_exc_base(&p, K0);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (raw_cpu_has_fpu) {
6928c2ecf20Sopenharmony_ci		/*
6938c2ecf20Sopenharmony_ci		 * If FPU is enabled, save FCR31 and clear it so that later
6948c2ecf20Sopenharmony_ci		 * ctc1's don't trigger FPE for pending exceptions.
6958c2ecf20Sopenharmony_ci		 */
6968c2ecf20Sopenharmony_ci		uasm_i_lui(&p, AT, ST0_CU1 >> 16);
6978c2ecf20Sopenharmony_ci		uasm_i_and(&p, V1, V0, AT);
6988c2ecf20Sopenharmony_ci		uasm_il_beqz(&p, &r, V1, label_fpu_1);
6998c2ecf20Sopenharmony_ci		 uasm_i_nop(&p);
7008c2ecf20Sopenharmony_ci		uasm_i_cfc1(&p, T0, 31);
7018c2ecf20Sopenharmony_ci		uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.fcr31),
7028c2ecf20Sopenharmony_ci			  K1);
7038c2ecf20Sopenharmony_ci		uasm_i_ctc1(&p, ZERO, 31);
7048c2ecf20Sopenharmony_ci		uasm_l_fpu_1(&l, p);
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (cpu_has_msa) {
7088c2ecf20Sopenharmony_ci		/*
7098c2ecf20Sopenharmony_ci		 * If MSA is enabled, save MSACSR and clear it so that later
7108c2ecf20Sopenharmony_ci		 * instructions don't trigger MSAFPE for pending exceptions.
7118c2ecf20Sopenharmony_ci		 */
7128c2ecf20Sopenharmony_ci		uasm_i_mfc0(&p, T0, C0_CONFIG5);
7138c2ecf20Sopenharmony_ci		uasm_i_ext(&p, T0, T0, 27, 1); /* MIPS_CONF5_MSAEN */
7148c2ecf20Sopenharmony_ci		uasm_il_beqz(&p, &r, T0, label_msa_1);
7158c2ecf20Sopenharmony_ci		 uasm_i_nop(&p);
7168c2ecf20Sopenharmony_ci		uasm_i_cfcmsa(&p, T0, MSA_CSR);
7178c2ecf20Sopenharmony_ci		uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.msacsr),
7188c2ecf20Sopenharmony_ci			  K1);
7198c2ecf20Sopenharmony_ci		uasm_i_ctcmsa(&p, MSA_CSR, ZERO);
7208c2ecf20Sopenharmony_ci		uasm_l_msa_1(&l, p);
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_MIPS_VZ
7248c2ecf20Sopenharmony_ci	/* Restore host ASID */
7258c2ecf20Sopenharmony_ci	if (!cpu_has_guestid) {
7268c2ecf20Sopenharmony_ci		UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, host_entryhi),
7278c2ecf20Sopenharmony_ci			  K1);
7288c2ecf20Sopenharmony_ci		UASM_i_MTC0(&p, K0, C0_ENTRYHI);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/*
7328c2ecf20Sopenharmony_ci	 * Set up normal Linux process pgd.
7338c2ecf20Sopenharmony_ci	 * This does roughly the same as TLBMISS_HANDLER_SETUP_PGD():
7348c2ecf20Sopenharmony_ci	 * - call tlbmiss_handler_setup_pgd(mm->pgd)
7358c2ecf20Sopenharmony_ci	 * - write mm->pgd into CP0_PWBase
7368c2ecf20Sopenharmony_ci	 */
7378c2ecf20Sopenharmony_ci	UASM_i_LW(&p, A0,
7388c2ecf20Sopenharmony_ci		  offsetof(struct kvm_vcpu_arch, host_pgd), K1);
7398c2ecf20Sopenharmony_ci	UASM_i_LA(&p, T9, (unsigned long)tlbmiss_handler_setup_pgd);
7408c2ecf20Sopenharmony_ci	uasm_i_jalr(&p, RA, T9);
7418c2ecf20Sopenharmony_ci	/* delay slot */
7428c2ecf20Sopenharmony_ci	if (cpu_has_htw)
7438c2ecf20Sopenharmony_ci		UASM_i_MTC0(&p, A0, C0_PWBASE);
7448c2ecf20Sopenharmony_ci	else
7458c2ecf20Sopenharmony_ci		uasm_i_nop(&p);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/* Clear GM bit so we don't enter guest mode when EXL is cleared */
7488c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, K0, C0_GUESTCTL0);
7498c2ecf20Sopenharmony_ci	uasm_i_ins(&p, K0, ZERO, MIPS_GCTL0_GM_SHIFT, 1);
7508c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_GUESTCTL0);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* Save GuestCtl0 so we can access GExcCode after CPU migration */
7538c2ecf20Sopenharmony_ci	uasm_i_sw(&p, K0,
7548c2ecf20Sopenharmony_ci		  offsetof(struct kvm_vcpu_arch, host_cp0_guestctl0), K1);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	if (cpu_has_guestid) {
7578c2ecf20Sopenharmony_ci		/*
7588c2ecf20Sopenharmony_ci		 * Clear root mode GuestID, so that root TLB operations use the
7598c2ecf20Sopenharmony_ci		 * root GuestID in the root TLB.
7608c2ecf20Sopenharmony_ci		 */
7618c2ecf20Sopenharmony_ci		uasm_i_mfc0(&p, T0, C0_GUESTCTL1);
7628c2ecf20Sopenharmony_ci		/* Set GuestCtl1.RID = MIPS_GCTL1_ROOT_GUESTID (i.e. 0) */
7638c2ecf20Sopenharmony_ci		uasm_i_ins(&p, T0, ZERO, MIPS_GCTL1_RID_SHIFT,
7648c2ecf20Sopenharmony_ci			   MIPS_GCTL1_RID_WIDTH);
7658c2ecf20Sopenharmony_ci		uasm_i_mtc0(&p, T0, C0_GUESTCTL1);
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci#endif
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Now that the new EBASE has been loaded, unset BEV and KSU_USER */
7708c2ecf20Sopenharmony_ci	uasm_i_addiu(&p, AT, ZERO, ~(ST0_EXL | KSU_USER | ST0_IE));
7718c2ecf20Sopenharmony_ci	uasm_i_and(&p, V0, V0, AT);
7728c2ecf20Sopenharmony_ci	uasm_i_lui(&p, AT, ST0_CU0 >> 16);
7738c2ecf20Sopenharmony_ci	uasm_i_or(&p, V0, V0, AT);
7748c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
7758c2ecf20Sopenharmony_ci	uasm_i_ori(&p, V0, V0, ST0_SX | ST0_UX);
7768c2ecf20Sopenharmony_ci#endif
7778c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, V0, C0_STATUS);
7788c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* Load up host GP */
7818c2ecf20Sopenharmony_ci	UASM_i_LW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* Need a stack before we can jump to "C" */
7848c2ecf20Sopenharmony_ci	UASM_i_LW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* Saved host state */
7878c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, SP, SP, -(int)sizeof(struct pt_regs));
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/*
7908c2ecf20Sopenharmony_ci	 * XXXKYMA do we need to load the host ASID, maybe not because the
7918c2ecf20Sopenharmony_ci	 * kernel entries are marked GLOBAL, need to verify
7928c2ecf20Sopenharmony_ci	 */
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* Restore host scratch registers, as we'll have clobbered them */
7958c2ecf20Sopenharmony_ci	kvm_mips_build_restore_scratch(&p, K0, SP);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/* Restore RDHWR access */
7988c2ecf20Sopenharmony_ci	UASM_i_LA_mostly(&p, K0, (long)&hwrena);
7998c2ecf20Sopenharmony_ci	uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0);
8008c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_HWRENA);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* Jump to handler */
8038c2ecf20Sopenharmony_ci	/*
8048c2ecf20Sopenharmony_ci	 * XXXKYMA: not sure if this is safe, how large is the stack??
8058c2ecf20Sopenharmony_ci	 * Now jump to the kvm_mips_handle_exit() to see if we can deal
8068c2ecf20Sopenharmony_ci	 * with this in the kernel
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci	uasm_i_move(&p, A0, S0);
8098c2ecf20Sopenharmony_ci	UASM_i_LA(&p, T9, (unsigned long)kvm_mips_handle_exit);
8108c2ecf20Sopenharmony_ci	uasm_i_jalr(&p, RA, T9);
8118c2ecf20Sopenharmony_ci	 UASM_i_ADDIU(&p, SP, SP, -CALLFRAME_SIZ);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	p = kvm_mips_build_ret_from_exit(p);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	return p;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci/**
8218c2ecf20Sopenharmony_ci * kvm_mips_build_ret_from_exit() - Assemble guest exit return handler.
8228c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
8238c2ecf20Sopenharmony_ci *
8248c2ecf20Sopenharmony_ci * Assemble the code to handle the return from kvm_mips_handle_exit(), either
8258c2ecf20Sopenharmony_ci * resuming the guest or returning to the host depending on the return value.
8268c2ecf20Sopenharmony_ci *
8278c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_from_exit(void *addr)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	u32 *p = addr;
8328c2ecf20Sopenharmony_ci	struct uasm_label labels[2];
8338c2ecf20Sopenharmony_ci	struct uasm_reloc relocs[2];
8348c2ecf20Sopenharmony_ci	struct uasm_label *l = labels;
8358c2ecf20Sopenharmony_ci	struct uasm_reloc *r = relocs;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	memset(labels, 0, sizeof(labels));
8388c2ecf20Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/* Return from handler Make sure interrupts are disabled */
8418c2ecf20Sopenharmony_ci	uasm_i_di(&p, ZERO);
8428c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/*
8458c2ecf20Sopenharmony_ci	 * XXXKYMA: k0/k1 could have been blown away if we processed
8468c2ecf20Sopenharmony_ci	 * an exception while we were handling the exception from the
8478c2ecf20Sopenharmony_ci	 * guest, reload k1
8488c2ecf20Sopenharmony_ci	 */
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	uasm_i_move(&p, K1, S0);
8518c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, K1, K1, offsetof(struct kvm_vcpu, arch));
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	/*
8548c2ecf20Sopenharmony_ci	 * Check return value, should tell us if we are returning to the
8558c2ecf20Sopenharmony_ci	 * host (handle I/O etc)or resuming the guest
8568c2ecf20Sopenharmony_ci	 */
8578c2ecf20Sopenharmony_ci	uasm_i_andi(&p, T0, V0, RESUME_HOST);
8588c2ecf20Sopenharmony_ci	uasm_il_bnez(&p, &r, T0, label_return_to_host);
8598c2ecf20Sopenharmony_ci	 uasm_i_nop(&p);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	p = kvm_mips_build_ret_to_guest(p);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	uasm_l_return_to_host(&l, p);
8648c2ecf20Sopenharmony_ci	p = kvm_mips_build_ret_to_host(p);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	return p;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci/**
8728c2ecf20Sopenharmony_ci * kvm_mips_build_ret_to_guest() - Assemble code to return to the guest.
8738c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
8748c2ecf20Sopenharmony_ci *
8758c2ecf20Sopenharmony_ci * Assemble the code to handle return from the guest exit handler
8768c2ecf20Sopenharmony_ci * (kvm_mips_handle_exit()) back to the guest.
8778c2ecf20Sopenharmony_ci *
8788c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
8798c2ecf20Sopenharmony_ci */
8808c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_to_guest(void *addr)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	u32 *p = addr;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* Put the saved pointer to vcpu (s0) back into the scratch register */
8858c2ecf20Sopenharmony_ci	UASM_i_MTC0(&p, S0, scratch_vcpu[0], scratch_vcpu[1]);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Load up the Guest EBASE to minimize the window where BEV is set */
8888c2ecf20Sopenharmony_ci	UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* Switch EBASE back to the one used by KVM */
8918c2ecf20Sopenharmony_ci	uasm_i_mfc0(&p, V1, C0_STATUS);
8928c2ecf20Sopenharmony_ci	uasm_i_lui(&p, AT, ST0_BEV >> 16);
8938c2ecf20Sopenharmony_ci	uasm_i_or(&p, K0, V1, AT);
8948c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_STATUS);
8958c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
8968c2ecf20Sopenharmony_ci	build_set_exc_base(&p, T0);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	/* Setup status register for running guest in UM */
8998c2ecf20Sopenharmony_ci	uasm_i_ori(&p, V1, V1, ST0_EXL | KSU_USER | ST0_IE);
9008c2ecf20Sopenharmony_ci	UASM_i_LA(&p, AT, ~(ST0_CU0 | ST0_MX | ST0_SX | ST0_UX));
9018c2ecf20Sopenharmony_ci	uasm_i_and(&p, V1, V1, AT);
9028c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, V1, C0_STATUS);
9038c2ecf20Sopenharmony_ci	uasm_i_ehb(&p);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	p = kvm_mips_build_enter_guest(p);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	return p;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/**
9118c2ecf20Sopenharmony_ci * kvm_mips_build_ret_to_host() - Assemble code to return to the host.
9128c2ecf20Sopenharmony_ci * @addr:	Address to start writing code.
9138c2ecf20Sopenharmony_ci *
9148c2ecf20Sopenharmony_ci * Assemble the code to handle return from the guest exit handler
9158c2ecf20Sopenharmony_ci * (kvm_mips_handle_exit()) back to the host, i.e. to the caller of the vcpu_run
9168c2ecf20Sopenharmony_ci * function generated by kvm_mips_build_vcpu_run().
9178c2ecf20Sopenharmony_ci *
9188c2ecf20Sopenharmony_ci * Returns:	Next address after end of written function.
9198c2ecf20Sopenharmony_ci */
9208c2ecf20Sopenharmony_cistatic void *kvm_mips_build_ret_to_host(void *addr)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	u32 *p = addr;
9238c2ecf20Sopenharmony_ci	unsigned int i;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* EBASE is already pointing to Linux */
9268c2ecf20Sopenharmony_ci	UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, host_stack), K1);
9278c2ecf20Sopenharmony_ci	UASM_i_ADDIU(&p, K1, K1, -(int)sizeof(struct pt_regs));
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/*
9308c2ecf20Sopenharmony_ci	 * r2/v0 is the return code, shift it down by 2 (arithmetic)
9318c2ecf20Sopenharmony_ci	 * to recover the err code
9328c2ecf20Sopenharmony_ci	 */
9338c2ecf20Sopenharmony_ci	uasm_i_sra(&p, K0, V0, 2);
9348c2ecf20Sopenharmony_ci	uasm_i_move(&p, V0, K0);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	/* Load context saved on the host stack */
9378c2ecf20Sopenharmony_ci	for (i = 16; i < 31; ++i) {
9388c2ecf20Sopenharmony_ci		if (i == 24)
9398c2ecf20Sopenharmony_ci			i = 28;
9408c2ecf20Sopenharmony_ci		UASM_i_LW(&p, i, offsetof(struct pt_regs, regs[i]), K1);
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* Restore RDHWR access */
9448c2ecf20Sopenharmony_ci	UASM_i_LA_mostly(&p, K0, (long)&hwrena);
9458c2ecf20Sopenharmony_ci	uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0);
9468c2ecf20Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_HWRENA);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	/* Restore RA, which is the address we will return to */
9498c2ecf20Sopenharmony_ci	UASM_i_LW(&p, RA, offsetof(struct pt_regs, regs[RA]), K1);
9508c2ecf20Sopenharmony_ci	uasm_i_jr(&p, RA);
9518c2ecf20Sopenharmony_ci	 uasm_i_nop(&p);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	return p;
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
956