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);