summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Horman <horms@verge.net.au>2010-09-09 17:10:25 +0900
committerSimon Horman <horms@verge.net.au>2010-09-09 17:10:25 +0900
commit9e58e01336a3f387c704b9c3c3b0e213013c20c9 (patch)
tree7da81eaa3108fee02caa65aba262bbf62e5d8846
parent62e9b42c82b6779a2802ff69460db93d406669ec (diff)
parentbec38dd6ae9bde528d8723b9e88024e121424ae4 (diff)
Merge branch 'master' of ../kexec-tools-devel
-rw-r--r--Makefile.in8
-rw-r--r--kexec/Makefile3
-rw-r--r--kexec/arch/i386/crashdump-x86.c333
-rw-r--r--kexec/arch/i386/crashdump-x86.h17
-rw-r--r--kexec/arch/i386/kexec-bzImage.c4
-rw-r--r--kexec/arch/i386/kexec-elf-x86.c2
-rw-r--r--kexec/arch/ia64/crashdump-ia64.c6
-rw-r--r--kexec/arch/x86_64/Makefile8
-rw-r--r--kexec/arch/x86_64/crashdump-x86_64.c684
-rw-r--r--kexec/arch/x86_64/crashdump-x86_64.h28
-rw-r--r--kexec/arch/x86_64/kexec-elf-x86_64.c2
-rw-r--r--kexec/arch/x86_64/kexec-x86_64.c11
-rw-r--r--kexec/arch_init.c4
-rw-r--r--kexec/crashdump-elf.c14
-rw-r--r--kexec/crashdump.h3
-rw-r--r--kexec/kernel_version.c (renamed from kexec/arch/x86_64/arch_init.c)31
-rw-r--r--kexec/kexec-elf.c14
-rw-r--r--kexec/kexec-elf.h44
-rw-r--r--kexec/kexec.c2
-rw-r--r--kexec/kexec.h9
-rw-r--r--vmcore-dmesg/Makefile29
-rw-r--r--vmcore-dmesg/vmcore-dmesg.842
-rw-r--r--vmcore-dmesg/vmcore-dmesg.c504
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;
+}