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

[PATCH v1 13/17] xen/riscv: add unprivileged guest memory read helper



Introduce riscv_vcpu_unpriv_read() to allow Xen to safely read guest memory
using HLV/HLVX instructions while reliably capturing trap context.

This is required for instruction fetch emulation and MMIO decoding, where
Xen must inspect guest memory that may not be directly accessible and may
fault.

The implementation is based on kvm_riscv_vcpu_unpriv_read() from Linux,
with one deviation: the hlv/hlvx instructions translate the guest address
through the live vsatp/hgatp CSRs, i.e. through the address space of the
currently running vCPU, so the function can only be called safely for
current. Instead of taking a struct vcpu argument, it always operates on
current directly.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
 xen/arch/riscv/guestcopy.c                | 91 +++++++++++++++++++++++
 xen/arch/riscv/include/asm/guest_access.h |  6 ++
 2 files changed, 97 insertions(+)

diff --git a/xen/arch/riscv/guestcopy.c b/xen/arch/riscv/guestcopy.c
index 8a89212e0bea..57844bc442f6 100644
--- a/xen/arch/riscv/guestcopy.c
+++ b/xen/arch/riscv/guestcopy.c
@@ -6,6 +6,7 @@
 #include <xen/string.h>
 
 #include <asm/guest_access.h>
+#include <asm/traps.h>
 
 #define COPY_from_guest     0U
 #define COPY_to_guest       BIT(0, U)
@@ -114,3 +115,93 @@ unsigned long copy_to_guest_phys(struct domain *d, paddr_t 
gpa, void *buf,
     return copy_guest(buf, gpa, len, GPA_INFO(d),
                       COPY_to_guest | COPY_gpa);
 }
+
+/*
+ * Read machine word from Guest memory
+ *
+ * @read_insn: Flag representing whether we are reading instruction
+ * @guest_addr: Guest address to read
+ * @trap: Output pointer to trap details
+ *
+ * The hlv/hlvx instructions translate guest_addr through the live
+ * vsatp/hgatp CSRs, so the read is only meaningful for the address
+ * space of the currently running vCPU.
+ */
+unsigned long riscv_vcpu_unpriv_read(bool read_insn,
+                                     unsigned long guest_addr,
+                                     struct trap_info *trap)
+{
+    unsigned long val, tmp;
+    unsigned long flags, old_hstatus;
+
+    /*
+     * As hstatus is going to be changed we don't want an interrupt to occur
+     * with guest's hstatus register.
+     */
+    local_irq_save(flags);
+
+    /*
+     * The hypervisor virtual-machine load and store instructions are valid
+     * only in M-mode or HS-mode, or in U-mode when hstatus.HU=1. Each
+     * instruction performs an explicit memory access as though V=1; i.e.,
+     * with the address translation and protection, and the endianness,
+     * that apply to memory accesses in either VS-mode or VU-mode.
+     * Field SPVP of hstatus controls the privilege level of the access.
+     * The explicit memory access is done as though in VU-mode when SPVP=0,
+     * and as though in VS-mode when SPVP=1.
+     *
+     * So it is necessary to restore vCPU's hstatus before execution of
+     * hlv* instruction.
+     */
+    old_hstatus = csr_swap(CSR_HSTATUS,
+                           vcpu_guest_cpu_user_regs(current)->hstatus);
+
+    if ( read_insn )
+    {
+        asm volatile ( "\n"
+            "1:\n"
+            "   hlvx.hu %[val], (%[addr])\n"
+            ASM_EXTABLE_TRAP_INFO(1b, 3f, %[ti])
+            "   andi %[tmp], %[val], 3\n"
+            "   addi %[tmp], %[tmp], -3\n"
+            "   bne %[tmp], zero, 3f\n"
+            "   addi %[addr], %[addr], 2\n"
+            "\n"
+            "2:\n"
+            "   hlvx.hu %[tmp], (%[addr])\n"
+            ASM_EXTABLE_TRAP_INFO(2b, 3f, %[ti])
+            "   sll %[tmp], %[tmp], 16\n"
+            "   add %[val], %[val], %[tmp]\n"
+            "3:\n"
+        : [val] "=&r" (val), [tmp] "=&r" (tmp), [addr] "+&r" (guest_addr)
+        : [ti] "r" (trap) : "memory" );
+
+        /*
+         * Although HLVX instructions' explicit memory accesses require execute
+         * permissions, they still raise the same exceptions as other load
+         * instructions, rather than raising fetch exceptions instead.
+         */
+        if ( trap->scause == CAUSE_LOAD_PAGE_FAULT )
+            trap->scause = CAUSE_FETCH_PAGE_FAULT;
+    }
+    else
+    {
+        asm volatile ( "\n"
+            "1:\n"
+#ifdef CONFIG_RISCV_64
+            "hlv.d %[val], (%[addr])\n"
+#else
+            "hlv.w %[val], (%[addr])\n"
+#endif
+            "2:\n"
+            ASM_EXTABLE_TRAP_INFO(1b, 2b, %[ti])
+        : [val] "=&r" (val)
+        : [addr] "r" (guest_addr), [ti] "r" (trap) : "memory" );
+    }
+
+    csr_write(CSR_HSTATUS, old_hstatus);
+
+    local_irq_restore(flags);
+
+    return val;
+}
diff --git a/xen/arch/riscv/include/asm/guest_access.h 
b/xen/arch/riscv/include/asm/guest_access.h
index 8d679319ded0..153ec6ce26d1 100644
--- a/xen/arch/riscv/include/asm/guest_access.h
+++ b/xen/arch/riscv/include/asm/guest_access.h
@@ -5,6 +5,8 @@
 #include <xen/types.h>
 
 struct domain;
+struct trap_info;
+struct vcpu;
 
 unsigned long raw_copy_to_guest(void *to, const void *from, unsigned len);
 unsigned long raw_copy_from_guest(void *to, const void *from, unsigned len);
@@ -25,6 +27,10 @@ unsigned long raw_clear_guest(void *to, unsigned int len);
 unsigned long copy_to_guest_phys(struct domain *d, paddr_t gpa, void *buf,
                                  unsigned long len);
 
+unsigned long riscv_vcpu_unpriv_read(bool read_insn,
+                                     unsigned long guest_addr,
+                                     struct trap_info *trap);
+
 #endif /* ASM__RISCV__GUEST_ACCESS_H */
 /*
  * Local variables:
-- 
2.54.0




 


Rackspace

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