Skip to content

Commit

Permalink
MIPS64: Fixes for the gathering of the active task registers for the …
Browse files Browse the repository at this point in the history
…'bt' command

dumpfiles:
 (1) If the kernel's crash_notes are not available, read them from
     ELF notes.
 (2) If an online CPUs did not save its ELF notes, then adjust
     the mapping of each ELF note to its CPU accordingly.

E.g. With this patch:
crash> bt
PID: 4768   TASK: 9800000243bcf200  CPU: 3   COMMAND: "bash"
 #0 [980000024291f930] __crash_kexec at ffffffff802fff84
 #1 [980000024291faa0] panic at ffffffff80248cac
 #2 [980000024291fb40] die at ffffffff8021b338
 #3 [980000024291fb70] do_page_fault at ffffffff802315e0
 #4 [980000024291fbd0] tlb_do_page_fault_1 at ffffffff80239388
 #5 [980000024291fd00] sysrq_handle_crash at ffffffff8085d308
 #6 [980000024291fd10] __handle_sysrq at ffffffff8085d9e0
 #7 [980000024291fd60] write_sysrq_trigger at ffffffff8085e020
 #8 [980000024291fd80] proc_reg_write at ffffffff804762f0
 #9 [980000024291fda0] __vfs_write at ffffffff803f3138

Signed-off-by: Huacai Chen <[email protected]>
Signed-off-by: Youling Tang <[email protected]>
  • Loading branch information
Youling Tang authored and k-hagio committed Apr 23, 2021
1 parent 0a0ef33 commit 4b962a2
Showing 1 changed file with 202 additions and 1 deletion.
203 changes: 202 additions & 1 deletion mips64.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ static void mips64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp);
static int mips64_get_dumpfile_stack_frame(struct bt_info *bt,
ulong *nip, ulong *ksp);
static int mips64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp);

static int mips64_init_active_task_regs(void);
static int mips64_get_crash_notes(void);
static int mips64_get_elf_notes(void);

/*
* 3 Levels paging PAGE_SIZE=16KB
Expand Down Expand Up @@ -799,6 +801,192 @@ mips64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
return TRUE;
}

static int
mips64_init_active_task_regs(void)
{
int retval;

retval = mips64_get_crash_notes();
if (retval == TRUE)
return retval;

return mips64_get_elf_notes();
}

/*
* Retrieve task registers for the time of the crash.
*/
static int
mips64_get_crash_notes(void)
{
struct machine_specific *ms = machdep->machspec;
ulong crash_notes;
Elf64_Nhdr *note;
ulong offset;
char *buf, *p;
ulong *notes_ptrs;
ulong i;

/*
* crash_notes contains per cpu memory for storing cpu states
* in case of system crash.
*/
if (!symbol_exists("crash_notes"))
return FALSE;

crash_notes = symbol_value("crash_notes");

notes_ptrs = (ulong *)GETBUF(kt->cpus*sizeof(notes_ptrs[0]));

/*
* Read crash_notes for the first CPU. crash_notes are in standard ELF
* note format.
*/
if (!readmem(crash_notes, KVADDR, &notes_ptrs[kt->cpus-1],
sizeof(notes_ptrs[kt->cpus-1]), "crash_notes",
RETURN_ON_ERROR)) {
error(WARNING, "cannot read crash_notes\n");
FREEBUF(notes_ptrs);
return FALSE;
}

if (symbol_exists("__per_cpu_offset")) {

/*
* Add __per_cpu_offset for each cpu to form the pointer to the notes
*/
for (i = 0; i < kt->cpus; i++)
notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i];
}

buf = GETBUF(SIZE(note_buf));

if (!(panic_task_regs = calloc((size_t)kt->cpus, sizeof(*panic_task_regs))))
error(FATAL, "cannot calloc panic_task_regs space\n");

for (i = 0; i < kt->cpus; i++) {

if (!readmem(notes_ptrs[i], KVADDR, buf, SIZE(note_buf), "note_buf_t",
RETURN_ON_ERROR)) {
error(WARNING,
"cannot find NT_PRSTATUS note for cpu: %d\n", i);
goto fail;
}

/*
* Do some sanity checks for this note before reading registers from it.
*/
note = (Elf64_Nhdr *)buf;
p = buf + sizeof(Elf64_Nhdr);

/*
* dumpfiles created with qemu won't have crash_notes, but there will
* be elf notes; dumpfiles created by kdump do not create notes for
* offline cpus.
*/
if (note->n_namesz == 0 && (DISKDUMP_DUMPFILE() || KDUMP_DUMPFILE())) {
if (DISKDUMP_DUMPFILE())
note = diskdump_get_prstatus_percpu(i);
else if (KDUMP_DUMPFILE())
note = netdump_get_prstatus_percpu(i);
if (note) {
/*
* SIZE(note_buf) accounts for a "final note", which is a
* trailing empty elf note header.
*/
long notesz = SIZE(note_buf) - sizeof(Elf64_Nhdr);

if (sizeof(Elf64_Nhdr) + roundup(note->n_namesz, 4) +
note->n_descsz == notesz)
BCOPY((char *)note, buf, notesz);
} else {
error(WARNING,
"cannot find NT_PRSTATUS note for cpu: %d\n", i);
continue;
}
}

/*
* Check the sanity of NT_PRSTATUS note only for each online cpu.
*/
if (note->n_type != NT_PRSTATUS) {
error(WARNING, "invalid NT_PRSTATUS note (n_type != NT_PRSTATUS)\n");
goto fail;
}
if (!STRNEQ(p, "CORE")) {
error(WARNING, "invalid NT_PRSTATUS note (name != \"CORE\"\n");
goto fail;
}

/*
* Find correct location of note data. This contains elf_prstatus
* structure which has registers etc. for the crashed task.
*/
offset = sizeof(Elf64_Nhdr);
offset = roundup(offset + note->n_namesz, 4);
p = buf + offset; /* start of elf_prstatus */

BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs[i],
sizeof(panic_task_regs[i]));
}

/*
* And finally we have the registers for the crashed task. This is
* used later on when dumping backtrace.
*/
ms->crash_task_regs = panic_task_regs;

FREEBUF(buf);
FREEBUF(notes_ptrs);
return TRUE;

fail:
FREEBUF(buf);
FREEBUF(notes_ptrs);
free(panic_task_regs);
return FALSE;
}

static int
mips64_get_elf_notes(void)
{
struct machine_specific *ms = machdep->machspec;
int i;

if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE())
return FALSE;

panic_task_regs = calloc(kt->cpus, sizeof(*panic_task_regs));
if (!panic_task_regs)
error(FATAL, "cannot calloc panic_task_regs space\n");

for (i = 0; i < kt->cpus; i++) {
Elf64_Nhdr *note = NULL;
size_t len;

if (DISKDUMP_DUMPFILE())
note = diskdump_get_prstatus_percpu(i);
else if (KDUMP_DUMPFILE())
note = netdump_get_prstatus_percpu(i);

if (!note) {
error(WARNING,
"cannot find NT_PRSTATUS note for cpu: %d\n", i);
continue;
}

len = sizeof(Elf64_Nhdr);
len = roundup(len + note->n_namesz, 4);

BCOPY((char *)note + len + OFFSET(elf_prstatus_pr_reg),
&panic_task_regs[i], sizeof(panic_task_regs[i]));
}

ms->crash_task_regs = panic_task_regs;

return TRUE;
}

/*
* Accept or reject a symbol from the kernel namelist.
*/
Expand Down Expand Up @@ -952,9 +1140,22 @@ mips64_init(int when)
mips64_stackframe_init();
if (!machdep->hz)
machdep->hz = 250;
MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
"pr_reg");
STRUCT_SIZE_INIT(note_buf, "note_buf_t");
break;

case POST_VM:
/*
* crash_notes contains machine specific information about the
* crash. In particular, it contains CPU registers at the time
* of the crash. We need this information to extract correct
* backtraces from the panic task.
*/
if (!ACTIVE() && !mips64_init_active_task_regs())
error(WARNING,
"cannot retrieve registers for active task%s\n\n",
kt->cpus > 1 ? "s" : "");
break;
}
}
Expand Down

0 comments on commit 4b962a2

Please sign in to comment.