162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Generation of main entry point for the guest, exception handling. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. 962306a36Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 2016 Imagination Technologies Ltd. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kvm_host.h> 1562306a36Sopenharmony_ci#include <linux/log2.h> 1662306a36Sopenharmony_ci#include <asm/mmu_context.h> 1762306a36Sopenharmony_ci#include <asm/msa.h> 1862306a36Sopenharmony_ci#include <asm/setup.h> 1962306a36Sopenharmony_ci#include <asm/tlbex.h> 2062306a36Sopenharmony_ci#include <asm/uasm.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Register names */ 2362306a36Sopenharmony_ci#define ZERO 0 2462306a36Sopenharmony_ci#define AT 1 2562306a36Sopenharmony_ci#define V0 2 2662306a36Sopenharmony_ci#define V1 3 2762306a36Sopenharmony_ci#define A0 4 2862306a36Sopenharmony_ci#define A1 5 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI32 3162306a36Sopenharmony_ci#define T0 8 3262306a36Sopenharmony_ci#define T1 9 3362306a36Sopenharmony_ci#define T2 10 3462306a36Sopenharmony_ci#define T3 11 3562306a36Sopenharmony_ci#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 3862306a36Sopenharmony_ci#define T0 12 3962306a36Sopenharmony_ci#define T1 13 4062306a36Sopenharmony_ci#define T2 14 4162306a36Sopenharmony_ci#define T3 15 4262306a36Sopenharmony_ci#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define S0 16 4562306a36Sopenharmony_ci#define S1 17 4662306a36Sopenharmony_ci#define T9 25 4762306a36Sopenharmony_ci#define K0 26 4862306a36Sopenharmony_ci#define K1 27 4962306a36Sopenharmony_ci#define GP 28 5062306a36Sopenharmony_ci#define SP 29 5162306a36Sopenharmony_ci#define RA 31 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Some CP0 registers */ 5462306a36Sopenharmony_ci#define C0_PWBASE 5, 5 5562306a36Sopenharmony_ci#define C0_HWRENA 7, 0 5662306a36Sopenharmony_ci#define C0_BADVADDR 8, 0 5762306a36Sopenharmony_ci#define C0_BADINSTR 8, 1 5862306a36Sopenharmony_ci#define C0_BADINSTRP 8, 2 5962306a36Sopenharmony_ci#define C0_PGD 9, 7 6062306a36Sopenharmony_ci#define C0_ENTRYHI 10, 0 6162306a36Sopenharmony_ci#define C0_GUESTCTL1 10, 4 6262306a36Sopenharmony_ci#define C0_STATUS 12, 0 6362306a36Sopenharmony_ci#define C0_GUESTCTL0 12, 6 6462306a36Sopenharmony_ci#define C0_CAUSE 13, 0 6562306a36Sopenharmony_ci#define C0_EPC 14, 0 6662306a36Sopenharmony_ci#define C0_EBASE 15, 1 6762306a36Sopenharmony_ci#define C0_CONFIG5 16, 5 6862306a36Sopenharmony_ci#define C0_DDATA_LO 28, 3 6962306a36Sopenharmony_ci#define C0_ERROREPC 30, 0 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define CALLFRAME_SIZ 32 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 7462306a36Sopenharmony_ci#define ST0_KX_IF_64 ST0_KX 7562306a36Sopenharmony_ci#else 7662306a36Sopenharmony_ci#define ST0_KX_IF_64 0 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic unsigned int scratch_vcpu[2] = { C0_DDATA_LO }; 8062306a36Sopenharmony_cistatic unsigned int scratch_tmp[2] = { C0_ERROREPC }; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cienum label_id { 8362306a36Sopenharmony_ci label_fpu_1 = 1, 8462306a36Sopenharmony_ci label_msa_1, 8562306a36Sopenharmony_ci label_return_to_host, 8662306a36Sopenharmony_ci label_kernel_asid, 8762306a36Sopenharmony_ci label_exit_common, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciUASM_L_LA(_fpu_1) 9162306a36Sopenharmony_ciUASM_L_LA(_msa_1) 9262306a36Sopenharmony_ciUASM_L_LA(_return_to_host) 9362306a36Sopenharmony_ciUASM_L_LA(_kernel_asid) 9462306a36Sopenharmony_ciUASM_L_LA(_exit_common) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void *kvm_mips_build_enter_guest(void *addr); 9762306a36Sopenharmony_cistatic void *kvm_mips_build_ret_from_exit(void *addr); 9862306a36Sopenharmony_cistatic void *kvm_mips_build_ret_to_guest(void *addr); 9962306a36Sopenharmony_cistatic void *kvm_mips_build_ret_to_host(void *addr); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * The version of this function in tlbex.c uses current_cpu_type(), but for KVM 10362306a36Sopenharmony_ci * we assume symmetry. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic int c0_kscratch(void) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return 31; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * kvm_mips_entry_setup() - Perform global setup for entry code. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Perform global setup for entry code, such as choosing a scratch register. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Returns: 0 on success. 11662306a36Sopenharmony_ci * -errno on failure. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ciint kvm_mips_entry_setup(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * We prefer to use KScratchN registers if they are available over the 12262306a36Sopenharmony_ci * defaults above, which may not work on all cores. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci unsigned int kscratch_mask = cpu_data[0].kscratch_mask; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (pgd_reg != -1) 12762306a36Sopenharmony_ci kscratch_mask &= ~BIT(pgd_reg); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Pick a scratch register for storing VCPU */ 13062306a36Sopenharmony_ci if (kscratch_mask) { 13162306a36Sopenharmony_ci scratch_vcpu[0] = c0_kscratch(); 13262306a36Sopenharmony_ci scratch_vcpu[1] = ffs(kscratch_mask) - 1; 13362306a36Sopenharmony_ci kscratch_mask &= ~BIT(scratch_vcpu[1]); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Pick a scratch register to use as a temp for saving state */ 13762306a36Sopenharmony_ci if (kscratch_mask) { 13862306a36Sopenharmony_ci scratch_tmp[0] = c0_kscratch(); 13962306a36Sopenharmony_ci scratch_tmp[1] = ffs(kscratch_mask) - 1; 14062306a36Sopenharmony_ci kscratch_mask &= ~BIT(scratch_tmp[1]); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void kvm_mips_build_save_scratch(u32 **p, unsigned int tmp, 14762306a36Sopenharmony_ci unsigned int frame) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci /* Save the VCPU scratch register value in cp0_epc of the stack frame */ 15062306a36Sopenharmony_ci UASM_i_MFC0(p, tmp, scratch_vcpu[0], scratch_vcpu[1]); 15162306a36Sopenharmony_ci UASM_i_SW(p, tmp, offsetof(struct pt_regs, cp0_epc), frame); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Save the temp scratch register value in cp0_cause of stack frame */ 15462306a36Sopenharmony_ci if (scratch_tmp[0] == c0_kscratch()) { 15562306a36Sopenharmony_ci UASM_i_MFC0(p, tmp, scratch_tmp[0], scratch_tmp[1]); 15662306a36Sopenharmony_ci UASM_i_SW(p, tmp, offsetof(struct pt_regs, cp0_cause), frame); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void kvm_mips_build_restore_scratch(u32 **p, unsigned int tmp, 16162306a36Sopenharmony_ci unsigned int frame) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Restore host scratch register values saved by 16562306a36Sopenharmony_ci * kvm_mips_build_save_scratch(). 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci UASM_i_LW(p, tmp, offsetof(struct pt_regs, cp0_epc), frame); 16862306a36Sopenharmony_ci UASM_i_MTC0(p, tmp, scratch_vcpu[0], scratch_vcpu[1]); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (scratch_tmp[0] == c0_kscratch()) { 17162306a36Sopenharmony_ci UASM_i_LW(p, tmp, offsetof(struct pt_regs, cp0_cause), frame); 17262306a36Sopenharmony_ci UASM_i_MTC0(p, tmp, scratch_tmp[0], scratch_tmp[1]); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * build_set_exc_base() - Assemble code to write exception base address. 17862306a36Sopenharmony_ci * @p: Code buffer pointer. 17962306a36Sopenharmony_ci * @reg: Source register (generated code may set WG bit in @reg). 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Assemble code to modify the exception base address in the EBase register, 18262306a36Sopenharmony_ci * using the appropriately sized access and setting the WG bit if necessary. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic inline void build_set_exc_base(u32 **p, unsigned int reg) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci if (cpu_has_ebase_wg) { 18762306a36Sopenharmony_ci /* Set WG so that all the bits get written */ 18862306a36Sopenharmony_ci uasm_i_ori(p, reg, reg, MIPS_EBASE_WG); 18962306a36Sopenharmony_ci UASM_i_MTC0(p, reg, C0_EBASE); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci uasm_i_mtc0(p, reg, C0_EBASE); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * kvm_mips_build_vcpu_run() - Assemble function to start running a guest VCPU. 19762306a36Sopenharmony_ci * @addr: Address to start writing code. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Assemble the start of the vcpu_run function to run a guest VCPU. The function 20062306a36Sopenharmony_ci * conforms to the following prototype: 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * int vcpu_run(struct kvm_vcpu *vcpu); 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * The exit from the guest and return to the caller is handled by the code 20562306a36Sopenharmony_ci * generated by kvm_mips_build_ret_to_host(). 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * Returns: Next address after end of written function. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_civoid *kvm_mips_build_vcpu_run(void *addr) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci u32 *p = addr; 21262306a36Sopenharmony_ci unsigned int i; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * A0: vcpu 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* k0/k1 not being used in host kernel context */ 21962306a36Sopenharmony_ci UASM_i_ADDIU(&p, K1, SP, -(int)sizeof(struct pt_regs)); 22062306a36Sopenharmony_ci for (i = 16; i < 32; ++i) { 22162306a36Sopenharmony_ci if (i == 24) 22262306a36Sopenharmony_ci i = 28; 22362306a36Sopenharmony_ci UASM_i_SW(&p, i, offsetof(struct pt_regs, regs[i]), K1); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Save host status */ 22762306a36Sopenharmony_ci uasm_i_mfc0(&p, V0, C0_STATUS); 22862306a36Sopenharmony_ci UASM_i_SW(&p, V0, offsetof(struct pt_regs, cp0_status), K1); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Save scratch registers, will be used to store pointer to vcpu etc */ 23162306a36Sopenharmony_ci kvm_mips_build_save_scratch(&p, V1, K1); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* VCPU scratch register has pointer to vcpu */ 23462306a36Sopenharmony_ci UASM_i_MTC0(&p, A0, scratch_vcpu[0], scratch_vcpu[1]); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Offset into vcpu->arch */ 23762306a36Sopenharmony_ci UASM_i_ADDIU(&p, K1, A0, offsetof(struct kvm_vcpu, arch)); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Save the host stack to VCPU, used for exception processing 24162306a36Sopenharmony_ci * when we exit from the Guest 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci UASM_i_SW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Save the kernel gp as well */ 24662306a36Sopenharmony_ci UASM_i_SW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Setup status register for running the guest in UM, interrupts 25062306a36Sopenharmony_ci * are disabled 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci UASM_i_LA(&p, K0, ST0_EXL | KSU_USER | ST0_BEV | ST0_KX_IF_64); 25362306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_STATUS); 25462306a36Sopenharmony_ci uasm_i_ehb(&p); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* load up the new EBASE */ 25762306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1); 25862306a36Sopenharmony_ci build_set_exc_base(&p, K0); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Now that the new EBASE has been loaded, unset BEV, set 26262306a36Sopenharmony_ci * interrupt mask as it was but make sure that timer interrupts 26362306a36Sopenharmony_ci * are enabled 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci uasm_i_addiu(&p, K0, ZERO, ST0_EXL | KSU_USER | ST0_IE | ST0_KX_IF_64); 26662306a36Sopenharmony_ci uasm_i_andi(&p, V0, V0, ST0_IM); 26762306a36Sopenharmony_ci uasm_i_or(&p, K0, K0, V0); 26862306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_STATUS); 26962306a36Sopenharmony_ci uasm_i_ehb(&p); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci p = kvm_mips_build_enter_guest(p); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return p; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * kvm_mips_build_enter_guest() - Assemble code to resume guest execution. 27862306a36Sopenharmony_ci * @addr: Address to start writing code. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Assemble the code to resume guest execution. This code is common between the 28162306a36Sopenharmony_ci * initial entry into the guest from the host, and returning from the exit 28262306a36Sopenharmony_ci * handler back to the guest. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * Returns: Next address after end of written function. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic void *kvm_mips_build_enter_guest(void *addr) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci u32 *p = addr; 28962306a36Sopenharmony_ci unsigned int i; 29062306a36Sopenharmony_ci struct uasm_label labels[2]; 29162306a36Sopenharmony_ci struct uasm_reloc relocs[2]; 29262306a36Sopenharmony_ci struct uasm_label __maybe_unused *l = labels; 29362306a36Sopenharmony_ci struct uasm_reloc __maybe_unused *r = relocs; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci memset(labels, 0, sizeof(labels)); 29662306a36Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Set Guest EPC */ 29962306a36Sopenharmony_ci UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, pc), K1); 30062306a36Sopenharmony_ci UASM_i_MTC0(&p, T0, C0_EPC); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Save normal linux process pgd (VZ guarantees pgd_reg is set) */ 30362306a36Sopenharmony_ci if (cpu_has_ldpte) 30462306a36Sopenharmony_ci UASM_i_MFC0(&p, K0, C0_PWBASE); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci UASM_i_MFC0(&p, K0, c0_kscratch(), pgd_reg); 30762306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_pgd), K1); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Set up KVM GPA pgd. 31162306a36Sopenharmony_ci * This does roughly the same as TLBMISS_HANDLER_SETUP_PGD(): 31262306a36Sopenharmony_ci * - call tlbmiss_handler_setup_pgd(mm->pgd) 31362306a36Sopenharmony_ci * - write mm->pgd into CP0_PWBase 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * We keep S0 pointing at struct kvm so we can load the ASID below. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci UASM_i_LW(&p, S0, (int)offsetof(struct kvm_vcpu, kvm) - 31862306a36Sopenharmony_ci (int)offsetof(struct kvm_vcpu, arch), K1); 31962306a36Sopenharmony_ci UASM_i_LW(&p, A0, offsetof(struct kvm, arch.gpa_mm.pgd), S0); 32062306a36Sopenharmony_ci UASM_i_LA(&p, T9, (unsigned long)tlbmiss_handler_setup_pgd); 32162306a36Sopenharmony_ci uasm_i_jalr(&p, RA, T9); 32262306a36Sopenharmony_ci /* delay slot */ 32362306a36Sopenharmony_ci if (cpu_has_htw) 32462306a36Sopenharmony_ci UASM_i_MTC0(&p, A0, C0_PWBASE); 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci uasm_i_nop(&p); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Set GM bit to setup eret to VZ guest context */ 32962306a36Sopenharmony_ci uasm_i_addiu(&p, V1, ZERO, 1); 33062306a36Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_GUESTCTL0); 33162306a36Sopenharmony_ci uasm_i_ins(&p, K0, V1, MIPS_GCTL0_GM_SHIFT, 1); 33262306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_GUESTCTL0); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (cpu_has_guestid) { 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * Set root mode GuestID, so that root TLB refill handler can 33762306a36Sopenharmony_ci * use the correct GuestID in the root TLB. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Get current GuestID */ 34162306a36Sopenharmony_ci uasm_i_mfc0(&p, T0, C0_GUESTCTL1); 34262306a36Sopenharmony_ci /* Set GuestCtl1.RID = GuestCtl1.ID */ 34362306a36Sopenharmony_ci uasm_i_ext(&p, T1, T0, MIPS_GCTL1_ID_SHIFT, 34462306a36Sopenharmony_ci MIPS_GCTL1_ID_WIDTH); 34562306a36Sopenharmony_ci uasm_i_ins(&p, T0, T1, MIPS_GCTL1_RID_SHIFT, 34662306a36Sopenharmony_ci MIPS_GCTL1_RID_WIDTH); 34762306a36Sopenharmony_ci uasm_i_mtc0(&p, T0, C0_GUESTCTL1); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* GuestID handles dealiasing so we don't need to touch ASID */ 35062306a36Sopenharmony_ci goto skip_asid_restore; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Root ASID Dealias (RAD) */ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Save host ASID */ 35662306a36Sopenharmony_ci UASM_i_MFC0(&p, K0, C0_ENTRYHI); 35762306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_entryhi), 35862306a36Sopenharmony_ci K1); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Set the root ASID for the Guest */ 36162306a36Sopenharmony_ci UASM_i_ADDIU(&p, T1, S0, 36262306a36Sopenharmony_ci offsetof(struct kvm, arch.gpa_mm.context.asid)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* t1: contains the base of the ASID array, need to get the cpu id */ 36562306a36Sopenharmony_ci /* smp_processor_id */ 36662306a36Sopenharmony_ci uasm_i_lw(&p, T2, offsetof(struct thread_info, cpu), GP); 36762306a36Sopenharmony_ci /* index the ASID array */ 36862306a36Sopenharmony_ci uasm_i_sll(&p, T2, T2, ilog2(sizeof(long))); 36962306a36Sopenharmony_ci UASM_i_ADDU(&p, T3, T1, T2); 37062306a36Sopenharmony_ci UASM_i_LW(&p, K0, 0, T3); 37162306a36Sopenharmony_ci#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * reuse ASID array offset 37462306a36Sopenharmony_ci * cpuinfo_mips is a multiple of sizeof(long) 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci uasm_i_addiu(&p, T3, ZERO, sizeof(struct cpuinfo_mips)/sizeof(long)); 37762306a36Sopenharmony_ci uasm_i_mul(&p, T2, T2, T3); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci UASM_i_LA_mostly(&p, AT, (long)&cpu_data[0].asid_mask); 38062306a36Sopenharmony_ci UASM_i_ADDU(&p, AT, AT, T2); 38162306a36Sopenharmony_ci UASM_i_LW(&p, T2, uasm_rel_lo((long)&cpu_data[0].asid_mask), AT); 38262306a36Sopenharmony_ci uasm_i_and(&p, K0, K0, T2); 38362306a36Sopenharmony_ci#else 38462306a36Sopenharmony_ci uasm_i_andi(&p, K0, K0, MIPS_ENTRYHI_ASID); 38562306a36Sopenharmony_ci#endif 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Set up KVM VZ root ASID (!guestid) */ 38862306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_ENTRYHI); 38962306a36Sopenharmony_ciskip_asid_restore: 39062306a36Sopenharmony_ci uasm_i_ehb(&p); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Disable RDHWR access */ 39362306a36Sopenharmony_ci uasm_i_mtc0(&p, ZERO, C0_HWRENA); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* load the guest context from VCPU and return */ 39662306a36Sopenharmony_ci for (i = 1; i < 32; ++i) { 39762306a36Sopenharmony_ci /* Guest k0/k1 loaded later */ 39862306a36Sopenharmony_ci if (i == K0 || i == K1) 39962306a36Sopenharmony_ci continue; 40062306a36Sopenharmony_ci UASM_i_LW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 40462306a36Sopenharmony_ci /* Restore hi/lo */ 40562306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, hi), K1); 40662306a36Sopenharmony_ci uasm_i_mthi(&p, K0); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, lo), K1); 40962306a36Sopenharmony_ci uasm_i_mtlo(&p, K0); 41062306a36Sopenharmony_ci#endif 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Restore the guest's k0/k1 registers */ 41362306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1); 41462306a36Sopenharmony_ci UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Jump to guest */ 41762306a36Sopenharmony_ci uasm_i_eret(&p); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return p; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/** 42562306a36Sopenharmony_ci * kvm_mips_build_tlb_refill_exception() - Assemble TLB refill handler. 42662306a36Sopenharmony_ci * @addr: Address to start writing code. 42762306a36Sopenharmony_ci * @handler: Address of common handler (within range of @addr). 42862306a36Sopenharmony_ci * 42962306a36Sopenharmony_ci * Assemble TLB refill exception fast path handler for guest execution. 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Returns: Next address after end of written function. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_civoid *kvm_mips_build_tlb_refill_exception(void *addr, void *handler) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci u32 *p = addr; 43662306a36Sopenharmony_ci struct uasm_label labels[2]; 43762306a36Sopenharmony_ci struct uasm_reloc relocs[2]; 43862306a36Sopenharmony_ci#ifndef CONFIG_CPU_LOONGSON64 43962306a36Sopenharmony_ci struct uasm_label *l = labels; 44062306a36Sopenharmony_ci struct uasm_reloc *r = relocs; 44162306a36Sopenharmony_ci#endif 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci memset(labels, 0, sizeof(labels)); 44462306a36Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Save guest k1 into scratch register */ 44762306a36Sopenharmony_ci UASM_i_MTC0(&p, K1, scratch_tmp[0], scratch_tmp[1]); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Get the VCPU pointer from the VCPU scratch register */ 45062306a36Sopenharmony_ci UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Save guest k0 into VCPU structure */ 45362306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu, arch.gprs[K0]), K1); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Some of the common tlbex code uses current_cpu_type(). For KVM we 45762306a36Sopenharmony_ci * assume symmetry and just disable preemption to silence the warning. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci preempt_disable(); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 46262306a36Sopenharmony_ci UASM_i_MFC0(&p, K1, C0_PGD); 46362306a36Sopenharmony_ci uasm_i_lddir(&p, K0, K1, 3); /* global page dir */ 46462306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 46562306a36Sopenharmony_ci uasm_i_lddir(&p, K1, K0, 1); /* middle page dir */ 46662306a36Sopenharmony_ci#endif 46762306a36Sopenharmony_ci uasm_i_ldpte(&p, K1, 0); /* even */ 46862306a36Sopenharmony_ci uasm_i_ldpte(&p, K1, 1); /* odd */ 46962306a36Sopenharmony_ci uasm_i_tlbwr(&p); 47062306a36Sopenharmony_ci#else 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * Now for the actual refill bit. A lot of this can be common with the 47362306a36Sopenharmony_ci * Linux TLB refill handler, however we don't need to handle so many 47462306a36Sopenharmony_ci * cases. We only need to handle user mode refills, and user mode runs 47562306a36Sopenharmony_ci * with 32-bit addressing. 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * Therefore the branch to label_vmalloc generated by build_get_pmde64() 47862306a36Sopenharmony_ci * that isn't resolved should never actually get taken and is harmless 47962306a36Sopenharmony_ci * to leave in place for now. 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci#ifdef CONFIG_64BIT 48362306a36Sopenharmony_ci build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ 48462306a36Sopenharmony_ci#else 48562306a36Sopenharmony_ci build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ 48662306a36Sopenharmony_ci#endif 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* we don't support huge pages yet */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci build_get_ptep(&p, K0, K1); 49162306a36Sopenharmony_ci build_update_entries(&p, K0, K1); 49262306a36Sopenharmony_ci build_tlb_write_entry(&p, &l, &r, tlb_random); 49362306a36Sopenharmony_ci#endif 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci preempt_enable(); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Get the VCPU pointer from the VCPU scratch register again */ 49862306a36Sopenharmony_ci UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Restore the guest's k0/k1 registers */ 50162306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu, arch.gprs[K0]), K1); 50262306a36Sopenharmony_ci uasm_i_ehb(&p); 50362306a36Sopenharmony_ci UASM_i_MFC0(&p, K1, scratch_tmp[0], scratch_tmp[1]); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Jump to guest */ 50662306a36Sopenharmony_ci uasm_i_eret(&p); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return p; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/** 51262306a36Sopenharmony_ci * kvm_mips_build_exception() - Assemble first level guest exception handler. 51362306a36Sopenharmony_ci * @addr: Address to start writing code. 51462306a36Sopenharmony_ci * @handler: Address of common handler (within range of @addr). 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * Assemble exception vector code for guest execution. The generated vector will 51762306a36Sopenharmony_ci * branch to the common exception handler generated by kvm_mips_build_exit(). 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * Returns: Next address after end of written function. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_civoid *kvm_mips_build_exception(void *addr, void *handler) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci u32 *p = addr; 52462306a36Sopenharmony_ci struct uasm_label labels[2]; 52562306a36Sopenharmony_ci struct uasm_reloc relocs[2]; 52662306a36Sopenharmony_ci struct uasm_label *l = labels; 52762306a36Sopenharmony_ci struct uasm_reloc *r = relocs; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci memset(labels, 0, sizeof(labels)); 53062306a36Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Save guest k1 into scratch register */ 53362306a36Sopenharmony_ci UASM_i_MTC0(&p, K1, scratch_tmp[0], scratch_tmp[1]); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Get the VCPU pointer from the VCPU scratch register */ 53662306a36Sopenharmony_ci UASM_i_MFC0(&p, K1, scratch_vcpu[0], scratch_vcpu[1]); 53762306a36Sopenharmony_ci UASM_i_ADDIU(&p, K1, K1, offsetof(struct kvm_vcpu, arch)); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Save guest k0 into VCPU structure */ 54062306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Branch to the common handler */ 54362306a36Sopenharmony_ci uasm_il_b(&p, &r, label_exit_common); 54462306a36Sopenharmony_ci uasm_i_nop(&p); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci uasm_l_exit_common(&l, handler); 54762306a36Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return p; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/** 55362306a36Sopenharmony_ci * kvm_mips_build_exit() - Assemble common guest exit handler. 55462306a36Sopenharmony_ci * @addr: Address to start writing code. 55562306a36Sopenharmony_ci * 55662306a36Sopenharmony_ci * Assemble the generic guest exit handling code. This is called by the 55762306a36Sopenharmony_ci * exception vectors (generated by kvm_mips_build_exception()), and calls 55862306a36Sopenharmony_ci * kvm_mips_handle_exit(), then either resumes the guest or returns to the host 55962306a36Sopenharmony_ci * depending on the return value. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Returns: Next address after end of written function. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_civoid *kvm_mips_build_exit(void *addr) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci u32 *p = addr; 56662306a36Sopenharmony_ci unsigned int i; 56762306a36Sopenharmony_ci struct uasm_label labels[3]; 56862306a36Sopenharmony_ci struct uasm_reloc relocs[3]; 56962306a36Sopenharmony_ci struct uasm_label *l = labels; 57062306a36Sopenharmony_ci struct uasm_reloc *r = relocs; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci memset(labels, 0, sizeof(labels)); 57362306a36Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Generic Guest exception handler. We end up here when the guest 57762306a36Sopenharmony_ci * does something that causes a trap to kernel mode. 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * Both k0/k1 registers will have already been saved (k0 into the vcpu 58062306a36Sopenharmony_ci * structure, and k1 into the scratch_tmp register). 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * The k1 register will already contain the kvm_vcpu_arch pointer. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Start saving Guest context to VCPU */ 58662306a36Sopenharmony_ci for (i = 0; i < 32; ++i) { 58762306a36Sopenharmony_ci /* Guest k0/k1 saved later */ 58862306a36Sopenharmony_ci if (i == K0 || i == K1) 58962306a36Sopenharmony_ci continue; 59062306a36Sopenharmony_ci UASM_i_SW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci#ifndef CONFIG_CPU_MIPSR6 59462306a36Sopenharmony_ci /* We need to save hi/lo and restore them on the way out */ 59562306a36Sopenharmony_ci uasm_i_mfhi(&p, T0); 59662306a36Sopenharmony_ci UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, hi), K1); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci uasm_i_mflo(&p, T0); 59962306a36Sopenharmony_ci UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, lo), K1); 60062306a36Sopenharmony_ci#endif 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Finally save guest k1 to VCPU */ 60362306a36Sopenharmony_ci uasm_i_ehb(&p); 60462306a36Sopenharmony_ci UASM_i_MFC0(&p, T0, scratch_tmp[0], scratch_tmp[1]); 60562306a36Sopenharmony_ci UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Now that context has been saved, we can use other registers */ 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Restore vcpu */ 61062306a36Sopenharmony_ci UASM_i_MFC0(&p, S0, scratch_vcpu[0], scratch_vcpu[1]); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Save Host level EPC, BadVaddr and Cause to VCPU, useful to process 61462306a36Sopenharmony_ci * the exception 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci UASM_i_MFC0(&p, K0, C0_EPC); 61762306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, pc), K1); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci UASM_i_MFC0(&p, K0, C0_BADVADDR); 62062306a36Sopenharmony_ci UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_badvaddr), 62162306a36Sopenharmony_ci K1); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_CAUSE); 62462306a36Sopenharmony_ci uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_cause), K1); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (cpu_has_badinstr) { 62762306a36Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_BADINSTR); 62862306a36Sopenharmony_ci uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, 62962306a36Sopenharmony_ci host_cp0_badinstr), K1); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (cpu_has_badinstrp) { 63362306a36Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_BADINSTRP); 63462306a36Sopenharmony_ci uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, 63562306a36Sopenharmony_ci host_cp0_badinstrp), K1); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Now restore the host state just enough to run the handlers */ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Switch EBASE to the one used by Linux */ 64162306a36Sopenharmony_ci /* load up the host EBASE */ 64262306a36Sopenharmony_ci uasm_i_mfc0(&p, V0, C0_STATUS); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci uasm_i_lui(&p, AT, ST0_BEV >> 16); 64562306a36Sopenharmony_ci uasm_i_or(&p, K0, V0, AT); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_STATUS); 64862306a36Sopenharmony_ci uasm_i_ehb(&p); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci UASM_i_LA_mostly(&p, K0, (long)&ebase); 65162306a36Sopenharmony_ci UASM_i_LW(&p, K0, uasm_rel_lo((long)&ebase), K0); 65262306a36Sopenharmony_ci build_set_exc_base(&p, K0); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (raw_cpu_has_fpu) { 65562306a36Sopenharmony_ci /* 65662306a36Sopenharmony_ci * If FPU is enabled, save FCR31 and clear it so that later 65762306a36Sopenharmony_ci * ctc1's don't trigger FPE for pending exceptions. 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci uasm_i_lui(&p, AT, ST0_CU1 >> 16); 66062306a36Sopenharmony_ci uasm_i_and(&p, V1, V0, AT); 66162306a36Sopenharmony_ci uasm_il_beqz(&p, &r, V1, label_fpu_1); 66262306a36Sopenharmony_ci uasm_i_nop(&p); 66362306a36Sopenharmony_ci uasm_i_cfc1(&p, T0, 31); 66462306a36Sopenharmony_ci uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.fcr31), 66562306a36Sopenharmony_ci K1); 66662306a36Sopenharmony_ci uasm_i_ctc1(&p, ZERO, 31); 66762306a36Sopenharmony_ci uasm_l_fpu_1(&l, p); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (cpu_has_msa) { 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * If MSA is enabled, save MSACSR and clear it so that later 67362306a36Sopenharmony_ci * instructions don't trigger MSAFPE for pending exceptions. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci uasm_i_mfc0(&p, T0, C0_CONFIG5); 67662306a36Sopenharmony_ci uasm_i_ext(&p, T0, T0, 27, 1); /* MIPS_CONF5_MSAEN */ 67762306a36Sopenharmony_ci uasm_il_beqz(&p, &r, T0, label_msa_1); 67862306a36Sopenharmony_ci uasm_i_nop(&p); 67962306a36Sopenharmony_ci uasm_i_cfcmsa(&p, T0, MSA_CSR); 68062306a36Sopenharmony_ci uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.msacsr), 68162306a36Sopenharmony_ci K1); 68262306a36Sopenharmony_ci uasm_i_ctcmsa(&p, MSA_CSR, ZERO); 68362306a36Sopenharmony_ci uasm_l_msa_1(&l, p); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Restore host ASID */ 68762306a36Sopenharmony_ci if (!cpu_has_guestid) { 68862306a36Sopenharmony_ci UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, host_entryhi), 68962306a36Sopenharmony_ci K1); 69062306a36Sopenharmony_ci UASM_i_MTC0(&p, K0, C0_ENTRYHI); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci * Set up normal Linux process pgd. 69562306a36Sopenharmony_ci * This does roughly the same as TLBMISS_HANDLER_SETUP_PGD(): 69662306a36Sopenharmony_ci * - call tlbmiss_handler_setup_pgd(mm->pgd) 69762306a36Sopenharmony_ci * - write mm->pgd into CP0_PWBase 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_ci UASM_i_LW(&p, A0, 70062306a36Sopenharmony_ci offsetof(struct kvm_vcpu_arch, host_pgd), K1); 70162306a36Sopenharmony_ci UASM_i_LA(&p, T9, (unsigned long)tlbmiss_handler_setup_pgd); 70262306a36Sopenharmony_ci uasm_i_jalr(&p, RA, T9); 70362306a36Sopenharmony_ci /* delay slot */ 70462306a36Sopenharmony_ci if (cpu_has_htw) 70562306a36Sopenharmony_ci UASM_i_MTC0(&p, A0, C0_PWBASE); 70662306a36Sopenharmony_ci else 70762306a36Sopenharmony_ci uasm_i_nop(&p); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* Clear GM bit so we don't enter guest mode when EXL is cleared */ 71062306a36Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_GUESTCTL0); 71162306a36Sopenharmony_ci uasm_i_ins(&p, K0, ZERO, MIPS_GCTL0_GM_SHIFT, 1); 71262306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_GUESTCTL0); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* Save GuestCtl0 so we can access GExcCode after CPU migration */ 71562306a36Sopenharmony_ci uasm_i_sw(&p, K0, 71662306a36Sopenharmony_ci offsetof(struct kvm_vcpu_arch, host_cp0_guestctl0), K1); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (cpu_has_guestid) { 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * Clear root mode GuestID, so that root TLB operations use the 72162306a36Sopenharmony_ci * root GuestID in the root TLB. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci uasm_i_mfc0(&p, T0, C0_GUESTCTL1); 72462306a36Sopenharmony_ci /* Set GuestCtl1.RID = MIPS_GCTL1_ROOT_GUESTID (i.e. 0) */ 72562306a36Sopenharmony_ci uasm_i_ins(&p, T0, ZERO, MIPS_GCTL1_RID_SHIFT, 72662306a36Sopenharmony_ci MIPS_GCTL1_RID_WIDTH); 72762306a36Sopenharmony_ci uasm_i_mtc0(&p, T0, C0_GUESTCTL1); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Now that the new EBASE has been loaded, unset BEV and KSU_USER */ 73162306a36Sopenharmony_ci uasm_i_addiu(&p, AT, ZERO, ~(ST0_EXL | KSU_USER | ST0_IE)); 73262306a36Sopenharmony_ci uasm_i_and(&p, V0, V0, AT); 73362306a36Sopenharmony_ci uasm_i_lui(&p, AT, ST0_CU0 >> 16); 73462306a36Sopenharmony_ci uasm_i_or(&p, V0, V0, AT); 73562306a36Sopenharmony_ci#ifdef CONFIG_64BIT 73662306a36Sopenharmony_ci uasm_i_ori(&p, V0, V0, ST0_SX | ST0_UX); 73762306a36Sopenharmony_ci#endif 73862306a36Sopenharmony_ci uasm_i_mtc0(&p, V0, C0_STATUS); 73962306a36Sopenharmony_ci uasm_i_ehb(&p); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Load up host GP */ 74262306a36Sopenharmony_ci UASM_i_LW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Need a stack before we can jump to "C" */ 74562306a36Sopenharmony_ci UASM_i_LW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Saved host state */ 74862306a36Sopenharmony_ci UASM_i_ADDIU(&p, SP, SP, -(int)sizeof(struct pt_regs)); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* 75162306a36Sopenharmony_ci * XXXKYMA do we need to load the host ASID, maybe not because the 75262306a36Sopenharmony_ci * kernel entries are marked GLOBAL, need to verify 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Restore host scratch registers, as we'll have clobbered them */ 75662306a36Sopenharmony_ci kvm_mips_build_restore_scratch(&p, K0, SP); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Restore RDHWR access */ 75962306a36Sopenharmony_ci UASM_i_LA_mostly(&p, K0, (long)&hwrena); 76062306a36Sopenharmony_ci uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0); 76162306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_HWRENA); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Jump to handler */ 76462306a36Sopenharmony_ci /* 76562306a36Sopenharmony_ci * XXXKYMA: not sure if this is safe, how large is the stack?? 76662306a36Sopenharmony_ci * Now jump to the kvm_mips_handle_exit() to see if we can deal 76762306a36Sopenharmony_ci * with this in the kernel 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci uasm_i_move(&p, A0, S0); 77062306a36Sopenharmony_ci UASM_i_LA(&p, T9, (unsigned long)kvm_mips_handle_exit); 77162306a36Sopenharmony_ci uasm_i_jalr(&p, RA, T9); 77262306a36Sopenharmony_ci UASM_i_ADDIU(&p, SP, SP, -CALLFRAME_SIZ); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci p = kvm_mips_build_ret_from_exit(p); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return p; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/** 78262306a36Sopenharmony_ci * kvm_mips_build_ret_from_exit() - Assemble guest exit return handler. 78362306a36Sopenharmony_ci * @addr: Address to start writing code. 78462306a36Sopenharmony_ci * 78562306a36Sopenharmony_ci * Assemble the code to handle the return from kvm_mips_handle_exit(), either 78662306a36Sopenharmony_ci * resuming the guest or returning to the host depending on the return value. 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * Returns: Next address after end of written function. 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_cistatic void *kvm_mips_build_ret_from_exit(void *addr) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci u32 *p = addr; 79362306a36Sopenharmony_ci struct uasm_label labels[2]; 79462306a36Sopenharmony_ci struct uasm_reloc relocs[2]; 79562306a36Sopenharmony_ci struct uasm_label *l = labels; 79662306a36Sopenharmony_ci struct uasm_reloc *r = relocs; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci memset(labels, 0, sizeof(labels)); 79962306a36Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Return from handler Make sure interrupts are disabled */ 80262306a36Sopenharmony_ci uasm_i_di(&p, ZERO); 80362306a36Sopenharmony_ci uasm_i_ehb(&p); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* 80662306a36Sopenharmony_ci * XXXKYMA: k0/k1 could have been blown away if we processed 80762306a36Sopenharmony_ci * an exception while we were handling the exception from the 80862306a36Sopenharmony_ci * guest, reload k1 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci uasm_i_move(&p, K1, S0); 81262306a36Sopenharmony_ci UASM_i_ADDIU(&p, K1, K1, offsetof(struct kvm_vcpu, arch)); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* 81562306a36Sopenharmony_ci * Check return value, should tell us if we are returning to the 81662306a36Sopenharmony_ci * host (handle I/O etc)or resuming the guest 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_ci uasm_i_andi(&p, T0, V0, RESUME_HOST); 81962306a36Sopenharmony_ci uasm_il_bnez(&p, &r, T0, label_return_to_host); 82062306a36Sopenharmony_ci uasm_i_nop(&p); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci p = kvm_mips_build_ret_to_guest(p); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci uasm_l_return_to_host(&l, p); 82562306a36Sopenharmony_ci p = kvm_mips_build_ret_to_host(p); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return p; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/** 83362306a36Sopenharmony_ci * kvm_mips_build_ret_to_guest() - Assemble code to return to the guest. 83462306a36Sopenharmony_ci * @addr: Address to start writing code. 83562306a36Sopenharmony_ci * 83662306a36Sopenharmony_ci * Assemble the code to handle return from the guest exit handler 83762306a36Sopenharmony_ci * (kvm_mips_handle_exit()) back to the guest. 83862306a36Sopenharmony_ci * 83962306a36Sopenharmony_ci * Returns: Next address after end of written function. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_cistatic void *kvm_mips_build_ret_to_guest(void *addr) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci u32 *p = addr; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Put the saved pointer to vcpu (s0) back into the scratch register */ 84662306a36Sopenharmony_ci UASM_i_MTC0(&p, S0, scratch_vcpu[0], scratch_vcpu[1]); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* Load up the Guest EBASE to minimize the window where BEV is set */ 84962306a36Sopenharmony_ci UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Switch EBASE back to the one used by KVM */ 85262306a36Sopenharmony_ci uasm_i_mfc0(&p, V1, C0_STATUS); 85362306a36Sopenharmony_ci uasm_i_lui(&p, AT, ST0_BEV >> 16); 85462306a36Sopenharmony_ci uasm_i_or(&p, K0, V1, AT); 85562306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_STATUS); 85662306a36Sopenharmony_ci uasm_i_ehb(&p); 85762306a36Sopenharmony_ci build_set_exc_base(&p, T0); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Setup status register for running guest in UM */ 86062306a36Sopenharmony_ci uasm_i_ori(&p, V1, V1, ST0_EXL | KSU_USER | ST0_IE); 86162306a36Sopenharmony_ci UASM_i_LA(&p, AT, ~(ST0_CU0 | ST0_MX | ST0_SX | ST0_UX)); 86262306a36Sopenharmony_ci uasm_i_and(&p, V1, V1, AT); 86362306a36Sopenharmony_ci uasm_i_mtc0(&p, V1, C0_STATUS); 86462306a36Sopenharmony_ci uasm_i_ehb(&p); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci p = kvm_mips_build_enter_guest(p); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return p; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/** 87262306a36Sopenharmony_ci * kvm_mips_build_ret_to_host() - Assemble code to return to the host. 87362306a36Sopenharmony_ci * @addr: Address to start writing code. 87462306a36Sopenharmony_ci * 87562306a36Sopenharmony_ci * Assemble the code to handle return from the guest exit handler 87662306a36Sopenharmony_ci * (kvm_mips_handle_exit()) back to the host, i.e. to the caller of the vcpu_run 87762306a36Sopenharmony_ci * function generated by kvm_mips_build_vcpu_run(). 87862306a36Sopenharmony_ci * 87962306a36Sopenharmony_ci * Returns: Next address after end of written function. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic void *kvm_mips_build_ret_to_host(void *addr) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci u32 *p = addr; 88462306a36Sopenharmony_ci unsigned int i; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* EBASE is already pointing to Linux */ 88762306a36Sopenharmony_ci UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, host_stack), K1); 88862306a36Sopenharmony_ci UASM_i_ADDIU(&p, K1, K1, -(int)sizeof(struct pt_regs)); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* 89162306a36Sopenharmony_ci * r2/v0 is the return code, shift it down by 2 (arithmetic) 89262306a36Sopenharmony_ci * to recover the err code 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci uasm_i_sra(&p, K0, V0, 2); 89562306a36Sopenharmony_ci uasm_i_move(&p, V0, K0); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* Load context saved on the host stack */ 89862306a36Sopenharmony_ci for (i = 16; i < 31; ++i) { 89962306a36Sopenharmony_ci if (i == 24) 90062306a36Sopenharmony_ci i = 28; 90162306a36Sopenharmony_ci UASM_i_LW(&p, i, offsetof(struct pt_regs, regs[i]), K1); 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Restore RDHWR access */ 90562306a36Sopenharmony_ci UASM_i_LA_mostly(&p, K0, (long)&hwrena); 90662306a36Sopenharmony_ci uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0); 90762306a36Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_HWRENA); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Restore RA, which is the address we will return to */ 91062306a36Sopenharmony_ci UASM_i_LW(&p, RA, offsetof(struct pt_regs, regs[RA]), K1); 91162306a36Sopenharmony_ci uasm_i_jr(&p, RA); 91262306a36Sopenharmony_ci uasm_i_nop(&p); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return p; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 917