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

[PATCH v1 1/4] xen/riscv: add exception table support



Introduce exception table handling for RISC-V so faults from selected
instructions can be recovered via fixup handlers instead of being
treated as fatal.

Add the RISC-V exception table format, sorting at boot to allow binary
search used furthuer, and lookup from the trap handler. Update the
linker script to emit the .ex_table section using introduced common
EX_TABLE macro shared with other architectures.

Also, the __start___ext_table is aligned now by POINTER_ALIGN instead
of just using hard-coded 8 as there is no too much sense to align
__start___ext_table by 8 for 32-bit systems.

This implementation is based on Linux 6.16.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
---
Open question:

With some renaming the following could be generic, at least, between
x86 and RISC-V:
 - ASM_EXTABLE() definition
 - All what is conencted with sort_extable().
 - With some change of how x86 searchs an extension this cmp_ex_search()
   could also go to common file.

Does it make sense to introduce xen/extable.h and common/extable.c?
---
 xen/arch/riscv/Kconfig                |  1 +
 xen/arch/riscv/Makefile               |  1 +
 xen/arch/riscv/extables.c             | 85 +++++++++++++++++++++++++++
 xen/arch/riscv/include/asm/extables.h | 72 +++++++++++++++++++++++
 xen/arch/riscv/setup.c                |  3 +
 xen/arch/riscv/traps.c                |  3 +
 xen/arch/riscv/xen.lds.S              |  3 +
 xen/arch/x86/xen.lds.S                |  6 +-
 xen/include/xen/xen.lds.h             | 10 ++++
 9 files changed, 179 insertions(+), 5 deletions(-)
 create mode 100644 xen/arch/riscv/extables.c
 create mode 100644 xen/arch/riscv/include/asm/extables.h

diff --git a/xen/arch/riscv/Kconfig b/xen/arch/riscv/Kconfig
index 89876b32175d..a5e87c1757f7 100644
--- a/xen/arch/riscv/Kconfig
+++ b/xen/arch/riscv/Kconfig
@@ -4,6 +4,7 @@ config RISCV
        select GENERIC_BUG_FRAME
        select GENERIC_UART_INIT
        select HAS_DEVICE_TREE_DISCOVERY
+       select HAS_EX_TABLE
        select HAS_PMAP
        select HAS_UBSAN
        select HAS_VMAP
diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile
index ffbd7062e214..6b3f3ed90bdb 100644
--- a/xen/arch/riscv/Makefile
+++ b/xen/arch/riscv/Makefile
@@ -3,6 +3,7 @@ obj-y += cpufeature.o
 obj-y += domain.o
 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
 obj-y += entry.o
+obj-$(CONFIG_HAS_EX_TABLE) += extables.o
 obj-y += imsic.o
 obj-y += intc.o
 obj-y += irq.o
diff --git a/xen/arch/riscv/extables.c b/xen/arch/riscv/extables.c
new file mode 100644
index 000000000000..5e6e453ead29
--- /dev/null
+++ b/xen/arch/riscv/extables.c
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/init.h>
+#include <xen/bsearch.h>
+#include <xen/lib.h>
+#include <xen/sort.h>
+#include <xen/virtual_region.h>
+
+#include <asm/extables.h>
+#include <asm/processor.h>
+
+#define EX_FIELD(ptr, field) ((unsigned long)&(ptr)->field + (ptr)->field)
+
+static inline unsigned long ex_insn(const struct exception_table_entry *ex)
+{
+    return EX_FIELD(ex, insn);
+}
+
+static inline unsigned long ex_fixup(const struct exception_table_entry *ex)
+{
+    return EX_FIELD(ex, fixup);
+}
+
+static void __init cf_check swap_ex(void *a, void *b)
+{
+    struct exception_table_entry *x = a, *y = b, tmp;
+    int delta = b - a;
+
+    tmp = *x;
+    x->insn = y->insn + delta;
+    y->insn = tmp.insn - delta;
+
+    x->fixup = y->fixup + delta;
+    y->fixup = tmp.fixup - delta;
+}
+
+static int __init cf_check cmp_ex_sort(const void *a, const void *b)
+{
+    const unsigned long l = ex_insn(a);
+    const unsigned long r = ex_insn(b);
+
+    /* avoid overflow */
+    return (l > r) - (l < r);
+}
+
+void __init sort_extable(void)
+{
+    sort(__start___ex_table,  __stop___ex_table - __start___ex_table,
+         sizeof(struct exception_table_entry), cmp_ex_sort, swap_ex);
+}
+
+static int cf_check cmp_ex_search(const void *key, const void *elt)
+{
+    const unsigned long k = *(const unsigned long *)key;
+    const unsigned long insn = ex_insn(elt);
+
+    /* avoid overflow */
+    return (k > insn) - (k < insn);
+}
+
+static bool ex_handler_fixup(const struct exception_table_entry *ex,
+                                        struct cpu_user_regs *regs)
+{
+       regs->sepc = ex_fixup(ex);
+
+       return true;
+}
+
+bool fixup_exception(struct cpu_user_regs *regs)
+{
+    unsigned long pc = regs->sepc;
+    const struct virtual_region *region = find_text_region(pc);
+    const struct exception_table_entry *ex;
+
+    if ( !region || !region->ex )
+        return false;
+
+    ex = bsearch(&pc, region->ex, region->ex_end - region->ex,
+                 sizeof(struct exception_table_entry), cmp_ex_search);
+
+    if ( !ex )
+        return false;
+
+    return ex_handler_fixup(ex, regs);
+}
diff --git a/xen/arch/riscv/include/asm/extables.h 
b/xen/arch/riscv/include/asm/extables.h
new file mode 100644
index 000000000000..139764f3808d
--- /dev/null
+++ b/xen/arch/riscv/include/asm/extables.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef ASM__RISCV__ASM_EXTABLES_H
+#define ASM__RISCV__ASM_EXTABLES_H
+
+#ifdef __ASSEMBLER__
+
+#define ASM_EXTABLE(insn, fixup)    \
+    .pushsection .ex_table, "a";    \
+    .balign     4;                  \
+    .long              ((insn) - .);       \
+    .long              ((fixup) - .);      \
+    .popsection;
+.endm
+
+#else /* __ASSEMBLER__ */
+
+#include <xen/bug.h>
+#include <xen/stringify.h>
+
+struct cpu_user_regs;
+
+#define ASM_EXTABLE(insn, fixup)        \
+    ".pushsection .ex_table, \"a\"\n"   \
+    ".balign    4\n"                    \
+    ".long      ((" #insn ") - .)\n"     \
+    ".long      ((" #fixup ") - .)\n"    \
+    ".popsection\n"
+
+/*
+ * The exception table consists of pairs of relative offsets: the first
+ * is the relative offset to an instruction that is allowed to fault,
+ * and the second is the relative offset at which the program should
+ * continue. No registers are modified, so it is entirely up to the
+ * continuation code to figure out what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+struct exception_table_entry {
+       int32_t insn, fixup;
+};
+
+extern struct exception_table_entry __start___ex_table[];
+extern struct exception_table_entry __stop___ex_table[];
+
+#ifdef CONFIG_HAS_EX_TABLE
+
+void sort_extable(void);
+bool fixup_exception(struct cpu_user_regs *regs);
+
+#else /* CONFIG_HAS_EX_TABLE */
+
+static inline void sort_extable(void)
+{
+    printk("%s: We don't support .ex_table\n", __func__);
+}
+
+static inline bool fixup_exception(struct cpu_user_regs *regs)
+{
+    dprintk("%s: We don't support .ex_table\n", __func__);
+
+    return false;
+}
+
+#endif /* CONFIG_HAS_EX_TABLE */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* ASM__RISCV__ASM_EXTABLES_H */
diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c
index cae49bb29626..4be6aa5a434e 100644
--- a/xen/arch/riscv/setup.c
+++ b/xen/arch/riscv/setup.c
@@ -19,6 +19,7 @@
 
 #include <public/version.h>
 
+#include <asm/extables.h>
 #include <asm/cpufeature.h>
 #include <asm/early_printk.h>
 #include <asm/fixmap.h>
@@ -81,6 +82,8 @@ void __init noreturn start_xen(unsigned long bootcpu_id,
 
     smp_prepare_boot_cpu();
 
+    sort_extable();
+
     set_cpuid_to_hartid(0, bootcpu_id);
 
     trap_init();
diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c
index 326f2be62823..242af0a7a5f3 100644
--- a/xen/arch/riscv/traps.c
+++ b/xen/arch/riscv/traps.c
@@ -12,6 +12,7 @@
 #include <xen/sched.h>
 #include <xen/softirq.h>
 
+#include <asm/extables.h>
 #include <asm/cpufeature.h>
 #include <asm/intc.h>
 #include <asm/processor.h>
@@ -217,6 +218,8 @@ void do_trap(struct cpu_user_regs *cpu_regs)
 
             break;
         }
+        else if ( fixup_exception(cpu_regs) )
+            break;
         fallthrough;
     default:
         if ( cause & CAUSE_IRQ_FLAG )
diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
index 331a7d63d3c9..65f136dce9f7 100644
--- a/xen/arch/riscv/xen.lds.S
+++ b/xen/arch/riscv/xen.lds.S
@@ -74,6 +74,9 @@ SECTIONS
     .data.ro_after_init : {
         __ro_after_init_start = .;
         *(.data.ro_after_init)
+
+        EX_TABLE
+
         . = ALIGN(PAGE_SIZE);
         __ro_after_init_end = .;
     } : text
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index c326538ebbb2..b9e888e5962f 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -113,11 +113,7 @@ SECTIONS
        __ro_after_init_start = .;
        *(.data.ro_after_init)
 
-       . = ALIGN(8);
-       /* Exception table */
-       __start___ex_table = .;
-       *(.ex_table)
-       __stop___ex_table = .;
+       EX_TABLE
 
        . = ALIGN(PAGE_SIZE);
        __ro_after_init_end = .;
diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
index 136849ecd515..85800f942aae 100644
--- a/xen/include/xen/xen.lds.h
+++ b/xen/include/xen/xen.lds.h
@@ -219,4 +219,14 @@
 #define VPCI_ARRAY
 #endif
 
+#ifdef CONFIG_HAS_EX_TABLE
+#define EX_TABLE                  \
+        . = ALIGN(POINTER_ALIGN); \
+        __start___ex_table = .;   \
+        *(.ex_table)              \
+        __stop___ex_table = .;
+#else
+#define EX_TABLE
+#endif
+
 #endif /* __XEN_LDS_H__ */
-- 
2.53.0




 


Rackspace

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