diff options
author | Simon Horman <horms@verge.net.au> | 2010-09-09 17:10:25 +0900 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2010-09-09 17:10:25 +0900 |
commit | 9e58e01336a3f387c704b9c3c3b0e213013c20c9 (patch) | |
tree | 7da81eaa3108fee02caa65aba262bbf62e5d8846 | |
parent | 62e9b42c82b6779a2802ff69460db93d406669ec (diff) | |
parent | bec38dd6ae9bde528d8723b9e88024e121424ae4 (diff) |
Merge branch 'master' of ../kexec-tools-devel
-rw-r--r-- | Makefile.in | 8 | ||||
-rw-r--r-- | kexec/Makefile | 3 | ||||
-rw-r--r-- | kexec/arch/i386/crashdump-x86.c | 333 | ||||
-rw-r--r-- | kexec/arch/i386/crashdump-x86.h | 17 | ||||
-rw-r--r-- | kexec/arch/i386/kexec-bzImage.c | 4 | ||||
-rw-r--r-- | kexec/arch/i386/kexec-elf-x86.c | 2 | ||||
-rw-r--r-- | kexec/arch/ia64/crashdump-ia64.c | 6 | ||||
-rw-r--r-- | kexec/arch/x86_64/Makefile | 8 | ||||
-rw-r--r-- | kexec/arch/x86_64/crashdump-x86_64.c | 684 | ||||
-rw-r--r-- | kexec/arch/x86_64/crashdump-x86_64.h | 28 | ||||
-rw-r--r-- | kexec/arch/x86_64/kexec-elf-x86_64.c | 2 | ||||
-rw-r--r-- | kexec/arch/x86_64/kexec-x86_64.c | 11 | ||||
-rw-r--r-- | kexec/arch_init.c | 4 | ||||
-rw-r--r-- | kexec/crashdump-elf.c | 14 | ||||
-rw-r--r-- | kexec/crashdump.h | 3 | ||||
-rw-r--r-- | kexec/kernel_version.c (renamed from kexec/arch/x86_64/arch_init.c) | 31 | ||||
-rw-r--r-- | kexec/kexec-elf.c | 14 | ||||
-rw-r--r-- | kexec/kexec-elf.h | 44 | ||||
-rw-r--r-- | kexec/kexec.c | 2 | ||||
-rw-r--r-- | kexec/kexec.h | 9 | ||||
-rw-r--r-- | vmcore-dmesg/Makefile | 29 | ||||
-rw-r--r-- | vmcore-dmesg/vmcore-dmesg.8 | 42 | ||||
-rw-r--r-- | vmcore-dmesg/vmcore-dmesg.c | 504 |
23 files changed, 917 insertions, 885 deletions
diff --git a/Makefile.in b/Makefile.in index 4d193b1..ba2e638 100644 --- a/Makefile.in +++ b/Makefile.in @@ -158,6 +158,10 @@ include $(srcdir)/kexec/Makefile # include $(srcdir)/kdump/Makefile +# vmcore-dmesg (read dmesg from a vmcore) +# +include $(srcdir)/vmcore-dmesg/Makefile + # # kexec_test (test program) # @@ -171,10 +175,10 @@ SRCS:= $(dist) PSRCS:=$(foreach s, $(SRCS), $(PACKAGE_NAME)-$(PACKAGE_VERSION)/$(s)) PGSRCS:=$(foreach s, $(GENERATED_SRCS), $(PACKAGE_NAME)-$(PACKAGE_VERSION)/$(s)) -MAN_PAGES:=$(KEXEC_MANPAGE) $(KDUMP_MANPAGE) +MAN_PAGES:=$(KEXEC_MANPAGE) $(KDUMP_MANPAGE) $(VMCORE_DMESG_MANPAGE) BINARIES_i386:=$(KEXEC_TEST) $(KEXEC_TEST) BINARIES_x86_64:=$(KEXEC) $(KDUMP) $(BINARIES_$(ARCH)) -BINARIES:=$(KEXEC) $(KDUMP) $(BINARIES_$(ARCH)) +BINARIES:=$(KEXEC) $(KDUMP) $(VMCORE_DMESG) $(BINARIES_$(ARCH)) TARGETS:=$(BINARIES) $(MAN_PAGES) diff --git a/kexec/Makefile b/kexec/Makefile index 23385fc..2137cab 100644 --- a/kexec/Makefile +++ b/kexec/Makefile @@ -22,6 +22,7 @@ KEXEC_SRCS += kexec/firmware_memmap.c KEXEC_SRCS += kexec/crashdump.c KEXEC_SRCS += kexec/crashdump-xen.c KEXEC_SRCS += kexec/phys_arch.c +KEXEC_SRCS += kexec/kernel_version.c KEXEC_SRCS += kexec/lzma.c KEXEC_SRCS += kexec/zlib.c @@ -47,8 +48,6 @@ $(ARCH)_ADD_BUFFER = kexec/add_buffer.c KEXEC_SRCS += $($(ARCH)_ADD_BUFFER) $(ARCH)_ARCH_REUSE_INITRD = kexec/arch_reuse_initrd.c KEXEC_SRCS += $($(ARCH)_ARCH_REUSE_INITRD) -$(ARCH)_ARCH_INIT = kexec/arch_init.c -KEXEC_SRCS += $($(ARCH)_ARCH_INIT) include $(srcdir)/kexec/arch/alpha/Makefile include $(srcdir)/kexec/arch/arm/Makefile diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c index 9d37442..06e5ae9 100644 --- a/kexec/arch/i386/crashdump-x86.c +++ b/kexec/arch/i386/crashdump-x86.c @@ -2,6 +2,7 @@ * kexec: Linux boots Linux * * Created by: Vivek Goyal (vgoyal@in.ibm.com) + * old x86_64 version Created by: Murali M Chakravarthy (muralim@in.ibm.com) * Copyright (C) IBM Corporation, 2005. All rights reserved * * This program is free software; you can redistribute it and/or modify @@ -36,8 +37,133 @@ extern struct arch_options_t arch_options; +static int get_kernel_page_offset(struct kexec_info *info, + struct crash_elf_info *elf_info) +{ + int kv; + + if (elf_info->machine == EM_X86_64) { + kv = kernel_version(); + if (kv < 0) + return -1; + + if (kv < KERNEL_VERSION(2, 6, 27)) + elf_info->page_offset = X86_64_PAGE_OFFSET_PRE_2_6_27; + else + elf_info->page_offset = X86_64_PAGE_OFFSET; + } + else if (elf_info->machine == EM_386) { + elf_info->page_offset = X86_PAGE_OFFSET; + } + + return 0; +} + +#define X86_64_KERN_VADDR_ALIGN 0x100000 /* 1MB */ + +/* Read kernel physical load addr from the file returned by proc_iomem() + * (Kernel Code) and store in kexec_info */ +static int get_kernel_paddr(struct kexec_info *info, + struct crash_elf_info *elf_info) +{ + uint64_t start; + + if (elf_info->machine != EM_X86_64) + return 0; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) { + elf_info->kern_paddr_start = start; +#ifdef DEBUG + printf("kernel load physical addr start = 0x%016Lx\n", start); +#endif + return 0; + } + + fprintf(stderr, "Cannot determine kernel physical load addr\n"); + return -1; +} + +/* Retrieve info regarding virtual address kernel has been compiled for and + * size of the kernel from /proc/kcore. Current /proc/kcore parsing from + * from kexec-tools fails because of malformed elf notes. A kernel patch has + * been submitted. For the folks using older kernels, this function + * hard codes the values to remain backward compatible. Once things stablize + * we should get rid of backward compatible code. */ + +static int get_kernel_vaddr_and_size(struct kexec_info *info, + struct crash_elf_info *elf_info) +{ + int result; + const char kcore[] = "/proc/kcore"; + char *buf; + struct mem_ehdr ehdr; + struct mem_phdr *phdr, *end_phdr; + int align; + unsigned long size; + uint32_t elf_flags = 0; + + if (elf_info->machine != EM_X86_64) + return 0; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + align = getpagesize(); + size = KCORE_ELF_HEADERS_SIZE; + buf = slurp_file_len(kcore, size); + if (!buf) { + fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno)); + return -1; + } + + /* Don't perform checks to make sure stated phdrs and shdrs are + * actually present in the core file. It is not practical + * to read the GB size file into a user space buffer, Given the + * fact that we don't use any info from that. + */ + elf_flags |= ELF_SKIP_FILESZ_CHECK; + result = build_elf_core_info(buf, size, &ehdr, elf_flags); + if (result < 0) { + /* Perhaps KCORE_ELF_HEADERS_SIZE is too small? */ + fprintf(stderr, "ELF core (kcore) parse failed\n"); + return -1; + } + + /* Traverse through the Elf headers and find the region where + * kernel is mapped. */ + end_phdr = &ehdr.e_phdr[ehdr.e_phnum]; + for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) { + if (phdr->p_type == PT_LOAD) { + unsigned long long saddr = phdr->p_vaddr; + unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz; + unsigned long long size; + + /* Look for kernel text mapping header. */ + if ((saddr >= X86_64__START_KERNEL_map) && + (eaddr <= X86_64__START_KERNEL_map + X86_64_KERNEL_TEXT_SIZE)) { + saddr = (saddr) & (~(X86_64_KERN_VADDR_ALIGN - 1)); + elf_info->kern_vaddr_start = saddr; + size = eaddr - saddr; + /* Align size to page size boundary. */ + size = (size + align - 1) & (~(align - 1)); + elf_info->kern_size = size; +#ifdef DEBUG + printf("kernel vaddr = 0x%lx size = 0x%llx\n", + saddr, size); +#endif + return 0; + } + } + } + fprintf(stderr, "Can't find kernel text map area from kcore\n"); + return -1; +} + /* Forward Declaration. */ -static int exclude_crash_reserve_region(int *nr_ranges); +static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end); /* Stores a sorted list of RAM memory ranges for which to create elf headers. * A separate program header is created for backup region */ @@ -58,13 +184,14 @@ static struct memory_range crash_reserved_mem; * be zone data structures exported from kernel. */ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, - int kexec_flags) + int kexec_flags, unsigned long lowmem_limit) { const char *iomem = proc_iomem(); - int memory_ranges = 0; + int memory_ranges = 0, gart = 0; char line[MAX_LINE]; FILE *fp; unsigned long long start, end; + uint64_t gart_start = 0, gart_end = 0; fp = fopen(iomem, "r"); if (!fp) { @@ -85,6 +212,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, while(fgets(line, sizeof(line), fp) != 0) { char *str; int type, consumed, count; + if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) break; count = sscanf(line, "%Lx-%Lx : %n", @@ -92,7 +220,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, if (count != 2) continue; str = line + consumed; -#if 0 +#ifdef DEBUG printf("%016Lx-%016Lx : %s", start, end, str); #endif @@ -106,6 +234,22 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, crash_reserved_mem.end = end; crash_reserved_mem.type = RANGE_RAM; continue; + } else if (memcmp(str, "ACPI Tables\n", 12) == 0) { + /* + * ACPI Tables area need to be passed to new + * kernel with appropriate memmap= option. This + * is needed so that x86_64 kernel creates linear + * mapping for this region which is required for + * initializing acpi tables in second kernel. + */ + type = RANGE_ACPI; + } else if(memcmp(str,"ACPI Non-volatile Storage\n",26) == 0 ) { + type = RANGE_ACPI_NVS; + } else if (memcmp(str, "GART\n", 5) == 0) { + gart_start = start; + gart_end = end; + gart = 1; + continue; } else { continue; } @@ -120,11 +264,12 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, memory_ranges++; /* Segregate linearly mapped region. */ - if ((MAXMEM - 1) >= start && (MAXMEM - 1) <= end) { - crash_memory_range[memory_ranges-1].end = MAXMEM -1; + if (lowmem_limit && + (lowmem_limit - 1) >= start && (lowmem_limit - 1) <= end) { + crash_memory_range[memory_ranges-1].end = lowmem_limit -1; /* Add segregated region. */ - crash_memory_range[memory_ranges].start = MAXMEM; + crash_memory_range[memory_ranges].start = lowmem_limit; crash_memory_range[memory_ranges].end = end; crash_memory_range[memory_ranges].type = type; memory_ranges++; @@ -148,11 +293,17 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, crash_reserved_mem.end = mem_max; crash_reserved_mem.type = RANGE_RAM; } - if (exclude_crash_reserve_region(&memory_ranges) < 0) + if (exclude_region(&memory_ranges, crash_reserved_mem.start, + crash_reserved_mem.end) < 0) return -1; + if (gart) { + /* exclude GART region if the system has one */ + if (exclude_region(&memory_ranges, gart_start, gart_end) < 0) + return -1; + } *range = crash_memory_range; *ranges = memory_ranges; -#if 0 +#ifdef DEBUG int i; printf("CRASH MEMORY RANGES\n"); for(i = 0; i < memory_ranges; i++) { @@ -167,32 +318,28 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges, /* Removes crash reserve region from list of memory chunks for whom elf program * headers have to be created. Assuming crash reserve region to be a single * continuous area fully contained inside one of the memory chunks */ -static int exclude_crash_reserve_region(int *nr_ranges) +static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end) { int i, j, tidx = -1; - unsigned long long cstart, cend; struct memory_range temp_region = {0, 0, 0}; - /* Crash reserved region. */ - cstart = crash_reserved_mem.start; - cend = crash_reserved_mem.end; for (i = 0; i < (*nr_ranges); i++) { unsigned long long mstart, mend; mstart = crash_memory_range[i].start; mend = crash_memory_range[i].end; - if (cstart < mend && cend > mstart) { - if (cstart != mstart && cend != mend) { + if (start < mend && end > mstart) { + if (start != mstart && end != mend) { /* Split memory region */ - crash_memory_range[i].end = cstart - 1; - temp_region.start = cend + 1; + crash_memory_range[i].end = start - 1; + temp_region.start = end + 1; temp_region.end = mend; temp_region.type = RANGE_RAM; tidx = i+1; - } else if (cstart != mstart) - crash_memory_range[i].end = cstart - 1; + } else if (start != mstart) + crash_memory_range[i].end = start - 1; else - crash_memory_range[i].start = cend + 1; + crash_memory_range[i].start = end + 1; } } /* Insert split memory region, if any. */ @@ -251,7 +398,7 @@ static int add_memmap(struct memory_range *memmap_p, unsigned long long addr, memmap_p[j+1] = memmap_p[j]; memmap_p[tidx].start = addr; memmap_p[tidx].end = addr + size - 1; -#if 0 +#ifdef DEBUG printf("Memmap after adding segment\n"); for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { mstart = memmap_p[i].start; @@ -337,7 +484,7 @@ static int delete_memmap(struct memory_range *memmap_p, unsigned long long addr, memmap_p[j-1] = memmap_p[j]; memmap_p[j-1].start = memmap_p[j-1].end = 0; } -#if 0 +#ifdef DEBUG printf("Memmap after deleting segment\n"); for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { mstart = memmap_p[i].start; @@ -412,8 +559,7 @@ static int cmdline_add_memmap(char *cmdline, struct memory_range *memmap_p) die("Command line overflow\n"); strcat(cmdline, str_mmap); } - -#if 0 +#ifdef DEBUG printf("Command line after adding memmap\n"); printf("%s\n", cmdline); #endif @@ -441,7 +587,7 @@ static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr) if (cmdlen > (COMMAND_LINE_SIZE - 1)) die("Command line overflow\n"); strcat(cmdline, str); -#if 0 +#ifdef DEBUG printf("Command line after adding elfcorehdr\n"); printf("%s\n", cmdline); #endif @@ -472,9 +618,9 @@ static int get_crash_notes(int cpu, uint64_t *addr, uint64_t *len) strerror(errno)); } - *addr = __pa(vaddr + (cpu * MAX_NOTE_BYTES)); + *addr = x86__pa(vaddr + (cpu * MAX_NOTE_BYTES)); *len = MAX_NOTE_BYTES; -#if 0 +#ifdef DEBUG printf("crash_notes addr = %Lx\n", *addr); #endif return 0; @@ -482,30 +628,6 @@ static int get_crash_notes(int cpu, uint64_t *addr, uint64_t *len) return get_crash_notes_per_cpu(cpu, addr, len); } -static struct crash_elf_info elf_info64 = -{ - class: ELFCLASS64, - data: ELFDATA2LSB, - machine: EM_386, - backup_src_start: BACKUP_SRC_START, - backup_src_end: BACKUP_SRC_END, - page_offset: PAGE_OFFSET, - lowmem_limit: MAXMEM, - get_note_info: get_crash_notes, -}; - -static struct crash_elf_info elf_info32 = -{ - class: ELFCLASS32, - data: ELFDATA2LSB, - machine: EM_386, - backup_src_start: BACKUP_SRC_START, - backup_src_end: BACKUP_SRC_END, - page_offset: PAGE_OFFSET, - lowmem_limit: MAXMEM, - get_note_info: get_crash_notes, -}; - static enum coretype get_core_type(struct kexec_info *info, struct memory_range *range, int ranges) { @@ -523,6 +645,39 @@ static enum coretype get_core_type(struct kexec_info *info, } } +/* Appends memmap=X#Y commandline for ACPI to command line*/ +static int cmdline_add_memmap_acpi(char *cmdline, unsigned long start, + unsigned long end) +{ + int cmdlen, len, align = 1024; + unsigned long startk, endk; + char str_mmap[256], str_tmp[20]; + + if (!(end - start)) + return 0; + + startk = start/1024; + endk = (end + align - 1)/1024; + strcpy (str_mmap, " memmap="); + ultoa((endk - startk), str_tmp); + strcat (str_mmap, str_tmp); + strcat (str_mmap, "K#"); + ultoa(startk, str_tmp); + strcat (str_mmap, str_tmp); + strcat (str_mmap, "K"); + len = strlen(str_mmap); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str_mmap); + +#ifdef DEBUG + printf("Command line after adding acpi memmap\n"); + printf("%s\n", cmdline); +#endif + return 0; +} + /* Loads additional segments in case of a panic kernel is being loaded. * One segment for backup region, another segment for storing elf headers * for crash memory image. @@ -531,13 +686,28 @@ int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, unsigned long max_addr, unsigned long min_base) { void *tmp; - unsigned long sz, elfcorehdr; - int nr_ranges, align = 1024; + unsigned long sz, bufsz, memsz, elfcorehdr; + int nr_ranges, align = 1024, i; struct memory_range *mem_range, *memmap_p; - unsigned machine; + struct crash_elf_info elf_info; + + /* Constant parts of the elf_info */ + elf_info.data = ELFDATA2LSB; + elf_info.backup_src_start = BACKUP_SRC_START; + elf_info.backup_src_end = BACKUP_SRC_END; + + /* Get the elf architecture of the running kernel */ + if ((info->kexec_flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_X86_64) { + elf_info.machine = EM_X86_64; + } else { + elf_info.machine = EM_386; + elf_info.lowmem_limit = X86_MAXMEM; + elf_info.get_note_info = get_crash_notes; + } if (get_crash_memory_ranges(&mem_range, &nr_ranges, - info->kexec_flags) < 0) + info->kexec_flags, + elf_info.lowmem_limit) < 0) return -1; /* @@ -548,13 +718,21 @@ int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, arch_options.core_header_type = get_core_type(info, mem_range, nr_ranges); } - - /* Get the elf architecture of the running kernel */ - machine = EM_386; - if ((info->kexec_flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_X86_64) { - machine = EM_X86_64; + /* Get the elf class... */ + elf_info.class = ELFCLASS32; + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + elf_info.class = ELFCLASS64; } + if (get_kernel_page_offset(info, &elf_info)) + return -1; + + if (get_kernel_paddr(info, &elf_info)) + return -1; + + if (get_kernel_vaddr_and_size(info, &elf_info)) + return -1; + /* Memory regions which panic kernel can safely use to boot into */ sz = (sizeof(struct memory_range) * (KEXEC_MAX_SEGMENTS + 1)); memmap_p = xmalloc(sz); @@ -578,21 +756,20 @@ int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, /* Create elf header segment and store crash image data. */ if (arch_options.core_header_type == CORE_TYPE_ELF64) { - elf_info64.machine = machine; - if (crash_create_elf64_headers(info, &elf_info64, + if (crash_create_elf64_headers(info, &elf_info, crash_memory_range, nr_ranges, - &tmp, &sz, + &tmp, &bufsz, ELF_CORE_HEADER_ALIGN) < 0) return -1; } else { - elf_info32.machine = machine; - if (crash_create_elf32_headers(info, &elf_info32, + if (crash_create_elf32_headers(info, &elf_info, crash_memory_range, nr_ranges, - &tmp, &sz, + &tmp, &bufsz, ELF_CORE_HEADER_ALIGN) < 0) return -1; } + /* the size of the elf headers allocated is returned in 'bufsz' */ /* Hack: With some ld versions (GNU ld version 2.14.90.0.4 20030523), * vmlinux program headers show a gap of two pages between bss segment @@ -601,13 +778,31 @@ int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, * elf core header segment to 16K to avoid being placed in such gaps. * This is a makeshift solution until it is fixed in kernel. */ - elfcorehdr = add_buffer(info, tmp, sz, 16*1024, align, min_base, + if (bufsz < (16*1024)) { + /* bufsize is big enough for all the PT_NOTE's and PT_LOAD's */ + memsz = 16*1024; + /* memsz will be the size of the memory hole we look for */ + } else { + memsz = bufsz; + } + elfcorehdr = add_buffer(info, tmp, bufsz, memsz, align, min_base, max_addr, -1); dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr); - if (delete_memmap(memmap_p, elfcorehdr, sz) < 0) + if (delete_memmap(memmap_p, elfcorehdr, memsz) < 0) return -1; cmdline_add_memmap(mod_cmdline, memmap_p); cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + /* Inform second kernel about the presence of ACPI tables. */ + for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) { + unsigned long start, end; + if ( !( mem_range[i].type == RANGE_ACPI + || mem_range[i].type == RANGE_ACPI_NVS) ) + continue; + start = mem_range[i].start; + end = mem_range[i].end; + cmdline_add_memmap_acpi(mod_cmdline, start, end); + } return 0; } diff --git a/kexec/arch/i386/crashdump-x86.h b/kexec/arch/i386/crashdump-x86.h index bb6ceb9..b61cf0a 100644 --- a/kexec/arch/i386/crashdump-x86.h +++ b/kexec/arch/i386/crashdump-x86.h @@ -5,11 +5,20 @@ struct kexec_info; int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, unsigned long max_addr, unsigned long min_base); -#define PAGE_OFFSET 0xc0000000 -#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +#define X86_PAGE_OFFSET 0xc0000000 +#define x86__pa(x) ((unsigned long)(x)-X86_PAGE_OFFSET) -#define __VMALLOC_RESERVE (128 << 20) -#define MAXMEM (-PAGE_OFFSET-__VMALLOC_RESERVE) +#define X86__VMALLOC_RESERVE (128 << 20) +#define X86_MAXMEM (-X86_PAGE_OFFSET-X86__VMALLOC_RESERVE) + +#define X86_64__START_KERNEL_map 0xffffffff80000000ULL +#define X86_64_PAGE_OFFSET_PRE_2_6_27 0xffff810000000000ULL +#define X86_64_PAGE_OFFSET 0xffff880000000000ULL + +#define X86_64_MAXMEM 0x3fffffffffffUL + +/* Kernel text size */ +#define X86_64_KERNEL_TEXT_SIZE (512UL*1024*1024) #define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) #define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c index ae18689..83d3a69 100644 --- a/kexec/arch/i386/kexec-bzImage.c +++ b/kexec/arch/i386/kexec-bzImage.c @@ -1,7 +1,7 @@ /* * kexec: Linux boots Linux * - * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * Copyright (C) 2003-2010 Eric Biederman (ebiederm@xmission.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -217,7 +217,7 @@ int do_bzImage_load(struct kexec_info *info, * anywhere as we will be just reading command line. */ setup_base = add_buffer(info, real_mode, setup_size, setup_size, - 16, 0x3000, -1, 1); + 16, 0x3000, -1, -1); } else if (real_mode->protocol_version >= 0x0200) { /* Careful setup_base must be greater than 8K */ diff --git a/kexec/arch/i386/kexec-elf-x86.c b/kexec/arch/i386/kexec-elf-x86.c index 2e9cf9a..8bd5ef6 100644 --- a/kexec/arch/i386/kexec-elf-x86.c +++ b/kexec/arch/i386/kexec-elf-x86.c @@ -59,7 +59,7 @@ int elf_x86_probe(const char *buf, off_t len) if ((ehdr.e_machine != EM_386) && (ehdr.e_machine != EM_486)) { /* for a different architecture */ if (probe_debug) { - fprintf(stderr, "Not x86_64 ELF executable\n"); + fprintf(stderr, "Not i386 ELF executable\n"); } result = -1; goto out; diff --git a/kexec/arch/ia64/crashdump-ia64.c b/kexec/arch/ia64/crashdump-ia64.c index 6b64271..8932395 100644 --- a/kexec/arch/ia64/crashdump-ia64.c +++ b/kexec/arch/ia64/crashdump-ia64.c @@ -238,7 +238,7 @@ int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr, get_crash_memory_ranges(&nr_ranges) == 0) { int i; - info->kern_paddr_start = kernel_code_start; + elf_info.kern_paddr_start = kernel_code_start; for (i=0; i < nr_ranges; i++) { unsigned long long mstart = crash_memory_range[i].start; unsigned long long mend = crash_memory_range[i].end; @@ -246,11 +246,11 @@ int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr, continue; if (kernel_code_start >= mstart && kernel_code_start < mend) { - info->kern_vaddr_start = mstart + LOAD_OFFSET; + elf_info.kern_vaddr_start = mstart + LOAD_OFFSET; break; } } - info->kern_size = kernel_code_end - kernel_code_start + 1; + elf_info.kern_size = kernel_code_end - kernel_code_start + 1; if (crash_create_elf64_headers(info, &elf_info, crash_memory_range, nr_ranges, &tmp, &sz, EFI_PAGE_SIZE) < 0) diff --git a/kexec/arch/x86_64/Makefile b/kexec/arch/x86_64/Makefile index 0f35a46..916babf 100644 --- a/kexec/arch/x86_64/Makefile +++ b/kexec/arch/x86_64/Makefile @@ -8,13 +8,11 @@ x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c x86_64_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-x86-common.c -x86_64_KEXEC_SRCS += kexec/arch/x86_64/crashdump-x86_64.c +x86_64_KEXEC_SRCS += kexec/arch/i386/crashdump-x86.c x86_64_KEXEC_SRCS += kexec/arch/x86_64/kexec-x86_64.c x86_64_KEXEC_SRCS += kexec/arch/x86_64/kexec-elf-x86_64.c x86_64_KEXEC_SRCS += kexec/arch/x86_64/kexec-elf-rel-x86_64.c -x86_64_ARCH_INIT = kexec/arch/x86_64/arch_init.c - -dist += kexec/arch/x86_64/Makefile $(x86_64_KEXEC_SRCS) $(x86_64_ARCH_INIT) \ - kexec/arch/x86_64/kexec-x86_64.h kexec/arch/x86_64/crashdump-x86_64.h \ +dist += kexec/arch/x86_64/Makefile $(x86_64_KEXEC_SRCS) \ + kexec/arch/x86_64/kexec-x86_64.h \ kexec/arch/x86_64/include/arch/options.h diff --git a/kexec/arch/x86_64/crashdump-x86_64.c b/kexec/arch/x86_64/crashdump-x86_64.c deleted file mode 100644 index 8b6581b..0000000 --- a/kexec/arch/x86_64/crashdump-x86_64.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * kexec: Linux boots Linux - * - * Created by: Murali M Chakravarthy (muralim@in.ibm.com) - * Copyright (C) IBM Corporation, 2005. All rights reserved - * Heavily borrowed from kexec/arch/i386/crashdump-x86.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation (version 2 of the License). - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <limits.h> -#include <elf.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include "../../kexec.h" -#include "../../kexec-elf.h" -#include "../../kexec-syscall.h" -#include "../../crashdump.h" -#include "kexec-x86_64.h" -#include "crashdump-x86_64.h" -#include <x86/x86-linux.h> - - -/* Forward Declaration. */ -static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end); - -#define KERN_VADDR_ALIGN 0x100000 /* 1MB */ - -/* Read kernel physical load addr from the file returned by proc_iomem() - * (Kernel Code) and store in kexec_info */ -static int get_kernel_paddr(struct kexec_info *info) -{ - uint64_t start; - - if (xen_present()) /* Kernel not entity mapped under Xen */ - return 0; - - if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) { - info->kern_paddr_start = start; -#ifdef DEBUG - printf("kernel load physical addr start = 0x%016Lx\n", start); -#endif - return 0; - } - - fprintf(stderr, "Cannot determine kernel physical load addr\n"); - return -1; -} - -/* Retrieve info regarding virtual address kernel has been compiled for and - * size of the kernel from /proc/kcore. Current /proc/kcore parsing from - * from kexec-tools fails because of malformed elf notes. A kernel patch has - * been submitted. For the folks using older kernels, this function - * hard codes the values to remain backward compatible. Once things stablize - * we should get rid of backward compatible code. */ - -static int get_kernel_vaddr_and_size(struct kexec_info *info) -{ - int result; - const char kcore[] = "/proc/kcore"; - char *buf; - struct mem_ehdr ehdr; - struct mem_phdr *phdr, *end_phdr; - int align; - unsigned long size; - uint32_t elf_flags = 0; - - if (xen_present()) /* Kernel not entity mapped under Xen */ - return 0; - - align = getpagesize(); - size = KCORE_ELF_HEADERS_SIZE; - buf = slurp_file_len(kcore, size); - if (!buf) { - fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno)); - return -1; - } - - /* Don't perform checks to make sure stated phdrs and shdrs are - * actually present in the core file. It is not practical - * to read the GB size file into a user space buffer, Given the - * fact that we don't use any info from that. - */ - elf_flags |= ELF_SKIP_FILESZ_CHECK; - result = build_elf_core_info(buf, size, &ehdr, elf_flags); - if (result < 0) { - fprintf(stderr, "ELF core (kcore) parse failed\n"); - return -1; - } - - /* Traverse through the Elf headers and find the region where - * kernel is mapped. */ - end_phdr = &ehdr.e_phdr[ehdr.e_phnum]; - for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) { - if (phdr->p_type == PT_LOAD) { - unsigned long saddr = phdr->p_vaddr; - unsigned long eaddr = phdr->p_vaddr + phdr->p_memsz; - unsigned long size; - - /* Look for kernel text mapping header. */ - if ((saddr >= __START_KERNEL_map) && - (eaddr <= __START_KERNEL_map + KERNEL_TEXT_SIZE)) { - saddr = (saddr) & (~(KERN_VADDR_ALIGN - 1)); - info->kern_vaddr_start = saddr; - size = eaddr - saddr; - /* Align size to page size boundary. */ - size = (size + align - 1) & (~(align - 1)); - info->kern_size = size; -#ifdef DEBUG - printf("kernel vaddr = 0x%lx size = 0x%lx\n", - saddr, size); -#endif - return 0; - } - } - } - fprintf(stderr, "Can't find kernel text map area from kcore\n"); - return -1; -} - -/* Stores a sorted list of RAM memory ranges for which to create elf headers. - * A separate program header is created for backup region */ -static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; - -/* Memory region reserved for storing panic kernel and other data. */ -static struct memory_range crash_reserved_mem; - -/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to - * create Elf headers. Keeping it separate from get_memory_ranges() as - * requirements are different in the case of normal kexec and crashdumps. - * - * Normal kexec needs to look at all of available physical memory irrespective - * of the fact how much of it is being used by currently running kernel. - * Crashdumps need to have access to memory regions actually being used by - * running kernel. Expecting a different file/data structure than /proc/iomem - * to look into down the line. May be something like /proc/kernelmem or may - * be zone data structures exported from kernel. - */ -static int get_crash_memory_ranges(struct memory_range **range, int *ranges, - int kexec_flags) -{ - const char *iomem= proc_iomem(); - int memory_ranges = 0, gart = 0; - char line[MAX_LINE]; - FILE *fp; - unsigned long long start, end; - uint64_t gart_start = 0, gart_end = 0; - - fp = fopen(iomem, "r"); - if (!fp) { - fprintf(stderr, "Cannot open %s: %s\n", - iomem, strerror(errno)); - return -1; - } - - /* First entry is for first 640K region. Different bios report first - * 640K in different manner hence hardcoding it */ - if (!(kexec_flags & KEXEC_PRESERVE_CONTEXT)) { - crash_memory_range[0].start = 0x00000000; - crash_memory_range[0].end = 0x0009ffff; - crash_memory_range[0].type = RANGE_RAM; - memory_ranges++; - } - - while(fgets(line, sizeof(line), fp) != 0) { - char *str; - int type, consumed, count; - - if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) - break; - count = sscanf(line, "%Lx-%Lx : %n", - &start, &end, &consumed); - if (count != 2) - continue; - str = line + consumed; -#ifdef DEBUG - printf("%016Lx-%016Lx : %s", - start, end, str); -#endif - /* Only Dumping memory of type System RAM. */ - if (memcmp(str, "System RAM\n", 11) == 0) { - type = RANGE_RAM; - } else if (memcmp(str, "Crash kernel\n", 13) == 0) { - /* Reserved memory region. New kernel can - * use this region to boot into. */ - crash_reserved_mem.start = start; - crash_reserved_mem.end = end; - crash_reserved_mem.type = RANGE_RAM; - continue; - } else if (memcmp(str, "ACPI Tables\n", 12) == 0) { - /* - * ACPI Tables area need to be passed to new - * kernel with appropriate memmap= option. This - * is needed so that x86_64 kernel creates linear - * mapping for this region which is required for - * initializing acpi tables in second kernel. - */ - type = RANGE_ACPI; - } else if(memcmp(str,"ACPI Non-volatile Storage\n",26) == 0 ) { - type = RANGE_ACPI_NVS; - } else if (memcmp(str, "GART\n", 5) == 0) { - gart_start = start; - gart_end = end; - gart = 1; - continue; - } else { - continue; - } - - /* First 640K already registered */ - if (end <= 0x0009ffff) - continue; - - crash_memory_range[memory_ranges].start = start; - crash_memory_range[memory_ranges].end = end; - crash_memory_range[memory_ranges].type = type; - memory_ranges++; - } - fclose(fp); - if (kexec_flags & KEXEC_PRESERVE_CONTEXT) { - int i; - for (i = 0; i < memory_ranges; i++) { - if (crash_memory_range[i].end > 0x0009ffff) { - crash_reserved_mem.start = \ - crash_memory_range[i].start; - break; - } - } - if (crash_reserved_mem.start >= mem_max) { - fprintf(stderr, "Too small mem_max: 0x%llx.\n", mem_max); - return -1; - } - crash_reserved_mem.end = mem_max; - crash_reserved_mem.type = RANGE_RAM; - } - if (exclude_region(&memory_ranges, crash_reserved_mem.start, - crash_reserved_mem.end) < 0) - return -1; - if (gart) { - /* exclude GART region if the system has one */ - if (exclude_region(&memory_ranges, gart_start, gart_end) < 0) - return -1; - } - *range = crash_memory_range; - *ranges = memory_ranges; -#ifdef DEBUG - int i; - printf("CRASH MEMORY RANGES\n"); - for(i = 0; i < memory_ranges; i++) { - start = crash_memory_range[i].start; - end = crash_memory_range[i].end; - printf("%016Lx-%016Lx\n", start, end); - } -#endif - return 0; -} - -/* Removes crash reserve region from list of memory chunks for whom elf program - * headers have to be created. Assuming crash reserve region to be a single - * continuous area fully contained inside one of the memory chunks */ -static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end) -{ - int i, j, tidx = -1; - struct memory_range temp_region = { 0, 0, 0 }; - - for (i = 0; i < (*nr_ranges); i++) { - unsigned long long mstart, mend; - mstart = crash_memory_range[i].start; - mend = crash_memory_range[i].end; - if (start < mend && end > mstart) { - if (start != mstart && end != mend) { - /* Split memory region */ - crash_memory_range[i].end = start - 1; - temp_region.start = end + 1; - temp_region.end = mend; - temp_region.type = RANGE_RAM; - tidx = i+1; - } else if (start != mstart) - crash_memory_range[i].end = start - 1; - else - crash_memory_range[i].start = end + 1; - } - } - /* Insert split memory region, if any. */ - if (tidx >= 0) { - if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) { - /* No space to insert another element. */ - fprintf(stderr, "Error: Number of crash memory ranges" - " excedeed the max limit\n"); - return -1; - } - for (j = (*nr_ranges - 1); j >= tidx; j--) - crash_memory_range[j+1] = crash_memory_range[j]; - crash_memory_range[tidx].start = temp_region.start; - crash_memory_range[tidx].end = temp_region.end; - crash_memory_range[tidx].type = temp_region.type; - (*nr_ranges)++; - } - return 0; -} - -/* Adds a segment from list of memory regions which new kernel can use to - * boot. Segment start and end should be aligned to 1K boundary. */ -static int add_memmap(struct memory_range *memmap_p, unsigned long long addr, - size_t size) -{ - int i, j, nr_entries = 0, tidx = 0, align = 1024; - unsigned long long mstart, mend; - - /* Do alignment check. */ - if ((addr%align) || (size%align)) - return -1; - - /* Make sure at least one entry in list is free. */ - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (!mstart && !mend) - break; - else - nr_entries++; - } - if (nr_entries == CRASH_MAX_MEMMAP_NR) - return -1; - - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (mstart == 0 && mend == 0) - break; - if (mstart <= (addr+size-1) && mend >=addr) - /* Overlapping region. */ - return -1; - else if (addr > mend) - tidx = i+1; - } - /* Insert the memory region. */ - for (j = nr_entries-1; j >= tidx; j--) - memmap_p[j+1] = memmap_p[j]; - memmap_p[tidx].start = addr; - memmap_p[tidx].end = addr + size - 1; -#ifdef DEBUG - printf("Memmap after adding segment\n"); - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (mstart == 0 && mend == 0) - break; - printf("%016llx - %016llx\n", - mstart, mend); - } -#endif - return 0; -} - -/* Removes a segment from list of memory regions which new kernel can use to - * boot. Segment start and end should be aligned to 1K boundary. */ -static int delete_memmap(struct memory_range *memmap_p, unsigned long long addr, - size_t size) -{ - int i, j, nr_entries = 0, tidx = -1, operation = 0, align = 1024; - unsigned long long mstart, mend; - struct memory_range temp_region = { 0, 0, 0 }; - - /* Do alignment check. */ - if ((addr%align) || (size%align)) - return -1; - - /* Make sure at least one entry in list is free. */ - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (!mstart && !mend) - break; - else - nr_entries++; - } - if (nr_entries == CRASH_MAX_MEMMAP_NR) - /* List if full */ - return -1; - - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (mstart == 0 && mend == 0) - /* Did not find the segment in the list. */ - return -1; - if (mstart <= addr && mend >= (addr + size - 1)) { - if (mstart == addr && mend == (addr + size - 1)) { - /* Exact match. Delete region */ - operation = -1; - tidx = i; - break; - } - if (mstart != addr && mend != (addr + size - 1)) { - /* Split in two */ - memmap_p[i].end = addr - 1; - temp_region.start = addr + size; - temp_region.end = mend; - operation = 1; - tidx = i; - break; - } - - /* No addition/deletion required. Adjust the existing.*/ - if (mstart != addr) { - memmap_p[i].end = addr - 1; - break; - } else { - memmap_p[i].start = addr + size; - break; - } - } - } - if ((operation == 1) && tidx >=0) { - /* Insert the split memory region. */ - for (j = nr_entries-1; j > tidx; j--) - memmap_p[j+1] = memmap_p[j]; - memmap_p[tidx+1] = temp_region; - } - if ((operation == -1) && tidx >=0) { - /* Delete the exact match memory region. */ - for (j = i+1; j < CRASH_MAX_MEMMAP_NR; j++) - memmap_p[j-1] = memmap_p[j]; - memmap_p[j-1].start = memmap_p[j-1].end = 0; - } -#ifdef DEBUG - printf("Memmap after deleting segment\n"); - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - mstart = memmap_p[i].start; - mend = memmap_p[i].end; - if (mstart == 0 && mend == 0) { - break; - } - printf("%016llx - %016llx\n", - mstart, mend); - } -#endif - return 0; -} - -/* Converts unsigned long to ascii string. */ -static void ultoa(unsigned long i, char *str) -{ - int j = 0, k; - char tmp; - - do { - str[j++] = i % 10 + '0'; - } while ((i /=10) > 0); - str[j] = '\0'; - - /* Reverse the string. */ - for (j = 0, k = strlen(str) - 1; j < k; j++, k--) { - tmp = str[k]; - str[k] = str[j]; - str[j] = tmp; - } -} - -/* Adds the appropriate memmap= options to command line, indicating the - * memory regions the new kernel can use to boot into. */ -static int cmdline_add_memmap(char *cmdline, struct memory_range *memmap_p) -{ - int i, cmdlen, len; - unsigned long min_sizek = 100; - char str_mmap[256], str_tmp[20]; - - /* Exact map */ - strcpy(str_mmap, " memmap=exactmap"); - len = strlen(str_mmap); - cmdlen = strlen(cmdline) + len; - if (cmdlen > (COMMAND_LINE_SIZE - 1)) - die("Command line overflow\n"); - strcat(cmdline, str_mmap); - - for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { - unsigned long startk, endk; - startk = (memmap_p[i].start/1024); - endk = ((memmap_p[i].end + 1)/1024); - if (!startk && !endk) - /* All regions traversed. */ - break; - - /* A region is not worth adding if region size < 100K. It eats - * up precious command line length. */ - if ((endk - startk) < min_sizek) - continue; - strcpy (str_mmap, " memmap="); - ultoa((endk-startk), str_tmp); - strcat (str_mmap, str_tmp); - strcat (str_mmap, "K@"); - ultoa(startk, str_tmp); - strcat (str_mmap, str_tmp); - strcat (str_mmap, "K"); - len = strlen(str_mmap); - cmdlen = strlen(cmdline) + len; - if (cmdlen > (COMMAND_LINE_SIZE - 1)) - die("Command line overflow\n"); - strcat(cmdline, str_mmap); - } -#ifdef DEBUG - printf("Command line after adding memmap\n"); - printf("%s\n", cmdline); -#endif - return 0; -} - -/* Adds the elfcorehdr= command line parameter to command line. */ -static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr) -{ - int cmdlen, len, align = 1024; - char str[30], *ptr; - - /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline. - * Ensure 1K alignment*/ - if (addr%align) - return -1; - addr = addr/align; - ptr = str; - strcpy(str, " elfcorehdr="); - ptr += strlen(str); - ultoa(addr, ptr); - strcat(str, "K"); - len = strlen(str); - cmdlen = strlen(cmdline) + len; - if (cmdlen > (COMMAND_LINE_SIZE - 1)) - die("Command line overflow\n"); - strcat(cmdline, str); -#ifdef DEBUG - printf("Command line after adding elfcorehdr\n"); - printf("%s\n", cmdline); -#endif - return 0; -} - -/* Appends memmap=X#Y commandline for ACPI to command line*/ -static int cmdline_add_memmap_acpi(char *cmdline, unsigned long start, - unsigned long end) -{ - int cmdlen, len, align = 1024; - unsigned long startk, endk; - char str_mmap[256], str_tmp[20]; - - if (!(end - start)) - return 0; - - startk = start/1024; - endk = (end + align - 1)/1024; - strcpy (str_mmap, " memmap="); - ultoa((endk - startk), str_tmp); - strcat (str_mmap, str_tmp); - strcat (str_mmap, "K#"); - ultoa(startk, str_tmp); - strcat (str_mmap, str_tmp); - strcat (str_mmap, "K"); - len = strlen(str_mmap); - cmdlen = strlen(cmdline) + len; - if (cmdlen > (COMMAND_LINE_SIZE - 1)) - die("Command line overflow\n"); - strcat(cmdline, str_mmap); - -#ifdef DEBUG - printf("Command line after adding acpi memmap\n"); - printf("%s\n", cmdline); -#endif - return 0; -} - -/* Loads additional segments in case of a panic kernel is being loaded. - * One segment for backup region, another segment for storing elf headers - * for crash memory image. - */ -int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, - unsigned long max_addr, unsigned long min_base) -{ - void *tmp; - unsigned long sz, bufsz, memsz, elfcorehdr; - int nr_ranges, align = 1024, i; - struct memory_range *mem_range, *memmap_p; - - struct crash_elf_info elf_info = - { - class: ELFCLASS64, - data: ELFDATA2LSB, - machine: EM_X86_64, - backup_src_start: BACKUP_SRC_START, - backup_src_end: BACKUP_SRC_END, - page_offset: page_offset, - }; - - if (get_kernel_paddr(info)) - return -1; - - if (get_kernel_vaddr_and_size(info)) - return -1; - - if (get_crash_memory_ranges(&mem_range, &nr_ranges, - info->kexec_flags) < 0) - return -1; - - /* Memory regions which panic kernel can safely use to boot into */ - sz = (sizeof(struct memory_range) * (KEXEC_MAX_SEGMENTS + 1)); - memmap_p = xmalloc(sz); - memset(memmap_p, 0, sz); - add_memmap(memmap_p, BACKUP_SRC_START, BACKUP_SRC_SIZE); - sz = crash_reserved_mem.end - crash_reserved_mem.start +1; - add_memmap(memmap_p, crash_reserved_mem.start, sz); - - /* Create a backup region segment to store backup data*/ - if (!(info->kexec_flags & KEXEC_PRESERVE_CONTEXT)) { - sz = (BACKUP_SRC_SIZE + align - 1) & ~(align - 1); - tmp = xmalloc(sz); - memset(tmp, 0, sz); - info->backup_start = add_buffer(info, tmp, sz, sz, align, - 0, max_addr, 1); - if (delete_memmap(memmap_p, info->backup_start, sz) < 0) - return -1; - } - - /* Create elf header segment and store crash image data. */ - if (crash_create_elf64_headers(info, &elf_info, - crash_memory_range, nr_ranges, - &tmp, &bufsz, - ELF_CORE_HEADER_ALIGN) < 0) - return -1; - /* the size of the elf headers allocated is returned in 'bufsz' */ - - /* Hack: With some ld versions (GNU ld version 2.14.90.0.4 20030523), - * vmlinux program headers show a gap of two pages between bss segment - * and data segment but effectively kernel considers it as bss segment - * and overwrites the any data placed there. Hence bloat the memsz of - * elf core header segment to 16K to avoid being placed in such gaps. - * This is a makeshift solution until it is fixed in kernel. - */ - if (bufsz < (16*1024)) - /* bufsize is big enough for all the PT_NOTE's and PT_LOAD's */ - memsz = 16*1024; - /* memsz will be the size of the memory hole we look for */ - else - memsz = bufsz; - elfcorehdr = add_buffer(info, tmp, bufsz, memsz, align, min_base, - max_addr, -1); - if (delete_memmap(memmap_p, elfcorehdr, memsz) < 0) - return -1; - cmdline_add_memmap(mod_cmdline, memmap_p); - cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); - - /* Inform second kernel about the presence of ACPI tables. */ - for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) { - unsigned long start, end; - if ( !( mem_range[i].type == RANGE_ACPI - || mem_range[i].type == RANGE_ACPI_NVS) ) - continue; - start = mem_range[i].start; - end = mem_range[i].end; - cmdline_add_memmap_acpi(mod_cmdline, start, end); - } - return 0; -} - -int is_crashkernel_mem_reserved(void) -{ - uint64_t start, end; - - return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? - (start != end) : 0; -} diff --git a/kexec/arch/x86_64/crashdump-x86_64.h b/kexec/arch/x86_64/crashdump-x86_64.h deleted file mode 100644 index 2129104..0000000 --- a/kexec/arch/x86_64/crashdump-x86_64.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CRASHDUMP_X86_64_H -#define CRASHDUMP_X86_64_H - -#include "../../kexec.h" - -int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, - unsigned long max_addr, unsigned long min_base); - -#define __START_KERNEL_map 0xffffffff80000000UL - -extern unsigned long page_offset; - -#define __pa(x) (((unsigned long)(x)>=__START_KERNEL_map)?(unsigned long)(x) - (unsigned long)__START_KERNEL_map:(unsigned long)(x) - page_offset) - -#define MAXMEM 0x3fffffffffffUL - -/* Kernel text size */ -#define KERNEL_TEXT_SIZE (512UL*1024*1024) - -#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) -#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) - -/* Backup Region, First 640K of System RAM. */ -#define BACKUP_SRC_START 0x00000000 -#define BACKUP_SRC_END 0x0009ffff -#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) - -#endif /* CRASHDUMP_X86_64_H */ diff --git a/kexec/arch/x86_64/kexec-elf-x86_64.c b/kexec/arch/x86_64/kexec-elf-x86_64.c index a8204bc..09402d9 100644 --- a/kexec/arch/x86_64/kexec-elf-x86_64.c +++ b/kexec/arch/x86_64/kexec-elf-x86_64.c @@ -37,7 +37,7 @@ #include "../../kexec-elf-boot.h" #include "../i386/x86-linux-setup.h" #include "kexec-x86_64.h" -#include "crashdump-x86_64.h" +#include "../i386/crashdump-x86.h" #include <arch/options.h> static const int probe_debug = 0; diff --git a/kexec/arch/x86_64/kexec-x86_64.c b/kexec/arch/x86_64/kexec-x86_64.c index a4f2d10..3092643 100644 --- a/kexec/arch/x86_64/kexec-x86_64.c +++ b/kexec/arch/x86_64/kexec-x86_64.c @@ -29,7 +29,7 @@ #include "../../kexec-elf.h" #include "../../kexec-syscall.h" #include "kexec-x86_64.h" -#include "crashdump-x86_64.h" +#include "../i386/crashdump-x86.h" #include <arch/options.h> struct file_type file_type[] = { @@ -55,14 +55,7 @@ void arch_usage(void) ); } -static struct { - uint8_t reset_vga; - uint16_t serial_base; - uint32_t serial_baud; - uint8_t console_vga; - uint8_t console_serial; - int core_header_type; -} arch_options = { +struct arch_options_t arch_options = { .reset_vga = 0, .serial_base = 0x3f8, .serial_baud = 0, diff --git a/kexec/arch_init.c b/kexec/arch_init.c deleted file mode 100644 index afce72f..0000000 --- a/kexec/arch_init.c +++ /dev/null @@ -1,4 +0,0 @@ -int arch_init(void) -{ - return 0; -} diff --git a/kexec/crashdump-elf.c b/kexec/crashdump-elf.c index f000e42..954d670 100644 --- a/kexec/crashdump-elf.c +++ b/kexec/crashdump-elf.c @@ -68,7 +68,7 @@ int FUNC(struct kexec_info *info, /* * Certain architectures such as x86_64 and ia64 require a separate * PT_LOAD program header for the kernel. This is controlled through - * info->kern_size. + * elf_info->kern_size. * * The separate PT_LOAD program header is required either because the * kernel is mapped at a different location than the rest of the @@ -85,7 +85,7 @@ int FUNC(struct kexec_info *info, * PT_LOAD program header and in the physical RAM program headers. */ - if (info->kern_size && !xen_present()) { + if (elf_info->kern_size && !xen_present()) { sz += sizeof(PHDR); } @@ -195,17 +195,17 @@ int FUNC(struct kexec_info *info, } /* Setup an PT_LOAD type program header for the region where - * Kernel is mapped if info->kern_size is non-zero. + * Kernel is mapped if elf_info->kern_size is non-zero. */ - if (info->kern_size && !xen_present()) { + if (elf_info->kern_size && !xen_present()) { phdr = (PHDR *) bufp; bufp += sizeof(PHDR); phdr->p_type = PT_LOAD; phdr->p_flags = PF_R|PF_W|PF_X; - phdr->p_offset = phdr->p_paddr = info->kern_paddr_start; - phdr->p_vaddr = info->kern_vaddr_start; - phdr->p_filesz = phdr->p_memsz = info->kern_size; + phdr->p_offset = phdr->p_paddr = elf_info->kern_paddr_start; + phdr->p_vaddr = elf_info->kern_vaddr_start; + phdr->p_filesz = phdr->p_memsz = elf_info->kern_size; phdr->p_align = 0; (elf->e_phnum)++; dbgprintf_phdr("Kernel text Elf header", phdr); diff --git a/kexec/crashdump.h b/kexec/crashdump.h index 31f711c..eccdb9f 100644 --- a/kexec/crashdump.h +++ b/kexec/crashdump.h @@ -27,6 +27,9 @@ struct crash_elf_info { unsigned long backup_src_end; unsigned long long page_offset; + unsigned long long kern_vaddr_start; + unsigned long long kern_paddr_start; + unsigned long kern_size; unsigned long lowmem_limit; int (*get_note_info)(int cpu, uint64_t *addr, uint64_t *len); diff --git a/kexec/arch/x86_64/arch_init.c b/kexec/kernel_version.c index 79fb642..079312b 100644 --- a/kexec/arch/x86_64/arch_init.c +++ b/kexec/kernel_version.c @@ -1,14 +1,10 @@ +#include "kexec.h" #include <errno.h> #include <string.h> #include <sys/utsname.h> -#include <stdlib.h> +#include <string.h> #include <limits.h> -#include <stdio.h> - -#include "crashdump-x86_64.h" - -#define KERNEL_VERSION(major, minor, patch) \ - (((major) << 16) | ((minor) << 8) | patch) +#include <stdlib.h> long kernel_version(void) { @@ -60,24 +56,3 @@ long kernel_version(void) return KERNEL_VERSION(major, minor, patch); } - -#define PAGE_OFFSET_PRE_2_6_27 0xffff810000000000UL -#define PAGE_OFFSET 0xffff880000000000UL - -unsigned long page_offset; - -int arch_init(void) -{ - int kv; - - kv = kernel_version(); - if (kv < 0) - return -1; - - if (kv < KERNEL_VERSION(2, 6, 27)) - page_offset = PAGE_OFFSET_PRE_2_6_27; - else - page_offset = PAGE_OFFSET; - - return 0; -} diff --git a/kexec/kexec-elf.c b/kexec/kexec-elf.c index 1cdae36..b88aced 100644 --- a/kexec/kexec-elf.c +++ b/kexec/kexec-elf.c @@ -390,11 +390,11 @@ static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, phdr_size *= ehdr->e_phnum; if ((uintmax_t)(ehdr->e_phoff + phdr_size) > (uintmax_t)len) { /* The program header did not fit in the file buffer */ - fprintf(stderr, "%d segments require a %ld-byte buffer\n", - ehdr->e_phnum, ehdr->e_phoff + phdr_size); - fprintf(stderr, "KCORE_ELF_HEADERS_SIZE %d too small\n", KCORE_ELF_HEADERS_SIZE); - if (probe_debug) { - fprintf(stderr, "ELF program segment truncated\n"); + if (probe_debug || (flags & ELF_SKIP_FILESZ_CHECK)) { + fprintf(stderr, "ELF program headers truncated" + " have %ju bytes need %ju bytes\n", + (uintmax_t)len, + (uintmax_t)(ehdr->e_phoff + phdr_size)); } return -1; } @@ -507,7 +507,7 @@ static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) break; } if (!size_ok) { - fprintf(stderr, "Bad section header(%x) entsize: %ld\n", + fprintf(stderr, "Bad section header(%x) entsize: %lld\n", shdr->sh_type, shdr->sh_entsize); return -1; } @@ -576,7 +576,7 @@ static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) break; } if (!size_ok) { - fprintf(stderr, "Bad section header(%x) entsize: %ld\n", + fprintf(stderr, "Bad section header(%x) entsize: %lld\n", shdr->sh_type, shdr->sh_entsize); return -1; } diff --git a/kexec/kexec-elf.h b/kexec/kexec-elf.h index db13001..99cb80b 100644 --- a/kexec/kexec-elf.h +++ b/kexec/kexec-elf.h @@ -13,9 +13,9 @@ struct mem_ehdr { unsigned e_phnum; unsigned e_shnum; unsigned e_shstrndx; - unsigned long e_entry; - unsigned long e_phoff; - unsigned long e_shoff; + unsigned long long e_entry; + unsigned long long e_phoff; + unsigned long long e_shoff; unsigned e_notenum; struct mem_phdr *e_phdr; struct mem_shdr *e_shdr; @@ -24,28 +24,28 @@ struct mem_ehdr { }; struct mem_phdr { - unsigned long p_paddr; - unsigned long p_vaddr; - unsigned long p_filesz; - unsigned long p_memsz; - unsigned long p_offset; + unsigned long long p_paddr; + unsigned long long p_vaddr; + unsigned long long p_filesz; + unsigned long long p_memsz; + unsigned long long p_offset; const char *p_data; unsigned p_type; unsigned p_flags; - unsigned p_align; + unsigned long long p_align; }; struct mem_shdr { unsigned sh_name; unsigned sh_type; - unsigned long sh_flags; - unsigned long sh_addr; - unsigned long sh_offset; - unsigned long sh_size; + unsigned long long sh_flags; + unsigned long long sh_addr; + unsigned long long sh_offset; + unsigned long long sh_size; unsigned sh_link; unsigned sh_info; - unsigned long sh_addralign; - unsigned long sh_entsize; + unsigned long long sh_addralign; + unsigned long long sh_entsize; const unsigned char *sh_data; }; @@ -53,16 +53,16 @@ struct mem_sym { unsigned long st_name; /* Symbol name (string tbl index) */ unsigned char st_info; /* No defined meaning, 0 */ unsigned char st_other; /* Symbol type and binding */ - unsigned long st_shndx; /* Section index */ - unsigned long st_value; /* Symbol value */ - unsigned long st_size; /* Symbol size */ + unsigned st_shndx; /* Section index */ + unsigned long long st_value; /* Symbol value */ + unsigned long long st_size; /* Symbol size */ }; struct mem_rela { - unsigned long r_offset; - unsigned long r_sym; - unsigned long r_type; - unsigned long r_addend; + unsigned long long r_offset; + unsigned r_sym; + unsigned r_type; + unsigned long long r_addend; }; struct mem_note { diff --git a/kexec/kexec.c b/kexec/kexec.c index 2b5dc42..10ad41d 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -1062,8 +1062,6 @@ int main(int argc, char *argv[]) }; static const char short_options[] = KEXEC_ALL_OPT_STR; - arch_init(); - while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch(opt) { diff --git a/kexec/kexec.h b/kexec/kexec.h index 4d22017..9a70224 100644 --- a/kexec/kexec.h +++ b/kexec/kexec.h @@ -122,9 +122,6 @@ struct kexec_info { struct mem_ehdr rhdr; unsigned long backup_start; unsigned long kexec_flags; - unsigned long kern_vaddr_start; - unsigned long kern_paddr_start; - unsigned long kern_size; }; struct arch_map_entry { @@ -135,6 +132,10 @@ struct arch_map_entry { extern const struct arch_map_entry arches[]; long physical_arch(void); +#define KERNEL_VERSION(major, minor, patch) \ + (((major) << 16) | ((minor) << 8) | patch) +long kernel_version(void); + void usage(void); int get_memory_ranges(struct memory_range **range, int *ranges, unsigned long kexec_flags); @@ -249,8 +250,6 @@ int kexec_iomem_for_each_line(char *match, int parse_iomem_single(char *str, uint64_t *start, uint64_t *end); const char * proc_iomem(void); -int arch_init(void); - extern int add_backup_segments(struct kexec_info *info, unsigned long backup_base, unsigned long backup_size); diff --git a/vmcore-dmesg/Makefile b/vmcore-dmesg/Makefile new file mode 100644 index 0000000..5a6d84a --- /dev/null +++ b/vmcore-dmesg/Makefile @@ -0,0 +1,29 @@ +# +# vmcore-dmesg (reading demsg from vmcore) +# + +VMCORE_DMESG_SRCS:= vmcore-dmesg/vmcore-dmesg.c + +VMCORE_DMESG_OBJS = $(call objify, $(VMCORE_DMESG_SRCS)) +VMCORE_DMESG_DEPS = $(call depify, $(VMCORE_DMESG_OBJS)) + +VMCORE_DMESG = $(SBINDIR)/vmcore-dmesg +VMCORE_DMESG_MANPAGE = $(MANDIR)/man8/vmcore-dmesg.8 + +dist += vmcore-dmesg/Makefile $(VMCORE_DMESG_SRCS) vmcore-dmesg/vmcore-dmesg.8 +clean += $(VMCORE_DMESG_OBJS) $(VMCORE_DMESG_DEPS) $(VMCORE_DMESG) $(VMCORE_DMESG_MANPAGE) + +-include $(VMCORE_DMESG_DEPS) + +$(VMCORE_DMESG): $(VMCORE_DMESG_OBJS) + @$(MKDIR) -p $(@D) + $(LINK.o) -o $@ $^ $(CFLAGS) + +$(VMCORE_DMESG_MANPAGE): vmcore-dmesg/vmcore-dmesg.8 + $(MKDIR) -p $(MANDIR)/man8 + cp vmcore-dmesg/vmcore-dmesg.8 $(VMCORE_DMESG_MANPAGE) +echo:: + @echo "VMCORE_DMESG_SRCS $(VMCORE_DMESG_SRCS)" + @echo "VMCORE_DMESG_DEPS $(VMCORE_DMESG_DEPS)" + @echo "VMCORE_DMESG_OBJS $(VMCORE_DMESG_OBJS)" + diff --git a/vmcore-dmesg/vmcore-dmesg.8 b/vmcore-dmesg/vmcore-dmesg.8 new file mode 100644 index 0000000..d9e3c62 --- /dev/null +++ b/vmcore-dmesg/vmcore-dmesg.8 @@ -0,0 +1,42 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VMCORE-DMESG 8 "Sep 7, 2010" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp <n> insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +vmcore-dmesg \- This is just a placeholder until real man page has been written +.SH SYNOPSIS +.B vmcore-dmesg +.RI " vmcore" +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB<whatever>\fP and +.\" \fI<whatever>\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBvmcore-dmesg\fP extracts the dmesg from a vmcore and write it to +standard out. \fBvmcore-dmesg\fP works against either +\fB/proc/vmcore\fP in a crash dump capture context or a copy +of \fB/proc/vmcore\fP that has been saved for later analysis. A +single build of \fBvmcore-dmesg\fP should work against any linux +vmcore written created on any architecture. + +.\"These programs follow the usual GNU command line syntax, with long +.\"options starting with two dashes (`-'). +.\"A summary of options is included below. +.\"For a complete description, see the Info files. +.SH SEE ALSO +kexec(8) +.SH AUTHOR +vmcore-dmesg was written by Eric Biederman. diff --git a/vmcore-dmesg/vmcore-dmesg.c b/vmcore-dmesg/vmcore-dmesg.c new file mode 100644 index 0000000..7015894 --- /dev/null +++ b/vmcore-dmesg/vmcore-dmesg.c @@ -0,0 +1,504 @@ +#define _XOPEN_SOURCE 600 +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include <endian.h> +#include <byteswap.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <elf.h> + +/* The 32bit and 64bit note headers make it clear we don't care */ +typedef Elf32_Nhdr Elf_Nhdr; + +static const char *fname; +static Elf64_Ehdr ehdr; +static Elf64_Phdr *phdr; + +static char osrelease[4096]; +static loff_t log_buf_vaddr; +static loff_t log_end_vaddr; +static loff_t log_buf_len_vaddr; +static loff_t logged_chars_vaddr; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATANATIVE ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATANATIVE ELFDATA2MSB +#else +#error "Unknown machine endian" +#endif + +static uint16_t file16_to_cpu(uint16_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_16(val); + return val; +} + +static uint32_t file32_to_cpu(uint32_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_32(val); + return val; +} + +static uint64_t file64_to_cpu(uint64_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_64(val); + return val; +} + +static uint64_t vaddr_to_offset(uint64_t vaddr) +{ + /* Just hand the simple case where kexec gets + * the virtual address on the program headers right. + */ + ssize_t i; + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_vaddr > vaddr) + continue; + if ((phdr[i].p_vaddr + phdr[i].p_memsz) <= vaddr) + continue; + return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; + } + fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", + (unsigned long long)vaddr); + exit(30); +} + +static void read_elf32(int fd) +{ + Elf32_Ehdr ehdr32; + Elf32_Phdr *phdr32; + size_t phdrs32_size, phdrs_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); + if (ret != sizeof(ehdr32)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr32.e_type); + ehdr.e_machine = file16_to_cpu(ehdr32.e_machine); + ehdr.e_version = file32_to_cpu(ehdr32.e_version); + ehdr.e_entry = file32_to_cpu(ehdr32.e_entry); + ehdr.e_phoff = file32_to_cpu(ehdr32.e_phoff); + ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr32.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr32.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr32.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr32.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr32.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf32_Phdr)); + exit(12); + } + if (ehdr.e_phnum > ULONG_MAX/sizeof(Elf64_Phdr)) { + fprintf(stderr, "Too many elf program header entries\n"); + exit(13); + } + phdrs32_size = ehdr.e_phnum * sizeof(Elf32_Phdr); + phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); + phdr32 = calloc(ehdr.e_phnum, sizeof(Elf32_Phdr)); + if (!phdr32) { + fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr32, phdrs32_size, ehdr.e_phoff); + if (ret != phdrs32_size) { + fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n", + (unsigned long long)ehdr.e_phoff, phdrs32_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr32[i].p_type); + phdr[i].p_offset = file32_to_cpu(phdr32[i].p_offset); + phdr[i].p_vaddr = file32_to_cpu(phdr32[i].p_vaddr); + phdr[i].p_paddr = file32_to_cpu(phdr32[i].p_paddr); + phdr[i].p_filesz = file32_to_cpu(phdr32[i].p_filesz); + phdr[i].p_memsz = file32_to_cpu(phdr32[i].p_memsz); + phdr[i].p_flags = file32_to_cpu(phdr32[i].p_flags); + phdr[i].p_align = file32_to_cpu(phdr32[i].p_align); + } + free(phdr32); +} + + +static void read_elf64(int fd) +{ + Elf64_Ehdr ehdr64; + Elf64_Phdr *phdr64; + size_t phdrs_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); + if (ret != sizeof(ehdr)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr64.e_type); + ehdr.e_machine = file16_to_cpu(ehdr64.e_machine); + ehdr.e_version = file32_to_cpu(ehdr64.e_version); + ehdr.e_entry = file64_to_cpu(ehdr64.e_entry); + ehdr.e_phoff = file64_to_cpu(ehdr64.e_phoff); + ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr64.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr64.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr64.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr64.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr64.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf64_Phdr)); + exit(12); + } + if (ehdr.e_phnum > ULONG_MAX/sizeof(Elf64_Phdr)) { + fprintf(stderr, "Too many program header entries\n"); + exit(13); + } + phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); + phdr64 = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr64) { + fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr64, phdrs_size, ehdr.e_phoff); + if (ret != phdrs_size) { + fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n", + (unsigned long long)(ehdr.e_phoff), phdrs_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr64[i].p_type); + phdr[i].p_flags = file32_to_cpu(phdr64[i].p_flags); + phdr[i].p_offset = file64_to_cpu(phdr64[i].p_offset); + phdr[i].p_vaddr = file64_to_cpu(phdr64[i].p_vaddr); + phdr[i].p_paddr = file64_to_cpu(phdr64[i].p_paddr); + phdr[i].p_filesz = file64_to_cpu(phdr64[i].p_filesz); + phdr[i].p_memsz = file64_to_cpu(phdr64[i].p_memsz); + phdr[i].p_align = file64_to_cpu(phdr64[i].p_align); + } + free(phdr64); +} + +static void scan_vmcoreinfo(char *start, size_t size) +{ + char *last = start + size - 1; + char *pos, *eol; +#define SYMBOL(sym) { \ + .str = "SYMBOL(" #sym ")=", \ + .name = #sym, \ + .len = sizeof("SYMBOL(" #sym ")=") - 1, \ + .vaddr = & sym ## _vaddr, \ + } + static struct symbol { + const char *str; + const char *name; + size_t len; + loff_t *vaddr; + } symbol[] = { + SYMBOL(log_buf), + SYMBOL(log_end), + SYMBOL(log_buf_len), + SYMBOL(logged_chars), + }; + + for (pos = start; pos <= last; pos = eol + 1) { + size_t len; + int i; + /* Find the end of the current line */ + for (eol = pos; (eol <= last) && (*eol != '\n') ; eol++) + ; + len = eol - pos + 1; + /* Stomp the last character so I am guaranteed a terminating null */ + *eol = '\0'; + /* Copy OSRELEASE if I see it */ + if ((len >= 10) && (memcmp("OSRELEASE=", pos, 10) == 0)) { + size_t to_copy = len - 10; + if (to_copy >= sizeof(osrelease)) + to_copy = sizeof(osrelease) - 1; + memcpy(osrelease, pos + 10, to_copy); + osrelease[to_copy] = '\0'; + } + /* See if the line is mentions a symbol I am looking for */ + for (i = 0; i < sizeof(symbol)/sizeof(symbol[0]); i++ ) { + unsigned long long vaddr; + if (symbol[i].len >= len) + continue; + if (memcmp(symbol[i].str, pos, symbol[i].len) != 0) + continue; + /* Found a symbol now decode it */ + vaddr = strtoull(pos + symbol[i].len, NULL, 16); + /* Remember the virtual address */ + *symbol[i].vaddr = vaddr; + } + } +} + +static void scan_notes(int fd, loff_t start, loff_t lsize) +{ + char *buf, *last, *note, *next; + size_t size; + ssize_t ret; + + if (lsize > LONG_MAX) { + fprintf(stderr, "Unable to handle note section of %llu bytes\n", + (unsigned long long)lsize); + exit(20); + } + size = lsize; + buf = malloc(size); + if (!buf) { + fprintf(stderr, "Cannot malloc %zu bytes\n", size); + exit(21); + } + last = buf + size - 1; + ret = pread(fd, buf, size, start); + if (ret != size) { + fprintf(stderr, "Cannot read note section @ 0x%llx of %zu bytes: %s\n", + (unsigned long long)start, size, strerror(errno)); + exit(22); + } + + for (note = buf; (note + sizeof(Elf_Nhdr)) < last; note = next) + { + Elf_Nhdr *hdr; + char *n_name, *n_desc; + size_t n_namesz, n_descsz, n_type; + + hdr = (Elf_Nhdr *)note; + n_namesz = file32_to_cpu(hdr->n_namesz); + n_descsz = file32_to_cpu(hdr->n_descsz); + n_type = file32_to_cpu(hdr->n_type); + + n_name = note + sizeof(*hdr); + n_desc = n_name + ((n_namesz + 3) & ~3); + next = n_desc + ((n_descsz + 3) & ~3); + + if (next > (last + 1)) + break; + + if ((memcmp(n_name, "VMCOREINFO", 11) != 0) || (n_type != 0)) + continue; + scan_vmcoreinfo(n_desc, n_descsz); + } + free(buf); +} + +static void scan_note_headers(int fd) +{ + int i; + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + scan_notes(fd, phdr[i].p_offset, phdr[i].p_filesz); + } +} + +static uint64_t read_file_pointer(int fd, uint64_t addr) +{ + uint64_t result; + ssize_t ret; + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + uint64_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file64_to_cpu(scratch); + } else { + uint32_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file32_to_cpu(scratch); + } + return result; +} + +static uint32_t read_file_u32(int fd, uint64_t addr) +{ + uint32_t scratch; + ssize_t ret; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read value @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(41); + } + return file32_to_cpu(scratch); +} + +static int32_t read_file_s32(int fd, uint64_t addr) +{ + return read_file_u32(fd, addr); +} + +static void dump_dmesg(int fd) +{ + uint64_t log_buf, log_buf_offset; + unsigned log_end, logged_chars, log_end_wrapped; + int log_buf_len, to_wrap; + char *buf; + ssize_t ret; + + if (!log_buf_vaddr) { + fprintf(stderr, "Missing the log_buf symbol\n"); + exit(50); + } + if (!log_end_vaddr) { + fprintf(stderr, "Missing the log_end symbol\n"); + exit(51); + } + if (!log_buf_len_vaddr) { + fprintf(stderr, "Missing the log_bug_len symbol\n"); + exit(52); + } + if (!logged_chars_vaddr) { + fprintf(stderr, "Missing the logged_chars symbol\n"); + exit(53); + } + + + log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); + log_end = read_file_u32(fd, vaddr_to_offset(log_end_vaddr)); + log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); + logged_chars = read_file_u32(fd, vaddr_to_offset(logged_chars_vaddr)); + + log_buf_offset = vaddr_to_offset(log_buf); + + buf = calloc(1, log_buf_len); + if (!buf) { + fprintf(stderr, "Failed to malloc %d bytes for the logbuf: %s\n", + log_buf_len, strerror(errno)); + exit(51); + } + + log_end_wrapped = log_end % log_buf_len; + to_wrap = log_buf_len - log_end_wrapped; + + ret = pread(fd, buf, to_wrap, log_buf_offset + log_end_wrapped); + if (ret != to_wrap) { + fprintf(stderr, "Failed to read the first half of the log buffer: %s\n", + strerror(errno)); + exit(52); + } + ret = pread(fd, buf + to_wrap, log_end_wrapped, log_buf_offset); + if (ret != log_end_wrapped) { + fprintf(stderr, "Faield to read the second half of the log buffer: %s\n", + strerror(errno)); + exit(53); + } + ret = write(STDOUT_FILENO, buf + (log_buf_len - logged_chars), logged_chars); + if (ret != logged_chars) { + fprintf(stderr, "Failed to write out the dmesg log buffer!: %s\n", + strerror(errno)); + exit(54); + } +} + +int main(int argc, char **argv) +{ + ssize_t ret; + int fd; + + if (argc != 2) { + fprintf(stderr, "usage: %s <kernel core file>\n", argv[0]); + return 1; + } + fname = argv[1]; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open %s: %s\n", + fname, strerror(errno)); + return 2; + } + ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); + if (ret != EI_NIDENT) { + fprintf(stderr, "Read of e_ident from %s failed: %s\n", + fname, strerror(errno)); + return 3; + } + if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "Missing elf signature\n"); + return 4; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "Bad elf version\n"); + return 5; + } + if ((ehdr.e_ident[EI_CLASS] != ELFCLASS32) && + (ehdr.e_ident[EI_CLASS] != ELFCLASS64)) + { + fprintf(stderr, "Unknown elf class %u\n", + ehdr.e_ident[EI_CLASS]); + return 6; + } + if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && + (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) + { + fprintf(stderr, "Unkown elf data order %u\n", + ehdr.e_ident[EI_DATA]); + return 7; + } + if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) + read_elf32(fd); + else + read_elf64(fd); + + scan_note_headers(fd); + dump_dmesg(fd); + close(fd); + + return 0; +} |