diff options
-rw-r--r-- | makedumpfile.c | 45 | ||||
-rw-r--r-- | makedumpfile.h | 41 | ||||
-rw-r--r-- | sadump_info.c | 168 | ||||
-rw-r--r-- | sadump_info.h | 6 |
4 files changed, 249 insertions, 11 deletions
diff --git a/makedumpfile.c b/makedumpfile.c index c7d55ff..921e45d 100644 --- a/makedumpfile.c +++ b/makedumpfile.c @@ -830,6 +830,7 @@ get_symbol_info(void) SYMBOL_INIT(cpu_online_mask, "cpu_online_mask"); if (SYMBOL(cpu_online_mask) == NOT_FOUND_SYMBOL) SYMBOL_INIT(cpu_online_mask, "cpu_online_map"); + SYMBOL_INIT(kexec_crash_image, "kexec_crash_image"); if (SYMBOL(node_data) != NOT_FOUND_SYMBOL) SYMBOL_ARRAY_TYPE_INIT(node_data, "node_data"); @@ -1116,6 +1117,23 @@ get_structure_info(void) } #endif /* __x86_64__ */ + OFFSET_INIT(kimage.segment, "kimage", "segment"); + + MEMBER_ARRAY_LENGTH_INIT(kimage.segment, "kimage", "segment"); + + SIZE_INIT(kexec_segment, "kexec_segment"); + OFFSET_INIT(kexec_segment.mem, "kexec_segment", "mem"); + + OFFSET_INIT(elf64_hdr.e_phnum, "elf64_hdr", "e_phnum"); + OFFSET_INIT(elf64_hdr.e_phentsize, "elf64_hdr", "e_phentsize"); + OFFSET_INIT(elf64_hdr.e_phoff, "elf64_hdr", "e_phoff"); + + SIZE_INIT(elf64_hdr, "elf64_hdr"); + OFFSET_INIT(elf64_phdr.p_type, "elf64_phdr", "p_type"); + OFFSET_INIT(elf64_phdr.p_offset, "elf64_phdr", "p_offset"); + OFFSET_INIT(elf64_phdr.p_paddr, "elf64_phdr", "p_paddr"); + OFFSET_INIT(elf64_phdr.p_memsz, "elf64_phdr", "p_memsz"); + return TRUE; } @@ -2622,6 +2640,16 @@ out: if (!get_versiondep_info()) return FALSE; + /* + * NOTE: This must be done before refering to + * VMALLOC'ed memory. The first 640kB contains data + * necessary for paging, like PTE. The absence of the + * region affects reading VMALLOC'ed memory such as + * module data. + */ + if (info->flag_sadump) + sadump_kdump_backup_region_init(); + if (!get_numnodes()) return FALSE; @@ -2759,6 +2787,12 @@ set_bit_on_1st_bitmap(unsigned long long pfn) } int +clear_bit_on_1st_bitmap(unsigned long long pfn) +{ + return set_bitmap(info->bitmap1, pfn, 0); +} + +int clear_bit_on_2nd_bitmap(unsigned long long pfn) { return set_bitmap(info->bitmap2, pfn, 0); @@ -2799,17 +2833,6 @@ is_in_segs(unsigned long long paddr) return FALSE; } -static inline int -is_zero_page(unsigned char *buf, long page_size) -{ - size_t i; - - for (i = 0; i < page_size; i++) - if (buf[i]) - return FALSE; - return TRUE; -} - int read_cache(struct cache_data *cd) { diff --git a/makedumpfile.h b/makedumpfile.h index 3abe9ad..8216e00 100644 --- a/makedumpfile.h +++ b/makedumpfile.h @@ -181,6 +181,7 @@ isAnon(unsigned long mapping) #define STRNEQ(A, B) (A && B && \ (strncmp((char *)(A), (char *)(B), strlen((char *)(B))) == 0)) +#define USHORT(ADDR) *((unsigned short *)(ADDR)) #define UINT(ADDR) *((unsigned int *)(ADDR)) #define ULONG(ADDR) *((unsigned long *)(ADDR)) @@ -1001,6 +1002,7 @@ struct symbol_table { unsigned long long __per_cpu_offset; unsigned long long __per_cpu_load; unsigned long long cpu_online_mask; + unsigned long long kexec_crash_image; }; struct size_table { @@ -1032,6 +1034,8 @@ struct size_table { long user_regs_struct; long cpumask; long cpumask_t; + long kexec_segment; + long elf64_hdr; }; struct offset_table { @@ -1140,6 +1144,27 @@ struct offset_table { long fs; long gs; } user_regs_struct; + + struct kimage_s { + long segment; + } kimage; + + struct kexec_segment_s { + long mem; + } kexec_segment; + + struct elf64_hdr_s { + long e_phnum; + long e_phentsize; + long e_phoff; + } elf64_hdr; + + struct elf64_phdr_s { + long p_type; + long p_offset; + long p_paddr; + long p_memsz; + } elf64_phdr; }; /* @@ -1164,6 +1189,9 @@ struct array_table { struct free_area_at { long free_list; } free_area; + struct kimage_at { + long segment; + } kimage; }; struct number_table { @@ -1336,7 +1364,20 @@ is_dumpable(struct dump_bitmap *bitmap, unsigned long long pfn) return is_on(bitmap->buf, pfn%PFN_BUFBITMAP); } +static inline int +is_zero_page(unsigned char *buf, long page_size) +{ + size_t i; + + for (i = 0; i < page_size; i++) + if (buf[i]) + return FALSE; + return TRUE; +} + void write_vmcoreinfo_data(void); +int set_bit_on_1st_bitmap(unsigned long long pfn); +int clear_bit_on_1st_bitmap(unsigned long long pfn); #ifdef __x86__ diff --git a/sadump_info.c b/sadump_info.c index 2538da6..69296ce 100644 --- a/sadump_info.c +++ b/sadump_info.c @@ -77,6 +77,12 @@ struct sadump_info { FILE *file_elf_note; char *cpu_online_mask_buf; size_t cpumask_size; +/* Backup Region, First 640K of System RAM. */ +#define KEXEC_BACKUP_SRC_END 0x0009ffff + unsigned long long backup_src_start; + unsigned long backup_src_size; + unsigned long long backup_offset; + int kdump_backed_up; }; static char *guid_to_str(efi_guid_t *guid, char *buf, size_t buflen); @@ -188,6 +194,30 @@ sadump_copy_1st_bitmap_from_memory(void) offset_page += sizeof(buf); } + /* + * kdump uses the first 640kB on the 2nd kernel. But both + * bitmaps should reflect the 1st kernel memory situation. We + * modify bitmap accordingly. + */ + if (si->kdump_backed_up) { + unsigned long long paddr, pfn, backup_src_pfn; + + for (paddr = si->backup_src_start; + paddr < si->backup_src_start + si->backup_src_size; + paddr += info->page_size) { + + pfn = paddr_to_pfn(paddr); + backup_src_pfn = paddr_to_pfn(paddr + + si->backup_offset - + si->backup_src_start); + + if (is_dumpable(info->bitmap_memory, backup_src_pfn)) + set_bit_on_1st_bitmap(pfn); + else + clear_bit_on_1st_bitmap(pfn); + } + } + return TRUE; } @@ -920,6 +950,11 @@ readpmem_sadump(unsigned long long paddr, void *bufptr, size_t size) char buf[info->page_size]; int fd_memory; + if (si->kdump_backed_up && + paddr >= si->backup_src_start && + paddr < si->backup_src_start + si->backup_src_size) + paddr += si->backup_offset - si->backup_src_start; + pfn = paddr_to_pfn(paddr); page_offset = paddr % info->page_size; @@ -1774,4 +1809,137 @@ free_sadump_info(void) free(si->cpu_online_mask_buf); } +void +sadump_kdump_backup_region_init(void) +{ + unsigned char buf[BUFSIZE]; + unsigned long i, total, kexec_crash_image_p, elfcorehdr_p; + Elf64_Off e_phoff; + uint16_t e_phnum, e_phentsize; + unsigned long long backup_offset; + unsigned long backup_src_start, backup_src_size; + size_t bufsize; + + if (!readmem(VADDR, SYMBOL(kexec_crash_image), &kexec_crash_image_p, + sizeof(unsigned long))) { + ERRMSG("Can't read kexec_crash_image pointer. %s\n", + strerror(errno)); + return; + } + + if (!kexec_crash_image_p) { + DEBUG_MSG("sadump: kexec crash image was not loaded\n"); + return; + } + + if (!readmem(VADDR, kexec_crash_image_p+OFFSET(kimage.segment), + buf, SIZE(kexec_segment)*ARRAY_LENGTH(kimage.segment))) { + ERRMSG("Can't read kexec_crash_image->segment. %s\n", + strerror(errno)); + return; + } + + elfcorehdr_p = 0; + for (i = 0; i < ARRAY_LENGTH(kimage.segment); ++i) { + char e_ident[EI_NIDENT]; + unsigned mem; + + mem=ULONG(buf+i*SIZE(kexec_segment)+OFFSET(kexec_segment.mem)); + if (!mem) + continue; + + if (!readmem(PADDR, mem, e_ident, SELFMAG)) { + DEBUG_MSG("sadump: failed to read elfcorehdr buffer\n"); + return; + } + + if (strncmp(ELFMAG, e_ident, SELFMAG) == 0) { + elfcorehdr_p = mem; + break; + } + } + if (!elfcorehdr_p) { + DEBUG_MSG("sadump: kexec_crash_image contains no elfcorehdr " + "segment\n"); + return; + } + + if (!readmem(PADDR, elfcorehdr_p, buf, SIZE(elf64_hdr))) { + ERRMSG("Can't read elfcorehdr ELF header. %s\n", + strerror(errno)); + return; + } + + e_phnum = USHORT(buf + OFFSET(elf64_hdr.e_phnum)); + e_phentsize = USHORT(buf + OFFSET(elf64_hdr.e_phentsize)); + e_phoff = ULONG(buf + OFFSET(elf64_hdr.e_phoff)); + + backup_src_start = backup_src_size = backup_offset = 0; + for (i = 0; i < e_phnum; ++i) { + unsigned long p_type, p_offset, p_paddr, p_memsz; + + if (!readmem(PADDR, elfcorehdr_p+e_phoff+i*e_phentsize, buf, + e_phentsize)) { + ERRMSG("Can't read elfcorehdr program header. %s\n", + strerror(errno)); + return; + } + + p_type = UINT(buf + OFFSET(elf64_phdr.p_type)); + p_offset = ULONG(buf + OFFSET(elf64_phdr.p_offset)); + p_paddr = ULONG(buf + OFFSET(elf64_phdr.p_paddr)); + p_memsz = ULONG(buf + OFFSET(elf64_phdr.p_memsz)); + + if (p_type == PT_LOAD && + p_paddr <= KEXEC_BACKUP_SRC_END && + p_paddr + p_memsz <= p_offset) { + + backup_src_start = p_paddr; + backup_src_size = p_memsz; + backup_offset = p_offset; + +DEBUG_MSG("sadump: SRC_START: %#016lx SRC_SIZE: %#016lx SRC_OFFSET: %#016llx\n", + backup_src_start, backup_src_size, backup_offset); + + break; + } + } + if (i == e_phnum) { +DEBUG_MSG("sadump: No PT_LOAD in elfcorehdr for backup area\n"); + return; + } + + bufsize = BUFSIZE; + for (total = 0; total < backup_src_size; total += bufsize) { + + if (backup_src_size - total < BUFSIZE) + bufsize = backup_src_size - total; + + if (!readmem(PADDR, backup_offset + total, buf, bufsize)) { + ERRMSG("Can't read bacckup region. %s\n", + strerror(errno)); + return; + } + + /* + * We're assuming that the backup region is full of 0 + * before kdump saves the first 640kB memory of the + * 1st kernel in the region. + */ + if (!is_zero_page(buf, bufsize)) { + + si->kdump_backed_up = TRUE; + si->backup_src_start = backup_src_start; + si->backup_src_size = backup_src_size; + si->backup_offset = backup_offset; + + DEBUG_MSG("sadump: kdump backup region used\n"); + + return; + } + } + + DEBUG_MSG("sadump: kdump backup region unused\n"); +} + #endif /* defined(__x86__) && defined(__x86_64__) */ diff --git a/sadump_info.h b/sadump_info.h index f90ea5a..1f74ee5 100644 --- a/sadump_info.h +++ b/sadump_info.h @@ -54,6 +54,7 @@ long sadump_page_size(void); char *sadump_head_disk_name_memory(void); char *sadump_format_type_name(void); void free_sadump_info(void); +void sadump_kdump_backup_region_init(void); static inline int sadump_is_supported_arch(void) { @@ -154,6 +155,11 @@ static inline int sadump_is_supported_arch(void) return FALSE; } +static inline void sadump_kdump_backup_region_init(void) +{ + return; +} + #endif #endif /* _SADUMP_INFO_H */ |