[Crash-utility] [PATCH 10/11] sadump: Read kexec backup region instead of the first 640kB memory

HATAYAMA Daisuke d.hatayama at jp.fujitsu.com
Thu Oct 20 10:43:06 UTC 2011


kdump saves first 640kB backup region for BIOS of the second kernel to
boot. We should read the backup region instead if kdump has run at
crash.

Surely if kdump has not run, we don't do any special thing. Now we
check this by checking if the backup region is full of 0.

Note that this initialization must be performed before trying to read
memory that requres paging to access such as VMALLOC'ed memory because
the first 640kB could contain page table entries.

This patch implements:

  1. identify location of the backup region by searching kexec
  resources, and put the data, and

  2. read backup region if read to the first 640kB memory is
  requested; but only if kdump has run at crash.

Signed-off-by: HATAYAMA Daisuke <d.hatayama at jp.fujitsu.com>
---

 defs.h   |    2 +
 main.c   |    3 +
 sadump.c |  181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sadump.h |    6 ++
 4 files changed, 192 insertions(+), 0 deletions(-)
-------------- next part --------------
diff --git a/defs.h b/defs.h
index a9a158d..aff735f 100755
--- a/defs.h
+++ b/defs.h
@@ -255,6 +255,7 @@ struct number_option {
 #define SADUMP_DISKSET (0x2)
 #define SADUMP_MEDIA   (0x4)
 #define SADUMP_ZERO_EXCLUDED (0x8)
+#define SADUMP_KDUMP_BACKUP  (0x10)
 #define SADUMP_VALID() (sd->flags & SADUMP_LOCAL)
 
 #define CRASHDEBUG(x) (pc->debug >= (x))
@@ -4751,6 +4752,7 @@ void sadump_show_diskset(void);
 int sadump_is_zero_excluded(void);
 void sadump_set_zero_excluded(void);
 void sadump_unset_zero_excluded(void);
+void sadump_kdump_backup_region_init(void);
 
 /*
  * qemu.c
diff --git a/main.c b/main.c
index 652d79f..b2de67e 100755
--- a/main.c
+++ b/main.c
@@ -626,6 +626,9 @@ main_loop(void)
         if (!(pc->flags & GDB_INIT)) {
 		gdb_session_init();
 		show_untrusted_files();
+		if (SADUMP_DUMPFILE()) {
+			sadump_kdump_backup_region_init();
+		}
 		if (XEN_HYPER_MODE()) {
 #ifdef XEN_HYPERVISOR_ARCH
 			machdep_init(POST_GDB);
diff --git a/sadump.c b/sadump.c
index 9ad5a24..795b4f8 100644
--- a/sadump.c
+++ b/sadump.c
@@ -749,6 +749,20 @@ int read_sadump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
 	ulong page_offset;
 	int dfd;
 
+	if (sd->flags & SADUMP_KDUMP_BACKUP &&
+	    paddr >= sd->backup_src_start &&
+	    paddr < sd->backup_src_start + sd->backup_src_size) {
+		ulong orig_paddr;
+
+		orig_paddr = paddr;
+		paddr += sd->backup_offset - sd->backup_src_start;
+
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: kdump backup region: %#llx => %#llx\n",
+			      orig_paddr, paddr);
+
+	}
+
 	pfn = paddr_to_pfn(paddr);
 
 	curpaddr = paddr & ~((physaddr_t)(sd->block_size-1));
@@ -905,6 +919,8 @@ int sadump_memory_dump(FILE *fp)
 		fprintf(fp, "%sSADUMP_MEDIA", others++ ? "|" : "");
 	if (sd->flags & SADUMP_ZERO_EXCLUDED)
 		fprintf(fp, "%sSADUMP_ZERO_EXCLUDED", others++ ? "|" : "");
+	if (sd->flags & SADUMP_KDUMP_BACKUP)
+		fprintf(fp, "%sSADUMP_KDUMP_BACKUP", others++ ? "|" : "");
 	fprintf(fp, ") \n");
         fprintf(fp, "               dfd: %d\n", sd->dfd);
         fprintf(fp, "      machine_type: %d ", sd->machine_type);
@@ -1041,6 +1057,9 @@ int sadump_memory_dump(FILE *fp)
 	fprintf(fp, "       block_table: %lx\n", (ulong)sd->block_table);
 	fprintf(fp, "       sd_list_len: %d\n", sd->sd_list_len);
 	fprintf(fp, "           sd_list: %lx\n", (ulong)sd->sd_list);
+	fprintf(fp, "  backup_src_start: %lx\n", sd->backup_src_start);
+	fprintf(fp, "   backup_src_size: %lx\n", sd->backup_src_size);
+	fprintf(fp, "     backup_offset: %llx\n", (ulonglong)sd->backup_src_size);
 
 	for (i = 0; i < sd->sd_list_len; ++i) {
 		struct sadump_diskset_data *sdd = sd->sd_list[i];
@@ -1588,3 +1607,165 @@ void sadump_unset_zero_excluded(void)
 {
 	sd->flags &= ~SADUMP_ZERO_EXCLUDED;
 }
+
+/**
+ * kdump saves the first 640kB physical memory for BIOS to use the
+ * range on boot of 2nd kernel. sadump translates read request to the
+ * 640kB region as to the back up region. This function seachs kexec
+ * resources for the backup region.
+ */
+void sadump_kdump_backup_region_init(void)
+{
+	char buf[BUFSIZE];
+	ulong i, total, kexec_crash_image_p, elfcorehdr_p;
+	Elf64_Off e_phoff;
+	uint16_t e_phnum, e_phentsize;
+	uint64_t backup_offset;
+	ulong backup_src_start, backup_src_size;
+	int kimage_segment_len;
+	size_t bufsize;
+
+	if (!readmem(symbol_value("kexec_crash_image"), KVADDR,
+		     &kexec_crash_image_p, sizeof(ulong),
+		     "kexec backup region: kexec_crash_image",
+		     QUIET|RETURN_ON_ERROR))
+		goto error;
+
+	if (!kexec_crash_image_p) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: kexec_crash_image not loaded\n");
+		return;
+	}
+
+	kimage_segment_len = get_array_length("kimage.segment", NULL,
+					      STRUCT_SIZE("kexec_segment"));
+
+	if (!readmem(kexec_crash_image_p + MEMBER_OFFSET("kimage", "segment"),
+		     KVADDR, buf, MEMBER_SIZE("kimage", "segment"),
+		     "kexec backup region: kexec_crash_image->segment",
+		     QUIET|RETURN_ON_ERROR))
+		goto error;
+
+	elfcorehdr_p = 0;
+	for (i = 0; i < kimage_segment_len; ++i) {
+		char e_ident[EI_NIDENT];
+		ulong mem;
+
+		mem = ULONG(buf + i * STRUCT_SIZE("kexec_segment") +
+			    MEMBER_OFFSET("kexec_segment", "mem"));
+		if (!mem)
+			continue;
+
+		if (!readmem(mem, PHYSADDR, e_ident, SELFMAG,
+			     "elfcorehdr: e_ident",
+			     QUIET|RETURN_ON_ERROR))
+			goto error;
+
+		if (strncmp(ELFMAG, e_ident, SELFMAG) == 0) {
+			elfcorehdr_p = mem;
+			break;
+		}
+	}
+	if (!elfcorehdr_p) {
+		if (CRASHDEBUG(1))
+			error(INFO,
+	"sadump: elfcorehdr not found in segments of kexec_crash_image\n");
+		goto error;
+	}
+	if (!readmem(elfcorehdr_p, PHYSADDR, buf, STRUCT_SIZE("elf64_hdr"),
+		     "elfcorehdr", QUIET|RETURN_ON_ERROR))
+		goto error;
+
+	e_phnum = USHORT(buf + MEMBER_OFFSET("elf64_hdr", "e_phnum"));
+	e_phentsize = USHORT(buf + MEMBER_OFFSET("elf64_hdr", "e_phentsize"));
+	e_phoff = ULONG(buf + MEMBER_OFFSET("elf64_hdr", "e_phoff"));
+
+	backup_src_start = backup_src_size = backup_offset = 0;
+
+	for (i = 0; i < e_phnum; ++i) {
+		uint32_t p_type;
+		Elf64_Off p_offset;
+		Elf64_Addr p_paddr;
+		uint64_t p_memsz;
+
+		if (!readmem(elfcorehdr_p + e_phoff + i * e_phentsize,
+			     PHYSADDR, buf, e_phentsize,
+			     "elfcorehdr: program heaer",
+			     QUIET|RETURN_ON_ERROR))
+			goto error;
+
+		p_type = UINT(buf+MEMBER_OFFSET("elf64_phdr","p_type"));
+		p_offset=ULONGLONG(buf+MEMBER_OFFSET("elf64_phdr","p_offset"));
+		p_paddr = ULONGLONG(buf+MEMBER_OFFSET("elf64_phdr","p_paddr"));
+		p_memsz = ULONGLONG(buf+MEMBER_OFFSET("elf64_phdr","p_memsz"));
+
+		/*
+		 * kexec marks backup region PT_LOAD by assigning
+		 * backup region address in p_offset, and p_addr in
+		 * p_offsets for other PT_LOAD entries.
+		 */
+		if (p_type == PT_LOAD &&
+		    p_paddr <= KEXEC_BACKUP_SRC_END &&
+		    p_paddr != p_offset) {
+
+			backup_src_start = p_paddr;
+			backup_src_size = p_memsz;
+			backup_offset = p_offset;
+
+			if (CRASHDEBUG(1))
+				error(INFO,
+				      "sadump: kexec backup region found: "
+			  "START: %#016lx SIZE: %#016lx OFFSET: %#016llx\n",
+			  backup_src_start, backup_src_size, backup_offset);
+
+			break;
+		}
+	}
+
+	if (!backup_offset) {
+		if (CRASHDEBUG(1))
+	error(WARNING, "sadump: backup region not found in elfcorehdr\n");
+		return;
+	}
+
+	bufsize = BUFSIZE;
+	for (total = 0; total < backup_src_size; total += bufsize) {
+		char backup_buf[BUFSIZE];
+		int j;
+
+		if (backup_src_size - total < BUFSIZE)
+			bufsize = backup_src_size - total;
+
+		if (!readmem(backup_offset + total, PHYSADDR, backup_buf,
+			     bufsize, "backup source", QUIET|RETURN_ON_ERROR))
+			goto error;
+
+		/*
+		 * We're assuming the backup resion is initialized
+		 * with 0 filled if kdump has not run.
+		 */
+		for (j = 0; j < bufsize; ++j) {
+			if (backup_buf[j]) {
+
+				sd->flags |= SADUMP_KDUMP_BACKUP;
+				sd->backup_src_start = backup_src_start;
+				sd->backup_src_size = backup_src_size;
+				sd->backup_offset = backup_offset;
+
+				if (CRASHDEBUG(1))
+error(INFO, "sadump: backup region is used: %lx\n", backup_offset + total + j);
+
+				return;
+			}
+		}
+	}
+
+	if (CRASHDEBUG(1))
+		error(INFO, "sadump: kexec backup region not used\n");
+
+	return;
+
+error:
+	error(WARNING, "failed to init kexec backup region\n");
+
+}
diff --git a/sadump.h b/sadump.h
index d108088..64c2630 100644
--- a/sadump.h
+++ b/sadump.h
@@ -201,6 +201,12 @@ struct sadump_data {
 
 	int sd_list_len;
 	struct sadump_diskset_data **sd_list;
+
+/* Backup Region, First 640K of System RAM. */
+#define KEXEC_BACKUP_SRC_END	0x0009ffff
+	ulong backup_src_start;
+	ulong backup_src_size;
+	ulonglong backup_offset;
 };
 
 struct sadump_data *sadump_get_sadump_data(void);


More information about the Crash-utility mailing list