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.

Reply via email to