[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [XEN v8 2/2] xen/arm64: io: Support instructions (for which ISS is not valid) on emulated MMIO region using MMIO/ioreq handler



Hi,

On 12/02/2022 23:34, Ayan Kumar Halder wrote:

  xen/arch/arm/arm32/traps.c        |  7 +++
  xen/arch/arm/arm64/traps.c        | 47 +++++++++++++++
  xen/arch/arm/decode.c             |  1 +
  xen/arch/arm/include/asm/domain.h |  4 ++
  xen/arch/arm/include/asm/mmio.h   | 15 ++++-
  xen/arch/arm/include/asm/traps.h  |  2 +
  xen/arch/arm/io.c                 | 98 ++++++++++++++++++++-----------
  xen/arch/arm/ioreq.c              |  7 ++-
  xen/arch/arm/traps.c              | 80 +++++++++++++++++++++----
  xen/arch/x86/include/asm/ioreq.h  |  3 +

This change technically needs an ack from the x86 maintainers. And...

  xen/include/xen/sched.h           |  2 +

this one for anyone from THE REST (Stefano and I are part of it). Please use scripts/add_maintainers.pl to automatically add the relevant maintainers in CC.

  11 files changed, 217 insertions(+), 49 deletions(-)

diff --git a/xen/arch/arm/arm32/traps.c b/xen/arch/arm/arm32/traps.c
index 9c9790a6d1..70c6238196 100644
--- a/xen/arch/arm/arm32/traps.c
+++ b/xen/arch/arm/arm32/traps.c
@@ -18,9 +18,11 @@
#include <xen/lib.h>
  #include <xen/kernel.h>
+#include <xen/sched.h>
#include <public/xen.h> +#include <asm/mmio.h>
  #include <asm/processor.h>
  #include <asm/traps.h>
@@ -82,6 +84,11 @@ void do_trap_data_abort(struct cpu_user_regs *regs)
          do_unexpected_trap("Data Abort", regs);
  }
+void post_increment_register(const struct instr_details *instr)
+{
+    domain_crash(current->domain);


Please add a comment explaning why this is resulting to a domain crash. AFAICT, this is because this should not be reachable (yet) for 32-bit.


+}
+
  /*
   * Local variables:
   * mode: C
diff --git a/xen/arch/arm/arm64/traps.c b/xen/arch/arm/arm64/traps.c
index 9113a15c7a..a6766689b3 100644
--- a/xen/arch/arm/arm64/traps.c
+++ b/xen/arch/arm/arm64/traps.c
@@ -23,6 +23,7 @@
  #include <asm/processor.h>
#include <public/xen.h>
+#include <xen/sched.h>

The headers should ordered so <xen/*.h> are first, then <asm/*.h>, then <public/*.h>. They should then be ordered alphabetically within each of the category.

So, this new header should be included right after <xen/lib.h>

[...]

diff --git a/xen/arch/arm/include/asm/mmio.h b/xen/arch/arm/include/asm/mmio.h
index 3354d9c635..745130b7fe 100644
--- a/xen/arch/arm/include/asm/mmio.h
+++ b/xen/arch/arm/include/asm/mmio.h
@@ -26,12 +26,22 @@
#define MAX_IO_HANDLER 16 +enum instr_decode_state
+{
+    INSTR_ERROR,                    /* Error encountered while decoding instr 
*/
+    INSTR_VALID,                    /* ISS is valid, so no need to decode */
+    INSTR_LDR_STR_POSTINDEXING,     /* Instruction is decoded successfully.
+                                       It is ldr/str post indexing */

Coding style: multiple-line comments for Xen should be:

/*
 * ...
 * ...
 */

In this case, I would simply move the comment on top.

[...]

diff --git a/xen/arch/arm/io.c b/xen/arch/arm/io.c
index a289d393f9..203466b869 100644
--- a/xen/arch/arm/io.c
+++ b/xen/arch/arm/io.c
@@ -95,57 +95,87 @@ static const struct mmio_handler *find_mmio_handler(struct 
domain *d,
      return handler;
  }
+void try_decode_instruction(const struct cpu_user_regs *regs,
+                            mmio_info_t *info)
+{
+    int rc;
+
+    /*
+     * Erratum 766422: Thumb store translation fault to Hypervisor may
+     * not have correct HSR Rt value.
+     */
+    if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
+         info->dabt.write )
+    {
+        rc = decode_instruction(regs, info);
+        if ( rc )
+        {
+            gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
+            info->dabt_instr.state = INSTR_ERROR;
+            return;
+        }
+    }

At the moment, the errata would only be handled when the ISS is valid. Now, you are moving it before we know if it is valid. Can you explain why?

[...]

  enum io_state try_handle_mmio(struct cpu_user_regs *regs,
-                              const union hsr hsr,
-                              paddr_t gpa)
+                              mmio_info_t *info)
  {
      struct vcpu *v = current;
      const struct mmio_handler *handler = NULL;
-    const struct hsr_dabt dabt = hsr.dabt;
-    mmio_info_t info = {
-        .gpa = gpa,
-        .dabt = dabt
-    };
+    int rc;
- ASSERT(hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
+    ASSERT(info->dabt.ec == HSR_EC_DATA_ABORT_LOWER_EL);
- handler = find_mmio_handler(v->domain, info.gpa);
+    handler = find_mmio_handler(v->domain, info->gpa);
      if ( !handler )
      {
-        int rc;
-
-        rc = try_fwd_ioserv(regs, v, &info);
+        rc = try_fwd_ioserv(regs, v, info);
          if ( rc == IO_HANDLED )
              return handle_ioserv(regs, v);
return rc;
      }
- /* All the instructions used on emulated MMIO region should be valid */
-    if ( !dabt.valid )
-        return IO_ABORT;
-

AFAIU, the assumption is now try_handle_mmio() and try_fwd_ioserv() will always be called when dabt.valid == 1. I think it would still be good to check that assumption.

So I would move the check at the beginning of try_handle_mmio() and add an ASSERT_UNREACHABLE in the if(). Something like:

if ( !dabt.valid )
{
    ASSERT_UNREACHABLE();
    return IO_ABORT;
}

      /*
-     * Erratum 766422: Thumb store translation fault to Hypervisor may
-     * not have correct HSR Rt value.
+     * At this point, we know that the instruction is either valid or has been
+     * decoded successfully. Thus, Xen should be allowed to execute the
+     * instruction on the emulated MMIO region.
       */
-    if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
-         dabt.write )
-    {
-        int rc;
-
-        rc = decode_instruction(regs, &info);
-        if ( rc )
-        {
-            gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
-            return IO_ABORT;
-        }
-    }
-
-    if ( info.dabt.write )
-        return handle_write(handler, v, &info);
+    if ( info->dabt.write )
+        rc = handle_write(handler, v, info);
      else
-        return handle_read(handler, v, &info);
+        rc = handle_read(handler, v, info);
+
+    return rc;

It looks like there are some left-over of the previous approach. It is fine to return directly from each branch.


  }
void register_mmio_handler(struct domain *d,
diff --git a/xen/arch/arm/ioreq.c b/xen/arch/arm/ioreq.c
index 308650b400..3c0a935ccf 100644
--- a/xen/arch/arm/ioreq.c
+++ b/xen/arch/arm/ioreq.c
@@ -47,6 +47,7 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
                               struct vcpu *v, mmio_info_t *info)
  {
      struct vcpu_io *vio = &v->io;
+    struct dabt_instr instr = info->dabt_instr;
      ioreq_t p = {
          .type = IOREQ_TYPE_COPY,
          .addr = info->gpa,
@@ -76,10 +77,8 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
      if ( !s )
          return IO_UNHANDLED;
- if ( !info->dabt.valid )
-        return IO_ABORT;
-

For this one, I would switch to ASSERT(dabt.valid);

      vio->req = p;
+    vio->info.dabt_instr = instr;
rc = ioreq_send(s, &p, 0);
      if ( rc != IO_RETRY || v->domain->is_shutting_down )
@@ -95,6 +94,7 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
  bool arch_ioreq_complete_mmio(void)
  {
      struct vcpu *v = current;
+    struct instr_details dabt_instr = v->io.info.dabt_instr;
      struct cpu_user_regs *regs = guest_cpu_user_regs();
      const union hsr hsr = { .bits = regs->hsr };
@@ -106,6 +106,7 @@ bool arch_ioreq_complete_mmio(void) if ( handle_ioserv(regs, v) == IO_HANDLED )
      {
+        post_increment_register(&dabt_instr);
          advance_pc(regs, hsr);
          return true;
      }
diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 9339d12f58..455e51cdbe 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -1893,6 +1893,21 @@ static bool try_map_mmio(gfn_t gfn)
      return !map_regions_p2mt(d, gfn, 1, mfn, p2m_mmio_direct_c);
  }
+static inline bool check_p2m(bool is_data, paddr_t gpa)
+{
+    /*
+     * First check if the translation fault can be resolved by the P2M 
subsystem.
+     * If that's the case nothing else to do.
+     */
+    if ( p2m_resolve_translation_fault(current->domain,gaddr_to_gfn(gpa)) )

Coding style: missing space before and after the comma.

+        return true;
+
+    if ( is_data && try_map_mmio(gaddr_to_gfn(gpa)) )
+        return true;
+
+    return false;
+}
+
  static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
                                         const union hsr hsr)
  {
@@ -1906,6 +1921,7 @@ static void do_trap_stage2_abort_guest(struct 
cpu_user_regs *regs,
      paddr_t gpa;
      uint8_t fsc = xabt.fsc & ~FSC_LL_MASK;
      bool is_data = (hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
+    mmio_info_t info;
/*
       * If this bit has been set, it means that this stage-2 abort is caused
@@ -1959,6 +1975,25 @@ static void do_trap_stage2_abort_guest(struct 
cpu_user_regs *regs,
          return;
      }
      case FSC_FLT_TRANS:
+    {
+        info.gpa = gpa;
+        info.dabt = hsr.dabt;
+
+        /* Check that the ISS is invalid and it is not data abort. */

This comment looks a bit pointless. You are writing literally what the check is doing. But you don't really explain why. I think you want to move some of the commint with the if here.

However,...

+        if ( !hsr.dabt.valid && !is_data )

... this code can be reached by Instruction Abort and Data Abort. So you can't use hsr.dabt. Instead, you should use xabt (or check is_data first).

If you use xabt, you will notice that the 'valid' bit is not existent
because the instruction syndrome only exists for data abort.

But then, I don't understand why this is only restricted to instruction abort. As I wrote in the previous versions and on IRC, there are valid use cases to trap a data abort with invalid syndrome. Below...


+        {
+
+            /*
+             * Assumption :- Most of the times when we get a translation fault
+             * and the ISS is invalid, the underlying cause is that the page
+             * tables have not been set up correctly.
+             */
+            if ( check_p2m(is_data, gpa) )
+                return;
+            else
+                goto inject_abt;
+        }
+
          /*
           * Attempt first to emulate the MMIO as the data abort will
           * likely happen in an emulated region.
@@ -1967,13 +2002,45 @@ static void do_trap_stage2_abort_guest(struct 
cpu_user_regs *regs,
           */
          if ( is_data )
          {
-            enum io_state state = try_handle_mmio(regs, hsr, gpa);
+            enum io_state state;
+
+            try_decode_instruction(regs, &info);
+
+            /*
+             * If Xen could not decode the instruction for any reason, then it
+             * should ask the caller to abort the guest.
+             */
+            if ( info.dabt_instr.state == INSTR_ERROR )
+                goto inject_abt;

... this will inject a data abort to the guest when we can't decode. This is not what we want. We should check whether this is a P2M translation fault or we need to map an MMIO region.

In pseudo-code, this would look like:

if ( !is_data || hsr.dabt.valid )
{
    if ( check_p2m() )
      return;


    if ( !is_data )
       goto inject_dabt;

    decode_instruction();
    if ( !dabt.invalid )
      goto inject_dabt;
}

try_handle_mmio();

if ( instruction was not decoded )
  check_p2m();

Cheers,

--
Julien Grall



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.