#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/proc.h>
#include "arm64.h"
#include "hyp.h"
struct hypctx;
uint64_t VMM_HYP_FUNC(do_call_guest)(struct hypctx *);
static void
vmm_hyp_reg_store_pmu_debug(struct hypctx *hypctx, bool guest)
{
uint64_t dfr0;
hypctx_write_sys_reg(hypctx, DBGCLAIMSET_EL1,
READ_SPECIALREG(dbgclaimset_el1));
dfr0 = READ_SPECIALREG(id_aa64dfr0_el1);
switch (ID_AA64DFR0_BRPs_VAL(dfr0) - 1) {
#define STORE_DBG_BRP(x) \
case x: \
hypctx_write_sys_reg(hypctx, DBGBCR_EL1(x), \
READ_SPECIALREG(dbgbcr ## x ## _el1)); \
hypctx_write_sys_reg(hypctx, DBGBVR_EL1(x), \
READ_SPECIALREG(dbgbvr ## x ## _el1))
STORE_DBG_BRP(15);
STORE_DBG_BRP(14);
STORE_DBG_BRP(13);
STORE_DBG_BRP(12);
STORE_DBG_BRP(11);
STORE_DBG_BRP(10);
STORE_DBG_BRP(9);
STORE_DBG_BRP(8);
STORE_DBG_BRP(7);
STORE_DBG_BRP(6);
STORE_DBG_BRP(5);
STORE_DBG_BRP(4);
STORE_DBG_BRP(3);
STORE_DBG_BRP(2);
STORE_DBG_BRP(1);
default:
STORE_DBG_BRP(0);
#undef STORE_DBG_BRP
}
switch (ID_AA64DFR0_WRPs_VAL(dfr0) - 1) {
#define STORE_DBG_WRP(x) \
case x: \
hypctx_write_sys_reg(hypctx, DBGWCR_EL1(x), \
READ_SPECIALREG(dbgwcr ## x ## _el1)); \
hypctx_write_sys_reg(hypctx, DBGWVR_EL1(x), \
READ_SPECIALREG(dbgwvr ## x ## _el1))
STORE_DBG_WRP(15);
STORE_DBG_WRP(14);
STORE_DBG_WRP(13);
STORE_DBG_WRP(12);
STORE_DBG_WRP(11);
STORE_DBG_WRP(10);
STORE_DBG_WRP(9);
STORE_DBG_WRP(8);
STORE_DBG_WRP(7);
STORE_DBG_WRP(6);
STORE_DBG_WRP(5);
STORE_DBG_WRP(4);
STORE_DBG_WRP(3);
STORE_DBG_WRP(2);
STORE_DBG_WRP(1);
default:
STORE_DBG_WRP(0);
#undef STORE_DBG_WRP
}
hypctx_write_sys_reg(hypctx, PMCR_EL0,
READ_SPECIALREG(pmcr_el0));
hypctx_write_sys_reg(hypctx, PMCCNTR_EL0,
READ_SPECIALREG(pmccntr_el0));
hypctx_write_sys_reg(hypctx, PMCCFILTR_EL0,
READ_SPECIALREG(pmccfiltr_el0));
hypctx_write_sys_reg(hypctx, PMUSERENR_EL0,
READ_SPECIALREG(pmuserenr_el0));
hypctx_write_sys_reg(hypctx, PMSELR_EL0,
READ_SPECIALREG(pmselr_el0));
hypctx_write_sys_reg(hypctx, PMXEVCNTR_EL0,
READ_SPECIALREG(pmxevcntr_el0));
hypctx_write_sys_reg(hypctx, PMCNTENSET_EL0,
READ_SPECIALREG(pmcntenset_el0));
hypctx_write_sys_reg(hypctx, PMINTENSET_EL1,
READ_SPECIALREG(pmintenset_el1));
hypctx_write_sys_reg(hypctx, PMOVSSET_EL0,
READ_SPECIALREG(pmovsset_el0));
switch ((hypctx_read_sys_reg(hypctx, PMCR_EL0) & PMCR_N_MASK) >>
PMCR_N_SHIFT) {
#define STORE_PMU(x) \
case (x + 1): \
hypctx_write_sys_reg(hypctx, PMEVCNTR_EL0(x), \
READ_SPECIALREG(pmevcntr ## x ## _el0)); \
hypctx_write_sys_reg(hypctx, PMEVTYPER_EL0(x), \
READ_SPECIALREG(pmevtyper ## x ## _el0))
STORE_PMU(30);
STORE_PMU(29);
STORE_PMU(28);
STORE_PMU(27);
STORE_PMU(26);
STORE_PMU(25);
STORE_PMU(24);
STORE_PMU(23);
STORE_PMU(22);
STORE_PMU(21);
STORE_PMU(20);
STORE_PMU(19);
STORE_PMU(18);
STORE_PMU(17);
STORE_PMU(16);
STORE_PMU(15);
STORE_PMU(14);
STORE_PMU(13);
STORE_PMU(12);
STORE_PMU(11);
STORE_PMU(10);
STORE_PMU(9);
STORE_PMU(8);
STORE_PMU(7);
STORE_PMU(6);
STORE_PMU(5);
STORE_PMU(4);
STORE_PMU(3);
STORE_PMU(2);
STORE_PMU(1);
STORE_PMU(0);
default:
break;
#undef STORE_PMU
}
if (!guest)
hypctx_write_sys_reg(hypctx, HOST_MDCR_EL2,
READ_SPECIALREG(mdcr_el2));
}
static void
vmm_hyp_reg_store_vgic(struct hypctx *hypctx, bool guest)
{
hypctx_write_sys_reg(hypctx, HOST_ICH_HCR_EL2,
READ_SPECIALREG(ich_hcr_el2));
hypctx_write_sys_reg(hypctx, HOST_ICH_VMCR_EL2,
READ_SPECIALREG(ich_vmcr_el2));
if (!guest)
return;
hypctx_write_sys_reg(hypctx, HOST_ICH_EISR_EL2,
READ_SPECIALREG(ich_eisr_el2));
hypctx_write_sys_reg(hypctx, HOST_ICH_ELRSR_EL2,
READ_SPECIALREG(ich_elrsr_el2));
hypctx_write_sys_reg(hypctx, HOST_ICH_MISR_EL2,
READ_SPECIALREG(ich_misr_el2));
switch (hypctx->vgic_v3.ich_lr_num - 1) {
#define STORE_LR(x) \
case x: \
hypctx_write_sys_reg(hypctx, HOST_ICH_LR_EL2(x), \
READ_SPECIALREG(ich_lr ## x ##_el2))
STORE_LR(15);
STORE_LR(14);
STORE_LR(13);
STORE_LR(12);
STORE_LR(11);
STORE_LR(10);
STORE_LR(9);
STORE_LR(8);
STORE_LR(7);
STORE_LR(6);
STORE_LR(5);
STORE_LR(4);
STORE_LR(3);
STORE_LR(2);
STORE_LR(1);
default:
STORE_LR(0);
#undef STORE_LR
}
switch (hypctx->vgic_v3.ich_apr_num - 1) {
#define STORE_APR(x) \
case x: \
hypctx_write_sys_reg(hypctx, HOST_ICH_AP0R_EL2(x), \
READ_SPECIALREG(ich_ap0r ## x ##_el2)); \
hypctx_write_sys_reg(hypctx, HOST_ICH_AP1R_EL2(x), \
READ_SPECIALREG(ich_ap1r ## x ##_el2))
STORE_APR(3);
STORE_APR(2);
STORE_APR(1);
default:
STORE_APR(0);
#undef STORE_APR
}
}
static void
vmm_hyp_reg_store_timer(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
bool ecv_poff;
if (guest) {
hypctx->vtimer_cpu.cntkctl_el1 =
READ_SPECIALREG(EL1_REG(CNTKCTL));
hypctx->vtimer_cpu.virt_timer.cntx_cval_el0 =
READ_SPECIALREG(EL0_REG(CNTV_CVAL));
hypctx->vtimer_cpu.virt_timer.cntx_ctl_el0 =
READ_SPECIALREG(EL0_REG(CNTV_CTL));
} else {
hypctx->vtimer_cpu.cntkctl_el1 =
READ_SPECIALREG(cntkctl_el1);
hypctx_write_sys_reg(hypctx, HOST_CNTHCTL_EL2,
READ_SPECIALREG(cnthctl_el2));
hypctx_write_sys_reg(hypctx, HOST_CNTVOFF_EL2,
READ_SPECIALREG(cntvoff_el2));
}
ecv_poff = (hypctx_read_sys_reg(hypctx, HOST_CNTHCTL_EL2) &
CNTHCTL_ECV_EN) != 0;
if (guest_or_nonvhe(guest) && ecv_poff) {
hypctx->vtimer_cpu.phys_timer.cntx_cval_el0 =
READ_SPECIALREG(EL0_REG(CNTP_CVAL));
hypctx->vtimer_cpu.phys_timer.cntx_ctl_el0 =
READ_SPECIALREG(EL0_REG(CNTP_CTL));
}
}
static void
vmm_hyp_reg_store_special(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
hypctx_write_sys_reg(hypctx, HOST_SP_EL1, READ_SPECIALREG(sp_el1));
hypctx_write_sys_reg(hypctx, HOST_ELR_EL2, READ_SPECIALREG(elr_el2));
hypctx_write_sys_reg(hypctx, HOST_SPSR_EL2, READ_SPECIALREG(spsr_el2));
if (guest) {
hypctx_write_sys_reg(hypctx, HOST_ESR_EL2,
READ_SPECIALREG(esr_el2));
hypctx_write_sys_reg(hypctx, HOST_FAR_EL2,
READ_SPECIALREG(far_el2));
hypctx_write_sys_reg(hypctx, PAR_EL1,
READ_SPECIALREG(par_el1));
}
hypctx_write_sys_reg(hypctx, SP_EL0, READ_SPECIALREG(sp_el0));
hypctx_write_sys_reg(hypctx, TPIDR_EL0, READ_SPECIALREG(tpidr_el0));
hypctx_write_sys_reg(hypctx, TPIDRRO_EL0, READ_SPECIALREG(tpidrro_el0));
hypctx_write_sys_reg(hypctx, TPIDR_EL1, READ_SPECIALREG(tpidr_el1));
hypctx_write_sys_reg(hypctx, ACTLR_EL1, READ_SPECIALREG(actlr_el1));
hypctx_write_sys_reg(hypctx, CSSELR_EL1, READ_SPECIALREG(csselr_el1));
hypctx_write_sys_reg(hypctx, MDCCINT_EL1, READ_SPECIALREG(mdccint_el1));
hypctx_write_sys_reg(hypctx, MDSCR_EL1, READ_SPECIALREG(mdscr_el1));
if (guest_or_nonvhe(guest)) {
hypctx_write_sys_reg(hypctx, ELR_EL1,
READ_SPECIALREG(EL1_REG(ELR)));
hypctx_write_sys_reg(hypctx, VBAR_EL1,
READ_SPECIALREG(EL1_REG(VBAR)));
hypctx_write_sys_reg(hypctx, AFSR0_EL1,
READ_SPECIALREG(EL1_REG(AFSR0)));
hypctx_write_sys_reg(hypctx, AFSR1_EL1,
READ_SPECIALREG(EL1_REG(AFSR1)));
hypctx_write_sys_reg(hypctx, AMAIR_EL1,
READ_SPECIALREG(EL1_REG(AMAIR)));
hypctx_write_sys_reg(hypctx, CONTEXTIDR_EL1,
READ_SPECIALREG(EL1_REG(CONTEXTIDR)));
hypctx_write_sys_reg(hypctx, CPACR_EL1,
READ_SPECIALREG(EL1_REG(CPACR)));
hypctx_write_sys_reg(hypctx, ESR_EL1,
READ_SPECIALREG(EL1_REG(ESR)));
hypctx_write_sys_reg(hypctx, FAR_EL1,
READ_SPECIALREG(EL1_REG(FAR)));
hypctx_write_sys_reg(hypctx, MAIR_EL1,
READ_SPECIALREG(EL1_REG(MAIR)));
hypctx_write_sys_reg(hypctx, SCTLR_EL1,
READ_SPECIALREG(EL1_REG(SCTLR)));
hypctx_write_sys_reg(hypctx, SPSR_EL1,
READ_SPECIALREG(EL1_REG(SPSR)));
hypctx_write_sys_reg(hypctx, TCR_EL1,
READ_SPECIALREG(EL1_REG(TCR)));
hypctx_write_sys_reg(hypctx, TCR2_EL1, 0);
hypctx_write_sys_reg(hypctx, TTBR0_EL1,
READ_SPECIALREG(EL1_REG(TTBR0)));
hypctx_write_sys_reg(hypctx, TTBR1_EL1,
READ_SPECIALREG(EL1_REG(TTBR1)));
}
hypctx_write_sys_reg(hypctx, HOST_CPTR_EL2,
READ_SPECIALREG(cptr_el2));
hypctx_write_sys_reg(hypctx, HOST_HCR_EL2,
READ_SPECIALREG(hcr_el2));
hypctx_write_sys_reg(hypctx, HOST_VPIDR_EL2,
READ_SPECIALREG(vpidr_el2));
hypctx_write_sys_reg(hypctx, HOST_VMPIDR_EL2,
READ_SPECIALREG(vmpidr_el2));
#ifndef VMM_VHE
if (!guest && ((hyp->feats & HYP_FEAT_HCX) != 0)) {
hypctx_write_sys_reg(hypctx, HOST_HCRX_EL2,
READ_SPECIALREG(MRS_REG_ALT_NAME(HCRX_EL2)));
}
#endif
}
static void
vmm_hyp_reg_restore_special(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
WRITE_SPECIALREG(hcr_el2, hypctx_read_sys_reg(hypctx, HOST_HCR_EL2));
if (guest_or_nonvhe(guest) && ((hyp->feats & HYP_FEAT_HCX) != 0)) {
WRITE_SPECIALREG(HCRX_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HCRX_EL2));
}
isb();
#ifdef VMM_VHE
if (guest) {
if ((hyp->feats & HYP_FEAT_FGT) != 0) {
WRITE_SPECIALREG(HDFGWTR_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HDFGWTR_EL2));
WRITE_SPECIALREG(HFGITR_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGITR_EL2));
WRITE_SPECIALREG(HFGRTR_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGRTR_EL2));
WRITE_SPECIALREG(HFGWTR_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGWTR_EL2));
}
if ((hyp->feats & HYP_FEAT_FGT2) != 0) {
WRITE_SPECIALREG(HDFGRTR2_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HDFGRTR2_EL2));
WRITE_SPECIALREG(HDFGWTR2_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HDFGWTR2_EL2));
WRITE_SPECIALREG(HFGITR2_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGITR2_EL2));
WRITE_SPECIALREG(HFGRTR2_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGRTR2_EL2));
WRITE_SPECIALREG(HFGWTR2_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_HFGWTR2_EL2));
}
}
#endif
WRITE_SPECIALREG(sp_el0, hypctx_read_sys_reg(hypctx, SP_EL0));
WRITE_SPECIALREG(tpidr_el0, hypctx_read_sys_reg(hypctx, TPIDR_EL0));
WRITE_SPECIALREG(tpidrro_el0, hypctx_read_sys_reg(hypctx, TPIDRRO_EL0));
WRITE_SPECIALREG(tpidr_el1, hypctx_read_sys_reg(hypctx, TPIDR_EL1));
WRITE_SPECIALREG(actlr_el1, hypctx_read_sys_reg(hypctx, ACTLR_EL1));
WRITE_SPECIALREG(csselr_el1, hypctx_read_sys_reg(hypctx, CSSELR_EL1));
WRITE_SPECIALREG(mdccint_el1, hypctx_read_sys_reg(hypctx, MDCCINT_EL1));
WRITE_SPECIALREG(mdscr_el1, hypctx_read_sys_reg(hypctx, MDSCR_EL1));
if (guest_or_nonvhe(guest)) {
WRITE_SPECIALREG(EL1_REG(ELR),
hypctx_read_sys_reg(hypctx, ELR_EL1));
WRITE_SPECIALREG(EL1_REG(VBAR),
hypctx_read_sys_reg(hypctx, VBAR_EL1));
WRITE_SPECIALREG(EL1_REG(AFSR0),
hypctx_read_sys_reg(hypctx, AFSR0_EL1));
WRITE_SPECIALREG(EL1_REG(AFSR1),
hypctx_read_sys_reg(hypctx, AFSR1_EL1));
WRITE_SPECIALREG(EL1_REG(AMAIR),
hypctx_read_sys_reg(hypctx, AMAIR_EL1));
WRITE_SPECIALREG(EL1_REG(CONTEXTIDR),
hypctx_read_sys_reg(hypctx, CONTEXTIDR_EL1));
WRITE_SPECIALREG(EL1_REG(CPACR),
hypctx_read_sys_reg(hypctx, CPACR_EL1));
WRITE_SPECIALREG(EL1_REG(ESR),
hypctx_read_sys_reg(hypctx, ESR_EL1));
WRITE_SPECIALREG(EL1_REG(FAR),
hypctx_read_sys_reg(hypctx, FAR_EL1));
WRITE_SPECIALREG(EL1_REG(MAIR),
hypctx_read_sys_reg(hypctx, MAIR_EL1));
WRITE_SPECIALREG(EL1_REG(SCTLR),
hypctx_read_sys_reg(hypctx, SCTLR_EL1));
WRITE_SPECIALREG(EL1_REG(SPSR),
hypctx_read_sys_reg(hypctx, SPSR_EL1));
WRITE_SPECIALREG(EL1_REG(TCR),
hypctx_read_sys_reg(hypctx, TCR_EL1));
WRITE_SPECIALREG(EL1_REG(TTBR0),
hypctx_read_sys_reg(hypctx, TTBR0_EL1));
WRITE_SPECIALREG(EL1_REG(TTBR1),
hypctx_read_sys_reg(hypctx, TTBR1_EL1));
}
if (guest) {
WRITE_SPECIALREG(par_el1, hypctx_read_sys_reg(hypctx, PAR_EL1));
}
WRITE_SPECIALREG(cptr_el2,
hypctx_read_sys_reg(hypctx, HOST_CPTR_EL2));
WRITE_SPECIALREG(vpidr_el2,
hypctx_read_sys_reg(hypctx, HOST_VPIDR_EL2));
WRITE_SPECIALREG(vmpidr_el2,
hypctx_read_sys_reg(hypctx, HOST_VMPIDR_EL2));
WRITE_SPECIALREG(sp_el1, hypctx_read_sys_reg(hypctx, HOST_SP_EL1));
WRITE_SPECIALREG(elr_el2, hypctx_read_sys_reg(hypctx, HOST_ELR_EL2));
WRITE_SPECIALREG(spsr_el2, hypctx_read_sys_reg(hypctx, HOST_SPSR_EL2));
}
static void
vmm_hyp_reg_restore_pmu_debug(struct hypctx *hypctx)
{
uint64_t dfr0;
WRITE_SPECIALREG(pmcr_el0,
hypctx_read_sys_reg(hypctx, PMCR_EL0));
WRITE_SPECIALREG(pmccntr_el0,
hypctx_read_sys_reg(hypctx, PMCCNTR_EL0));
WRITE_SPECIALREG(pmccfiltr_el0,
hypctx_read_sys_reg(hypctx, PMCCFILTR_EL0));
WRITE_SPECIALREG(pmuserenr_el0,
hypctx_read_sys_reg(hypctx, PMUSERENR_EL0));
WRITE_SPECIALREG(pmselr_el0,
hypctx_read_sys_reg(hypctx, PMSELR_EL0));
WRITE_SPECIALREG(pmxevcntr_el0,
hypctx_read_sys_reg(hypctx, PMXEVCNTR_EL0));
WRITE_SPECIALREG(pmcntenclr_el0, ~0ul);
WRITE_SPECIALREG(pmcntenset_el0,
hypctx_read_sys_reg(hypctx, PMCNTENSET_EL0));
WRITE_SPECIALREG(pmintenclr_el1, ~0ul);
WRITE_SPECIALREG(pmintenset_el1,
hypctx_read_sys_reg(hypctx, PMINTENSET_EL1));
WRITE_SPECIALREG(pmovsclr_el0, ~0ul);
WRITE_SPECIALREG(pmovsset_el0,
hypctx_read_sys_reg(hypctx, PMOVSSET_EL0));
switch ((hypctx_read_sys_reg(hypctx, PMCR_EL0) & PMCR_N_MASK) >>
PMCR_N_SHIFT) {
#define LOAD_PMU(x) \
case (x + 1): \
WRITE_SPECIALREG(pmevcntr ## x ## _el0, \
hypctx_read_sys_reg(hypctx, PMEVCNTR_EL0(x))); \
WRITE_SPECIALREG(pmevtyper ## x ## _el0, \
hypctx_read_sys_reg(hypctx, PMEVTYPER_EL0(x)))
LOAD_PMU(30);
LOAD_PMU(29);
LOAD_PMU(28);
LOAD_PMU(27);
LOAD_PMU(26);
LOAD_PMU(25);
LOAD_PMU(24);
LOAD_PMU(23);
LOAD_PMU(22);
LOAD_PMU(21);
LOAD_PMU(20);
LOAD_PMU(19);
LOAD_PMU(18);
LOAD_PMU(17);
LOAD_PMU(16);
LOAD_PMU(15);
LOAD_PMU(14);
LOAD_PMU(13);
LOAD_PMU(12);
LOAD_PMU(11);
LOAD_PMU(10);
LOAD_PMU(9);
LOAD_PMU(8);
LOAD_PMU(7);
LOAD_PMU(6);
LOAD_PMU(5);
LOAD_PMU(4);
LOAD_PMU(3);
LOAD_PMU(2);
LOAD_PMU(1);
LOAD_PMU(0);
default:
break;
#undef LOAD_PMU
}
WRITE_SPECIALREG(dbgclaimclr_el1, ~0ul);
WRITE_SPECIALREG(dbgclaimclr_el1,
hypctx_read_sys_reg(hypctx, DBGCLAIMSET_EL1));
dfr0 = READ_SPECIALREG(id_aa64dfr0_el1);
switch (ID_AA64DFR0_BRPs_VAL(dfr0) - 1) {
#define LOAD_DBG_BRP(x) \
case x: \
WRITE_SPECIALREG(dbgbcr ## x ## _el1, \
hypctx_read_sys_reg(hypctx, DBGBCR_EL1(x))); \
WRITE_SPECIALREG(dbgbvr ## x ## _el1, \
hypctx_read_sys_reg(hypctx, DBGBVR_EL1(x)))
LOAD_DBG_BRP(15);
LOAD_DBG_BRP(14);
LOAD_DBG_BRP(13);
LOAD_DBG_BRP(12);
LOAD_DBG_BRP(11);
LOAD_DBG_BRP(10);
LOAD_DBG_BRP(9);
LOAD_DBG_BRP(8);
LOAD_DBG_BRP(7);
LOAD_DBG_BRP(6);
LOAD_DBG_BRP(5);
LOAD_DBG_BRP(4);
LOAD_DBG_BRP(3);
LOAD_DBG_BRP(2);
LOAD_DBG_BRP(1);
default:
LOAD_DBG_BRP(0);
#undef LOAD_DBG_BRP
}
switch (ID_AA64DFR0_WRPs_VAL(dfr0) - 1) {
#define LOAD_DBG_WRP(x) \
case x: \
WRITE_SPECIALREG(dbgwcr ## x ## _el1, \
hypctx_read_sys_reg(hypctx, DBGWCR_EL1(x))); \
WRITE_SPECIALREG(dbgwvr ## x ## _el1, \
hypctx_read_sys_reg(hypctx, DBGWVR_EL1(x)))
LOAD_DBG_WRP(15);
LOAD_DBG_WRP(14);
LOAD_DBG_WRP(13);
LOAD_DBG_WRP(12);
LOAD_DBG_WRP(11);
LOAD_DBG_WRP(10);
LOAD_DBG_WRP(9);
LOAD_DBG_WRP(8);
LOAD_DBG_WRP(7);
LOAD_DBG_WRP(6);
LOAD_DBG_WRP(5);
LOAD_DBG_WRP(4);
LOAD_DBG_WRP(3);
LOAD_DBG_WRP(2);
LOAD_DBG_WRP(1);
default:
LOAD_DBG_WRP(0);
#undef LOAD_DBG_WRP
}
}
static void
vmm_hyp_reg_restore_timer(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
bool ecv_poff;
ecv_poff = (hypctx_read_sys_reg(hypctx, HOST_CNTHCTL_EL2) &
CNTHCTL_ECV_EN) != 0;
WRITE_SPECIALREG(cnthctl_el2,
hypctx_read_sys_reg(hypctx, HOST_CNTHCTL_EL2));
WRITE_SPECIALREG(cntvoff_el2,
hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2));
if (guest) {
WRITE_SPECIALREG(EL1_REG(CNTKCTL),
hypctx->vtimer_cpu.cntkctl_el1);
WRITE_SPECIALREG(EL0_REG(CNTV_CVAL),
hypctx->vtimer_cpu.virt_timer.cntx_cval_el0);
WRITE_SPECIALREG(EL0_REG(CNTV_CTL),
hypctx->vtimer_cpu.virt_timer.cntx_ctl_el0);
if (ecv_poff) {
WRITE_SPECIALREG(CNTPOFF_EL2_REG,
hypctx_read_sys_reg(hypctx, HOST_CNTVOFF_EL2));
isb();
}
} else {
WRITE_SPECIALREG(cntkctl_el1, hypctx->vtimer_cpu.cntkctl_el1);
}
if (guest_or_nonvhe(guest) && ecv_poff) {
WRITE_SPECIALREG(EL0_REG(CNTP_CVAL),
hypctx->vtimer_cpu.phys_timer.cntx_cval_el0);
WRITE_SPECIALREG(EL0_REG(CNTP_CTL),
hypctx->vtimer_cpu.phys_timer.cntx_ctl_el0);
}
}
static void
vmm_hyp_reg_restore_vgic(struct hypctx *hypctx, bool guest)
{
WRITE_SPECIALREG(ich_hcr_el2,
hypctx_read_sys_reg(hypctx, HOST_ICH_HCR_EL2));
WRITE_SPECIALREG(ich_vmcr_el2,
hypctx_read_sys_reg(hypctx, HOST_ICH_VMCR_EL2));
if (!guest)
return;
switch (hypctx->vgic_v3.ich_lr_num - 1) {
#define LOAD_LR(x) \
case x: \
WRITE_SPECIALREG(ich_lr ## x ##_el2, \
hypctx_read_sys_reg(hypctx, HOST_ICH_LR_EL2(x)))
LOAD_LR(15);
LOAD_LR(14);
LOAD_LR(13);
LOAD_LR(12);
LOAD_LR(11);
LOAD_LR(10);
LOAD_LR(9);
LOAD_LR(8);
LOAD_LR(7);
LOAD_LR(6);
LOAD_LR(5);
LOAD_LR(4);
LOAD_LR(3);
LOAD_LR(2);
LOAD_LR(1);
default:
LOAD_LR(0);
#undef LOAD_LR
}
switch (hypctx->vgic_v3.ich_apr_num - 1) {
#define LOAD_APR(x) \
case x: \
WRITE_SPECIALREG(ich_ap0r ## x ##_el2, \
hypctx_read_sys_reg(hypctx, HOST_ICH_AP0R_EL2(x))); \
WRITE_SPECIALREG(ich_ap1r ## x ##_el2, \
hypctx_read_sys_reg(hypctx, HOST_ICH_AP1R_EL2(x)))
LOAD_APR(3);
LOAD_APR(2);
LOAD_APR(1);
default:
LOAD_APR(0);
#undef LOAD_APR
}
}
static void
vmm_hyp_reg_store(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
vmm_hyp_reg_store_vgic(hypctx, guest);
vmm_hyp_reg_store_timer(hypctx, hyp, guest);
vmm_hyp_reg_store_pmu_debug(hypctx, guest);
vmm_hyp_reg_store_special(hypctx, hyp, guest);
}
static void
vmm_hyp_reg_restore(struct hypctx *hypctx, struct hyp *hyp, bool guest)
{
vmm_hyp_reg_restore_special(hypctx, hyp, guest);
vmm_hyp_reg_restore_pmu_debug(hypctx);
vmm_hyp_reg_restore_timer(hypctx, hyp, guest);
vmm_hyp_reg_restore_vgic(hypctx, guest);
}
static void
vmm_hyp_handle_guest_exit(struct hypctx *hypctx, uint64_t *ret)
{
bool hpfar_valid;
uint64_t s1e1r, hpfar_el2, host_esr_el2, host_far_el2;
hpfar_valid = true;
host_esr_el2 = hypctx_read_sys_reg(hypctx, HOST_ESR_EL2);
if (*ret == EXCP_TYPE_EL1_SYNC) {
switch (ESR_ELx_EXCEPTION(host_esr_el2)) {
case EXCP_INSN_ABORT_L:
case EXCP_DATA_ABORT_L:
if ((host_esr_el2 & ISS_DATA_S1PTW) != 0)
break;
switch (host_esr_el2 & ISS_DATA_DFSC_MASK) {
case ISS_DATA_DFSC_PF_L1:
case ISS_DATA_DFSC_PF_L2:
case ISS_DATA_DFSC_PF_L3:
hpfar_valid = false;
break;
}
break;
}
}
if (hpfar_valid) {
hypctx_write_sys_reg(hypctx, HOST_HPFAR_EL2,
READ_SPECIALREG(hpfar_el2));
} else {
host_far_el2 = hypctx_read_sys_reg(hypctx, HOST_FAR_EL2);
s1e1r = arm64_address_translate_s1e1r(host_far_el2);
if (PAR_SUCCESS(s1e1r)) {
hpfar_el2 = (s1e1r & PAR_PA_MASK) >> PAR_PA_SHIFT;
hpfar_el2 <<= HPFAR_EL2_FIPA_SHIFT;
hypctx_write_sys_reg(hypctx, HOST_HPFAR_EL2, hpfar_el2);
} else {
*ret = EXCP_TYPE_REENTER;
}
}
}
static uint64_t
__vmm_hyp_call_guest(struct hypctx *hypctx, struct hypctx *host_hypctx)
{
uint64_t ret;
WRITE_SPECIALREG(vttbr_el2, hypctx_read_sys_reg(hypctx, HOST_VTTBR_EL2));
WRITE_SPECIALREG(mdcr_el2, hypctx_read_sys_reg(hypctx, HOST_MDCR_EL2));
ret = VMM_HYP_FUNC(do_call_guest)(hypctx);
WRITE_SPECIALREG(mdcr_el2, hypctx_read_sys_reg(host_hypctx, HOST_MDCR_EL2));
isb();
return ret;
}
static uint64_t
vmm_hyp_call_guest(struct hyp *hyp, struct hypctx *hypctx)
{
struct hypctx host_hypctx;
uint64_t ret;
host_hypctx.vncr_regs = hypctx->host_vncr_regs;
#ifndef VMM_VHE
host_hypctx.el2_vncr_addr = hypctx->el2_host_vncr_addr;
#endif
vmm_hyp_reg_store(&host_hypctx, hyp, false);
vmm_hyp_reg_restore(hypctx, hyp, true);
ret = __vmm_hyp_call_guest(hypctx, &host_hypctx);
vmm_hyp_reg_store(hypctx, hyp, true);
vmm_hyp_handle_guest_exit(hypctx, &ret);
vmm_hyp_reg_restore(&host_hypctx, hyp, false);
return (ret);
}
VMM_STATIC uint64_t
VMM_HYP_FUNC(enter_guest)(struct hyp *hyp, struct hypctx *hypctx)
{
uint64_t ret;
do {
ret = vmm_hyp_call_guest(hyp, hypctx);
} while (ret == EXCP_TYPE_REENTER);
return (ret);
}
VMM_STATIC uint64_t
VMM_HYP_FUNC(read_reg)(uint64_t reg)
{
switch (reg) {
case HYP_REG_ICH_VTR:
return (READ_SPECIALREG(ich_vtr_el2));
}
return (0);
}
VMM_STATIC void
VMM_HYP_FUNC(clean_s2_tlbi)(void)
{
dsb(ishst);
__asm __volatile("tlbi alle1is");
dsb(ish);
}
VMM_STATIC void
VMM_HYP_FUNC(s2_tlbi_range)(uint64_t vttbr, vm_offset_t sva, vm_offset_t eva,
bool final_only)
{
uint64_t end, r, start;
uint64_t host_vttbr;
#ifdef VMM_VHE
uint64_t host_tcr;
#endif
dsb(ishst);
#define TLBI_VA_SHIFT 12
#define TLBI_VA_MASK ((1ul << 44) - 1)
#define TLBI_VA(addr) (((addr) >> TLBI_VA_SHIFT) & TLBI_VA_MASK)
#define TLBI_VA_L3_INCR (L3_SIZE >> TLBI_VA_SHIFT)
host_vttbr = READ_SPECIALREG(vttbr_el2);
WRITE_SPECIALREG(vttbr_el2, vttbr);
isb();
#ifdef VMM_VHE
host_tcr = READ_SPECIALREG(tcr_el2);
WRITE_SPECIALREG(tcr_el2, host_tcr & ~HCR_TGE);
isb();
#endif
start = TLBI_VA(sva);
end = TLBI_VA(eva);
for (r = start; r < end; r += TLBI_VA_L3_INCR) {
if (final_only)
__asm __volatile("tlbi ipas2le1is, %0" : : "r"(r));
else
__asm __volatile("tlbi ipas2e1is, %0" : : "r"(r));
}
dsb(ish);
__asm __volatile("tlbi vmalle1is");
dsb(ish);
isb();
#ifdef VMM_VHE
WRITE_SPECIALREG(tcr_el2, host_tcr);
isb();
#endif
WRITE_SPECIALREG(vttbr_el2, host_vttbr);
isb();
}
VMM_STATIC void
VMM_HYP_FUNC(s2_tlbi_all)(uint64_t vttbr)
{
uint64_t host_vttbr;
dsb(ishst);
host_vttbr = READ_SPECIALREG(vttbr_el2);
WRITE_SPECIALREG(vttbr_el2, vttbr);
isb();
__asm __volatile("tlbi vmalls12e1is");
dsb(ish);
isb();
WRITE_SPECIALREG(vttbr_el2, host_vttbr);
isb();
}