Skip to content

Commit

Permalink
Additional fixes for the ARM64 "bt" command for Linux 4.14 kernels.
Browse files Browse the repository at this point in the history
The patch corrects the contents of in-kernel exception frame register
dumps, and properly transitions the backtrace from the IRQ stack
to the process stack.
(takahiro.akashi@linaro.org)
  • Loading branch information
Dave Anderson committed Oct 17, 2017
1 parent 9e5255a commit 2b93c03
Showing 1 changed file with 140 additions and 43 deletions.
183 changes: 140 additions & 43 deletions arm64.c
Expand Up @@ -72,6 +72,7 @@ static void arm64_cmd_mach(void);
static void arm64_display_machine_stats(void);
static int arm64_get_smp_cpus(void);
static void arm64_clear_machdep_cache(void);
static int arm64_on_process_stack(struct bt_info *, ulong);
static int arm64_in_alternate_stack(int, ulong);
static int arm64_on_irq_stack(int, ulong);
static void arm64_set_irq_stack(struct bt_info *);
Expand Down Expand Up @@ -1333,34 +1334,64 @@ arm64_irq_stack_init(void)
int i;
struct syment *sp;
struct gnu_request request, *req;
req = &request;
struct machine_specific *ms = machdep->machspec;
ulong p;
req = &request;

if (!symbol_exists("irq_stack") ||
!(sp = per_cpu_symbol_search("irq_stack")) ||
!get_symbol_type("irq_stack", NULL, req) ||
(req->typecode != TYPE_CODE_ARRAY) ||
(req->target_typecode != TYPE_CODE_INT))
return;

if (CRASHDEBUG(1)) {
fprintf(fp, "irq_stack: \n");
fprintf(fp, " type: %s\n",
(req->typecode == TYPE_CODE_ARRAY) ? "TYPE_CODE_ARRAY" : "other");
fprintf(fp, " target_typecode: %s\n",
req->target_typecode == TYPE_CODE_INT ? "TYPE_CODE_INT" : "other");
fprintf(fp, " target_length: %ld\n", req->target_length);
fprintf(fp, " length: %ld\n", req->length);
}

ms->irq_stack_size = req->length;
if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong)))))
error(FATAL, "cannot malloc irq_stack addresses\n");
if (symbol_exists("irq_stack") &&
(sp = per_cpu_symbol_search("irq_stack")) &&
get_symbol_type("irq_stack", NULL, req)) {
/* before v4.14 or CONFIG_VMAP_STACK disabled */
if (CRASHDEBUG(1)) {
fprintf(fp, "irq_stack: \n");
fprintf(fp, " type: %s\n",
(req->typecode == TYPE_CODE_ARRAY) ?
"TYPE_CODE_ARRAY" : "other");
fprintf(fp, " target_typecode: %s\n",
req->target_typecode == TYPE_CODE_INT ?
"TYPE_CODE_INT" : "other");
fprintf(fp, " target_length: %ld\n",
req->target_length);
fprintf(fp, " length: %ld\n", req->length);
}

if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong)))))
error(FATAL, "cannot malloc irq_stack addresses\n");
ms->irq_stack_size = req->length;
machdep->flags |= IRQ_STACKS;

for (i = 0; i < kt->cpus; i++)
ms->irq_stacks[i] = kt->__per_cpu_offset[i] + sp->value;
for (i = 0; i < kt->cpus; i++)
ms->irq_stacks[i] = kt->__per_cpu_offset[i] + sp->value;
} else if (symbol_exists("irq_stack_ptr") &&
(sp = per_cpu_symbol_search("irq_stack_ptr")) &&
get_symbol_type("irq_stack_ptr", NULL, req)) {
/* v4.14 and later with CONFIG_VMAP_STACK enabled */
if (CRASHDEBUG(1)) {
fprintf(fp, "irq_stack_ptr: \n");
fprintf(fp, " type: %x, %s\n",
(int)req->typecode,
(req->typecode == TYPE_CODE_PTR) ?
"TYPE_CODE_PTR" : "other");
fprintf(fp, " target_typecode: %x, %s\n",
(int)req->target_typecode,
req->target_typecode == TYPE_CODE_INT ?
"TYPE_CODE_INT" : "other");
fprintf(fp, " target_length: %ld\n",
req->target_length);
fprintf(fp, " length: %ld\n", req->length);
}

if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong)))))
error(FATAL, "cannot malloc irq_stack addresses\n");
ms->irq_stack_size = 16384;
machdep->flags |= IRQ_STACKS;

machdep->flags |= IRQ_STACKS;
for (i = 0; i < kt->cpus; i++) {
p = kt->__per_cpu_offset[i] + sp->value;
readmem(p, KVADDR, &(ms->irq_stacks[i]), sizeof(ulong),
"IRQ stack pointer", RETURN_ON_ERROR);
}
}
}

/*
Expand Down Expand Up @@ -1750,11 +1781,20 @@ arm64_display_full_frame(struct bt_info *bt, ulong sp)
if (bt->frameptr == sp)
return;

if (!INSTACK(sp, bt) || !INSTACK(bt->frameptr, bt)) {
if (sp == 0)
sp = bt->stacktop - USER_EFRAME_OFFSET;
else
return;
if (INSTACK(bt->frameptr, bt)) {
if (INSTACK(sp, bt)) {
; /* normal case */
} else {
if (sp == 0)
/* interrupt in user mode */
sp = bt->stacktop - USER_EFRAME_OFFSET;
else
/* interrupt in kernel mode */
sp = bt->stacktop;
}
} else {
/* IRQ exception frame */
return;
}

words = (sp - bt->frameptr) / sizeof(ulong);
Expand Down Expand Up @@ -1860,6 +1900,9 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
if ((frame->fp == 0) && (frame->pc == 0))
return FALSE;

if (!(machdep->flags & IRQ_STACKS))
return TRUE;

/*
* The kernel's manner of determining the end of the IRQ stack:
*
Expand All @@ -1872,7 +1915,25 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
* irq_stack_ptr = IRQ_STACK_PTR(raw_smp_processor_id());
* orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr); (pt_regs pointer on process stack)
*/
if (machdep->flags & IRQ_STACKS) {
if (machdep->flags & UNW_4_14) {
if ((bt->flags & BT_IRQSTACK) &&
!arm64_on_irq_stack(bt->tc->processor, frame->fp)) {
if (arm64_on_process_stack(bt, frame->fp)) {
arm64_set_process_stack(bt);

frame->sp = frame->fp - SIZE(pt_regs) + 16;
/* for switch_stack */
/* fp still points to irq stack */
bt->bptr = fp;
/* for display_full_frame */
/* sp points to process stack */
bt->frameptr = frame->sp;
} else {
/* irq -> user */
return FALSE;
}
}
} else { /* !UNW_4_14 */
ms = machdep->machspec;
irq_stack_ptr = ms->irq_stacks[bt->tc->processor] + ms->irq_stack_size - 16;

Expand All @@ -1896,7 +1957,7 @@ arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame)
return FALSE;
}
}
}
} /* UNW_4_14 */

return TRUE;
}
Expand Down Expand Up @@ -2086,10 +2147,17 @@ arm64_unwind_frame_v2(struct bt_info *bt, struct arm64_stackframe *frame,
* We are on process stack. Just add a faked frame
*/

if (!arm64_on_irq_stack(bt->tc->processor, ext_frame.fp))
frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs);
else {
if (!arm64_on_irq_stack(bt->tc->processor, ext_frame.fp)) {
if (MEMBER_EXISTS("pt_regs", "stackframe")) {
frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs) - 16;
frame->fp = ext_frame.fp;
} else {
frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs);
frame->fp = frame->sp;
}
} else {
/*
* FIXME: very exceptional case
* We are already back on process stack, but
Expand All @@ -2109,10 +2177,10 @@ arm64_unwind_frame_v2(struct bt_info *bt, struct arm64_stackframe *frame,
* Really ugly
*/
frame->sp = frame->fp + 0x20;
frame->fp = frame->sp;
fprintf(ofp, " (Next exception frame might be wrong)\n");
}

frame->fp = frame->sp;
} else {
/* We are on IRQ stack */

Expand All @@ -2122,9 +2190,15 @@ arm64_unwind_frame_v2(struct bt_info *bt, struct arm64_stackframe *frame,
if (ext_frame.fp != irq_stack_ptr) {
/* (2) Just add a faked frame */

frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs);
frame->fp = frame->sp;
if (MEMBER_EXISTS("pt_regs", "stackframe")) {
frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs);
frame->fp = ext_frame.fp;
} else {
frame->sp = ext_frame.fp
- sizeof(struct arm64_pt_regs) - 16;
frame->fp = frame->sp;
}
} else {
/*
* (3)
Expand Down Expand Up @@ -2303,12 +2377,17 @@ arm64_back_trace_cmd(struct bt_info *bt)

if (arm64_in_exception_text(bt->instptr) && INSTACK(stackframe.fp, bt)) {
if (!(bt->flags & BT_IRQSTACK) ||
(((stackframe.sp + SIZE(pt_regs)) < bt->stacktop)))
exception_frame = stackframe.fp - SIZE(pt_regs);
(((stackframe.sp + SIZE(pt_regs)) < bt->stacktop))) {
if (MEMBER_EXISTS("pt_regs", "stackframe"))
/* v4.14 or later */
exception_frame = stackframe.fp - SIZE(pt_regs) + 16;
else
exception_frame = stackframe.fp - SIZE(pt_regs);
}
}

if ((bt->flags & BT_IRQSTACK) &&
!arm64_on_irq_stack(bt->tc->processor, stackframe.sp)) {
!arm64_on_irq_stack(bt->tc->processor, stackframe.fp)) {
bt->flags &= ~BT_IRQSTACK;
if (arm64_switch_stack(bt, &stackframe, ofp) == USER_MODE)
break;
Expand Down Expand Up @@ -2424,6 +2503,8 @@ arm64_back_trace_cmd_v2(struct bt_info *bt)
* otherwise show an exception frame.
* Since exception entry code doesn't have a real
* stackframe, we fake a dummy frame here.
* Note: Since we have a real stack frame in pt_regs,
* We no longer need a dummy frame on v4.14 or later.
*/
if (!arm64_in_exp_entry(stackframe.pc))
continue;
Expand Down Expand Up @@ -2669,7 +2750,9 @@ arm64_switch_stack(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp
if (frame->fp == 0)
return USER_MODE;

arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp);
if (!(machdep->flags & UNW_4_14))
arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp);

return KERNEL_MODE;
}

Expand Down Expand Up @@ -3362,6 +3445,20 @@ arm64_clear_machdep_cache(void) {
return;
}

static int
arm64_on_process_stack(struct bt_info *bt, ulong stkptr)
{
ulong stackbase, stacktop;

stackbase = GET_STACKBASE(bt->task);
stacktop = GET_STACKTOP(bt->task);

if ((stkptr >= stackbase) && (stkptr < stacktop))
return TRUE;

return FALSE;
}

static int
arm64_on_irq_stack(int cpu, ulong stkptr)
{
Expand Down

0 comments on commit 2b93c03

Please sign in to comment.