With this patch we can search the stack for possible kernel and user
mode exception frames via 'bt -e' command.
TEST: a lkdtm DIRECT EXCEPTION vmcore
```
crash> bt -e
PID: 1 TASK: ff600000000e0000 CPU: 1 COMMAND: "sh"
KERNEL-MODE EXCEPTION FRAME AT: ff200000000138d8
PC: ffffffff805303c0 [lkdtm_EXCEPTION+6]
RA: ffffffff8052fe36 [lkdtm_do_action+16]
SP: ff20000000013cf0 CAUSE: 000000000000000f
epc : ffffffff805303c0 ra : ffffffff8052fe36 sp : ff20000000013cf0
gp : ffffffff814ef848 tp : ff600000000e0000 t0 : 6500000000000000
t1 : 000000000000006c t2 : 6550203a6d74646b s0 : ff20000000013d00
s1 : 000000000000000a a0 : ffffffff814aef40 a1 : c0000000ffffefff
a2 : 0000000000000010 a3 : 0000000000000001 a4 : 5d53ea10ca096e00
a5 : ffffffff805303ba a6 : 0000000000000008 a7 : 0000000000000038
s2 : ff60000001324000 s3 : ffffffff814aef40 s4 : ff20000000013e30
s5 : 000000000000000a s6 : ff20000000013e30 s7 : ff600000000ce000
s8 : 0000555560f0f8a8 s9 : 00007ffff497f6b4 s10: 00007ffff497f6b0
s11: 0000555560fa30e0 t3 : ffffffff81502197 t4 : ffffffff81502197
t5 : ffffffff81502198 t6 : ff20000000013b28
status: 0000000200000120 badaddr: 0000000000000000
cause: 000000000000000f orig_a0: 0000000000000000
USER-MODE EXCEPTION FRAME AT: ff20000000013ee0
PC: 007fff8780431aff RA: 007fff877b168400 SP: 007ffff497f5b000
ORIG_A0: 0000000000000100 SYSCALLNO: 0000000000004000
epc : 007fff8780431aff ra : 007fff877b168400 sp : 007ffff497f5b000
gp : 00555560f5134800 tp : 007fff8774378000 t0 : 0000000000100000
t1 : 00555560e427bc00 t2 : 0000000000271000 s0 : 007ffff497f5e000
s1 : 0000000000000a00 a0 : 0000000000000100 a1 : 00555560faa68000
a2 : 0000000000000a00 a3 : 4000000000000000 a4 : 20000000000000a8
a5 : 0000000000000054 a6 : 0000000000000400 a7 : 0000000000004000
s2 : 00555560faa68000 s3 : 007fff878b33f800 s4 : 0000000000000a00
s5 : 00555560faa68000 s6 : 0000000000000a00 s7 : 00555560f5131400
s8 : 00555560f0f8a800 s9 : 007ffff497f6b400 s10: 007ffff497f6b000
s11: 00555560fa30e000 t3 : 007fff877af1fe00 t4 : 00555560fa6f2000
t5 : 0000000000000100 t6 : 9e1fea5bf8683300
status: 00000200004020b9 badaddr: 0000000000000000
cause: 0000000000000800 orig_a0: 0000000000000100
crash>
```
Signed-off-by: Song Shuai <songshuaishuai(a)tinylab.org>
---
defs.h | 15 +++--
riscv64.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 182 insertions(+), 25 deletions(-)
diff --git a/defs.h b/defs.h
index 5218a94..a1d821d 100644
--- a/defs.h
+++ b/defs.h
@@ -6996,17 +6996,16 @@ int riscv64_IS_VMALLOC_ADDR(ulong);
#define display_idt_table() \
error(FATAL, "-d option is not applicable to RISCV64 architecture\n")
-/* from arch/riscv/include/asm/ptrace.h */
+/*
+ * regs[0,31] : struct user_regs_struct
+ * from arch/riscv/include/uapi/asm/ptrace.h
+ * regs[0,35] : struct pt_regs
+ * from arch/riscv/include/asm/ptrace.h
+*/
struct riscv64_register {
ulong regs[36];
};
-struct riscv64_pt_regs {
- ulong badvaddr;
- ulong cause;
- ulong epc;
-};
-
struct riscv64_unwind_frame {
ulong fp;
ulong sp;
@@ -7070,6 +7069,8 @@ struct machine_specific {
#define RISCV64_REGS_RA 1
#define RISCV64_REGS_SP 2
#define RISCV64_REGS_FP 8
+#define RISCV64_REGS_STATUS 32
+#define RISCV64_REGS_CAUSE 34
#endif /* RISCV64 */
diff --git a/riscv64.c b/riscv64.c
index 872be59..f6c83ff 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -35,6 +35,7 @@ static int riscv64_kvtop(struct task_context *tc, ulong kvaddr,
static void riscv64_cmd_mach(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);
static int riscv64_get_dumpfile_stack_frame(struct bt_info *bt,
ulong *nip, ulong *ksp);
static void riscv64_get_stack_frame(struct bt_info *bt, ulong *pcp,
@@ -51,6 +52,8 @@ static int riscv64_get_elf_notes(void);
static void riscv64_get_va_range(struct machine_specific *ms);
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 );
#define REG_FMT "%016lx"
#define SZ_2G 0x80000000
@@ -210,6 +213,7 @@ riscv64_dump_machdep_table(ulong arg)
machdep->memsize, machdep->memsize);
fprintf(fp, " bits: %d\n", machdep->bits);
fprintf(fp, " back_trace: riscv64_back_trace_cmd()\n");
+ fprintf(fp, " eframe_search: riscv64_eframe_search()\n");
fprintf(fp, " processor_speed: riscv64_processor_speed()\n");
fprintf(fp, " uvtop: riscv64_uvtop()\n");
fprintf(fp, " kvtop: riscv64_kvtop()\n");
@@ -1398,6 +1402,7 @@ riscv64_init(int when)
machdep->cmd_mach = riscv64_cmd_mach;
machdep->get_stack_frame = riscv64_get_stack_frame;
machdep->back_trace = riscv64_back_trace_cmd;
+ machdep->eframe_search = riscv64_eframe_search;
machdep->vmalloc_start = riscv64_vmalloc_start;
machdep->processor_speed = riscv64_processor_speed;
@@ -1452,25 +1457,10 @@ riscv64_init(int when)
}
}
-/*
- * 'help -r' command output
- */
-void
-riscv64_display_regs_from_elf_notes(int cpu, FILE *ofp)
+/* bool pt_regs : pass 1 to dump pt_regs , pass 0 to dump user_regs_struct */
+static void
+riscv64_dump_pt_regs(struct riscv64_register *regs, FILE *ofp, bool pt_regs)
{
- const struct machine_specific *ms = machdep->machspec;
- struct riscv64_register *regs;
-
- if (!ms->crash_task_regs) {
- error(INFO, "registers not collected for cpu %d\n", cpu);
- return;
- }
-
- regs = &ms->crash_task_regs[cpu];
- if (!regs->regs[RISCV64_REGS_SP] && !regs->regs[RISCV64_REGS_EPC]) {
- error(INFO, "registers not collected for cpu %d\n", cpu);
- return;
- }
/* Print riscv64 32 regs */
fprintf(ofp,
@@ -1496,6 +1486,172 @@ riscv64_display_regs_from_elf_notes(int cpu, FILE *ofp)
regs->regs[24], regs->regs[25], regs->regs[26],
regs->regs[27], regs->regs[28], regs->regs[29],
regs->regs[30], regs->regs[31]);
+
+ if (pt_regs)
+ fprintf(ofp,
+ " status: " REG_FMT " badaddr: " REG_FMT "\n"
+ " cause: " REG_FMT " orig_a0: " REG_FMT "\n",
+ regs->regs[32], regs->regs[33], regs->regs[34],
+ regs->regs[35]);
+}
+
+/*
+ * 'help -r' command output
+ */
+
+void
+riscv64_display_regs_from_elf_notes(int cpu, FILE *ofp)
+{
+ const struct machine_specific *ms = machdep->machspec;
+ struct riscv64_register *regs;
+
+ if (!ms->crash_task_regs) {
+ error(INFO, "registers not collected for cpu %d\n", cpu);
+ return;
+ }
+
+ regs = &ms->crash_task_regs[cpu];
+ if (!regs->regs[RISCV64_REGS_SP] && !regs->regs[RISCV64_REGS_EPC]) {
+ error(INFO, "registers not collected for cpu %d\n", cpu);
+ return;
+ }
+
+ riscv64_dump_pt_regs(regs, ofp, 0);
+}
+
+#define USER_MODE (0)
+#define KERNEL_MODE (1)
+
+static void
+riscv64_print_exception_frame(struct bt_info *bt, ulong ptr, int mode)
+{
+
+ struct syment *sp;
+ ulong PC, RA, SP, offset;
+ struct riscv64_register *regs;
+
+ regs = (struct riscv64_register
*)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(ptr))];
+
+ PC = regs->regs[RISCV64_REGS_EPC];
+ RA = regs->regs[RISCV64_REGS_RA];
+ SP = regs->regs[RISCV64_REGS_SP];
+
+ switch (mode) {
+ case USER_MODE:
+ fprintf(fp,
+ " PC: %016lx RA: %016lx SP: %016lx\n"
+ " ORIG_A0: %016lx SYSCALLNO: %016lx\n",
+ PC, RA, SP, regs->regs[35], regs->regs[17]);
+
+ break;
+
+ case KERNEL_MODE:
+ fprintf(fp, " PC: %016lx ", PC);
+ if (is_kernel_text(PC) && (sp = value_search(PC, &offset)))
{
+ fprintf(fp, "[%s", sp->name);
+ if (offset)
+ fprintf(fp, (*gdb_output_radix == 16) ?
+ "+0x%lx" : "+%ld", offset);
+ fprintf(fp, "]\n");
+ } else
+ fprintf(fp, "[unknown or invalid address]\n");
+
+ fprintf(fp, " RA: %016lx ", RA);
+ if (is_kernel_text(RA) && (sp = value_search(RA, &offset)))
{
+ fprintf(fp, "[%s", sp->name);
+ if (offset)
+ fprintf(fp, (*gdb_output_radix == 16) ?
+ "+0x%lx" : "+%ld",
+ offset);
+ fprintf(fp, "]\n");
+ } else
+ fprintf(fp, "[unknown or invalid address]\n");
+
+ fprintf(fp, " SP: %016lx CAUSE: %016lx\n",
+ SP, regs->regs[RISCV64_REGS_CAUSE]);
+
+ break;
+ }
+
+ riscv64_dump_pt_regs(regs, fp, 1);
+
+}
+
+static int
+riscv64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr)
+{
+ struct riscv64_register *regs;
+
+ if (stkptr > STACKSIZE() && !INSTACK(stkptr, bt)) {
+ if (CRASHDEBUG(1))
+ error(WARNING, "stkptr: %lx is outside the kernel stack range\n", stkptr);
+ return FALSE;
+ }
+
+ regs = (struct riscv64_register
*)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(stkptr))];
+
+ if (INSTACK(regs->regs[RISCV64_REGS_SP], bt) &&
+ INSTACK(regs->regs[RISCV64_REGS_FP], bt) &&
+ is_kernel_text(regs->regs[RISCV64_REGS_RA]) &&
+ is_kernel_text(regs->regs[RISCV64_REGS_EPC]) &&
+ ((regs->regs[RISCV64_REGS_STATUS] >> 8) & 0x1) && //
sstatus.SPP != 0
+ !((regs->regs[RISCV64_REGS_CAUSE] >> 63) & 0x1 ) && //
scause.Interrupt != 1
+ !(regs->regs[RISCV64_REGS_CAUSE] == 0x00000008UL)) { // scause != ecall from
U-mode
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+riscv64_dump_kernel_eframes(struct bt_info *bt)
+{
+ ulong ptr;
+ int count;
+
+ /* use old_regs to avoid the identical contiguous kernel exception frames
+ * created by Linux handle_exception() path ending at riscv_crash_save_regs() */
+
+ struct riscv64_register *regs, *old_regs;
+
+ count = 0;
+ old_regs = NULL;
+
+ for (ptr = bt->stackbase; ptr < bt->stacktop - SIZE(pt_regs); ptr++) {
+
+ regs = (struct riscv64_register
*)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(ptr))];
+
+ if (riscv64_is_kernel_exception_frame(bt, ptr)){
+ if (!old_regs || (old_regs &&
+ memcmp(old_regs, regs, sizeof(struct riscv64_register))) != 0){
+ old_regs = regs;
+ fprintf(fp, "\nKERNEL-MODE EXCEPTION FRAME AT: %lx\n", ptr);
+ riscv64_print_exception_frame(bt, ptr, KERNEL_MODE);
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+static int
+riscv64_eframe_search(struct bt_info *bt)
+{
+ ulong ptr;
+ int count;
+
+ count = riscv64_dump_kernel_eframes(bt);
+
+ if (is_kernel_thread(bt->tc->task))
+ return count;
+
+ ptr = bt->stacktop - SIZE(pt_regs);
+ fprintf(fp, "%sUSER-MODE EXCEPTION FRAME AT: %lx\n", count++ ? "\n"
: "", ptr);
+ riscv64_print_exception_frame(bt, ptr, USER_MODE);
+
+ return count;
}
#else /* !RISCV64 */
--
2.20.1