>From d650a854e6737064d753dd38c12e3d3d262dc29f Mon Sep 17 00:00:00 2001
From: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
Date: Thu, 15 Sep 2011 11:10:24 +0900
Subject: [PATCH 08/12] sadump: Add interface for accessing registers

  sadump searchs three kinds of locations for register values according to the
progress of kdump execution at crash: SMRAM CPU STATES, NT_PRSTATUS notes in
crash_notes and frames on kernel stacks.

Specifically, selecting a location is proceeded as follows:

  1. If kdump was not triggered at crash or triggered but crash_notes was not
  initialized, use SMRAM CPU STATES that are register values collected by
  firmware through System Management Mode. Note that the register values could be
  the ones in user-mode execution mode.

  2. If kdump was triggered and then crash_notes was initialized, first try to
  use registers contained on kernel stacks; this is done by existing crash's bt
  feature. If any of registers is not found, then try to retrieve registers saved
  in NT_PRSTATUS note entry at crash_notes. (So this column is exactly the same
  as the current kdump's behaviour.)

  SMRAM CPU STATE is contained in dump sub header, and each state is
corresponding to one APIC ID, not Logical CPU ID. cpu_to_apicid() resolves each
mapping of a given logical cpu id to some apic id by looking up bios_cpu_apicid
or x86_bios_cpu_apicid.

  Each of crash_notes, bios_cpu_apicid and x86_bios_cpu_apicid is per cpu
symbol or early per cpu symbol. To access these symbols, introduced are
per_cpu_ptr(), legacy_per_cpu_ptr() and early_per_cpu_ptr(), clones of the
macro in the kernel source code.
---
 defs.h   |    3 +
 sadump.c |  378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 x86.c    |    6 +
 x86_64.c |    6 +
 4 files changed, 393 insertions(+), 0 deletions(-)

diff --git a/defs.h b/defs.h
index a110881..9a6a879 100755
--- a/defs.h
+++ b/defs.h
@@ -1728,6 +1728,7 @@ struct size_table {         /* stash of commonly-used sizes */
 	long softirq_action;
 	long irq_data;
 	long s390_stack_frame;
+	long percpu_data;
 };
 
 struct array_table {
@@ -4727,6 +4728,8 @@ int sadump_is_diskset(void);
 ulong get_sadump_panic_task(void);
 ulong get_sadump_switch_stack(ulong);
 FILE *set_sadump_fp(FILE *);
+void get_sadump_regs(struct bt_info *bt, ulong *ipp, ulong *spp);
+void sadump_display_regs(int, FILE *);
 int sadump_phys_base(ulong *);
 
 /*
diff --git a/sadump.c b/sadump.c
index 0996860..fc815de 100644
--- a/sadump.c
+++ b/sadump.c
@@ -40,6 +40,12 @@ static int lookup_diskset(ulong whole_offset, int *diskid, ulong *disk_offset);
 static struct tm *efi_time_t_to_tm(const efi_time_t *e, struct tm *t);
 static char * guid_to_str(efi_guid_t *guid, char *buf, size_t buflen);
 static int verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]);
+static ulong per_cpu_ptr(ulong ptr, int cpu);
+static ulong early_per_cpu_ptr(char *symbol, struct syment *sym, int cpu);
+static ulong legacy_per_cpu_ptr(ulong ptr, int cpu);
+static int get_prstatus_from_crash_notes(int cpu, char *prstatus);
+static int cpu_to_apicid(int cpu, int *apicid);
+static int get_sadump_smram_cpu_state(int cpu, struct sadump_smram_cpu_state *smram);
 static int block_table_init(void);
 static ulong pfn_to_block(ulong pfn);
 
@@ -830,6 +836,378 @@ verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE])
 	return TRUE;
 }
 
+static ulong
+per_cpu_ptr(ulong ptr, int cpu)
+{
+	if (cpu < 0 || cpu >= kt->cpus)
+		return 0UL;
+
+	if (kt->cpus == 1)
+		return ptr;
+
+	if (!(kt->flags & PER_CPU_OFF))
+		return 0UL;
+
+	if (machine_type("X86_64")) {
+		ulong __per_cpu_load;
+
+		readmem(symbol_value("__per_cpu_load"), KVADDR,
+			&__per_cpu_load, sizeof(__per_cpu_load),
+			"__per_cpu_load", FAULT_ON_ERROR);
+
+		if (kt->__per_cpu_offset[cpu] == __per_cpu_load)
+			return 0UL;
+
+	} else if (machine_type("X86")) {
+		if (kt->__per_cpu_offset[cpu] == 0)
+			return 0UL;
+
+	}
+
+	return ptr + kt->__per_cpu_offset[cpu];
+}
+
+static ulong
+early_per_cpu_ptr(char *symbol, struct syment *sym, int cpu)
+{
+	char sym_early_ptr[BUFSIZE], sym_early_map[BUFSIZE];
+	ulong early_ptr;
+
+	if (cpu < 0 || cpu >= kt->cpus)
+		return 0UL;
+
+	if (!sym && !(sym = per_cpu_symbol_search(symbol)))
+		return 0UL;
+
+	if (!(kt->flags & SMP))
+		return per_cpu_ptr(sym->value, cpu);
+
+	snprintf(sym_early_ptr, BUFSIZE, "%s_early_ptr", symbol);
+	snprintf(sym_early_map, BUFSIZE, "%s_early_map", symbol);
+
+	if (!symbol_exists(sym_early_ptr) || !symbol_exists(sym_early_map))
+		return 0UL;
+
+	readmem(symbol_value(sym_early_ptr), KVADDR, &early_ptr,
+		sizeof(early_ptr), sym_early_ptr, FAULT_ON_ERROR);
+
+	return early_ptr
+		? symbol_value(sym_early_map)+cpu*sizeof(uint16_t)
+		: per_cpu_ptr(sym->value, cpu);
+}
+
+static ulong
+legacy_per_cpu_ptr(ulong ptr, int cpu)
+{
+	ulong addr;
+
+	if (!(kt->flags & SMP))
+		return ptr;
+
+	if (cpu < 0 || cpu >= kt->cpus)
+		return 0UL;
+
+	if (!readmem(~ptr + cpu * sizeof(ulong), KVADDR, &addr, sizeof(ulong),
+		     "search percpu_data", FAULT_ON_ERROR))
+		return 0UL;
+
+	return addr;
+}
+
+/**
+ * Retrieve eip and esp register values from crash_notes saved by
+ * kdump at crash. If register values has not been saved yet, set 0 to
+ * eip and esp instead.
+ */
+static int
+get_prstatus_from_crash_notes(int cpu, char *prstatus)
+{
+	ulong crash_notes, crash_notes_ptr, percpu_addr;
+	char *prstatus_ptr, *note_buf, *zero_buf, *name;
+	uint32_t *buf;
+
+	if (cpu < 0 || kt->cpus <= cpu) {
+		error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
+		return FALSE;
+	}
+
+	if (!symbol_exists("crash_notes")) {
+		error(INFO, "sadump: symbol crash_notes doesn't exist\n");
+		return FALSE;
+	}
+
+	crash_notes = symbol_value("crash_notes");
+
+	readmem(crash_notes, KVADDR, &crash_notes_ptr, sizeof(ulong),
+		"dereference crash_notes", FAULT_ON_ERROR);
+
+	if (!crash_notes_ptr) {
+		if (CRASHDEBUG(1))
+			error(INFO,
+			      "sadump: buffer for crash_notes is NULL\n");
+		return FALSE;
+	}
+
+	percpu_addr = VALID_STRUCT(percpu_data)
+		? legacy_per_cpu_ptr(crash_notes_ptr, cpu)
+		: per_cpu_ptr(crash_notes_ptr, cpu);
+
+	zero_buf = GETBUF(SIZE(note_buf));
+	BZERO(zero_buf, SIZE(note_buf));
+
+	note_buf = GETBUF(SIZE(note_buf));
+
+	readmem(percpu_addr, KVADDR, note_buf, SIZE(note_buf),
+		"read crash_notes", FAULT_ON_ERROR);
+
+	if (memcmp(note_buf, zero_buf, SIZE(note_buf)) == 0)
+		return FALSE;
+
+	if (BITS64()) {
+		Elf64_Nhdr *note64;
+
+		note64 = (Elf64_Nhdr *)note_buf;
+		buf = (uint32_t *)note_buf;
+		name = (char *)(note64 + 1);
+
+		if (note64->n_type != NT_PRSTATUS ||
+		    note64->n_namesz != strlen("CORE") + 1 ||
+		    strncmp(name, "CORE", note64->n_namesz) ||
+		    note64->n_descsz != SIZE(elf_prstatus))
+			return FALSE;
+
+		prstatus_ptr = (char *)(buf + (sizeof(*note64) + 3) / 4 +
+					(note64->n_namesz + 3) / 4);
+
+	} else {
+		Elf32_Nhdr *note32;
+
+		note32 = (Elf32_Nhdr *)note_buf;
+		buf = (uint32_t *)note_buf;
+		name = (char *)(note32 + 1);
+
+		if ((note32->n_type != NT_PRSTATUS) &&
+		    (note32->n_namesz != strlen("CORE") + 1 ||
+		     strncmp(name, "CORE", note32->n_namesz) ||
+		     note32->n_descsz != SIZE(elf_prstatus)))
+			return FALSE;
+
+		prstatus_ptr = (char *)(buf + (sizeof(*note32) + 3) / 4 +
+					(note32->n_namesz + 3) / 4);
+
+	}
+
+	memcpy(prstatus, prstatus_ptr, SIZE(elf_prstatus));
+
+	return TRUE;
+}
+
+int
+sadump_get_smram_cpu_state(int apicid,
+			   struct sadump_smram_cpu_state *smram)
+{
+	ulong offset;
+
+	if (!sd->sub_hdr_offset || !sd->smram_cpu_state_size ||
+	    apicid >= sd->dump_header->nr_cpus)
+		return FALSE;
+
+	offset = sd->sub_hdr_offset + sizeof(uint32_t) +
+		sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
+
+	if (lseek(sd->dfd, offset + apicid * sd->smram_cpu_state_size,
+		  SEEK_SET) == failed)
+		error(FATAL,
+		      "sadump: cannot lseek smram cpu state in dump sub header\n");
+
+	if (read(sd->dfd, smram, sd->smram_cpu_state_size) != sd->smram_cpu_state_size)
+		error(FATAL, "sadump: cannot read smram cpu state in dump sub "
+		      "header\n");
+
+	return TRUE;
+}
+
+static int cpu_to_apicid(int cpu, int *apicid)
+{
+	struct syment *sym;
+
+	if (symbol_exists("bios_cpu_apicid")) {
+		uint8_t apicid_u8;
+
+		readmem(symbol_value("bios_cpu_apicid") + cpu*sizeof(uint8_t),
+			KVADDR, &apicid_u8, sizeof(uint8_t), "bios_cpu_apicid",
+			FAULT_ON_ERROR);
+
+		*apicid = (int)apicid_u8;
+
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: apicid %u for cpu %d from "
+			      "bios_cpu_apicid\n", apicid_u8, cpu);
+
+	} else if ((sym = per_cpu_symbol_search("x86_bios_cpu_apicid"))) {
+		uint16_t apicid_u16;
+
+		readmem(early_per_cpu_ptr("x86_bios_cpu_apicid", sym, cpu),
+			KVADDR, &apicid_u16, sizeof(uint16_t),
+			"x86_bios_cpu_apicid", FAULT_ON_ERROR);
+
+		*apicid = (int)apicid_u16;
+
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: apicid %u for cpu %d from "
+			      "x86_bios_cpu_apicid\n", apicid_u16, cpu);
+
+	} else {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: no symbols for access to apicid\n");
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int
+get_sadump_smram_cpu_state(int cpu, struct sadump_smram_cpu_state *smram)
+{
+	int apicid = 0;
+
+	if (cpu < 0 || kt->cpus <= cpu) {
+		error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
+		return FALSE;
+	}
+
+	if (!cpu_to_apicid(cpu, &apicid))
+		return FALSE;
+
+	sadump_get_smram_cpu_state(apicid, smram);
+
+	return TRUE;
+}
+
+void get_sadump_regs(struct bt_info *bt, ulong *ipp, ulong *spp)
+{
+	ulong ip, sp;
+	struct sadump_smram_cpu_state smram;
+	char *prstatus;
+	int cpu = bt->tc->processor;
+
+	if (!is_task_active(bt->task)) {
+		machdep->get_stack_frame(bt, ipp, spp);
+		return;
+	}
+
+	bt->flags |= BT_DUMPFILE_SEARCH;
+	if (machine_type("X86_64"))
+		machdep->get_stack_frame(bt, ipp, spp);
+	else if (machine_type("X86"))
+		get_netdump_regs_x86(bt, ipp, spp);
+	if (bt->flags & BT_DUMPFILE_SEARCH)
+		return;
+
+	prstatus = GETBUF(SIZE(elf_prstatus));
+
+	if (get_prstatus_from_crash_notes(cpu, prstatus)) {
+		ip = ULONG(prstatus +
+			   OFFSET(elf_prstatus_pr_reg) +
+			   OFFSET(user_regs_struct_rip));
+		sp = ULONG(prstatus +
+			   OFFSET(elf_prstatus_pr_reg) +
+			   OFFSET(user_regs_struct_rsp));
+		if (ip || sp) {
+			*ipp = ip;
+			*spp = sp;
+			return;
+		}
+	}
+
+	get_sadump_smram_cpu_state(cpu, &smram);
+	ip = smram.Rip;
+	sp = ((uint64_t)smram.RspUpper << 32) + smram.RspLower;
+
+	if (is_kernel_text(ip) &&
+	    (((sp >= GET_STACKBASE(bt->task)) &&
+	      (sp < GET_STACKTOP(bt->task))) ||
+	     in_alternate_stack(bt->tc->processor, sp))) {
+		*ipp = ip;
+		*spp = sp;
+		bt->flags |= BT_KERNEL_SPACE;
+		return;
+	}
+
+	if (!is_kernel_text(ip) &&
+	    in_user_stack(bt->tc->task, sp))
+		bt->flags |= BT_USER_SPACE;
+
+}
+
+void
+sadump_display_regs(int cpu, FILE *ofp)
+{
+	struct sadump_smram_cpu_state smram;
+
+	if (cpu < 0 || cpu >= kt->cpus) {
+		error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
+		return;
+	}
+
+	get_sadump_smram_cpu_state(cpu, &smram);
+
+	if (machine_type("X86_64")) {
+		fprintf(ofp,
+			"    RIP: %016llx  RSP: %016llx  RFLAGS: %08llx\n"
+			"    RAX: %016llx  RBX: %016llx  RCX: %016llx\n"
+			"    RDX: %016llx  RSI: %016llx  RDI: %016llx\n"
+			"    RBP: %016llx   R8: %016llx   R9: %016llx\n"
+			"    R10: %016llx  R11: %016llx  R12: %016llx\n"
+			"    R13: %016llx  R14: %016llx  R15: %016llx\n"
+			"    CS: %04x  SS: %04x\n",
+			(ulonglong)(smram.Rip),
+			(ulonglong)(((uint64_t)smram.RspUpper<<32)+smram.RspLower),
+			(ulonglong)(smram.Rflags),
+			(ulonglong)(((uint64_t)smram.RaxUpper<<32)+smram.RaxLower),
+			(ulonglong)(((uint64_t)smram.RbxUpper<<32)+smram.RbxLower),
+			(ulonglong)(((uint64_t)smram.RcxUpper<<32)+smram.RcxLower),
+			(ulonglong)(((uint64_t)smram.RdxUpper<<32)+smram.RdxLower),
+			(ulonglong)(((uint64_t)smram.RsiUpper<<32)+smram.RsiLower),
+			(ulonglong)(((uint64_t)smram.RdiUpper<<32)+smram.RdiLower),
+			(ulonglong)(((uint64_t)smram.RbpUpper<<32)+smram.RbpLower),
+			(ulonglong)(((uint64_t)smram.R8Upper<<32)+smram.R8Lower),
+			(ulonglong)(((uint64_t)smram.R9Upper<<32)+smram.R9Lower),
+			(ulonglong)(((uint64_t)smram.R10Upper<<32)+smram.R10Lower),
+			(ulonglong)(((uint64_t)smram.R11Upper<<32)+smram.R11Lower),
+			(ulonglong)(((uint64_t)smram.R12Upper<<32)+smram.R12Lower),
+			(ulonglong)(((uint64_t)smram.R13Upper<<32)+smram.R13Lower),
+			(ulonglong)(((uint64_t)smram.R14Upper<<32)+smram.R14Lower),
+			(ulonglong)(((uint64_t)smram.R15Upper<<32)+smram.R15Lower),
+			smram.Cs,
+			smram.Ss);
+	}
+
+	if (machine_type("X86")) {
+		fprintf(ofp,
+			"    EAX: %08llx  EBX: %08llx  ECX: %08llx  EDX: %08llx\n"
+			"    DS:  %04x      ESI: %08llx  ES:  %04x      EDI: %08llx\n"
+			"    SS:  %04x      ESP: %08llx  EBP: %08llx  GS:  %04x\n"
+			"    CS:  %04x      EIP: %08llx  EFLAGS: %08llx\n",
+			(ulonglong)smram.RaxLower,
+			(ulonglong)smram.RbxLower,
+			(ulonglong)smram.RcxLower,
+			(ulonglong)smram.RdxLower,
+			smram.Ds & 0xffff,
+			(ulonglong)smram.RsiLower,
+			smram.Es & 0xffff,
+			(ulonglong)smram.RdiLower,
+			smram.Ss,
+			(ulonglong)smram.RspLower,
+			(ulonglong)smram.RbpLower,
+			smram.Gs,
+			smram.Cs,
+			(ulonglong)smram.Rip,
+			(ulonglong)smram.Rflags);
+	}
+}
+
 /*
  * sadump does not save phys_base; it must resort to another way.
  */
diff --git a/x86.c b/x86.c
index 9acab9f..cdd6468 100755
--- a/x86.c
+++ b/x86.c
@@ -1994,6 +1994,12 @@ x86_init(int when)
 		else
 			machdep->machspec->page_protnone = _PAGE_PSE;
 
+		STRUCT_SIZE_INIT(note_buf, "note_buf_t");
+		STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus");
+		MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
+				   "pr_reg");
+		STRUCT_SIZE_INIT(percpu_data, "percpu_data");
+
 		break;
 
 	case POST_INIT:
diff --git a/x86_64.c b/x86_64.c
index c8bdd3f..5120cf1 100755
--- a/x86_64.c
+++ b/x86_64.c
@@ -525,6 +525,12 @@ x86_64_init(int when)
 		else
 			machdep->machspec->page_protnone = _PAGE_PSE;
 
+		STRUCT_SIZE_INIT(note_buf, "note_buf_t");
+		STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus");
+		MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
+				   "pr_reg");
+		STRUCT_SIZE_INIT(percpu_data, "percpu_data");
+
 		break;
 
 	case POST_VM:
-- 
1.7.4.4

