This patch introduces per-cpu IRQ stacks for RISCV64 to let
"bt" do backtrace on it and 'bt -E' search eframes on it,
and the 'help -m' command displays the addresses of each
per-cpu IRQ stack.
TEST: a vmcore dumped via hacking the handle_irq_event_percpu()
( Why not using lkdtm INT_HW_IRQ_EN EXCEPTION ?
There is a deadlock[1] in crash_kexec path if use that)
```
crash> bt
PID: 0 TASK: ffffffff8140db00 CPU: 0 COMMAND: "swapper/0"
#0 [ff20000000003e60] __handle_irq_event_percpu at ffffffff8006462e
#1 [ff20000000003ed0] handle_irq_event_percpu at ffffffff80064702
#2 [ff20000000003ef0] handle_irq_event at ffffffff8006477c
#3 [ff20000000003f20] handle_fasteoi_irq at ffffffff80068664
#4 [ff20000000003f50] generic_handle_domain_irq at ffffffff80063988
#5 [ff20000000003f60] plic_handle_irq at ffffffff8046633e
#6 [ff20000000003fb0] generic_handle_domain_irq at ffffffff80063988
#7 [ff20000000003fc0] riscv_intc_irq at ffffffff80465f8e
#8 [ff20000000003fd0] handle_riscv_irq at ffffffff808361e8
PC: ffffffff80837314 [default_idle_call+50]
RA: ffffffff80837310 [default_idle_call+46]
SP: ffffffff81403da0 CAUSE: 8000000000000009
epc : ffffffff80837314 ra : ffffffff80837310 sp : ffffffff81403da0
gp : ffffffff814ef848 tp : ffffffff8140db00 t0 : ff2000000004bb18
t1 : 0000000000032c73 t2 : ffffffff81200a48 s0 : ffffffff81403db0
s1 : 0000000000000000 a0 : 0000000000000004 a1 : 0000000000000000
a2 : ff6000009f1e7000 a3 : 0000000000002304 a4 : ffffffff80c1c2d8
a5 : 0000000000000000 a6 : ff6000001fe01958 a7 : 00002496ea89dbf1
s2 : ffffffff814f0220 s3 : 0000000000000001 s4 : 000000000000003f
s5 : ffffffff814f03d8 s6 : 0000000000000000 s7 : ffffffff814f00d0
s8 : ffffffff81526f10 s9 : ffffffff80c1d880 s10: 0000000000000000
s11: 0000000000000001 t3 : 0000000000003392 t4 : 0000000000000000
t5 : 0000000000000000 t6 : 0000000000000040
status: 0000000200000120 badaddr: 0000000000000000
cause: 8000000000000009 orig_a0: ffffffff80837310
--- <IRQ stack> ---
#9 [ffffffff81403da0] default_idle_call at ffffffff80837314
#10 [ffffffff81403db0] do_idle at ffffffff8004d0a0
#11 [ffffffff81403e40] cpu_startup_entry at ffffffff8004d21e
#12 [ffffffff81403e60] kernel_init at ffffffff8083746a
#13 [ffffffff81403e70] arch_post_acpi_subsys_init at ffffffff80a006d8
#14 [ffffffff81403e80] console_on_rootfs at ffffffff80a00c92
crash>
crash> bt -E
CPU 0 IRQ STACK:
KERNEL-MODE EXCEPTION FRAME AT: ff20000000003a48
PC: ffffffff8006462e [__handle_irq_event_percpu+30]
RA: ffffffff80064702 [handle_irq_event_percpu+18]
SP: ff20000000003e60 CAUSE: 000000000000000d
epc : ffffffff8006462e ra : ffffffff80064702 sp : ff20000000003e60
gp : ffffffff814ef848 tp : ffffffff8140db00 t0 : 0000000000046600
t1 : ffffffff80836464 t2 : ffffffff81200a48 s0 : ff20000000003ed0
s1 : 0000000000000000 a0 : 0000000000000000 a1 : 0000000000000118
a2 : 0000000000000052 a3 : 0000000000000000 a4 : 0000000000000000
a5 : 0000000000010001 a6 : ff6000001fe01958 a7 : 00002496ea89dbf1
s2 : ff60000000941ab0 s3 : ffffffff814a0658 s4 : ff60000000089230
s5 : ffffffff814a0518 s6 : ffffffff814a0620 s7 : ffffffff80e5f0f8
s8 : ffffffff80fc50b0 s9 : ffffffff80c1d880 s10: 0000000000000000
s11: 0000000000000001 t3 : 0000000000003392 t4 : 0000000000000000
t5 : 0000000000000000 t6 : 0000000000000040
status: 0000000200000100 badaddr: 0000000000000078
cause: 000000000000000d orig_a0: ff20000000003ea0
CPU 1 IRQ STACK:(none found)
crash>
crash> help -m
<snip>
machspec: ced1e0
irq_stack_size: 16384
irq_stacks[0]: ff20000000000000
irq_stacks[1]: ff20000000008000
crash>
```
[1]:
https://lore.kernel.org/linux-riscv/20231208111015.173237-1-songshuaishua...
Signed-off-by: Song Shuai <songshuaishuai(a)tinylab.org>
---
defs.h | 9 ++-
help.c | 2 +-
riscv64.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 196 insertions(+), 6 deletions(-)
diff --git a/defs.h b/defs.h
index a1d821d..7b9fcce 100644
--- a/defs.h
+++ b/defs.h
@@ -3642,6 +3642,10 @@ typedef signed int s32;
#define pmd_index_l5_4k(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
#define pte_index_l5_4k(addr) (((addr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1))
+
+/* machdep->flags */
+
+#define KSYMS_START (0x1)
#define VM_L3_4K (0x2)
#define VM_L3_2M (0x4)
#define VM_L3_1G (0x8)
@@ -3651,6 +3655,7 @@ typedef signed int s32;
#define VM_L5_4K (0x80)
#define VM_L5_2M (0x100)
#define VM_L5_1G (0x200)
+#define IRQ_STACKS (0x400)
#define VM_FLAGS (VM_L3_4K | VM_L3_2M | VM_L3_1G | \
VM_L4_4K | VM_L4_2M | VM_L4_1G | \
@@ -7012,8 +7017,6 @@ struct riscv64_unwind_frame {
ulong pc;
};
-#define KSYMS_START (0x1)
-
struct machine_specific {
ulong phys_base;
ulong page_offset;
@@ -7043,6 +7046,8 @@ struct machine_specific {
ulong struct_page_size;
struct riscv64_register *crash_task_regs;
+ ulong irq_stack_size;
+ ulong *irq_stacks;
};
/* from arch/riscv/include/asm/pgtable-bits.h */
#define _PAGE_PRESENT (machdep->machspec->_page_present)
diff --git a/help.c b/help.c
index cc7ab20..d1819ed 100644
--- a/help.c
+++ b/help.c
@@ -1938,7 +1938,7 @@ char *help_bt[] = {
" fails or the -t option starts too high in the process stack).",
" -l show file and line number of each stack trace text location.",
" -e search the stack for possible kernel and user mode exception
frames.",
-" -E search the IRQ stacks (x86, x86_64, arm64, and ppc64), and the",
+" -E search the IRQ stacks (x86, x86_64, arm64, riscv64 and ppc64), and
the",
" exception stacks (x86_64) for possible exception frames; all
other",
" arguments except for -c will be ignored since this is not a
context-",
" sensitive operation.",
diff --git a/riscv64.c b/riscv64.c
index f6c83ff..a69dfe5 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -33,6 +33,7 @@ static int riscv64_uvtop(struct task_context *tc, ulong vaddr,
static int riscv64_kvtop(struct task_context *tc, ulong kvaddr,
physaddr_t *paddr, int verbose);
static void riscv64_cmd_mach(void);
+static void riscv64_irq_stack_init(void);
static void riscv64_stackframe_init(void);
static void riscv64_back_trace_cmd(struct bt_info *bt);
static int riscv64_eframe_search(struct bt_info *bt);
@@ -54,6 +55,10 @@ static void riscv64_get_va_bits(struct machine_specific *ms);
static void riscv64_get_struct_page_size(struct machine_specific *ms);
static void riscv64_print_exception_frame(struct bt_info *, ulong , int );
static int riscv64_is_kernel_exception_frame(struct bt_info *, ulong );
+static int riscv64_on_irq_stack(int , ulong);
+static int riscv64_on_process_stack(struct bt_info *, ulong );
+static void riscv64_set_process_stack(struct bt_info *);
+static void riscv64_set_irq_stack(struct bt_info *);
#define REG_FMT "%016lx"
#define SZ_2G 0x80000000
@@ -191,11 +196,14 @@ riscv64_verify_symbol(const char *name, ulong value, char type)
void
riscv64_dump_machdep_table(ulong arg)
{
- int others = 0;
+ const struct machine_specific *ms = machdep->machspec;
+ int others = 0, i = 0;
fprintf(fp, " flags: %lx (", machdep->flags);
if (machdep->flags & KSYMS_START)
fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
+ if (machdep->flags & IRQ_STACKS)
+ fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
@@ -251,6 +259,15 @@ riscv64_dump_machdep_table(ulong arg)
fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits);
fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root);
fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec);
+ if (machdep->flags & IRQ_STACKS) {
+ fprintf(fp, " irq_stack_size: %ld\n", ms->irq_stack_size);
+ for (i = 0; i < kt->cpus; i++)
+ fprintf(fp, " irq_stacks[%d]: %lx\n",
+ i, ms->irq_stacks[i]);
+ } else {
+ fprintf(fp, " irq_stack_size: (unused)\n");
+ fprintf(fp, " irq_stacks: (unused)\n");
+ }
}
static ulong
@@ -665,6 +682,111 @@ riscv64_display_full_frame(struct bt_info *bt, struct
riscv64_unwind_frame *curr
fprintf(fp, "\n");
}
+/*
+ * Gather IRQ stack values.
+ */
+static void
+riscv64_irq_stack_init(void)
+{
+ int i;
+ struct syment *sp;
+ struct gnu_request request, *req;
+ struct machine_specific *ms = machdep->machspec;
+ ulong p, sz;
+ req = &request;
+
+ if (symbol_exists("irq_stack_ptr") &&
+ (sp = per_cpu_symbol_search("irq_stack_ptr")) &&
+ get_symbol_type("irq_stack_ptr", NULL, req)) {
+ 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");
+
+ /*
+ * find IRQ_STACK_SIZE (i.e. THREAD_SIZE) via thread_union.stack
+ * or set STACKSIZE() as default.
+ */
+ if (MEMBER_EXISTS("thread_union", "stack")) {
+ if ((sz = MEMBER_SIZE("thread_union", "stack")) > 0)
+ ms->irq_stack_size = sz;
+ } else
+ ms->irq_stack_size = machdep->stacksize;
+
+ machdep->flags |= IRQ_STACKS;
+
+ for (i = 0; i < kt->cpus; i++) {
+ p = kt->__per_cpu_offset[i] + sp->value;
+ if (CRASHDEBUG(1))
+ fprintf(fp, " IRQ stack pointer[%d] is %lx\n",i ,p);
+ readmem(p, KVADDR, &(ms->irq_stacks[i]), sizeof(ulong),
+ "IRQ stack pointer", RETURN_ON_ERROR);
+ }
+ }
+}
+
+static int
+riscv64_on_irq_stack(int cpu, ulong stkptr)
+{
+ struct machine_specific *ms = machdep->machspec;
+ ulong * stacks = ms->irq_stacks;
+ ulong stack_size = ms->irq_stack_size;
+
+ if ((cpu >= kt->cpus) || (stacks == NULL) || !stack_size)
+ return FALSE;
+
+ if ((stkptr >= stacks[cpu]) &&
+ (stkptr < (stacks[cpu] + stack_size)))
+ return TRUE;
+
+ return FALSE;
+}
+
+static int
+riscv64_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 void
+riscv64_set_irq_stack(struct bt_info *bt)
+{
+ struct machine_specific *ms = machdep->machspec;
+
+ bt->stackbase = ms->irq_stacks[bt->tc->processor];
+ bt->stacktop = bt->stackbase + ms->irq_stack_size;
+ alter_stackbuf(bt);
+}
+
+static void
+riscv64_set_process_stack(struct bt_info *bt)
+{
+ bt->stackbase = GET_STACKBASE(bt->task);
+ bt->stacktop = GET_STACKTOP(bt->task);
+ alter_stackbuf(bt);
+}
+
static void
riscv64_stackframe_init(void)
{
@@ -751,7 +873,7 @@ riscv64_back_trace_cmd(struct bt_info *bt)
{
struct riscv64_unwind_frame current, previous;
struct stackframe curr_frame;
- struct riscv64_register * regs;
+ struct riscv64_register *regs, *irq_regs;
int level = 0;
if (bt->flags & BT_REGS_NOT_FOUND)
@@ -759,6 +881,12 @@ riscv64_back_trace_cmd(struct bt_info *bt)
regs = (struct riscv64_register *) bt->machdep;
+
+ if (riscv64_on_irq_stack(bt->tc->processor, bt->frameptr)) {
+ riscv64_set_irq_stack(bt);
+ bt->flags |= BT_IRQSTACK;
+ }
+
current.pc = bt->instptr;
current.sp = bt->stkptr;
current.fp = bt->frameptr;
@@ -813,6 +941,35 @@ riscv64_back_trace_cmd(struct bt_info *bt)
current.fp = previous.fp;
current.sp = previous.sp;
+ /* When backtracing to do_irq(), find the original FP of do_irq()
+ * and then use the saved pt_regs in process stack to continue
+ */
+
+ if ((bt->flags & BT_IRQSTACK) &&
+ !riscv64_on_irq_stack(bt->tc->processor, current.fp)){
+ if (riscv64_on_process_stack(bt, current.fp)){
+
+ frameptr = (struct stackframe *)current.fp - 1;
+
+ if (!readmem((ulong)frameptr, KVADDR, &curr_frame,
+ sizeof(curr_frame), "get do_irq stack frame", RETURN_ON_ERROR))
+ return;
+
+ riscv64_set_process_stack(bt);
+
+ irq_regs = (struct riscv64_register *)
+ &bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(curr_frame.fp))];
+
+ current.pc = irq_regs->regs[RISCV64_REGS_EPC];
+ current.fp = irq_regs->regs[RISCV64_REGS_FP];
+ current.sp = irq_regs->regs[RISCV64_REGS_SP];
+
+ bt->flags &= ~BT_IRQSTACK;
+ riscv64_print_exception_frame(bt, curr_frame.fp, KERNEL_MODE);
+ fprintf(fp, "--- <IRQ stack> ---\n");
+ }
+ }
+
if (CRASHDEBUG(8))
fprintf(fp, "next %d pc %#lx sp %#lx fp %lx\n",
level, current.pc, current.sp, current.fp);
@@ -1423,6 +1580,8 @@ riscv64_init(int when)
case POST_GDB:
machdep->section_size_bits = _SECTION_SIZE_BITS;
machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
+
+ riscv64_irq_stack_init();
riscv64_stackframe_init();
riscv64_page_type_init();
@@ -1640,7 +1799,33 @@ static int
riscv64_eframe_search(struct bt_info *bt)
{
ulong ptr;
- int count;
+ int count, c;
+ struct machine_specific *ms = machdep->machspec;
+
+ if (bt->flags & BT_EFRAME_SEARCH2) {
+ if (!(machdep->flags & IRQ_STACKS))
+ error(FATAL, "IRQ stacks do not exist in this kernel\n");
+
+ for (c = 0; c < kt->cpus; c++) {
+ if ((bt->flags & BT_CPUMASK) &&
+ !(NUM_IN_BITMAP(bt->cpumask, c)))
+ continue;
+
+ fprintf(fp, "CPU %d IRQ STACK:", c);
+ bt->stackbase = ms->irq_stacks[c];
+ bt->stacktop = bt->stackbase + ms->irq_stack_size;
+ alter_stackbuf(bt);
+
+ count = riscv64_dump_kernel_eframes(bt);
+
+ if (count)
+ fprintf(fp, "\n");
+ else
+ fprintf(fp, "(none found)\n\n");
+ }
+
+ return 0;
+ }
count = riscv64_dump_kernel_eframes(bt);
--
2.20.1