[Crash-utility] [PATCH] Fix NMI backtrace for Linux 3.3+
Dave Anderson
anderson at redhat.com
Wed Apr 30 18:55:01 UTC 2014
Nicely done, Petr -- I really appreciate this fix. Queued for crash-7.0.7:
https://github.com/crash-utility/crash/commit/8e15958e1b7183bbfbdf004f0ad8f2b62f023f9f
Thanks,
Dave
----- Original Message -----
> Kernel commit 3f3c8b8c4b2a34776c3470142a7c8baafcda6eb0 changed the NMI stack
> layout, adding 12 more values on the stack. The fix has two parts:
>
> 1. Determine if this kernel has the nested NMI layout and set a
> machine-specific flag (NESTED_NMI) if it does.
>
> 2. When backtracing an NMI stack, use the saved values instead of those
> found at the top of stack.
>
> Additionally, kernel commit 28696f434fef0efa97534b59986ad33b9c4df7f8 changed
> the stack layout again, swapping the location of the "saved" and "copied"
> registers. This can be detected automatically, because the "copied" registers
> contain either a copy of the "saved" registers, or point to repeat_nmi. So,
> if restart_nmi is found as the return address, assume that this is the old
> layout and adjust the stack pointer again.
>
> Without the patch, wrong register values are shown in the NMI backtrace.
>
> Signed-off-by: Petr Tesarik <ptesarik at suse.cz>
> ---
> defs.h | 1 +
> x86_64.c | 73
> +++++++++++++++++++++++++++++++++++++++++++++++++---------------
> 2 files changed, 57 insertions(+), 17 deletions(-)
>
> diff --git a/defs.h b/defs.h
> index 711b154..4054de4 100644
> --- a/defs.h
> +++ b/defs.h
> @@ -5123,6 +5123,7 @@ struct machine_specific {
> #define VM_XEN_RHEL4 (0x100)
> #define FRAMEPOINTER (0x200)
> #define GART_REGION (0x400)
> +#define NESTED_NMI (0x800)
>
> #define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4)
>
> diff --git a/x86_64.c b/x86_64.c
> index 5364c30..fd384ac 100644
> --- a/x86_64.c
> +++ b/x86_64.c
> @@ -468,6 +468,8 @@ x86_64_init(int when)
> else
> x86_64_per_cpu_init();
> x86_64_ist_init();
> + if (symbol_exists("repeat_nmi"))
> + machdep->flags |= NESTED_NMI;
> machdep->in_alternate_stack = x86_64_in_alternate_stack;
> if ((machdep->machspec->irqstack = (char *)
> malloc(machdep->machspec->stkinfo.isize)) == NULL)
> @@ -3009,6 +3011,8 @@ in_exception_stack:
> }
>
> stacktop = bt->stacktop - SIZE(pt_regs);
> + if ((machdep->flags & NESTED_NMI) && estack_index == NMI_STACK)
> + stacktop -= 12*sizeof(ulong);
>
> bt->flags &= ~BT_FRAMESIZE_DISABLE;
>
> @@ -3046,21 +3050,37 @@ in_exception_stack:
> }
>
> cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0,
> - bt->stackbuf + (bt->stacktop - bt->stackbase) -
> - SIZE(pt_regs), bt, ofp);
> + bt->stackbuf + (stacktop - bt->stackbase),
> + bt, ofp);
>
> if (!BT_REFERENCE_CHECK(bt))
> fprintf(fp, "--- <%s exception stack> ---\n",
> x86_64_exception_stacks[estack_index]);
>
> - /*
> - * stack = (unsigned long *) estack_end[-2];
> + /*
> + * Find the CPU-saved, or handler-saved registers
> */
> up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]);
> - up -= 2;
> - rsp = bt->stkptr = *up;
> - up -= 3;
> - bt->instptr = *up;
> + up -= 5;
> + if ((machdep->flags & NESTED_NMI) &&
> + estack_index == NMI_STACK &&
> + bt->stkptr <= bt->stacktop - 17*sizeof(ulong)) {
> + up -= 12;
> + /* Copied and saved regs are swapped in pre-3.8 kernels */
> + if (*up == symbol_value("repeat_nmi"))
> + up += 5;
> + }
> +
> + /* Registers (as saved by CPU):
> + *
> + * up[4] SS
> + * up[3] RSP
> + * up[2] RFLAGS
> + * up[1] CS
> + * up[0] RIP
> + */
> + rsp = bt->stkptr = up[3];
> + bt->instptr = up[0];
> if (cs & 3)
> done = TRUE; /* user-mode exception */
> else
> @@ -3513,27 +3533,46 @@ in_exception_stack:
> }
>
> stacktop = bt->stacktop - SIZE(pt_regs);
> -
> + if ((machdep->flags & NESTED_NMI) &&
> + estack_index == NMI_STACK)
> + stacktop -= 12*sizeof(ulong);
> +
> if (!done) {
> level = dwarf_backtrace(bt, level, stacktop);
> done = TRUE;
> }
>
> cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0,
> - bt->stackbuf + (bt->stacktop - bt->stackbase) -
> - SIZE(pt_regs), bt, ofp);
> + bt->stackbuf + (stacktop - bt->stackbase),
> + bt, ofp);
>
> if (!BT_REFERENCE_CHECK(bt))
> fprintf(fp, "--- <exception stack> ---\n");
>
> - /*
> - * stack = (unsigned long *) estack_end[-2];
> + /*
> + * Find the CPU-saved, or handler-saved registers
> */
> up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]);
> - up -= 2;
> - rsp = bt->stkptr = *up;
> - up -= 3;
> - bt->instptr = *up;
> + up -= 5;
> + if ((machdep->flags & NESTED_NMI) &&
> + estack_index == NMI_STACK &&
> + bt->stkptr <= bt->stacktop - 17*sizeof(ulong)) {
> + up -= 12;
> + /* Copied and saved regs are swapped in pre-3.8 kernels */
> + if (*up == symbol_value("repeat_nmi"))
> + up += 5;
> + }
> +
> + /* Registers (as saved by CPU):
> + *
> + * up[4] SS
> + * up[3] RSP
> + * up[2] RFLAGS
> + * up[1] CS
> + * up[0] RIP
> + */
> + rsp = bt->stkptr = up[3];
> + bt->instptr = up[0];
> if (cs & 3)
> done = TRUE; /* user-mode exception */
> else
> --
> 1.8.4.5
>
More information about the Crash-utility
mailing list