diff mbox series

[Xen-devel,v2,16/16] xen/arm: traps: Merge do_trap_instr_abort_guest and do_trap_data_abort_guest

Message ID 20171212190212.5535-17-julien.grall@linaro.org
State Accepted
Commit 6c84027dd20be0760a0b6803b251b4392a8674cc
Headers show
Series xen/arm: Stage-2 handling cleanup | expand

Commit Message

Julien Grall Dec. 12, 2017, 7:02 p.m. UTC
The two helpers do_trap_instr_abort_guest and do_trap_data_abort_guest
are used trap stage-2 abort. While the former is only handling prefetch
abort and the latter data abort, they are very similarly and does not
warrant to have separate helpers.

For instance, merging the both will make easier to maintain stage-2 abort
handling. So consolidate the two helpers in a new helper
do_trap_stage2_abort.

Signed-off-by: Julien Grall <julien.grall@linaro.org>

---
    Changes in v2
        - Fix the way to compute npfec.write_access
---
 xen/arch/arm/traps.c | 133 ++++++++++++++++-----------------------------------
 1 file changed, 41 insertions(+), 92 deletions(-)

Comments

Stefano Stabellini Dec. 12, 2017, 8:11 p.m. UTC | #1
On Tue, 12 Dec 2017, Julien Grall wrote:
> The two helpers do_trap_instr_abort_guest and do_trap_data_abort_guest
> are used trap stage-2 abort. While the former is only handling prefetch
> abort and the latter data abort, they are very similarly and does not
> warrant to have separate helpers.
> 
> For instance, merging the both will make easier to maintain stage-2 abort
> handling. So consolidate the two helpers in a new helper
> do_trap_stage2_abort.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


> ---
>     Changes in v2
>         - Fix the way to compute npfec.write_access
> ---
>  xen/arch/arm/traps.c | 133 ++++++++++++++++-----------------------------------
>  1 file changed, 41 insertions(+), 92 deletions(-)
> 
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index 082396c26d..013c1600ec 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -1861,79 +1861,6 @@ static inline bool hpfar_is_valid(bool s1ptw, uint8_t fsc)
>      return s1ptw || (fsc == FSC_FLT_TRANS && !check_workaround_834220());
>  }
>  
> -static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
> -                                      const union hsr hsr)
> -{
> -    int rc;
> -    register_t gva;
> -    uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
> -    paddr_t gpa;
> -    mfn_t mfn;
> -
> -    gva = get_hfar(false /* is_data */);
> -
> -    /*
> -     * If this bit has been set, it means that this instruction abort is caused
> -     * by a guest external abort. We can handle this instruction abort as guest
> -     * SError.
> -     */
> -    if ( hsr.iabt.eat )
> -        return __do_trap_serror(regs, true);
> -
> -
> -    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
> -        gpa = get_faulting_ipa(gva);
> -    else
> -    {
> -        /*
> -         * Flush the TLB to make sure the DTLB is clear before
> -         * doing GVA->IPA translation. If we got here because of
> -         * an entry only present in the ITLB, this translation may
> -         * still be inaccurate.
> -         */
> -        flush_tlb_local();
> -
> -        /*
> -         * We may not be able to translate because someone is
> -         * playing with the Stage-2 page table of the domain.
> -         * Return to the guest.
> -         */
> -        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
> -        if ( rc == -EFAULT )
> -            return; /* Try again */
> -    }
> -
> -    switch ( fsc )
> -    {
> -    case FSC_FLT_PERM:
> -    {
> -        const struct npfec npfec = {
> -            .insn_fetch = 1,
> -            .gla_valid = 1,
> -            .kind = hsr.iabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
> -        };
> -
> -        p2m_mem_access_check(gpa, gva, npfec);
> -        /*
> -         * The only way to get here right now is because of mem_access,
> -         * thus reinjecting the exception to the guest is never required.
> -         */
> -        return;
> -    }
> -    case FSC_FLT_TRANS:
> -        /*
> -         * The PT walk may have failed because someone was playing
> -         * with the Stage-2 page table. Walk the Stage-2 PT to check
> -         * if the entry exists. If it's the case, return to the guest
> -         */
> -        mfn = gfn_to_mfn(current->domain, _gfn(paddr_to_pfn(gpa)));
> -        if ( !mfn_eq(mfn, INVALID_MFN) )
> -            return;
> -    }
> -
> -    inject_iabt_exception(regs, gva, hsr.len);
> -}
> -
>  static bool try_handle_mmio(struct cpu_user_regs *regs,
>                              const union hsr hsr,
>                              paddr_t gpa)
> @@ -1945,6 +1872,8 @@ static bool try_handle_mmio(struct cpu_user_regs *regs,
>      };
>      int rc;
>  
> +    ASSERT(hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
> +
>      /* stage-1 page table should never live in an emulated MMIO region */
>      if ( dabt.s1ptw )
>          return false;
> @@ -2000,29 +1929,43 @@ static bool try_map_mmio(gfn_t gfn)
>      return !map_regions_p2mt(d, gfn, 1, mfn, p2m_mmio_direct_c);
>  }
>  
> -static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
> -                                     const union hsr hsr)
> +static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
> +                                       const union hsr hsr)
>  {
> -    const struct hsr_dabt dabt = hsr.dabt;
> +    /*
> +     * The encoding of hsr_iabt is a subset of hsr_dabt. So use
> +     * hsr_dabt to represent an abort fault.
> +     */
> +    const struct hsr_xabt xabt = hsr.xabt;
>      int rc;
>      vaddr_t gva;
>      paddr_t gpa;
> -    uint8_t fsc = hsr.dabt.dfsc & ~FSC_LL_MASK;
> +    uint8_t fsc = xabt.fsc & ~FSC_LL_MASK;
>      mfn_t mfn;
> +    bool is_data = (hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
>  
>      /*
> -     * If this bit has been set, it means that this data abort is caused
> -     * by a guest external abort. We treat this data abort as guest SError.
> +     * If this bit has been set, it means that this stage-2 abort is caused
> +     * by a guest external abort. We treat this stage-2 abort as guest SError.
>       */
> -    if ( dabt.eat )
> +    if ( xabt.eat )
>          return __do_trap_serror(regs, true);
>  
> -    gva = get_hfar(true /* is_data */);
> +    gva = get_hfar(is_data);
>  
> -    if ( hpfar_is_valid(dabt.s1ptw, fsc) )
> +    if ( hpfar_is_valid(xabt.s1ptw, fsc) )
>          gpa = get_faulting_ipa(gva);
>      else
>      {
> +        /*
> +         * Flush the TLB to make sure the DTLB is clear before
> +         * doing GVA->IPA translation. If we got here because of
> +         * an entry only present in the ITLB, this translation may
> +         * still be inaccurate.
> +         */
> +        if ( !is_data )
> +            flush_tlb_local();
> +
>          rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>          /*
>           * We may not be able to translate because someone is
> @@ -2038,10 +1981,11 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>      case FSC_FLT_PERM:
>      {
>          const struct npfec npfec = {
> -            .read_access = !dabt.write,
> -            .write_access = dabt.write,
> +            .insn_fetch = !is_data,
> +            .read_access = is_data && !hsr.dabt.write,
> +            .write_access = is_data && hsr.dabt.write,
>              .gla_valid = 1,
> -            .kind = dabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
> +            .kind = xabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
>          };
>  
>          p2m_mem_access_check(gpa, gva, npfec);
> @@ -2055,8 +1999,10 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>          /*
>           * Attempt first to emulate the MMIO as the data abort will
>           * likely happen in an emulated region.
> +         *
> +         * Note that emulated region cannot be executed
>           */
> -        if ( try_handle_mmio(regs, hsr, gpa) )
> +        if ( is_data && try_handle_mmio(regs, hsr, gpa) )
>          {
>              advance_pc(regs, hsr);
>              return;
> @@ -2071,18 +2017,21 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>          if ( !mfn_eq(mfn, INVALID_MFN) )
>              return;
>  
> -        if ( try_map_mmio(gaddr_to_gfn(gpa)) )
> +        if ( is_data && try_map_mmio(gaddr_to_gfn(gpa)) )
>              return;
>  
>          break;
>      default:
> -        gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
> -                hsr.bits, dabt.dfsc);
> +        gprintk(XENLOG_WARNING, "Unsupported FSC: HSR=%#x DFSC=%#x\n",
> +                hsr.bits, xabt.fsc);
>      }
>  
>      gdprintk(XENLOG_DEBUG, "HSR=0x%x pc=%#"PRIregister" gva=%#"PRIvaddr
>               " gpa=%#"PRIpaddr"\n", hsr.bits, regs->pc, gva, gpa);
> -    inject_dabt_exception(regs, gva, hsr.len);
> +    if ( is_data )
> +        inject_dabt_exception(regs, gva, hsr.len);
> +    else
> +        inject_iabt_exception(regs, gva, hsr.len);
>  }
>  
>  static void enter_hypervisor_head(struct cpu_user_regs *regs)
> @@ -2215,11 +2164,11 @@ void do_trap_guest_sync(struct cpu_user_regs *regs)
>  
>      case HSR_EC_INSTR_ABORT_LOWER_EL:
>          perfc_incr(trap_iabt);
> -        do_trap_instr_abort_guest(regs, hsr);
> +        do_trap_stage2_abort_guest(regs, hsr);
>          break;
>      case HSR_EC_DATA_ABORT_LOWER_EL:
>          perfc_incr(trap_dabt);
> -        do_trap_data_abort_guest(regs, hsr);
> +        do_trap_stage2_abort_guest(regs, hsr);
>          break;
>  
>      default:
> -- 
> 2.11.0
>
diff mbox series

Patch

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 082396c26d..013c1600ec 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -1861,79 +1861,6 @@  static inline bool hpfar_is_valid(bool s1ptw, uint8_t fsc)
     return s1ptw || (fsc == FSC_FLT_TRANS && !check_workaround_834220());
 }
 
-static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
-                                      const union hsr hsr)
-{
-    int rc;
-    register_t gva;
-    uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
-    paddr_t gpa;
-    mfn_t mfn;
-
-    gva = get_hfar(false /* is_data */);
-
-    /*
-     * If this bit has been set, it means that this instruction abort is caused
-     * by a guest external abort. We can handle this instruction abort as guest
-     * SError.
-     */
-    if ( hsr.iabt.eat )
-        return __do_trap_serror(regs, true);
-
-
-    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
-        gpa = get_faulting_ipa(gva);
-    else
-    {
-        /*
-         * Flush the TLB to make sure the DTLB is clear before
-         * doing GVA->IPA translation. If we got here because of
-         * an entry only present in the ITLB, this translation may
-         * still be inaccurate.
-         */
-        flush_tlb_local();
-
-        /*
-         * We may not be able to translate because someone is
-         * playing with the Stage-2 page table of the domain.
-         * Return to the guest.
-         */
-        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
-        if ( rc == -EFAULT )
-            return; /* Try again */
-    }
-
-    switch ( fsc )
-    {
-    case FSC_FLT_PERM:
-    {
-        const struct npfec npfec = {
-            .insn_fetch = 1,
-            .gla_valid = 1,
-            .kind = hsr.iabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
-        };
-
-        p2m_mem_access_check(gpa, gva, npfec);
-        /*
-         * The only way to get here right now is because of mem_access,
-         * thus reinjecting the exception to the guest is never required.
-         */
-        return;
-    }
-    case FSC_FLT_TRANS:
-        /*
-         * The PT walk may have failed because someone was playing
-         * with the Stage-2 page table. Walk the Stage-2 PT to check
-         * if the entry exists. If it's the case, return to the guest
-         */
-        mfn = gfn_to_mfn(current->domain, _gfn(paddr_to_pfn(gpa)));
-        if ( !mfn_eq(mfn, INVALID_MFN) )
-            return;
-    }
-
-    inject_iabt_exception(regs, gva, hsr.len);
-}
-
 static bool try_handle_mmio(struct cpu_user_regs *regs,
                             const union hsr hsr,
                             paddr_t gpa)
@@ -1945,6 +1872,8 @@  static bool try_handle_mmio(struct cpu_user_regs *regs,
     };
     int rc;
 
+    ASSERT(hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
+
     /* stage-1 page table should never live in an emulated MMIO region */
     if ( dabt.s1ptw )
         return false;
@@ -2000,29 +1929,43 @@  static bool try_map_mmio(gfn_t gfn)
     return !map_regions_p2mt(d, gfn, 1, mfn, p2m_mmio_direct_c);
 }
 
-static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
-                                     const union hsr hsr)
+static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
+                                       const union hsr hsr)
 {
-    const struct hsr_dabt dabt = hsr.dabt;
+    /*
+     * The encoding of hsr_iabt is a subset of hsr_dabt. So use
+     * hsr_dabt to represent an abort fault.
+     */
+    const struct hsr_xabt xabt = hsr.xabt;
     int rc;
     vaddr_t gva;
     paddr_t gpa;
-    uint8_t fsc = hsr.dabt.dfsc & ~FSC_LL_MASK;
+    uint8_t fsc = xabt.fsc & ~FSC_LL_MASK;
     mfn_t mfn;
+    bool is_data = (hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
 
     /*
-     * If this bit has been set, it means that this data abort is caused
-     * by a guest external abort. We treat this data abort as guest SError.
+     * If this bit has been set, it means that this stage-2 abort is caused
+     * by a guest external abort. We treat this stage-2 abort as guest SError.
      */
-    if ( dabt.eat )
+    if ( xabt.eat )
         return __do_trap_serror(regs, true);
 
-    gva = get_hfar(true /* is_data */);
+    gva = get_hfar(is_data);
 
-    if ( hpfar_is_valid(dabt.s1ptw, fsc) )
+    if ( hpfar_is_valid(xabt.s1ptw, fsc) )
         gpa = get_faulting_ipa(gva);
     else
     {
+        /*
+         * Flush the TLB to make sure the DTLB is clear before
+         * doing GVA->IPA translation. If we got here because of
+         * an entry only present in the ITLB, this translation may
+         * still be inaccurate.
+         */
+        if ( !is_data )
+            flush_tlb_local();
+
         rc = gva_to_ipa(gva, &gpa, GV2M_READ);
         /*
          * We may not be able to translate because someone is
@@ -2038,10 +1981,11 @@  static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
     case FSC_FLT_PERM:
     {
         const struct npfec npfec = {
-            .read_access = !dabt.write,
-            .write_access = dabt.write,
+            .insn_fetch = !is_data,
+            .read_access = is_data && !hsr.dabt.write,
+            .write_access = is_data && hsr.dabt.write,
             .gla_valid = 1,
-            .kind = dabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
+            .kind = xabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
         };
 
         p2m_mem_access_check(gpa, gva, npfec);
@@ -2055,8 +1999,10 @@  static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
         /*
          * Attempt first to emulate the MMIO as the data abort will
          * likely happen in an emulated region.
+         *
+         * Note that emulated region cannot be executed
          */
-        if ( try_handle_mmio(regs, hsr, gpa) )
+        if ( is_data && try_handle_mmio(regs, hsr, gpa) )
         {
             advance_pc(regs, hsr);
             return;
@@ -2071,18 +2017,21 @@  static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
         if ( !mfn_eq(mfn, INVALID_MFN) )
             return;
 
-        if ( try_map_mmio(gaddr_to_gfn(gpa)) )
+        if ( is_data && try_map_mmio(gaddr_to_gfn(gpa)) )
             return;
 
         break;
     default:
-        gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
-                hsr.bits, dabt.dfsc);
+        gprintk(XENLOG_WARNING, "Unsupported FSC: HSR=%#x DFSC=%#x\n",
+                hsr.bits, xabt.fsc);
     }
 
     gdprintk(XENLOG_DEBUG, "HSR=0x%x pc=%#"PRIregister" gva=%#"PRIvaddr
              " gpa=%#"PRIpaddr"\n", hsr.bits, regs->pc, gva, gpa);
-    inject_dabt_exception(regs, gva, hsr.len);
+    if ( is_data )
+        inject_dabt_exception(regs, gva, hsr.len);
+    else
+        inject_iabt_exception(regs, gva, hsr.len);
 }
 
 static void enter_hypervisor_head(struct cpu_user_regs *regs)
@@ -2215,11 +2164,11 @@  void do_trap_guest_sync(struct cpu_user_regs *regs)
 
     case HSR_EC_INSTR_ABORT_LOWER_EL:
         perfc_incr(trap_iabt);
-        do_trap_instr_abort_guest(regs, hsr);
+        do_trap_stage2_abort_guest(regs, hsr);
         break;
     case HSR_EC_DATA_ABORT_LOWER_EL:
         perfc_incr(trap_dabt);
-        do_trap_data_abort_guest(regs, hsr);
+        do_trap_stage2_abort_guest(regs, hsr);
         break;
 
     default: