The HPFAR can be invalid if the stage 2 fault did not happen during a stage 1 page table walk (the ESR_EL2.S1PTW bit is clear) and one of the two following cases are true: 1). The fault was due to a permission fault 2). The processor carries errata 834220
Therefore, for all non S1PTW faults where we either have a permission fault or the errata workaround is enabled, we resolve the IPA using the AT instruction. Signed-off-by: Dongjiu Geng <[email protected]> --- hypervisor/arch/arm64/include/asm/paging.h | 8 ++ hypervisor/arch/arm64/include/asm/sysregs.h | 8 ++ hypervisor/arch/arm64/include/asm/traps.h | 2 + hypervisor/arch/arm64/mmio.c | 7 +- hypervisor/arch/arm64/traps.c | 89 +++++++++++++++++++-- 5 files changed, 105 insertions(+), 9 deletions(-) diff --git a/hypervisor/arch/arm64/include/asm/paging.h b/hypervisor/arch/arm64/include/asm/paging.h index e600cf58..4f0cb81c 100644 --- a/hypervisor/arch/arm64/include/asm/paging.h +++ b/hypervisor/arch/arm64/include/asm/paging.h @@ -198,6 +198,14 @@ unsigned int get_cpu_parange(void); | (cpu_parange_encoded << TCR_PS_SHIFT) \ | VTCR_RES1) +/* Flags for get fault ipa from gva */ +#define GV2M_READ (0u<<0) +#define GV2M_WRITE (1u<<0) + +/* Indicates address translation aborted */ +#define PAR_F (1UL) +#define PADDR_MASK ((1UL << 48) - 1UL) + int arm_paging_cell_init(struct cell *cell); void arm_paging_cell_destroy(struct cell *cell); diff --git a/hypervisor/arch/arm64/include/asm/sysregs.h b/hypervisor/arch/arm64/include/asm/sysregs.h index 868ef887..2c683832 100644 --- a/hypervisor/arch/arm64/include/asm/sysregs.h +++ b/hypervisor/arch/arm64/include/asm/sysregs.h @@ -117,6 +117,14 @@ #define ESR_IL(esr) GET_FIELD((esr), 25, 25) /* Instruction specific syndrome */ #define ESR_ISS(esr) GET_FIELD((esr), 24, 0) + +/* Fault status code of instruction specific syndrome */ +#define ESR_ISS_FSC(esr) GET_FIELD((esr), 5, 0) + +/* Shared ISS fault status code(IFSC/DFSC) for Data/Instruction aborts */ +#define ESR_ISS_FSC_TYPE (0x3C) +#define ESR_ISS_FSC_PERM (0x0C) + /* Exception classes values */ #define ESR_EC_UNKNOWN 0x00 #define ESR_EC_WFx 0x01 diff --git a/hypervisor/arch/arm64/include/asm/traps.h b/hypervisor/arch/arm64/include/asm/traps.h index a7c07624..0efedef1 100644 --- a/hypervisor/arch/arm64/include/asm/traps.h +++ b/hypervisor/arch/arm64/include/asm/traps.h @@ -25,6 +25,8 @@ struct trap_context { void arch_handle_trap(union registers *guest_regs); void arch_el2_abt(union registers *regs); +bool arch_get_fault_ipa(struct trap_context *ctx, unsigned long *ipa, + unsigned int flag); /* now include from arm-common */ #include_next <asm/traps.h> diff --git a/hypervisor/arch/arm64/mmio.c b/hypervisor/arch/arm64/mmio.c index 7fbfef75..70301ab3 100644 --- a/hypervisor/arch/arm64/mmio.c +++ b/hypervisor/arch/arm64/mmio.c @@ -43,7 +43,6 @@ enum trap_return arch_handle_dabt(struct trap_context *ctx) { enum mmio_result mmio_result; struct mmio_access mmio; - unsigned long hpfar; unsigned long hdfar; /* Decode the syndrome fields */ u32 iss = ESR_ISS(ctx->esr); @@ -57,10 +56,10 @@ enum trap_return arch_handle_dabt(struct trap_context *ctx) u32 is_write = iss >> 6 & 0x1; u32 size = 1 << sas; - arm_read_sysreg(HPFAR_EL2, hpfar); arm_read_sysreg(FAR_EL2, hdfar); - mmio.address = hpfar << 8; - mmio.address |= hdfar & 0xfff; + + if (!arch_get_fault_ipa(ctx, &mmio.address, GV2M_READ)) + return TRAP_HANDLED; /* Try again */ this_cpu_public()->stats[JAILHOUSE_CPU_STAT_VMEXITS_MMIO]++; diff --git a/hypervisor/arch/arm64/traps.c b/hypervisor/arch/arm64/traps.c index 488dd7f8..10441b4b 100644 --- a/hypervisor/arch/arm64/traps.c +++ b/hypervisor/arch/arm64/traps.c @@ -33,6 +33,85 @@ void arch_skip_instruction(struct trap_context *ctx) arm_write_sysreg(ELR_EL2, pc); } +static bool check_workaround_834220(void) +{ + unsigned long midr; + unsigned int variant, revision, part; + + arm_read_sysreg(MIDR_EL1, midr); + + variant = (midr >> 20) & 0xf; + revision = midr & 0xf; + part = (midr >> 4) & 0xfff; + + /* Cortex-A57 r0p0 - r1p2 */ + if (part == 0xD07 && variant <= 1 && revision <= 2) + return true; + + return false; +} + +static bool hpfar_is_not_valid(bool s1ptw, u8 fsc) +{ + /* + * The HPFAR can be invalid if the stage 2 fault did not + * happen during a stage 1 page table walk (the ESR_EL2.S1PTW + * bit is clear) and one of the two following cases are true: + * 1. The fault was due to a permission fault + * 2. The processor carries errata 834220 + * + */ + return (s1ptw == 0U) && (((fsc & ESR_ISS_FSC_TYPE) == ESR_ISS_FSC_PERM) || check_workaround_834220()); +} + +bool arch_get_fault_ipa(struct trap_context *ctx, unsigned long *ipa, unsigned int flags) +{ + unsigned long hpfar, hdfar, par, tmp; + + u32 s1ptw = ESR_ISS(ctx->esr) >> 7 & 0x1; + u8 fsc = ESR_ISS_FSC(ctx->esr); + + arm_read_sysreg(FAR_EL2, hdfar); + + if (hpfar_is_not_valid(s1ptw, fsc)) { + + /* Save current PAR_EL1 */ + arm_read_sysreg(PAR_EL1, tmp); + + /* + * Performs stage 1 address translation, with permissions as if + * writing to or reading from the given virtual address from EL1 + */ + if ( (flags & GV2M_WRITE) == GV2M_WRITE ) { + asm volatile ("at s1e1w, %0;" : : "r" (hdfar)); + } else { + asm volatile ("at s1e1r, %0;" : : "r" (hdfar)); + } + + isb(); + + /* The resulting address can be read from the PAR_EL1 */ + arm_read_sysreg(PAR_EL1, par); + + /* Recover current PAR_EL1 */ + arm_write_sysreg(PAR_EL1, tmp); + + /* If PAR_EL1.F = 1, address translation aborted */ + if ((par & PAR_F) == PAR_F) { + printk("Failed to ipa!\n"); + return false; + } else { + *ipa = (par & PADDR_MASK & PAGE_MASK) | (hdfar & (~PAGE_MASK)); + } + } else { + arm_read_sysreg(HPFAR_EL2, hpfar); + *ipa = hpfar << 8; + *ipa |= hdfar & 0xfff; + } + + return true; +} + static enum trap_return handle_hvc(struct trap_context *ctx) { unsigned long *regs = ctx->regs; @@ -71,7 +150,7 @@ static enum trap_return handle_sysreg(struct trap_context *ctx) static enum trap_return handle_iabt(struct trap_context *ctx) { - unsigned long hpfar, hdfar; + unsigned long hpfar; if (this_cpu_data()->sdei_event) { this_cpu_data()->sdei_event = false; @@ -83,11 +162,11 @@ static enum trap_return handle_iabt(struct trap_context *ctx) return TRAP_HANDLED; } - arm_read_sysreg(HPFAR_EL2, hpfar); - arm_read_sysreg(FAR_EL2, hdfar); + if (arch_get_fault_ipa(ctx, &hpfar, GV2M_READ)) + panic_printk("FATAL: instruction abort at 0x%lx\n", hpfar); + else + panic_printk("FATAL: instruction abort and can not get ipa\n"); - panic_printk("FATAL: instruction abort at 0x%lx\n", - (hpfar << 8) | (hdfar & 0xfff)); return TRAP_FORBIDDEN; } -- 2.25.1 -- You received this message because you are subscribed to the Google Groups "Jailhouse" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/jailhouse-dev/20220603131124.2007946-1-gengdongjiu1%40gmail.com.
