Merge branches 'acpi-processor', 'acpi-cppc', 'acpi-dbg', 'acpi-misc' and 'acpi-pci'
[linux-2.6-microblaze.git] / arch / x86 / kernel / unwind_orc.c
index e9cc182..7f969b2 100644 (file)
@@ -8,19 +8,21 @@
 #include <asm/orc_lookup.h>
 
 #define orc_warn(fmt, ...) \
-       printk_deferred_once(KERN_WARNING pr_fmt("WARNING: " fmt), ##__VA_ARGS__)
+       printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__)
+
+#define orc_warn_current(args...)                                      \
+({                                                                     \
+       if (state->task == current)                                     \
+               orc_warn(args);                                         \
+})
 
 extern int __start_orc_unwind_ip[];
 extern int __stop_orc_unwind_ip[];
 extern struct orc_entry __start_orc_unwind[];
 extern struct orc_entry __stop_orc_unwind[];
 
-static DEFINE_MUTEX(sort_mutex);
-int *cur_orc_ip_table = __start_orc_unwind_ip;
-struct orc_entry *cur_orc_table = __start_orc_unwind;
-
-unsigned int lookup_num_blocks;
-bool orc_init;
+static bool orc_init __ro_after_init;
+static unsigned int lookup_num_blocks __ro_after_init;
 
 static inline unsigned long orc_ip(const int *ip)
 {
@@ -142,9 +144,6 @@ static struct orc_entry *orc_find(unsigned long ip)
 {
        static struct orc_entry *orc;
 
-       if (!orc_init)
-               return NULL;
-
        if (ip == 0)
                return &null_orc_entry;
 
@@ -189,6 +188,10 @@ static struct orc_entry *orc_find(unsigned long ip)
 
 #ifdef CONFIG_MODULES
 
+static DEFINE_MUTEX(sort_mutex);
+static int *cur_orc_ip_table = __start_orc_unwind_ip;
+static struct orc_entry *cur_orc_table = __start_orc_unwind;
+
 static void orc_sort_swap(void *_a, void *_b, int size)
 {
        struct orc_entry *orc_a, *orc_b;
@@ -317,12 +320,19 @@ EXPORT_SYMBOL_GPL(unwind_get_return_address);
 
 unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
 {
+       struct task_struct *task = state->task;
+
        if (unwind_done(state))
                return NULL;
 
        if (state->regs)
                return &state->regs->ip;
 
+       if (task != current && state->sp == task->thread.sp) {
+               struct inactive_task_frame *frame = (void *)task->thread.sp;
+               return &frame->ret_addr;
+       }
+
        if (state->sp)
                return (unsigned long *)state->sp - 1;
 
@@ -381,9 +391,38 @@ static bool deref_stack_iret_regs(struct unwind_state *state, unsigned long addr
        return true;
 }
 
+/*
+ * If state->regs is non-NULL, and points to a full pt_regs, just get the reg
+ * value from state->regs.
+ *
+ * Otherwise, if state->regs just points to IRET regs, and the previous frame
+ * had full regs, it's safe to get the value from the previous regs.  This can
+ * happen when early/late IRQ entry code gets interrupted by an NMI.
+ */
+static bool get_reg(struct unwind_state *state, unsigned int reg_off,
+                   unsigned long *val)
+{
+       unsigned int reg = reg_off/8;
+
+       if (!state->regs)
+               return false;
+
+       if (state->full_regs) {
+               *val = ((unsigned long *)state->regs)[reg];
+               return true;
+       }
+
+       if (state->prev_regs) {
+               *val = ((unsigned long *)state->prev_regs)[reg];
+               return true;
+       }
+
+       return false;
+}
+
 bool unwind_next_frame(struct unwind_state *state)
 {
-       unsigned long ip_p, sp, orig_ip = state->ip, prev_sp = state->sp;
+       unsigned long ip_p, sp, tmp, orig_ip = state->ip, prev_sp = state->sp;
        enum stack_type prev_type = state->stack_info.type;
        struct orc_entry *orc;
        bool indirect = false;
@@ -445,43 +484,39 @@ bool unwind_next_frame(struct unwind_state *state)
                break;
 
        case ORC_REG_R10:
-               if (!state->regs || !state->full_regs) {
-                       orc_warn("missing regs for base reg R10 at ip %pB\n",
-                                (void *)state->ip);
+               if (!get_reg(state, offsetof(struct pt_regs, r10), &sp)) {
+                       orc_warn_current("missing R10 value at %pB\n",
+                                        (void *)state->ip);
                        goto err;
                }
-               sp = state->regs->r10;
                break;
 
        case ORC_REG_R13:
-               if (!state->regs || !state->full_regs) {
-                       orc_warn("missing regs for base reg R13 at ip %pB\n",
-                                (void *)state->ip);
+               if (!get_reg(state, offsetof(struct pt_regs, r13), &sp)) {
+                       orc_warn_current("missing R13 value at %pB\n",
+                                        (void *)state->ip);
                        goto err;
                }
-               sp = state->regs->r13;
                break;
 
        case ORC_REG_DI:
-               if (!state->regs || !state->full_regs) {
-                       orc_warn("missing regs for base reg DI at ip %pB\n",
-                                (void *)state->ip);
+               if (!get_reg(state, offsetof(struct pt_regs, di), &sp)) {
+                       orc_warn_current("missing RDI value at %pB\n",
+                                        (void *)state->ip);
                        goto err;
                }
-               sp = state->regs->di;
                break;
 
        case ORC_REG_DX:
-               if (!state->regs || !state->full_regs) {
-                       orc_warn("missing regs for base reg DX at ip %pB\n",
-                                (void *)state->ip);
+               if (!get_reg(state, offsetof(struct pt_regs, dx), &sp)) {
+                       orc_warn_current("missing DX value at %pB\n",
+                                        (void *)state->ip);
                        goto err;
                }
-               sp = state->regs->dx;
                break;
 
        default:
-               orc_warn("unknown SP base reg %d for ip %pB\n",
+               orc_warn("unknown SP base reg %d at %pB\n",
                         orc->sp_reg, (void *)state->ip);
                goto err;
        }
@@ -504,44 +539,48 @@ bool unwind_next_frame(struct unwind_state *state)
 
                state->sp = sp;
                state->regs = NULL;
+               state->prev_regs = NULL;
                state->signal = false;
                break;
 
        case ORC_TYPE_REGS:
                if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) {
-                       orc_warn("can't dereference registers at %p for ip %pB\n",
-                                (void *)sp, (void *)orig_ip);
+                       orc_warn_current("can't access registers at %pB\n",
+                                        (void *)orig_ip);
                        goto err;
                }
 
                state->regs = (struct pt_regs *)sp;
+               state->prev_regs = NULL;
                state->full_regs = true;
                state->signal = true;
                break;
 
        case ORC_TYPE_REGS_IRET:
                if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) {
-                       orc_warn("can't dereference iret registers at %p for ip %pB\n",
-                                (void *)sp, (void *)orig_ip);
+                       orc_warn_current("can't access iret registers at %pB\n",
+                                        (void *)orig_ip);
                        goto err;
                }
 
+               if (state->full_regs)
+                       state->prev_regs = state->regs;
                state->regs = (void *)sp - IRET_FRAME_OFFSET;
                state->full_regs = false;
                state->signal = true;
                break;
 
        default:
-               orc_warn("unknown .orc_unwind entry type %d for ip %pB\n",
+               orc_warn("unknown .orc_unwind entry type %d at %pB\n",
                         orc->type, (void *)orig_ip);
-               break;
+               goto err;
        }
 
        /* Find BP: */
        switch (orc->bp_reg) {
        case ORC_REG_UNDEFINED:
-               if (state->regs && state->full_regs)
-                       state->bp = state->regs->bp;
+               if (get_reg(state, offsetof(struct pt_regs, bp), &tmp))
+                       state->bp = tmp;
                break;
 
        case ORC_REG_PREV_SP:
@@ -564,8 +603,8 @@ bool unwind_next_frame(struct unwind_state *state)
        if (state->stack_info.type == prev_type &&
            on_stack(&state->stack_info, (void *)state->sp, sizeof(long)) &&
            state->sp <= prev_sp) {
-               orc_warn("stack going in the wrong direction? ip=%pB\n",
-                        (void *)orig_ip);
+               orc_warn_current("stack going in the wrong direction? at %pB\n",
+                                (void *)orig_ip);
                goto err;
        }
 
@@ -588,17 +627,20 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
        memset(state, 0, sizeof(*state));
        state->task = task;
 
+       if (!orc_init)
+               goto err;
+
        /*
         * Refuse to unwind the stack of a task while it's executing on another
         * CPU.  This check is racy, but that's ok: the unwinder has other
         * checks to prevent it from going off the rails.
         */
        if (task_on_another_cpu(task))
-               goto done;
+               goto err;
 
        if (regs) {
                if (user_mode(regs))
-                       goto done;
+                       goto the_end;
 
                state->ip = regs->ip;
                state->sp = regs->sp;
@@ -631,6 +673,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
                 * generate some kind of backtrace if this happens.
                 */
                void *next_page = (void *)PAGE_ALIGN((unsigned long)state->sp);
+               state->error = true;
                if (get_stack_info(next_page, state->task, &state->stack_info,
                                   &state->stack_mask))
                        return;
@@ -651,13 +694,14 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
        /* Otherwise, skip ahead to the user-specified starting frame: */
        while (!unwind_done(state) &&
               (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
-                       state->sp <= (unsigned long)first_frame))
+                       state->sp < (unsigned long)first_frame))
                unwind_next_frame(state);
 
        return;
 
-done:
+err:
+       state->error = true;
+the_end:
        state->stack_info.type = STACK_TYPE_UNKNOWN;
-       return;
 }
 EXPORT_SYMBOL_GPL(__unwind_start);