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

[Xen-devel] [PATCH v4 07/34] arm/x86: Use struct virtual_region to do bug, symbol, and (x86) exception tables



lookup.

During execution of the hypervisor we have two regions of
executable code - stext -> _etext, and _sinittext -> _einitext.

The later is not needed after bootup.

We also have various built-in macros and functions to search
in between those two swaths depending on the state of the system.

That is either for bug_frames, exceptions (x86) or symbol
names for the instruction.

With xSplice in the picture - we need a mechansim for new payloads
to searched as well for all of this.

Originally we had extra 'if (xsplice)...' but that gets
a bit tiring and does not hook up nicely.

This 'struct virtual_region' and virtual_region_list provide a
mechanism to search for the bug_frames,exception table,
and symbol names entries without having various calls in
other sub-components in the system.

Code which wishes to participate in bug_frames and exception table
entries search has to only use two public APIs:
 - register_virtual_region
 - unregister_virtual_region

to let the core code know. Furthermore there are also overrides
via the .skip function. There are three possible flags that
can be passed in - depending on what kind of search is being
done. A return of 1 means skip this region. If the .skip is
NULL the region will be considered.

The ->lookup_symbol will only be used if ->skip returns 1
for CHECKING_SYMBOLS (and of course if it points to
a function). Otherwise the default internal symbol lookup
mechanism is used.

Suggested-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>

---
Cc: Stefano Stabellini <stefano.stabellini@xxxxxxxxxx>
Cc: Julien Grall <julien.grall@xxxxxxx>
Cc: Keir Fraser <keir@xxxxxxx>
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
---
 xen/arch/arm/traps.c             |  45 ++++++++++-----
 xen/arch/x86/extable.c           |  16 +++++-
 xen/arch/x86/setup.c             |   3 +-
 xen/arch/x86/traps.c             |  46 +++++++++------
 xen/common/Makefile              |   1 +
 xen/common/bug_ex_symbols.c      | 119 +++++++++++++++++++++++++++++++++++++++
 xen/common/symbols.c             |  29 +++++++++-
 xen/include/xen/bug_ex_symbols.h |  74 ++++++++++++++++++++++++
 xen/include/xen/kernel.h         |   2 +
 xen/include/xen/symbols.h        |   9 +++
 10 files changed, 307 insertions(+), 37 deletions(-)
 create mode 100644 xen/common/bug_ex_symbols.c
 create mode 100644 xen/include/xen/bug_ex_symbols.h

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 31d2115..b62c91f 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -16,6 +16,7 @@
  * GNU General Public License for more details.
  */
 
+#include <xen/bug_ex_symbols.h>
 #include <xen/config.h>
 #include <xen/stdbool.h>
 #include <xen/init.h>
@@ -101,6 +102,8 @@ integer_param("debug_stack_lines", debug_stack_lines);
 
 void init_traps(void)
 {
+    setup_virtual_regions();
+
     /* Setup Hyp vector base */
     WRITE_SYSREG((vaddr_t)hyp_traps_vector, VBAR_EL2);
 
@@ -1077,27 +1080,39 @@ void do_unexpected_trap(const char *msg, struct 
cpu_user_regs *regs)
 
 int do_bug_frame(struct cpu_user_regs *regs, vaddr_t pc)
 {
-    const struct bug_frame *bug;
+    const struct bug_frame *bug = NULL;
     const char *prefix = "", *filename, *predicate;
     unsigned long fixup;
-    int id, lineno;
-    static const struct bug_frame *const stop_frames[] = {
-        __stop_bug_frames_0,
-        __stop_bug_frames_1,
-        __stop_bug_frames_2,
-        NULL
-    };
+    int id = -1, lineno;
+    struct virtual_region *region;
 
-    for ( bug = __start_bug_frames, id = 0; stop_frames[id]; ++bug )
+    list_for_each_entry( region, &virtual_region_list, list )
     {
-        while ( unlikely(bug == stop_frames[id]) )
-            ++id;
+        unsigned int i;
 
-        if ( ((vaddr_t)bug_loc(bug)) == pc )
-            break;
-    }
+        if ( region->skip && region->skip(CHECKING_BUG_FRAME, region->priv) )
+            continue;
+
+        if ( pc < region->start || pc > region->end )
+            continue;
 
-    if ( !stop_frames[id] )
+        for ( id = 0; id < BUGFRAME_NR; id++ )
+        {
+            const struct bug_frame *b = NULL;
+
+            for ( i = 0, b = region->frame[id].bugs;
+                  i < region->frame[id].n_bugs; b++, i++ )
+            {
+                if ( ((vaddr_t)bug_loc(b)) == pc )
+                {
+                    bug = b;
+                    goto found;
+                }
+            }
+        }
+    }
+ found:
+    if ( !bug )
         return -ENOENT;
 
     /* WARN, BUG or ASSERT: decode the filename pointer and line number. */
diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c
index 89b5bcb..6e083a8 100644
--- a/xen/arch/x86/extable.c
+++ b/xen/arch/x86/extable.c
@@ -1,6 +1,8 @@
 
+#include <xen/bug_ex_symbols.h>
 #include <xen/config.h>
 #include <xen/init.h>
+#include <xen/list.h>
 #include <xen/perfc.h>
 #include <xen/sort.h>
 #include <xen/spinlock.h>
@@ -80,8 +82,18 @@ search_one_table(const struct exception_table_entry *first,
 unsigned long
 search_exception_table(unsigned long addr)
 {
-    return search_one_table(
-        __start___ex_table, __stop___ex_table-1, addr);
+    struct virtual_region *region;
+
+    list_for_each_entry( region, &virtual_region_list, list )
+    {
+        if ( region->skip && region->skip(CHECKING_EXCEPTION, region->priv) )
+            continue;
+
+        if ( (addr >= region->start) && (addr < region->end) )
+            return search_one_table(region->ex, region->ex_end-1, addr);
+    }
+
+    return 0;
 }
 
 unsigned long
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index a8bf2c9..115e6fd 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -1,3 +1,4 @@
+#include <xen/bug_ex_symbols.h>
 #include <xen/config.h>
 #include <xen/init.h>
 #include <xen/lib.h>
@@ -614,8 +615,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     load_system_tables();
 
     smp_prepare_boot_cpu();
-    sort_exception_tables();
 
+    setup_virtual_regions();
     /* Full exception support from here on in. */
 
     loader = (mbi->flags & MBI_LOADERNAME)
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 564a107..eeada97 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -24,6 +24,7 @@
  * Gareth Hughes <gareth@xxxxxxxxxxx>, May 2000
  */
 
+#include <xen/bug_ex_symbols.h>
 #include <xen/config.h>
 #include <xen/init.h>
 #include <xen/sched.h>
@@ -1132,18 +1133,12 @@ static int emulate_forced_invalid_op(struct 
cpu_user_regs *regs)
 
 void do_invalid_op(struct cpu_user_regs *regs)
 {
-    const struct bug_frame *bug;
+    const struct bug_frame *bug = NULL;
     u8 bug_insn[2];
     const char *prefix = "", *filename, *predicate, *eip = (char *)regs->eip;
     unsigned long fixup;
-    int id, lineno;
-    static const struct bug_frame *const stop_frames[] = {
-        __stop_bug_frames_0,
-        __stop_bug_frames_1,
-        __stop_bug_frames_2,
-        __stop_bug_frames_3,
-        NULL
-    };
+    int id = -1, lineno;
+    struct virtual_region *region;
 
     DEBUGGER_trap_entry(TRAP_invalid_op, regs);
 
@@ -1160,16 +1155,35 @@ void do_invalid_op(struct cpu_user_regs *regs)
          memcmp(bug_insn, "\xf\xb", sizeof(bug_insn)) )
         goto die;
 
-    for ( bug = __start_bug_frames, id = 0; stop_frames[id]; ++bug )
+    list_for_each_entry( region, &virtual_region_list, list )
     {
-        while ( unlikely(bug == stop_frames[id]) )
-            ++id;
-        if ( bug_loc(bug) == eip )
-            break;
+        unsigned int i;
+
+        if ( region->skip && region->skip(CHECKING_BUG_FRAME, region->priv) )
+            continue;
+
+        if ( regs->eip < region->start || regs->eip > region->end )
+            continue;
+
+        for ( id = 0; id < BUGFRAME_NR; id++ )
+        {
+            const struct bug_frame *b = NULL;
+
+            for ( i = 0, b = region->frame[id].bugs;
+                  i < region->frame[id].n_bugs; b++, i++ )
+            {
+                if ( bug_loc(b) == eip )
+                {
+                    bug = b;
+                    goto found;
+                }
+            }
+        }
     }
-    if ( !stop_frames[id] )
-        goto die;
 
+ found:
+    if ( !bug )
+        goto die;
     eip += sizeof(bug_insn);
     if ( id == BUGFRAME_run_fn )
     {
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 82625a5..76d7b07 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -1,4 +1,5 @@
 obj-y += bitmap.o
+obj-y += bug_ex_symbols.o
 obj-$(CONFIG_CORE_PARKING) += core_parking.o
 obj-y += cpu.o
 obj-y += cpupool.o
diff --git a/xen/common/bug_ex_symbols.c b/xen/common/bug_ex_symbols.c
new file mode 100644
index 0000000..77bb72b
--- /dev/null
+++ b/xen/common/bug_ex_symbols.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ *
+ */
+
+#include <xen/bug_ex_symbols.h>
+#include <xen/config.h>
+#include <xen/kernel.h>
+#include <xen/init.h>
+#include <xen/spinlock.h>
+
+extern char __stext[];
+
+struct virtual_region kernel_text = {
+    .list = LIST_HEAD_INIT(kernel_text.list),
+    .start = (unsigned long)_stext,
+    .end = (unsigned long)_etext,
+#ifdef CONFIG_X86
+    .ex = (struct exception_table_entry *)__start___ex_table,
+    .ex_end = (struct exception_table_entry *)__stop___ex_table,
+#endif
+};
+
+/*
+ * The kernel_inittext should only be used when system_state
+ * is booting. Otherwise all accesses should be ignored.
+ */
+static bool_t ignore_if_active(unsigned int flag, unsigned long priv)
+{
+    return (system_state >= SYS_STATE_active);
+}
+
+/*
+ * Becomes irrelevant when __init sections are cleared.
+ */
+struct virtual_region kernel_inittext  = {
+    .list = LIST_HEAD_INIT(kernel_inittext.list),
+    .skip = ignore_if_active,
+    .start = (unsigned long)_sinittext,
+    .end = (unsigned long)_einittext,
+#ifdef CONFIG_X86
+    /* Even if they are __init their exception entry still gets stuck here. */
+    .ex = (struct exception_table_entry *)__start___ex_table,
+    .ex_end = (struct exception_table_entry *)__stop___ex_table,
+#endif
+};
+
+/*
+ * No locking. Additions are done either at startup (when there is only
+ * one CPU) or when all CPUs are running without IRQs.
+ *
+ * Deletions are big tricky. We MUST make sure all but one CPU
+ * are running cpu_relax().
+ *
+ */
+LIST_HEAD(virtual_region_list);
+
+int register_virtual_region(struct virtual_region *r)
+{
+    ASSERT(!local_irq_is_enabled());
+
+    list_add_tail(&r->list, &virtual_region_list);
+    return 0;
+}
+
+void unregister_virtual_region(struct virtual_region *r)
+{
+    ASSERT(!local_irq_is_enabled());
+
+    list_del_init(&r->list);
+}
+
+void __init setup_virtual_regions(void)
+{
+    ssize_t sz;
+    unsigned int i, idx;
+    static const struct bug_frame *const stop_frames[] = {
+        __start_bug_frames,
+        __stop_bug_frames_0,
+        __stop_bug_frames_1,
+        __stop_bug_frames_2,
+#ifdef CONFIG_X86
+        __stop_bug_frames_3,
+#endif
+        NULL
+    };
+
+#ifdef CONFIG_X86
+    sort_exception_tables();
+#endif
+
+    /* N.B. idx != i */
+    for ( idx = 0, i = 1; stop_frames[i]; i++, idx++ )
+    {
+        struct bug_frame *s;
+
+        s = (struct bug_frame *)stop_frames[i-1];
+        sz = stop_frames[i] - s;
+
+        kernel_text.frame[idx].n_bugs = sz;
+        kernel_text.frame[idx].bugs = s;
+
+        kernel_inittext.frame[idx].n_bugs = sz;
+        kernel_inittext.frame[idx].bugs = s;
+    }
+
+    register_virtual_region(&kernel_text);
+    register_virtual_region(&kernel_inittext);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/symbols.c b/xen/common/symbols.c
index a59c59d..2cc416e 100644
--- a/xen/common/symbols.c
+++ b/xen/common/symbols.c
@@ -10,6 +10,7 @@
  *      compression (see tools/symbols.c for a more complete description)
  */
 
+#include <xen/bug_ex_symbols.h>
 #include <xen/config.h>
 #include <xen/symbols.h>
 #include <xen/kernel.h>
@@ -95,10 +96,28 @@ static unsigned int get_symbol_offset(unsigned long pos)
     return name - symbols_names;
 }
 
+bool_t __is_active_kernel_text(unsigned long addr, symbols_lookup_t *cb)
+{
+    struct virtual_region *region;
+
+    list_for_each_entry( region, &virtual_region_list, list )
+    {
+        if ( region->skip && region->skip(CHECKING_SYMBOL, region->priv) )
+            continue;
+
+        if ( addr >= region->start && addr < region->end )
+        {
+            if ( cb && region->symbols_lookup )
+                *cb = region->symbols_lookup;
+            return 1;
+        }
+    }
+    return 0;
+}
+
 bool_t is_active_kernel_text(unsigned long addr)
 {
-    return (is_kernel_text(addr) ||
-            (system_state < SYS_STATE_active && is_kernel_inittext(addr)));
+    return __is_active_kernel_text(addr, NULL);
 }
 
 const char *symbols_lookup(unsigned long addr,
@@ -108,13 +127,17 @@ const char *symbols_lookup(unsigned long addr,
 {
     unsigned long i, low, high, mid;
     unsigned long symbol_end = 0;
+    symbols_lookup_t symbol_lookup = NULL;
 
     namebuf[KSYM_NAME_LEN] = 0;
     namebuf[0] = 0;
 
-    if (!is_active_kernel_text(addr))
+    if (!__is_active_kernel_text(addr, &symbol_lookup))
         return NULL;
 
+    if (symbol_lookup)
+        return (symbol_lookup)(addr, symbolsize, offset, namebuf);
+
         /* do a binary search on the sorted symbols_addresses array */
     low = 0;
     high = symbols_num_syms;
diff --git a/xen/include/xen/bug_ex_symbols.h b/xen/include/xen/bug_ex_symbols.h
new file mode 100644
index 0000000..6f3401b
--- /dev/null
+++ b/xen/include/xen/bug_ex_symbols.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ *
+ */
+
+#ifndef __BUG_EX_SYMBOL_LIST__
+#define __BUG_EX_SYMBOL_LIST__
+
+#include <xen/config.h>
+#include <xen/list.h>
+#include <xen/symbols.h>
+
+#ifdef CONFIG_X86
+#include <asm/uaccess.h>
+#endif
+#include <asm/bug.h>
+
+struct virtual_region
+{
+    struct list_head list;
+
+#define CHECKING_SYMBOL         (1<<1)
+#define CHECKING_BUG_FRAME      (1<<2)
+#define CHECKING_EXCEPTION      (1<<3)
+    /*
+     * Whether to skip this region for particular searches. The flag
+     * can be CHECKING_[SYMBOL|BUG_FRAMES|EXCEPTION].
+     *
+     * If the function returns 1 this region will be skipped.
+     */
+    bool_t (*skip)(unsigned int flag, unsigned long priv);
+
+    unsigned long start;        /* Virtual address start. */
+    unsigned long end;          /* Virtual address start. */
+
+    /*
+     * If ->skip returns false for CHECKING_SYMBOL we will use
+     * 'symbols_lookup' callback to retrieve the name of the
+     * addr between start and end. If this is NULL the
+     * default lookup mechanism is used (the skip value is
+     * ignored).
+     */
+    symbols_lookup_t symbols_lookup;
+
+    struct {
+        struct bug_frame *bugs; /* The pointer to array of bug frames. */
+        ssize_t n_bugs;         /* The number of them. */
+    } frame[BUGFRAME_NR];
+
+#ifdef CONFIG_X86
+    struct exception_table_entry *ex;
+    struct exception_table_entry *ex_end;
+#endif
+
+    unsigned long priv;         /* To be used by above funcionts if need to. */
+};
+
+extern struct list_head virtual_region_list;
+
+extern void setup_virtual_regions(void);
+extern int register_virtual_region(struct virtual_region *r);
+extern void unregister_virtual_region(struct virtual_region *r);
+
+#endif /* __BUG_EX_SYMBOL_LIST__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/xen/kernel.h b/xen/include/xen/kernel.h
index 548b64d..8cf7af7 100644
--- a/xen/include/xen/kernel.h
+++ b/xen/include/xen/kernel.h
@@ -65,12 +65,14 @@
        1;                                      \
 })
 
+
 extern char _start[], _end[], start[];
 #define is_kernel(p) ({                         \
     char *__p = (char *)(unsigned long)(p);     \
     (__p >= _start) && (__p < _end);            \
 })
 
+/* For symbols_lookup usage. */
 extern char _stext[], _etext[];
 #define is_kernel_text(p) ({                    \
     char *__p = (char *)(unsigned long)(p);     \
diff --git a/xen/include/xen/symbols.h b/xen/include/xen/symbols.h
index 1fa0537..fe9ed8f 100644
--- a/xen/include/xen/symbols.h
+++ b/xen/include/xen/symbols.h
@@ -5,6 +5,15 @@
 
 #define KSYM_NAME_LEN 127
 
+/*
+ * Typedef for the callback functions that symbols_lookup
+ * can call if virtual_region_list has an callback for it.
+ */
+typedef const char *(*symbols_lookup_t)(unsigned long addr,
+                                        unsigned long *symbolsize,
+                                        unsigned long *offset,
+                                        char *namebuf);
+
 /* Lookup an address. */
 const char *symbols_lookup(unsigned long addr,
                            unsigned long *symbolsize,
-- 
2.5.0


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

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