kexec-tools/kexec-tools-1.101-kdump.patch

5944 lines
183 KiB
Diff

diff -urNp -X dontdiff kexec-tools-1.101/configure kexec-tools-1.101-kdump/configure
--- kexec-tools-1.101/configure 2005-02-16 18:07:44.000000000 +0530
+++ kexec-tools-1.101-kdump/configure 2006-02-22 11:30:09.912285648 +0530
@@ -1384,12 +1384,18 @@ case $host_cpu in
powerpc )
host_cpu="ppc"
;;
+ powerpc64 )
+ host_cpu="ppc64"
+ ;;
+ s390x )
+ host_cpu="s390"
+ ;;
* )
host_cpu="$host_cpu"
;;
esac
case $host_cpu in
- i386|ppc|x86_64|alpha|ppc64|ia64)
+ i386|ppc|x86_64|alpha|ppc64|ia64|s390)
;;
* )
{ { echo "$as_me:$LINENO: error: unsupported architecture $host_cpu" >&5
@@ -1406,6 +1412,12 @@ if test "${host_alias}" ; then
fi
EXTRA_CFLAGS=""
+# Check whether ppc64. Add -m64 for building 64-bit binary
+# Add -mcall-aixdesc to generate dot-symbols as in gcc 3.3.3
+if test "$ARCH" = ppc64; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -m64 -mcall-aixdesc"
+fi;
+
# Check whether --with-objdir or --without-objdir was given.
if test "${with_objdir+set}" = set; then
withval="$with_objdir"
@@ -1421,7 +1433,6 @@ if test "${with_gamecube+set}" = set; th
EXTRA_CFLAGS="$EXTRA_CFLAGS -DCONFIG_GAMECUBE=1"
fi;
-
# Check whether --with-zlib or --without-zlib was given.
if test "${with_zlib+set}" = set; then
withval="$with_zlib"
diff -urNp -X dontdiff kexec-tools-1.101/configure.ac kexec-tools-1.101-kdump/configure.ac
--- kexec-tools-1.101/configure.ac 2005-01-09 07:06:57.000000000 +0530
+++ kexec-tools-1.101-kdump/configure.ac 2006-02-22 11:30:08.809453304 +0530
@@ -25,12 +25,18 @@ case $host_cpu in
powerpc )
host_cpu="ppc"
;;
+ powerpc64 )
+ host_cpu="ppc64"
+ ;;
+ s390x )
+ host_cpu="s390"
+ ;;
* )
host_cpu="$host_cpu"
;;
esac
case $host_cpu in
- i386|ppc|x86_64|alpha|ppc64|ia64)
+ i386|ppc|x86_64|alpha|ppc64|ia64|s390)
;;
* )
AC_MSG_ERROR([ unsupported architecture $host_cpu])
@@ -45,6 +51,13 @@ if test "${host_alias}" ; then
OBJDIR="$OBJDIR-${host_alias}"
fi
EXTRA_CFLAGS=""
+
+# Check whether ppc64. Add -m64 for building 64-bit binary
+# Add -mcall-aixdesc to generate dot-symbols as in gcc 3.3.3
+if test "$ARCH" = ppc64; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -m64 -mcall-aixdesc"
+fi;
+
AC_ARG_WITH([objdir], AC_HELP_STRING([--with-objdir=<dir>],[select directory for object files]),
[ OBJDIR="$withval" ], [ OBJDIR="$OBJDIR" ])
diff -urNp -X dontdiff kexec-tools-1.101/kdump/kdump.c kexec-tools-1.101-kdump/kdump/kdump.c
--- kexec-tools-1.101/kdump/kdump.c 2005-02-06 07:28:15.000000000 +0530
+++ kexec-tools-1.101-kdump/kdump/kdump.c 2006-02-22 11:30:11.080108112 +0530
@@ -54,7 +54,7 @@ static void *xmalloc(size_t size)
result = malloc(size);
if (result == NULL) {
fprintf(stderr, "malloc of %u bytes failed: %s\n",
- size, strerror(errno));
+ (unsigned int)size, strerror(errno));
exit(7);
}
return result;
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/crashdump-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.c
--- kexec-tools-1.101/kexec/arch/i386/crashdump-x86.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.c 2006-02-22 11:30:01.525560624 +0530
@@ -0,0 +1,728 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Vivek Goyal (vgoyal@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <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.h"
+#include "crashdump-x86.h"
+#include <x86/x86-linux.h>
+
+extern struct arch_options_t arch_options;
+
+/* Forward Declaration. */
+static int exclude_crash_reserve_region(int *nr_ranges);
+
+/* 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)
+{
+ const char iomem[]= "/proc/iomem";
+ int memory_ranges = 0;
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+
+ 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 */
+ 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;
+#if 0
+ 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 {
+ continue;
+ }
+
+ /* First 640K already registered */
+ if (start >= 0x00000000 && 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++;
+
+ /* Segregate linearly mapped region. */
+ if ((MAXMEM - 1) >= start && (MAXMEM - 1) <= end) {
+ crash_memory_range[memory_ranges-1].end = MAXMEM -1;
+
+ /* Add segregated region. */
+ crash_memory_range[memory_ranges].start = MAXMEM;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+ if (exclude_crash_reserve_region(&memory_ranges) < 0)
+ return -1;
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+#if 0
+ 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_crash_reserve_region(int *nr_ranges)
+{
+ int i, j, tidx = -1;
+ unsigned long long cstart, cend;
+ struct memory_range temp_region;
+
+ /* 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) {
+ /* Split memory region */
+ crash_memory_range[i].end = cstart - 1;
+ temp_region.start = cend + 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
+ crash_memory_range[i].start = cend + 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;
+#if 0
+ 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;
+
+ /* 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;
+ }
+#if 0
+ 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, 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);
+ }
+
+#if 0
+ 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);
+#if 0
+ printf("Command line after adding elfcorehdr\n");
+ printf("%s\n", cmdline);
+#endif
+ return 0;
+}
+
+
+/*
+ * This routine is specific to i386 architecture to maintain the
+ * backward compatibility, other architectures can use the per
+ * cpu version get_crash_notes_per_cpu() directly.
+ */
+static int get_crash_notes(int cpu, uint64_t *addr)
+{
+ char crash_notes[PATH_MAX];
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long vaddr;
+ int count;
+
+ sprintf(crash_notes, "/sys/kernel/crash_notes");
+ fp = fopen(crash_notes, "r");
+ if (fp) {
+ if (fgets(line, sizeof(line), fp) != 0) {
+ count = sscanf(line, "%lx", &vaddr);
+ if (count != 1)
+ die("Cannot parse %s: %s\n", crash_notes,
+ strerror(errno));
+ }
+ *addr = __pa(vaddr + (cpu * MAX_NOTE_BYTES));
+#if 0
+ printf("crash_notes addr = %Lx\n", *addr);
+#endif
+ return 0;
+ } else
+ return get_crash_notes_per_cpu(cpu, addr);
+}
+
+/* Prepares the crash memory elf64 headers and stores in supplied buffer. */
+static int prepare_crash_memory_elf64_headers(struct kexec_info *info,
+ void *buf, unsigned long size)
+{
+ Elf64_Ehdr *elf;
+ Elf64_Phdr *phdr;
+ int i;
+ char *bufp;
+ long int nr_cpus = 0;
+ uint64_t notes_addr;
+
+ bufp = (char*) buf;
+
+ /* Setup ELF Header*/
+ elf = (Elf64_Ehdr *) bufp;
+ bufp += sizeof(Elf64_Ehdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELFCLASS64;
+ elf->e_ident[EI_DATA] = ELFDATA2LSB;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = EM_386;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(Elf64_Ehdr);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(Elf64_Ehdr);
+ elf->e_phentsize= sizeof(Elf64_Phdr);
+ elf->e_phnum = 0;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* PT_NOTE program headers. One per cpu*/
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ return -1;
+ }
+
+ for (i = 0; i < nr_cpus; i++) {
+ if (get_crash_notes(i, &notes_addr) < 0) {
+ /* This cpu is not present. Skip it. */
+ continue;
+ }
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_NOTE;
+ phdr->p_flags = 0;
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_vaddr = 0;
+ phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+
+ /* Setup PT_LOAD type program header for every system RAM chunk.
+ * A seprate program header for Backup Region*/
+ for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) {
+ unsigned long long mstart, mend;
+ if (crash_memory_range[i].type != RANGE_RAM)
+ continue;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (!mstart && !mend)
+ continue;
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ if (mstart == BACKUP_START && mend == BACKUP_END)
+ phdr->p_offset = info->backup_start;
+ else
+ phdr->p_offset = mstart;
+ /* Handle linearly mapped region.*/
+ if (mend <= (MAXMEM - 1))
+ phdr->p_vaddr = mstart + PAGE_OFFSET;
+ else
+ phdr->p_vaddr = -1ULL;
+ phdr->p_paddr = mstart;
+ phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+ return 0;
+}
+
+/* Prepares the crash memory elf32 headers and stores in supplied buffer. */
+static int prepare_crash_memory_elf32_headers(struct kexec_info *info,
+ void *buf, unsigned long size)
+{
+ Elf32_Ehdr *elf;
+ Elf32_Phdr *phdr;
+ int i;
+ char *bufp;
+ long int nr_cpus = 0;
+ uint64_t notes_addr;
+
+ bufp = (char*) buf;
+
+ /* Setup ELF Header*/
+ elf = (Elf32_Ehdr *) bufp;
+ bufp += sizeof(Elf32_Ehdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELFCLASS32;
+ elf->e_ident[EI_DATA] = ELFDATA2LSB;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = EM_386;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(Elf32_Ehdr);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(Elf32_Ehdr);
+ elf->e_phentsize= sizeof(Elf32_Phdr);
+ elf->e_phnum = 0;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* PT_NOTE program headers. One per cpu*/
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ return -1;
+ }
+
+ /* Need to find a better way to determine per cpu notes section size. */
+#define MAX_NOTE_BYTES 1024
+ for (i = 0; i < nr_cpus; i++) {
+ if (get_crash_notes(i, &notes_addr) < 0) {
+ /* This cpu is not present. Skip it. */
+ return -1;
+ }
+ phdr = (Elf32_Phdr *) bufp;
+ bufp += sizeof(Elf32_Phdr);
+ phdr->p_type = PT_NOTE;
+ phdr->p_flags = 0;
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_vaddr = 0;
+ phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+
+ /* Setup PT_LOAD type program header for every system RAM chunk.
+ * A seprate program header for Backup Region*/
+ for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) {
+ unsigned long long mstart, mend;
+ if (crash_memory_range[i].type != RANGE_RAM)
+ continue;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (!mstart && !mend)
+ continue;
+ phdr = (Elf32_Phdr *) bufp;
+ bufp += sizeof(Elf32_Phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ if (mstart == BACKUP_START && mend == BACKUP_END)
+ phdr->p_offset = info->backup_start;
+ else
+ phdr->p_offset = mstart;
+ /* Handle linearly mapped region.*/
+ if (mend <= (MAXMEM - 1))
+ phdr->p_vaddr = mstart + PAGE_OFFSET;
+ else
+ phdr->p_vaddr = UINT_MAX;
+ phdr->p_paddr = mstart;
+ phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+ 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, elfcorehdr;
+ int nr_ranges, align = 1024;
+ long int nr_cpus = 0;
+ struct memory_range *mem_range, *memmap_p;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 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_START, BACKUP_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*/
+ sz = (BACKUP_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. */
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ fprintf(stderr,"kexec_load (elf header segment)"
+ " failed: %s\n", strerror(errno));
+ return -1;
+ }
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ sz = sizeof(Elf64_Ehdr) +
+ nr_cpus * sizeof(Elf64_Phdr) +
+ nr_ranges * sizeof(Elf64_Phdr);
+ } else {
+ sz = sizeof(Elf32_Ehdr) +
+ nr_cpus * sizeof(Elf32_Phdr) +
+ nr_ranges * sizeof(Elf32_Phdr);
+ }
+ sz = (sz + align - 1) & ~(align -1);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (prepare_crash_memory_elf64_headers(info, tmp, sz) < 0)
+ return -1;
+ } else {
+ if (prepare_crash_memory_elf32_headers(info, tmp, sz) < 0)
+ return -1;
+ }
+
+ /* 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.
+ */
+ elfcorehdr = add_buffer(info, tmp, sz, 16*1024, align, min_base,
+ max_addr, 1);
+ if (delete_memmap(memmap_p, elfcorehdr, sz) < 0)
+ return -1;
+ cmdline_add_memmap(mod_cmdline, memmap_p);
+ cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr);
+ return 0;
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/crashdump-x86.h kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.h
--- kexec-tools-1.101/kexec/arch/i386/crashdump-x86.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.h 2006-01-19 11:41:30.000000000 +0530
@@ -0,0 +1,21 @@
+#ifndef CRASHDUMP_X86_H
+#define CRASHDUMP_X86_H
+
+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 __VMALLOC_RESERVE (128 << 20)
+#define MAXMEM (-PAGE_OFFSET-__VMALLOC_RESERVE)
+
+#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_START 0x00000000
+#define BACKUP_END 0x0009ffff
+#define BACKUP_SIZE (BACKUP_END - BACKUP_START + 1)
+
+#endif /* CRASHDUMP_X86_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/include/arch/options.h kexec-tools-1.101-kdump/kexec/arch/i386/include/arch/options.h
--- kexec-tools-1.101/kexec/arch/i386/include/arch/options.h 2004-12-22 02:23:37.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/include/arch/options.h 2006-01-19 11:41:36.000000000 +0530
@@ -6,7 +6,9 @@
#define OPT_SERIAL_BAUD (OPT_MAX+2)
#define OPT_CONSOLE_VGA (OPT_MAX+3)
#define OPT_CONSOLE_SERIAL (OPT_MAX+4)
-#define OPT_ARCH_MAX (OPT_MAX+5)
+#define OPT_ELF32_CORE (OPT_MAX+5)
+#define OPT_ELF64_CORE (OPT_MAX+6)
+#define OPT_ARCH_MAX (OPT_MAX+7)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
@@ -15,6 +17,8 @@
{ "serial-baud", 1, 0, OPT_SERIAL_BAUD }, \
{ "console-vga", 0, 0, OPT_CONSOLE_VGA }, \
{ "console-serial", 0, 0, OPT_CONSOLE_SERIAL }, \
+ { "elf32-core-headers", 0, 0, OPT_ELF32_CORE }, \
+ { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/kexec-bzImage.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-bzImage.c
--- kexec-tools-1.101/kexec/arch/i386/kexec-bzImage.c 2005-01-13 19:02:01.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/kexec-bzImage.c 2006-01-19 11:41:27.000000000 +0530
@@ -214,7 +214,7 @@ int do_bzImage_load(struct kexec_info *i
/* Fill in the information BIOS calls would normally provide. */
if (!real_mode_entry) {
- setup_linux_system_parameters(real_mode);
+ setup_linux_system_parameters(real_mode, info->kexec_flags);
}
return 0;
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/kexec-elf-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-elf-x86.c
--- kexec-tools-1.101/kexec/arch/i386/kexec-elf-x86.c 2005-01-13 19:29:19.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/kexec-elf-x86.c 2006-01-19 11:41:32.000000000 +0530
@@ -32,10 +32,12 @@
#include <elf.h>
#include <x86/x86-linux.h>
#include "../../kexec.h"
+#include "../../kexec-syscall.h"
#include "../../kexec-elf.h"
#include "../../kexec-elf-boot.h"
#include "x86-linux-setup.h"
#include "kexec-x86.h"
+#include "crashdump-x86.h"
#include <arch/options.h>
static const int probe_debug = 0;
@@ -86,7 +88,9 @@ int elf_x86_load(int argc, char **argv,
{
struct mem_ehdr ehdr;
const char *command_line;
+ char *modified_cmdline;
int command_line_len;
+ int modified_cmdline_len;
const char *ramdisk;
unsigned long entry, max_addr;
int arg_style;
@@ -119,6 +123,8 @@ int elf_x86_load(int argc, char **argv,
*/
arg_style = ARG_STYLE_ELF;
command_line = 0;
+ modified_cmdline = 0;
+ modified_cmdline_len = 0;
ramdisk = 0;
while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
@@ -156,6 +162,20 @@ int elf_x86_load(int argc, char **argv,
command_line_len = strlen(command_line) +1;
}
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ modified_cmdline_len = strlen(modified_cmdline);
+ }
+
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len);
@@ -203,10 +223,20 @@ int elf_x86_load(int argc, char **argv,
const unsigned char *ramdisk_buf;
off_t ramdisk_length;
struct entry32_regs regs;
+ int rc = 0;
/* Get the linux parameter header */
hdr = xmalloc(sizeof(*hdr));
- param_base = add_buffer(info, hdr, sizeof(*hdr), sizeof(*hdr),
+
+ /* Hack: With some ld versions, 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 parameter segment to 16K to avoid being placed
+ * in such gaps.
+ * This is a makeshift solution until it is fixed in kernel
+ */
+ param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024,
16, 0, max_addr, 1);
/* Initialize the parameter header */
@@ -216,9 +246,19 @@ int elf_x86_load(int argc, char **argv,
/* Add a ramdisk to the current image */
ramdisk_buf = NULL;
ramdisk_length = 0;
- if (ramdisk) {
- unsigned char *ramdisk_buf;
+ if (ramdisk)
ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ rc = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (rc < 0)
+ return -1;
+ /* Use new command line. */
+ command_line = modified_cmdline;
+ command_line_len = strlen(modified_cmdline) + 1;
}
/* Tell the kernel what is going on */
@@ -228,7 +268,7 @@ int elf_x86_load(int argc, char **argv,
ramdisk_buf, ramdisk_length);
/* Fill in the information bios calls would usually provide */
- setup_linux_system_parameters(&hdr->hdr);
+ setup_linux_system_parameters(&hdr->hdr, info->kexec_flags);
/* Initialize the registers */
elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/kexec-multiboot-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-multiboot-x86.c
--- kexec-tools-1.101/kexec/arch/i386/kexec-multiboot-x86.c 2005-01-25 01:28:04.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/kexec-multiboot-x86.c 2006-01-19 11:41:27.000000000 +0530
@@ -246,7 +246,8 @@ int multiboot_x86_load(int argc, char **
mbi->boot_loader_name = sizeof(*mbi) + command_line_len;
/* Memory map */
- if ((get_memory_ranges(&range, &ranges) < 0) || ranges == 0) {
+ if ((get_memory_ranges(&range, &ranges, info->kexec_flags) < 0)
+ || ranges == 0) {
fprintf(stderr, "Cannot get memory information\n");
return -1;
}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/kexec-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.c
--- kexec-tools-1.101/kexec/arch/i386/kexec-x86.c 2005-02-06 04:54:35.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.c 2006-01-19 18:19:07.000000000 +0530
@@ -30,14 +30,14 @@
#include "../../kexec-elf.h"
#include "../../kexec-syscall.h"
#include "kexec-x86.h"
+#include "crashdump-x86.h"
#include <arch/options.h>
-#define MAX_MEMORY_RANGES 64
-#define MAX_LINE 160
static struct memory_range memory_range[MAX_MEMORY_RANGES];
/* Return a sorted list of memory ranges. */
-int get_memory_ranges(struct memory_range **range, int *ranges)
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
{
const char iomem[]= "/proc/iomem";
int memory_ranges = 0;
@@ -79,6 +79,20 @@ int get_memory_ranges(struct memory_rang
else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
type = RANGE_ACPI_NVS;
}
+ else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+ /* Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported values are
+ * subset of user defined values.
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+ continue;
+ }
else {
continue;
}
@@ -120,21 +134,18 @@ void arch_usage(void)
" --serial-baud=<buad_rate> Specify the serial port baud rate\n"
" --console-vga Enable the vga console\n"
" --console-serial Enable the serial console\n"
+ " --elf32-core-headers Prepare core headers in ELF32 format\n"
+ " --elf64-core-headers Prepare core headers in ELF64 format\n"
);
}
-static struct {
- uint8_t reset_vga;
- uint16_t serial_base;
- uint32_t serial_baud;
- uint8_t console_vga;
- uint8_t console_serial;
-} arch_options = {
+struct arch_options_t arch_options = {
.reset_vga = 0,
.serial_base = 0x3f8,
.serial_baud = 0,
.console_vga = 0,
.console_serial = 0,
+ .core_header_type = CORE_TYPE_ELF64,
};
int arch_process_options(int argc, char **argv)
@@ -198,6 +209,12 @@ int arch_process_options(int argc, char
}
arch_options.serial_baud = value;
break;
+ case OPT_ELF32_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF32;
+ break;
+ case OPT_ELF64_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF64;
+ break;
}
}
/* Reset getopt for the next pass; called in other source modules */
@@ -206,7 +223,7 @@ int arch_process_options(int argc, char
return 0;
}
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+int arch_compat_trampoline(struct kexec_info *info)
{
int result;
struct utsname utsname;
@@ -224,11 +241,11 @@ int arch_compat_trampoline(struct kexec_
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_386 here.
*/
- *flags |= KEXEC_ARCH_DEFAULT;
+ info->kexec_flags |= KEXEC_ARCH_DEFAULT;
}
else if (strcmp(utsname.machine, "x86_64") == 0)
{
- *flags |= KEXEC_ARCH_X86_64;
+ info->kexec_flags |= KEXEC_ARCH_X86_64;
if (!info->rhdr.e_shdr) {
fprintf(stderr,
"A trampoline is required for cross architecture support\n");
@@ -249,6 +266,8 @@ int arch_compat_trampoline(struct kexec_
void arch_update_purgatory(struct kexec_info *info)
{
+ uint8_t panic_kernel = 0;
+
elf_rel_set_symbol(&info->rhdr, "reset_vga",
&arch_options.reset_vga, sizeof(arch_options.reset_vga));
elf_rel_set_symbol(&info->rhdr, "serial_base",
@@ -259,4 +278,11 @@ void arch_update_purgatory(struct kexec_
&arch_options.console_vga, sizeof(arch_options.console_vga));
elf_rel_set_symbol(&info->rhdr, "console_serial",
&arch_options.console_serial, sizeof(arch_options.console_serial));
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ panic_kernel = 1;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &info->backup_start, sizeof(info->backup_start));
+ }
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &panic_kernel, sizeof(panic_kernel));
}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/kexec-x86.h kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.h
--- kexec-tools-1.101/kexec/arch/i386/kexec-x86.h 2005-02-06 04:41:32.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.h 2006-01-19 11:41:29.000000000 +0530
@@ -1,6 +1,10 @@
#ifndef KEXEC_X86_H
#define KEXEC_X86_H
+#define MAX_MEMORY_RANGES 64
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
extern unsigned char compat_x86_64[];
extern uint32_t compat_x86_64_size, compat_x86_64_entry32;
@@ -35,6 +39,15 @@ struct entry16_regs {
uint16_t pad;
};
+struct arch_options_t {
+ uint8_t reset_vga;
+ uint16_t serial_base;
+ uint32_t serial_baud;
+ uint8_t console_vga;
+ uint8_t console_serial;
+ int core_header_type;
+};
+
int multiboot_x86_probe(const char *buf, off_t len);
int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info);
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/Makefile kexec-tools-1.101-kdump/kexec/arch/i386/Makefile
--- kexec-tools-1.101/kexec/arch/i386/Makefile 2005-02-06 04:53:58.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/Makefile 2006-01-19 11:41:29.000000000 +0530
@@ -9,3 +9,4 @@ KEXEC_C_SRCS+= kexec/arch/i386/kexec-mul
KEXEC_C_SRCS+= kexec/arch/i386/kexec-beoboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-nbi.c
KEXEC_C_SRCS+= kexec/arch/i386/x86-linux-setup.c
+KEXEC_C_SRCS+= kexec/arch/i386/crashdump-x86.c
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/x86-linux-setup.c kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.c
--- kexec-tools-1.101/kexec/arch/i386/x86-linux-setup.c 2005-01-13 18:40:01.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.c 2006-02-01 14:41:09.000000000 +0530
@@ -94,7 +94,8 @@ void setup_linux_bootloader_parameters(
cmdline_ptr[cmdline_len - 1] = '\0';
}
-void setup_linux_system_parameters(struct x86_linux_param_header *real_mode)
+void setup_linux_system_parameters(struct x86_linux_param_header *real_mode,
+ unsigned long kexec_flags)
{
/* Fill in information the BIOS would usually provide */
struct memory_range *range;
@@ -135,7 +136,7 @@ void setup_linux_system_parameters(struc
real_mode->aux_device_info = 0;
/* Fill in the memory info */
- if ((get_memory_ranges(&range, &ranges) < 0) || ranges == 0) {
+ if ((get_memory_ranges(&range, &ranges, kexec_flags) < 0) || ranges == 0) {
die("Cannot get memory information\n");
}
if (ranges > E820MAX) {
@@ -164,7 +165,7 @@ void setup_linux_system_parameters(struc
if (range[i].type != RANGE_RAM)
continue;
if ((range[i].start <= 0x100000) && range[i].end > 0x100000) {
- unsigned long long mem_k = (range[i].end >> 10) - 0x100000;
+ unsigned long long mem_k = (range[i].end >> 10) - 0x400;
real_mode->ext_mem_k = mem_k;
real_mode->alt_mem_k = mem_k;
if (mem_k > 0xfc00) {
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/i386/x86-linux-setup.h kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.h
--- kexec-tools-1.101/kexec/arch/i386/x86-linux-setup.h 2004-12-20 17:50:22.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.h 2006-01-19 11:41:27.000000000 +0530
@@ -7,7 +7,8 @@ void setup_linux_bootloader_parameters(
unsigned long real_mode_base, unsigned long cmdline_offset,
const char *cmdline, off_t cmdline_len,
const unsigned char *initrd_buf, off_t initrd_size);
-void setup_linux_system_parameters(struct x86_linux_param_header *real_mode);
+void setup_linux_system_parameters(struct x86_linux_param_header *real_mode,
+ unsigned long kexec_flags);
#define SETUP_BASE 0x90000
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/kexec-ia64.c kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-ia64.c
--- kexec-tools-1.101/kexec/arch/ia64/kexec-ia64.c 2005-01-11 11:58:36.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-ia64.c 2006-01-19 18:19:07.000000000 +0530
@@ -34,11 +34,11 @@
#include <arch/options.h>
#define MAX_MEMORY_RANGES 64
-#define MAX_LINE 160
static struct memory_range memory_range[MAX_MEMORY_RANGES];
/* Return a sorted list of available memory ranges. */
-int get_memory_ranges(struct memory_range **range, int *ranges)
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
{
int memory_ranges;
/*
@@ -103,7 +103,7 @@ int arch_process_options(int argc, char
return 0;
}
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+int arch_compat_trampoline(struct kexec_info *info)
{
int result;
struct utsname utsname;
@@ -115,7 +115,7 @@ int arch_compat_trampoline(struct kexec_
}
if (strcmp(utsname.machine, "ia64") == 0)
{
- *flags |= KEXEC_ARCH_X86_64;
+ info->kexec_flags |= KEXEC_ARCH_X86_64;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
@@ -125,7 +125,7 @@ int arch_compat_trampoline(struct kexec_
return 0;
}
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+int arch_compat_trampoline(struct kexec_info *info)
{
int result;
struct utsname utsname;
@@ -140,7 +140,7 @@ int arch_compat_trampoline(struct kexec_
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here.
*/
- *flags |= KEXEC_ARCH_DEFAULT;
+ info->kexec_flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc/kexec-ppc.c kexec-tools-1.101-kdump/kexec/arch/ppc/kexec-ppc.c
--- kexec-tools-1.101/kexec/arch/ppc/kexec-ppc.c 2005-01-11 11:58:03.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc/kexec-ppc.c 2006-01-19 18:19:07.000000000 +0530
@@ -19,11 +19,11 @@
#include <arch/options.h>
#define MAX_MEMORY_RANGES 64
-#define MAX_LINE 160
static struct memory_range memory_range[MAX_MEMORY_RANGES];
/* Return a sorted list of memory ranges. */
-int get_memory_ranges(struct memory_range **range, int *ranges)
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
{
int memory_ranges = 0;
#ifdef CONFIG_GAMECUBE
@@ -120,7 +120,7 @@ int arch_process_options(int argc, char
return 0;
}
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+int arch_compat_trampoline(struct kexec_info *info)
{
int result;
struct utsname utsname;
@@ -135,7 +135,7 @@ int arch_compat_trampoline(struct kexec_
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_PPC here.
*/
- *flags |= KEXEC_ARCH_DEFAULT;
+ info->kexec_flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/crashdump-ppc64.c kexec-tools-1.101-kdump/kexec/arch/ppc64/crashdump-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/crashdump-ppc64.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/crashdump-ppc64.c 2006-01-19 18:20:07.000000000 +0530
@@ -0,0 +1,511 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: R Sharada (sharada@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <dirent.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-ppc64.h"
+#include "crashdump-ppc64.h"
+
+extern struct arch_options_t arch_options;
+
+/* 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];
+
+/*
+ * Used to save various memory ranges/regions needed for the captured
+ * kernel to boot. (lime memmap= option in other archs)
+ */
+mem_rgns_t usablemem_rgns = {0, };
+
+/* array to store memory regions to be excluded from elf header creation */
+mem_rgns_t exclude_rgns = {0, };
+
+/*
+ * To store the memory size of the first kernel and this value will be
+ * passed to the second kernel as command line (savemaxmem=xM).
+ * The second kernel will be calculated saved_max_pfn based on this
+ * variable.
+ * Since we are creating/using usable-memory property, there is no way
+ * we can determine the RAM size unless parsing the device-tree/memoy@/reg
+ * property in the kernel.
+ */
+unsigned long saved_max_mem = 0;
+
+static int sort_regions(mem_rgns_t *rgn);
+
+/* 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 memory_ranges = 0;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES-1];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int i, n, match;
+ unsigned long long start, end, cstart, cend;
+
+ /* create a separate program header for the backup region */
+ crash_memory_range[0].start = 0x0000000000000000;
+ crash_memory_range[0].end = 0x0000000000008000;
+ crash_memory_range[0].type = RANGE_RAM;
+ memory_ranges++;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ start = ((unsigned long long *)buf)[0];
+ end = start + ((unsigned long long *)buf)[1];
+ if (start == 0 && end >= 0x8000)
+ start = 0x8000;
+ match = 0;
+ sort_regions(&exclude_rgns);
+
+ /* exclude crash reserved regions */
+ for (i = 0; i < exclude_rgns.size; i++) {
+ cstart = exclude_rgns.ranges[i].start;
+ cend = exclude_rgns.ranges[i].end;
+ if (cstart < end && cend > start) {
+ if ((cstart == start) && (cend == end)) {
+ match = 1;
+ continue;
+ }
+ if (start < cstart && end > cend) {
+ match = 1;
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = cstart - 1;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ crash_memory_range[memory_ranges].start = cend + 1;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ break;
+ } else if (start < cstart) {
+ match = 1;
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = cstart - 1;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ end = cstart - 1;
+ continue;
+ } else if (end > cend){
+ match = 1;
+ crash_memory_range[memory_ranges].start = cend + 1;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ start = cend + 1;
+ continue;
+ }
+ }
+
+ } /* end of for loop */
+ if (!match) {
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+ }
+
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+
+ /*
+ * Can not trust the memory regions order that we read from
+ * device-tree. Hence, get the MAX end value.
+ */
+ for (i = 0; i < memory_ranges; i++)
+ if (saved_max_mem < crash_memory_range[i].end)
+ saved_max_mem = crash_memory_range[i].end;
+
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+#if DEBUG
+ int i;
+ printf("CRASH MEMORY RANGES\n");
+ for(i = 0; i < *ranges; i++) {
+ start = crash_memory_range[i].start;
+ end = crash_memory_range[i].end;
+ fprintf(stderr, "%016Lx-%016Lx\n", start, end);
+ }
+#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;
+ }
+}
+
+static int add_cmdline_param(char *cmdline, unsigned long addr,
+ char *cmdstr, char *byte)
+{
+ int cmdlen, len, align = 1024;
+ char str[COMMAND_LINE_SIZE], *ptr;
+
+ /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/
+ switch (byte[0]) {
+ case 'K':
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ break;
+ case 'M':
+ addr = addr/(align *align);
+ break;
+ }
+ ptr = str;
+ strcpy(str, cmdstr);
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ strcat(str, byte);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+#if DEBUG
+ fprintf(stderr, "Command line after adding elfcorehdr: %s\n", cmdline);
+#endif
+ return 0;
+}
+
+/* Prepares the crash memory elf64 headers and stores in supplied buffer. */
+static int prepare_crash_memory_elf64_headers(struct kexec_info *info,
+ void *buf, unsigned long size)
+{
+ Elf64_Ehdr *elf;
+ Elf64_Phdr *phdr;
+ int i;
+ char *bufp;
+ long int nr_cpus = 0;
+ unsigned long notes_addr;
+
+ bufp = (char*) buf;
+
+ /* Setup ELF Header*/
+ elf = (Elf64_Ehdr *) bufp;
+ bufp += sizeof(Elf64_Ehdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELFCLASS64;
+ elf->e_ident[EI_DATA] = ELFDATA2MSB;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = EM_PPC64;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(Elf64_Ehdr);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(Elf64_Ehdr);
+ elf->e_phentsize= sizeof(Elf64_Phdr);
+ elf->e_phnum = 0;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* PT_NOTE program headers. One per cpu*/
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0)
+ return -1;
+
+ /* Need to find a better way to determine per cpu notes section size. */
+#define MAX_NOTE_BYTES 1024
+ for (i = 0; i < nr_cpus; i++) {
+ if (get_crash_notes_per_cpu(i, &notes_addr) < 0) {
+ /* This cpu is not present. Skip it. */
+ continue;
+ }
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_NOTE;
+ phdr->p_flags = 0;
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_vaddr = 0;
+ phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+
+ /* Setup PT_LOAD type program header for every system RAM chunk.
+ * A seprate program header for Backup Region
+ */
+ for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) {
+ unsigned long long mstart, mend;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (!mstart && !mend)
+ break;
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ if (mstart == BACKUP_START && mend == BACKUP_END)
+ phdr->p_offset = info->backup_start;
+ else
+ phdr->p_offset = mstart;
+ /* Handle linearly mapped region.*/
+ if (mend <= (MAXMEM - 1))
+ phdr->p_vaddr = mstart + PAGE_OFFSET;
+ else
+ phdr->p_vaddr = -1ULL;
+ phdr->p_paddr = mstart;
+ phdr->p_filesz = phdr->p_memsz = mend - mstart;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+ 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, elfcorehdr;
+ int nr_ranges, align = 1024;
+ long int nr_cpus = 0;
+ struct memory_range *mem_range;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ /* Create a backup region segment to store backup data*/
+ sz = (BACKUP_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);
+ reserve(info->backup_start, sz);
+ /* Create elf header segment and store crash image data. */
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ fprintf(stderr,"kexec_load (elf header segment)"
+ " failed: %s\n", strerror(errno));
+ return -1;
+ }
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ sz = sizeof(Elf64_Ehdr) +
+ nr_cpus * sizeof(Elf64_Phdr) +
+ nr_ranges * sizeof(Elf64_Phdr);
+ } else {
+ sz = sizeof(Elf32_Ehdr) +
+ nr_cpus * sizeof(Elf32_Phdr) +
+ nr_ranges * sizeof(Elf32_Phdr);
+ }
+ sz = (sz + align - 1) & ~(align -1);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (prepare_crash_memory_elf64_headers(info, tmp, sz) < 0)
+ return -1;
+ }
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, align, min_base,
+ max_addr, 1);
+ reserve(elfcorehdr, sz);
+ /* modify and store the cmdline in a global array. This is later
+ * read by flatten_device_tree and modified if required
+ */
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ add_cmdline_param(mod_cmdline, saved_max_mem, " savemaxmem=", "M");
+ return 0;
+}
+
+/*
+ * Used to save various memory regions needed for the captured kernel.
+ */
+
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size)
+{
+ int i;
+ unsigned long long end = base + size;
+ unsigned long long ustart, uend;
+
+ base = _ALIGN_DOWN(base, PAGE_SIZE);
+ end = _ALIGN_UP(end, PAGE_SIZE);
+
+ for (i=0; i < usablemem_rgns.size; i++) {
+ ustart = usablemem_rgns.ranges[i].start;
+ uend = usablemem_rgns.ranges[i].end;
+ if (base < uend && end > ustart) {
+ if ((base >= ustart) && (end <= uend))
+ return;
+ if (base < ustart && end > uend) {
+ usablemem_rgns.ranges[i].start = base;
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ } else if (base < ustart) {
+ usablemem_rgns.ranges[i].start = base;
+ return;
+ } else if (end > uend){
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ }
+ }
+ }
+ usablemem_rgns.ranges[usablemem_rgns.size].start = base;
+ usablemem_rgns.ranges[usablemem_rgns.size++].end = end;
+
+#ifdef DEBUG
+ fprintf(stderr, "usable memory rgns size:%d base:%lx size:%lx\n", usablemem_rgns.size, base, size);
+#endif
+}
+
+/*
+ * Used to exclude various memory regions that do not need elf hdr generation
+ */
+
+void add_exclude_rgns(unsigned long long base, unsigned long long size)
+{
+ int i;
+ unsigned long long end = base + size;
+ unsigned long long xstart, xend;
+
+ for (i=0; i < exclude_rgns.size; i++) {
+ xstart = exclude_rgns.ranges[i].start;
+ xend = exclude_rgns.ranges[i].end;
+ if (base < xend && end > xstart) {
+ if ((base >= xstart) && (end <= xend))
+ return;
+ if (base < xstart && end > xend) {
+ exclude_rgns.ranges[i].start = base;
+ exclude_rgns.ranges[i].end = end;
+ return;
+ } else if (base < xstart) {
+ exclude_rgns.ranges[i].start = base;
+ exclude_rgns.ranges[i].end = xend;
+ return;
+ } else if (end > xend){
+ exclude_rgns.ranges[i].start = xstart;
+ exclude_rgns.ranges[i].end = end;
+ return;
+ }
+ }
+ }
+ exclude_rgns.ranges[exclude_rgns.size].start = base;
+ exclude_rgns.ranges[exclude_rgns.size++].end = end;
+
+#ifdef DEBUG
+ fprintf(stderr, "exclude rgns size:%d base:%lx end:%lx size:%lx\n", exclude_rgns.size, base, end, size);
+#endif
+}
+
+static int sort_regions(mem_rgns_t *rgn)
+{
+ int i, j;
+ unsigned long long tstart, tend;
+ for (i = 0; i < rgn->size; i++) {
+ for (j = 0; j < rgn->size - i - 1; j++) {
+ if (rgn->ranges[j].start > rgn->ranges[j+1].start) {
+ tstart = rgn->ranges[j].start;
+ tend = rgn->ranges[j].end;
+ rgn->ranges[j].start = rgn->ranges[j+1].start;
+ rgn->ranges[j].end = rgn->ranges[j+1].end;
+ rgn->ranges[j+1].start = tstart;
+ rgn->ranges[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+
+}
+
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/crashdump-ppc64.h kexec-tools-1.101-kdump/kexec/arch/ppc64/crashdump-ppc64.h
--- kexec-tools-1.101/kexec/arch/ppc64/crashdump-ppc64.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/crashdump-ppc64.h 2006-02-22 11:30:11.080108112 +0530
@@ -0,0 +1,35 @@
+#ifndef CRASHDUMP_PPC64_H
+#define CRASHDUMP_PPC64_H
+
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+void add_exclude_rgns(unsigned long long base, unsigned long long size);
+
+#define PAGE_OFFSET 0xC000000000000000
+#define KERNELBASE PAGE_OFFSET
+#define VMALLOCBASE 0xD000000000000000
+
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+
+#define MAXMEM (-KERNELBASE-VMALLOCBASE)
+
+#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 6)
+
+#define COMMAND_LINE_SIZE 512 /* from kernel */
+/* Backup Region, First 32K of System RAM. */
+#define BACKUP_START 0x0000
+#define BACKUP_END 0x8000
+#define BACKUP_SIZE (BACKUP_END - BACKUP_START + 1)
+
+#define KDUMP_BACKUP_LIMIT 0x8000
+#define _ALIGN_UP(addr,size) (((addr)+((size)-1))&(~((size)-1)))
+#define _ALIGN_DOWN(addr,size) ((addr)&(~((size)-1)))
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+extern unsigned long long crash_base;
+extern unsigned long long crash_size;
+
+#endif /* CRASHDUMP_PPC64_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/fs2dt.c kexec-tools-1.101-kdump/kexec/arch/ppc64/fs2dt.c
--- kexec-tools-1.101/kexec/arch/ppc64/fs2dt.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/fs2dt.c 2006-02-22 11:41:54.993097072 +0530
@@ -0,0 +1,462 @@
+/*
+ * fs2dt: creates a flattened device-tree
+ *
+ * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "../../kexec.h"
+#include "kexec-ppc64.h"
+#include "crashdump-ppc64.h"
+
+#define MAXPATH 1024 /* max path name length */
+#define NAMESPACE 16384 /* max bytes for property names */
+#define TREEWORDS 65536 /* max 32 bit words for property values */
+#define MEMRESERVE 256 /* max number of reserved memory blocks */
+
+enum {
+ ERR_NONE,
+ ERR_USAGE,
+ ERR_OPENDIR,
+ ERR_READDIR,
+ ERR_STAT,
+ ERR_OPEN,
+ ERR_READ,
+ ERR_RESERVE,
+};
+
+void err(const char *str, int rc)
+{
+ if (errno)
+ perror(str);
+ else
+ fprintf(stderr, "%s: unrecoverable error\n", str);
+ exit(rc);
+}
+
+typedef unsigned dvt;
+struct stat statbuf[1];
+char pathname[MAXPATH], *pathstart;
+char propnames[NAMESPACE];
+dvt dtstruct[TREEWORDS], *dt;
+unsigned long long mem_rsrv[2*MEMRESERVE];
+
+static int initrd_found = 0;
+static int crash_param = 0;
+char local_cmdline[COMMAND_LINE_SIZE] = { "" };
+dvt *dt_len; /* changed len of modified cmdline in flat device-tree */
+extern mem_rgns_t usablemem_rgns;
+struct bootblock bb[1];
+
+void reserve(unsigned long long where, unsigned long long length)
+{
+ unsigned long long *mr;
+
+ mr = mem_rsrv;
+
+ while(mr[1])
+ mr += 2;
+
+ mr[0] = where;
+ mr[1] = length;
+}
+
+/* look for properties we need to reserve memory space for */
+void checkprop(char *name, dvt *data)
+{
+ static unsigned long long base, size, end;
+
+ if ((data == NULL) && (base || size || end))
+ err((void *)data, ERR_RESERVE);
+ else if (!strcmp(name, "linux,rtas-base"))
+ base = *data;
+ else if (!strcmp(name, "linux,tce-base"))
+ base = *(unsigned long long *) data;
+ else if (!strcmp(name, "rtas-size") ||
+ !strcmp(name, "linux,tce-size"))
+ size = *data;
+
+ if (size && end)
+ err(name, ERR_RESERVE);
+ if (base && size) {
+ reserve(base, size);
+ base = size = 0;
+ }
+ if (base && end) {
+ reserve(base, end-base);
+ base = end = 0;
+ }
+}
+
+/*
+ * return the property index for a property name, creating a new one
+ * if needed.
+ */
+dvt propnum(const char *name)
+{
+ dvt offset = 0;
+
+ while(propnames[offset])
+ if (strcmp(name, propnames+offset))
+ offset += strlen(propnames+offset)+1;
+ else
+ return offset;
+
+ strcpy(propnames+offset, name);
+
+ return offset;
+}
+
+void add_usable_mem_property(int fd, int len)
+{
+ char fname[MAXPATH], *bname;
+ char buf[MAXBYTES +1];
+ unsigned long ranges[2*MAX_MEMORY_RANGES];
+ unsigned long long base, end, loc_base, loc_end;
+ int range, rlen = 0;
+
+ strcpy(fname, pathname);
+ bname = strrchr(fname,'/');
+ bname[0] = '\0';
+ bname = strrchr(fname,'/');
+ if (strncmp(bname, "/memory@", 8))
+ return;
+
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, buf, len) != len)
+ err(pathname, ERR_READ);
+
+ base = ((unsigned long long *)buf)[0];
+ end = base + ((unsigned long long *)buf)[1];
+
+ for (range = 0; range < usablemem_rgns.size; range++) {
+ loc_base = usablemem_rgns.ranges[range].start;
+ loc_end = usablemem_rgns.ranges[range].end;
+ if (loc_base >= base && loc_end <= end) {
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ } else if (base < loc_end && end > loc_base) {
+ if (loc_base < base)
+ loc_base = base;
+ if (loc_end > end)
+ loc_end = end;
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ }
+ }
+
+ if (!rlen) {
+ /*
+ * User did not pass any ranges for thsi region. Hence, write
+ * (0,0) duple in linux,usable-memory property such that
+ * this region will be ignored.
+ */
+ ranges[rlen++] = 0;
+ ranges[rlen++] = 0;
+ }
+
+ rlen = rlen * sizeof(unsigned long);
+ /*
+ * No add linux,usable-memory property.
+ */
+ *dt++ = 3;
+ *dt++ = rlen;
+ *dt++ = propnum("linux,usable-memory");
+ if ((rlen >= 8) && ((unsigned long)dt & 0x4))
+ dt++;
+ memcpy(dt,&ranges,rlen);
+ dt += (rlen + 3)/4;
+}
+
+/* put all properties (files) in the property structure */
+void putprops(char *fn, struct dirent **nlist, int numlist)
+{
+ struct dirent *dp;
+ int i = 0;
+
+ for (i = 0; i < numlist; i++) {
+ dp = nlist[i];
+ strcpy(fn, dp->d_name);
+
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+
+ if (lstat(pathname, statbuf))
+ err(pathname, ERR_STAT);
+
+ if (!crash_param && !strcmp(fn,"linux,crashkernel-base"))
+ continue;
+
+ if (!crash_param && !strcmp(fn,"linux,crashkernel-size"))
+ continue;
+
+ /*
+ * This property will be created for each node during kexec
+ * boot. So, ignore it.
+ */
+ if (!strcmp(dp->d_name, "linux,pci-domain") ||
+ !strcmp(dp->d_name, "linux,htab-base") ||
+ !strcmp(dp->d_name, "linux,htab-size") ||
+ !strcmp(dp->d_name, "linux,kernel-end"))
+ continue;
+
+ /* This property will be created/modified later in putnode()
+ * So ignore it.
+ */
+ if (!strcmp(dp->d_name, "linux,initrd-start") ||
+ !strcmp(dp->d_name, "linux,initrd-end"))
+ continue;
+
+ if (S_ISREG(statbuf[0].st_mode)) {
+ int fd, len = statbuf[0].st_size;
+
+ *dt++ = 3;
+ dt_len = dt;
+ *dt++ = len;
+ *dt++ = propnum(fn);
+
+ if ((len >= 8) && ((unsigned long)dt & 0x4))
+ dt++;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd == -1)
+ err(pathname, ERR_OPEN);
+
+ if (read(fd, dt, len) != len)
+ err(pathname, ERR_READ);
+
+ checkprop(fn, dt);
+
+ /* Get the cmdline from the device-tree and modify it */
+ if (!strcmp(dp->d_name, "bootargs")) {
+ int cmd_len;
+ char temp_cmdline[COMMAND_LINE_SIZE] = { "" };
+ char *param = NULL;
+ cmd_len = strlen(local_cmdline);
+ if (cmd_len != 0) {
+ param = strstr(local_cmdline,
+ "crashkernel=");
+ if (param)
+ crash_param = 1;
+ param = strstr(local_cmdline, "root=");
+ }
+ if (!param) {
+ char *old_param;
+ memcpy(temp_cmdline, dt, len);
+ param = strstr(temp_cmdline, "root=");
+ old_param = strtok(param, " ");
+ if (cmd_len != 0)
+ strcat(local_cmdline, " ");
+ strcat(local_cmdline, old_param);
+ }
+ strcat(local_cmdline, " ");
+ cmd_len = strlen(local_cmdline);
+ cmd_len = cmd_len + 1;
+ memcpy(dt,local_cmdline,cmd_len);
+ len = cmd_len;
+ *dt_len = cmd_len;
+ fprintf(stderr, "Modified cmdline:%s\n", local_cmdline);
+ }
+
+ dt += (len + 3)/4;
+ if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size)
+ add_usable_mem_property(fd, len);
+ close(fd);
+ }
+ }
+ fn[0] = '\0';
+ if(errno == ENOSYS)
+ errno = 0;
+ if (errno)
+ err(pathname, ERR_READDIR);
+ checkprop(pathname, NULL);
+}
+
+/*
+ * Compare function used to sort the device-tree directories
+ * This function will be passed to scandir.
+ */
+int comparefunc(const void *dentry1, const void *dentry2)
+{
+ char *str1 = (*(struct dirent **)dentry1)->d_name;
+ char *str2 = (*(struct dirent **)dentry2)->d_name;
+
+ /*
+ * strcmp scans from left to right and fails to idetify for some
+ * strings such as memory@10000000 and memory@f000000.
+ * Therefore, we get the wrong sorted order like memory@10000000 and
+ * memory@f000000.
+ */
+ if (strchr(str1, '@') && strchr(str2, '@') &&
+ (strlen(str1) > strlen(str2)))
+ return 1;
+
+ return strcmp(str1, str2);
+}
+
+/*
+ * put a node (directory) in the property structure. first properties
+ * then children.
+ */
+void putnode(void)
+{
+ char *dn;
+ struct dirent *dp;
+ char *basename;
+ struct dirent **namelist;
+ int numlist, i;
+
+ *dt++ = 1;
+ strcpy((void *)dt, *pathstart ? pathstart : "/");
+ while(*dt)
+ dt++;
+ if (dt[-1] & 0xff)
+ dt++;
+
+ numlist = scandir(pathname, &namelist, 0, comparefunc);
+ if (numlist == 0)
+ err(pathname, ERR_OPENDIR);
+
+ basename = strrchr(pathname,'/');
+
+ strcat(pathname, "/");
+ dn = pathname + strlen(pathname);
+
+ putprops(dn, namelist, numlist);
+
+ /* Add initrd entries to the second kernel if first kernel does not
+ * have and second kernel needs.
+ */
+ if (initrd_base && !initrd_found && !strcmp(basename,"/chosen/")) {
+ int len = 8;
+ unsigned long long initrd_end;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-start");
+
+ if ((len >= 8) && ((unsigned long)dt & 0x4))
+ dt++;
+
+ memcpy(dt,&initrd_base,len);
+ dt += (len + 3)/4;
+
+ len = 8;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-end");
+
+ initrd_end = initrd_base + initrd_size;
+ if ((len >= 8) && ((unsigned long)dt & 0x4))
+ dt++;
+
+ memcpy(dt,&initrd_end,len);
+ dt += (len + 3)/4;
+
+ reserve(initrd_base, initrd_size);
+ }
+
+ for (i=0; i < numlist; i++) {
+ dp = namelist[i];
+ strcpy(dn, dp->d_name);
+ free(namelist[i]);
+
+ if (!strcmp(dn, ".") || !strcmp(dn, ".."))
+ continue;
+
+ if (lstat(pathname, statbuf))
+ err(pathname, ERR_STAT);
+
+ if (S_ISDIR(statbuf[0].st_mode))
+ putnode();
+ }
+ if (errno)
+ err(pathname, ERR_READDIR);
+
+ *dt++ = 2;
+ dn[-1] = '\0';
+ free(namelist);
+}
+
+int create_flatten_tree(struct kexec_info *info, unsigned char **bufp,
+ unsigned long *sizep, char *cmdline)
+{
+ unsigned long len;
+ unsigned long tlen;
+ unsigned char *buf;
+ unsigned long me;
+
+ me = 0;
+
+ strcpy(pathname, "/proc/device-tree/");
+
+ pathstart = pathname + strlen(pathname);
+ dt = dtstruct;
+
+ if (cmdline)
+ strcpy(local_cmdline, cmdline);
+
+ putnode();
+ *dt++ = 9;
+
+ len = sizeof(bb[0]);
+ len += 7; len &= ~7;
+
+ bb->off_mem_rsvmap = len;
+
+ for (len = 1; mem_rsrv[len]; len += 2)
+ ;
+ len+= 3;
+ len *= sizeof(mem_rsrv[0]);
+
+ bb->off_dt_struct = bb->off_mem_rsvmap + len;
+
+ len = dt - dtstruct;
+ len *= sizeof(dvt);
+ bb->off_dt_strings = bb->off_dt_struct + len;
+
+ len = propnum("");
+ len += 3; len &= ~3;
+ bb->totalsize = bb->off_dt_strings + len;
+
+ bb->magic = 0xd00dfeed;
+ bb->version = 2;
+ bb->last_comp_version = 2;
+
+ reserve(me, bb->totalsize); /* patched later in kexec_load */
+
+ buf = (unsigned char *) malloc(bb->totalsize);
+ *bufp = buf;
+ memcpy(buf, bb, bb->off_mem_rsvmap);
+ tlen = bb->off_mem_rsvmap;
+ memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap);
+ tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap);
+ memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct);
+ tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct);
+ memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings);
+ tlen = tlen + bb->totalsize - bb->off_dt_strings;
+ *sizep = tlen;
+ return 0;
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/include/arch/options.h kexec-tools-1.101-kdump/kexec/arch/ppc64/include/arch/options.h
--- kexec-tools-1.101/kexec/arch/ppc64/include/arch/options.h 2004-12-22 01:56:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/include/arch/options.h 2006-01-19 18:20:07.000000000 +0530
@@ -2,9 +2,11 @@
#define KEXEC_ARCH_PPC64_OPTIONS_H
#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_ELF64_CORE (OPT_MAX+1)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
+ { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-ppc64.c kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-elf-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-ppc64.c 2004-12-17 15:02:04.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-elf-ppc64.c 2006-02-22 11:38:31.003108264 +0530
@@ -3,6 +3,8 @@
*
* Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
* Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
+ * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.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
@@ -32,46 +34,19 @@
#include <linux/elf.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
#include "kexec-ppc64.h"
+#include "crashdump-ppc64.h"
+#include <arch/options.h>
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
-#define MAX_COMMAND_LINE 256
-#define UPSZ(X) ((sizeof(X) + 3) & ~3)
-static struct boot_notes {
- Elf_Bhdr hdr;
- Elf_Nhdr bl_hdr;
- unsigned char bl_desc[UPSZ(BOOTLOADER)];
- Elf_Nhdr blv_hdr;
- unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)];
- Elf_Nhdr cmd_hdr;
- unsigned char command_line[0];
-} elf_boot_notes = {
- .hdr = {
- .b_signature = 0x0E1FB007,
- .b_size = sizeof(elf_boot_notes),
- .b_checksum = 0,
- .b_records = 3,
- },
- .bl_hdr = {
- .n_namesz = 0,
- .n_descsz = sizeof(BOOTLOADER),
- .n_type = EBN_BOOTLOADER_NAME,
- },
- .bl_desc = BOOTLOADER,
- .blv_hdr = {
- .n_namesz = 0,
- .n_descsz = sizeof(BOOTLOADER_VERSION),
- .n_type = EBN_BOOTLOADER_VERSION,
- },
- .blv_desc = BOOTLOADER_VERSION,
- .cmd_hdr = {
- .n_namesz = 0,
- .n_descsz = 0,
- .n_type = EBN_COMMAND_LINE,
- },
-};
+unsigned long initrd_base, initrd_size;
+
+int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *,
+ char *);
+int my_r2(struct mem_ehdr *ehdr);
int elf_ppc64_probe(const char *buf, off_t len)
{
@@ -94,12 +69,94 @@ int elf_ppc64_probe(const char *buf, off
return result;
}
-int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
- struct kexec_info *info)
+int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
{
struct mem_ehdr ehdr;
+ char *cmdline, *modified_cmdline;
+ const char *ramdisk, *devicetreeblob;
+ int cmdline_len, modified_cmdline_len;
+ unsigned long long max_addr, hole_addr;
+ unsigned char *seg_buf = NULL;
+ off_t seg_size = 0;
+ struct mem_phdr *phdr;
+ size_t size;
+ unsigned long long *rsvmap_ptr;
+ struct bootblock *bb_ptr;
+ unsigned int nr_segments, i;
+ int result, opt;
+ unsigned long my_kernel, my_dt_offset;
+ unsigned int my_panic_kernel;
+ unsigned long my_stack, my_backup_start;
+ unsigned long toc_addr;
+
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_RAMDISK (OPT_ARCH_MAX+1)
+#define OPT_DEVICETREEBLOB (OPT_ARCH_MAX+2)
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
/* Parse command line arguments */
+ initrd_base = 0;
+ initrd_size = 0;
+ cmdline = 0;
+ ramdisk = 0;
+ devicetreeblob = 0;
+ max_addr = 0xFFFFFFFFFFFFFFFFUL;
+ hole_addr = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case '?':
+ usage();
+ return -1;
+ case OPT_APPEND:
+ cmdline = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_DEVICETREEBLOB:
+ devicetreeblob = optarg;
+ break;
+ }
+ }
+
+ cmdline_len = 0;
+ if (cmdline)
+ cmdline_len = strlen(cmdline) + 1;
+ else
+ fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n");
+
+ setup_memory_ranges(info->kexec_flags);
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (cmdline) {
+ strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ modified_cmdline_len = strlen(modified_cmdline);
+ }
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr);
@@ -108,13 +165,173 @@ int elf_ppc64_load(int argc, char **argv
return result;
}
- /* Load the Elf data */
+ /* Load the Elf data. Physical load addresses in elf64 header do not
+ * show up correctly. Use user supplied address for now to patch the
+ * elf header
+ */
+
+ phdr = &ehdr.e_phdr[0];
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz)
+ size = phdr->p_memsz;
+
+ hole_addr = (unsigned long)locate_hole(info, size, 0, 0,
+ max_addr, 1);
+ ehdr.e_phdr[0].p_paddr = hole_addr;
result = elf_exec_load(&ehdr, info);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
- return 1;
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (result < 0)
+ return -1;
+ /* Use new command line. */
+ cmdline = modified_cmdline;
+ cmdline_len = strlen(modified_cmdline) + 1;
+ }
+
+ /* Add v2wrap to the current image */
+ seg_buf = NULL;
+ seg_size = 0;
+
+ seg_buf = (unsigned char *) malloc(purgatory_size);
+ if (seg_buf == NULL) {
+ free_elf_info(&ehdr);
+ return -1;
+ }
+ memcpy(seg_buf, purgatory, purgatory_size);
+ seg_size = purgatory_size;
+ elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
+ purgatory_size, 0, max_addr, 1);
+
+ /* Add a ram-disk to the current image
+ * Note: Add the ramdisk after elf_rel_build_load
+ */
+ if (ramdisk) {
+ if (devicetreeblob) {
+ fprintf(stderr,
+ "Can't use ramdisk with device tree blob input\n");
+ return -1;
+ }
+ seg_buf = (unsigned char *)slurp_file(ramdisk, &seg_size);
+ add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, 1);
+ hole_addr = (unsigned long long)
+ info->segment[info->nr_segments-1].mem;
+ initrd_base = hole_addr;
+ initrd_size = (unsigned long long)
+ info->segment[info->nr_segments-1].memsz;
+ } /* ramdisk */
+
+ if (devicetreeblob) {
+ unsigned char *blob_buf = NULL;
+ off_t blob_size = 0;
+
+ /* Grab device tree from buffer */
+ blob_buf =
+ (unsigned char *)slurp_file(devicetreeblob, &blob_size);
+ add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
+ max_addr, -1);
+
+ } else {
+ /* create from fs2dt */
+ seg_buf = NULL;
+ seg_size = 0;
+ create_flatten_tree(info, (unsigned char **)&seg_buf,
+ (unsigned long *)&seg_size,cmdline);
+ add_buffer(info, seg_buf, seg_size, seg_size,
+ 0, 0, max_addr, -1);
+ }
+
+ /* patch reserve map address for flattened device-tree
+ * find last entry (both 0) in the reserve mem list. Assume DT
+ * entry is before this one
+ */
+ bb_ptr = (struct bootblock *)(
+ (unsigned char *)info->segment[(info->nr_segments)-1].buf);
+ rsvmap_ptr = (unsigned long long *)(
+ (unsigned char *)info->segment[(info->nr_segments)-1].buf +
+ bb_ptr->off_mem_rsvmap);
+ while (*rsvmap_ptr || *(rsvmap_ptr+1))
+ rsvmap_ptr += 2;
+ rsvmap_ptr -= 2;
+ *rsvmap_ptr = (unsigned long long)(
+ info->segment[(info->nr_segments)-1].mem);
+ rsvmap_ptr++;
+ *rsvmap_ptr = (unsigned long long)bb_ptr->totalsize;
+
+ nr_segments = info->nr_segments;
+
+ /* Set kernel */
+ my_kernel = (unsigned long )info->segment[0].mem;
+ elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
+
+ /* Set dt_offset */
+ my_dt_offset = (unsigned long )info->segment[nr_segments-1].mem;
+ elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
+ sizeof(my_dt_offset));
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ my_panic_kernel = 1;
+ /* Set panic flag */
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &my_panic_kernel, sizeof(my_panic_kernel));
+
+ /* Set backup address */
+ my_backup_start = info->backup_start;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &my_backup_start, sizeof(my_backup_start));
+ }
+
+ /* Set stack address */
+ my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1);
+ my_stack += 16*1024;
+ elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
+
+ /* Set toc */
+ toc_addr = (unsigned long) my_r2(&info->rhdr);
+ elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr));
+
+#ifdef DEBUG
+ my_kernel = 0;
+ my_dt_offset = 0;
+ my_panic_kernel = 0;
+ my_backup_start = 0;
+ my_stack = 0;
+ toc_addr = 0;
+
+ elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
+ elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
+ sizeof(my_dt_offset));
+ elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel,
+ sizeof(my_panic_kernel));
+ elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start,
+ sizeof(my_backup_start));
+ elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
+ elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr,
+ sizeof(toc_addr));
+
+ fprintf(stderr, "info->entry is %p\n", info->entry);
+ fprintf(stderr, "kernel is %Lx\n", my_kernel);
+ fprintf(stderr, "dt_offset is %Lx\n", my_dt_offset);
+ fprintf(stderr, "panic_kernel is %x\n", my_panic_kernel);
+ fprintf(stderr, "backup_start is %Lx\n", my_backup_start);
+ fprintf(stderr, "stack is %Lx\n", my_stack);
+ fprintf(stderr, "toc_addr is %Lx\n", toc_addr);
+ fprintf(stderr, "purgatory size is %d\n", purgatory_size);
+#endif
+
+ for (i = 0; i < nr_segments; i++)
+ fprintf(stderr, "segment[%d].mem:%p memsz:%ld\n", i,
+ info->segment[i].mem, info->segment[i].memsz);
+
+ return 0;
}
void elf_ppc64_usage(void)
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-rel-ppc64.c kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-elf-rel-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-elf-rel-ppc64.c 2004-12-20 07:36:04.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-elf-rel-ppc64.c 2006-02-22 11:30:11.082107808 +0530
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <elf.h>
+#include <string.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
@@ -21,20 +22,20 @@ static struct mem_shdr *toc_section(cons
{
struct mem_shdr *shdr, *shdr_end;
unsigned char *strtab;
- strtab = ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
- shdr_end = &ehdr->e_shdr[ehdr->shnum];
- for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
- if (strcmp(shdr->sh_name, ".toc") == 0) {
+ strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
+ shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
+ for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++)
+ if ( shdr->sh_size &&
+ strcmp((char *)&strtab[shdr->sh_name],
+ ".toc") == 0)
return shdr;
- }
- }
return NULL;
}
/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
gives the value maximum span in an instruction which uses a signed
offset) */
-static unsigned long my_r2(const struct mem_ehdr *ehdr)
+unsigned long my_r2(const struct mem_ehdr *ehdr)
{
struct mem_shdr *shdr;
shdr = toc_section(ehdr);
@@ -53,7 +54,7 @@ void machine_apply_elf_rel(struct mem_eh
/* Simply set it */
*(uint32_t *)location = value;
break;
-
+
case R_PPC64_ADDR64:
/* Simply set it */
*(uint64_t *)location = value;
@@ -78,15 +79,34 @@ void machine_apply_elf_rel(struct mem_eh
/* Convert value to relative */
value -= address;
if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
- die("REL24 %li out of range!\n",
+ die("REL24 %li out of range!\n",
(long int)value);
}
/* Only replace bits 2 through 26 */
- *(uint32_t *)location
- = (*(uint32_t *)location & ~0x03fffffc)
- | (value & 0x03fffffc);
+ *(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc)
+ | (value & 0x03fffffc);
+ break;
+
+ case R_PPC64_ADDR16_LO:
+ *(uint16_t *)location = value & 0xffff;
+ break;
+
+ case R_PPC64_ADDR16_HI:
+ *(uint16_t *)location = (value>>16) & 0xffff;
break;
+
+ case R_PPC64_ADDR16_HA:
+ *(uint16_t *)location = ((value>>16) & 0xffff);
+ break;
+
+ case R_PPC64_ADDR16_HIGHEST:
+ *(uint16_t *)location = ((value>>48) & 0xffff);
+ break;
+ case R_PPC64_ADDR16_HIGHER:
+ *(uint16_t *)location = ((value>>32) & 0xffff);
+ break;
+
default:
die("Unknown rela relocation: %lu\n", r_type);
break;
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/kexec-ppc64.c kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-ppc64.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-ppc64.c 2006-01-19 18:20:07.000000000 +0530
@@ -0,0 +1,640 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation
+ *
+ * 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 <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <getopt.h>
+#include <sys/utsname.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc64.h"
+#include "crashdump-ppc64.h"
+#include <arch/options.h>
+
+/* Platforms supported by kexec on PPC64 */
+#define PLATFORM_PSERIES 0x0100
+#define PLATFORM_PSERIES_LPAR 0x0101
+
+static struct exclude_range exclude_range[MAX_MEMORY_RANGES];
+static unsigned long long rmo_top;
+static unsigned int platform;
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+static struct memory_range base_memory_range[MAX_MEMORY_RANGES];
+unsigned long long memory_max = 0;
+static int nr_memory_ranges, nr_exclude_ranges;
+unsigned long long crash_base, crash_size;
+
+static int sort_base_ranges();
+
+/* Get base memory ranges */
+static int get_base_ranges()
+{
+ int local_memory_ranges = 0;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES-1];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int n;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((dmem = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ return -1;
+ }
+ if (local_memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ base_memory_range[local_memory_ranges].start =
+ ((unsigned long long *)buf)[0];
+ base_memory_range[local_memory_ranges].end =
+ base_memory_range[local_memory_ranges].start +
+ ((unsigned long long *)buf)[1];
+ base_memory_range[local_memory_ranges].type = RANGE_RAM;
+ local_memory_ranges++;
+#ifdef DEBUG
+ fprintf(stderr, "%016Lx-%016Lx : %x\n", memory_range[local_memory_ranges-1].start, memory_range[local_memory_ranges-1].end, memory_range[local_memory_ranges].type);
+#endif
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+ nr_memory_ranges = local_memory_ranges;
+ sort_base_ranges();
+ memory_max = base_memory_range[nr_memory_ranges - 1].end;
+#ifdef DEBUG
+ fprintf(stderr, "get base memory ranges:%d\n", nr_memory_ranges);
+#endif
+ return 0;
+}
+
+/* Sort the base ranges in memory - this is useful for ensuring that our
+ * ranges are in ascending order, even if device-tree read of memory nodes
+ * is done differently. Also, could be used for other range coalescing later
+ */
+static int sort_base_ranges()
+{
+ int i, j;
+ unsigned long long tstart, tend;
+
+ for (i = 0; i < nr_memory_ranges - 1; i++) {
+ for (j = 0; j < nr_memory_ranges - i - 1; j++) {
+ if (base_memory_range[j].start > base_memory_range[j+1].start) {
+ tstart = base_memory_range[j].start;
+ tend = base_memory_range[j].end;
+ base_memory_range[j].start = base_memory_range[j+1].start;
+ base_memory_range[j].end = base_memory_range[j+1].end;
+ base_memory_range[j+1].start = tstart;
+ base_memory_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Sort the exclude ranges in memory */
+static int sort_ranges()
+{
+ int i, j;
+ unsigned long long tstart, tend;
+ for (i = 0; i < nr_exclude_ranges - 1; i++) {
+ for (j = 0; j < nr_exclude_ranges - i - 1; j++) {
+ if (exclude_range[j].start > exclude_range[j+1].start) {
+ tstart = exclude_range[j].start;
+ tend = exclude_range[j].end;
+ exclude_range[j].start = exclude_range[j+1].start;
+ exclude_range[j].end = exclude_range[j+1].end;
+ exclude_range[j+1].start = tstart;
+ exclude_range[j+1].end = tend;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Get devtree details and create exclude_range array
+ * Also create usablemem_ranges for KEXEC_ON_CRASH
+ */
+static int get_devtree_details(unsigned long kexec_flags)
+{
+ unsigned long long rmo_base;
+ unsigned long long tce_base;
+ unsigned int tce_size;
+ unsigned int rtas_base, rtas_size;
+ unsigned long long htab_base, htab_size;
+ unsigned long long kernel_end;
+ char buf[MAXBYTES-1];
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ DIR *dir, *cdir;
+ FILE *file;
+ struct dirent *dentry;
+ int n, i = 0;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "chosen", 6) &&
+ strncmp(dentry->d_name, "memory@0", 8) &&
+ strncmp(dentry->d_name, "pci@", 4) &&
+ strncmp(dentry->d_name, "rtas", 4))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((cdir = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+
+ if (strncmp(dentry->d_name, "chosen", 6) == 0) {
+ /* get platform details from /chosen node */
+ strcat(fname, "/linux,platform");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&platform, sizeof(int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,kernel-end");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&kernel_end, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ fclose(file);
+
+ /* Add kernel memory to exclude_range */
+ exclude_range[i].start = 0x0UL;
+ exclude_range[i].end = kernel_end;
+ i++;
+
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,crashkernel-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&crash_base, sizeof(unsigned long), 1,
+ file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,crashkernel-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&crash_size, sizeof(unsigned long), 1,
+ file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+
+ if (crash_base > mem_min)
+ mem_min = crash_base;
+ if (crash_base + crash_size < mem_max)
+ mem_max = crash_base + crash_size;
+
+ add_usable_mem_rgns(0, crash_base + crash_size);
+ reserve(KDUMP_BACKUP_LIMIT, crash_base-KDUMP_BACKUP_LIMIT);
+ }
+
+ /* if LPAR, no need to read any more from /chosen */
+ if (platform != PLATFORM_PSERIES) {
+ closedir(cdir);
+ continue;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&htab_base, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&htab_size, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ /* Add htab address to exclude_range - NON-LPAR only */
+ exclude_range[i].start = htab_base;
+ exclude_range[i].end = htab_base + htab_size;
+ i++;
+ } /* chosen */
+
+ if (strncmp(dentry->d_name, "rtas", 4) == 0) {
+ strcat(fname, "/linux,rtas-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/rtas-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ closedir(cdir);
+ /* Add rtas to exclude_range */
+ exclude_range[i].start = rtas_base;
+ exclude_range[i].end = rtas_base + rtas_size;
+ i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(rtas_base, rtas_size);
+ } /* rtas */
+
+ if (strncmp(dentry->d_name, "memory@0", 8) == 0) {
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ rmo_base = ((unsigned long long *)buf)[0];
+ rmo_top = rmo_base + ((unsigned long long *)buf)[1];
+ if (platform == PLATFORM_PSERIES) {
+ if (rmo_top > 0x30000000UL)
+ rmo_top = 0x30000000UL;
+ }
+ fclose(file);
+ closedir(cdir);
+ } /* memory */
+
+ if (strncmp(dentry->d_name, "pci@", 4) == 0) {
+ if (platform != PLATFORM_PSERIES) {
+ closedir(cdir);
+ continue;
+ }
+ strcat(fname, "/linux,tce-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&tce_base, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,tce-size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&tce_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ /* Add tce to exclude_range - NON-LPAR only */
+ exclude_range[i].start = tce_base;
+ exclude_range[i].end = tce_base + tce_size;
+ i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(tce_base, tce_size);
+ closedir(cdir);
+ } /* pci */
+ }
+ closedir(dir);
+
+ nr_exclude_ranges = i;
+
+ sort_ranges();
+
+ /* add crash_region and remove rtas range from exclude regions if it
+ * lies within crash region
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ unsigned long new_crash_size;
+ if (crash_base < rtas_base &&
+ ((crash_base + crash_size) > (rtas_base + rtas_size))){
+ new_crash_size = rtas_base - crash_base;
+ add_exclude_rgns(crash_base, new_crash_size);
+ new_crash_size = (crash_base + crash_size) - (rtas_base + rtas_size);
+ add_exclude_rgns(rtas_base + rtas_size, new_crash_size);
+ } else if (crash_base < rtas_base &&
+ ((rtas_base + rtas_size) > (crash_base + crash_size))){
+ new_crash_size = rtas_base - crash_base;
+ add_exclude_rgns(crash_base, new_crash_size);
+ } else if (crash_base > rtas_base &&
+ ((rtas_base + rtas_size) < (crash_base + crash_size))){
+ new_crash_size = (crash_base + crash_size) - (rtas_base + rtas_size);
+ add_exclude_rgns(rtas_base + rtas_size, new_crash_size);
+ } else
+ add_exclude_rgns(crash_base, crash_size);
+ }
+
+#ifdef DEBUG
+ int k;
+ for (k = 0; k < i; k++)
+ fprintf(stderr, "exclude_range sorted exclude_range[%d] start:%lx, end:%lx\n", k, exclude_range[k].start, exclude_range[k].end);
+#endif
+ return 0;
+}
+
+/* Setup a sorted list of memory ranges. */
+int setup_memory_ranges(unsigned long kexec_flags)
+{
+ int i, j = 0;
+
+ /* Get the base list of memory ranges from /proc/device-tree/memory
+ * nodes. Build list of ranges to be excluded from valid memory
+ */
+
+ get_base_ranges();
+ get_devtree_details(kexec_flags);
+
+ for (i = 0; i < nr_exclude_ranges; i++) {
+ /* If first exclude range does not start with 0, include the
+ * first hole of valid memory from 0 - exclude_range[0].start
+ */
+ if (i == 0) {
+ if (exclude_range[i].start != 0) {
+ memory_range[j].start = 0;
+ memory_range[j].end = exclude_range[i].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ }
+ } /* i == 0 */
+ /* If the last exclude range does not end at memory_max, include
+ * the last hole of valid memory from exclude_range[last].end -
+ * memory_max
+ */
+ if (i == nr_exclude_ranges - 1) {
+ if (exclude_range[i].end < memory_max) {
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = memory_max;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ /* Limit the end to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) &&
+ (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_top;
+ break;
+ }
+ continue;
+ }
+ } /* i == nr_exclude_ranges - 1 */
+ /* contiguous exclude ranges - skip */
+ if (exclude_range[i+1].start == exclude_range[i].end + 1)
+ continue;
+ memory_range[j].start = exclude_range[i].end + 1;
+ memory_range[j].end = exclude_range[i+1].start - 1;
+ memory_range[j].type = RANGE_RAM;
+ j++;
+ /* Limit range to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) &&
+ (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_top;
+ break;
+ }
+ }
+ nr_memory_ranges = j;
+
+#ifdef DEBUG
+ int k;
+ for (k = 0; k < j; k++)
+ fprintf(stderr, "setup_memory_ranges memory_range[%d] start:%lx, end:%lx\n", k, memory_range[k].start, memory_range[k].end);
+#endif
+ return 0;
+}
+
+/* Return a list of valid memory ranges */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ setup_memory_ranges(kexec_flags);
+ *range = memory_range;
+ *ranges = nr_memory_ranges;
+ fprintf(stderr, "get memory ranges:%d\n", nr_memory_ranges);
+ return 0;
+}
+
+struct file_type file_type[] = {
+ { "elf-ppc64", elf_ppc64_probe, elf_ppc64_load, elf_ppc64_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+ fprintf(stderr, " --devicetreeblob=<filename> Specify device tree blob file.\n");
+ fprintf(stderr, " --elf64-core-headers Prepare core headers in ELF64 format\n");
+}
+
+struct arch_options_t arch_options = {
+ .core_header_type = CORE_TYPE_ELF64,
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ case OPT_ELF64_CORE:
+ arch_options.core_header_type = CORE_TYPE_ELF64;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+int arch_compat_trampoline(struct kexec_info *info)
+{
+ int result;
+ struct utsname utsname;
+ result = uname(&utsname);
+ if (result < 0) {
+ fprintf(stderr, "uname failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (strcmp(utsname.machine, "ppc64") == 0)
+ {
+ /* We are running a 32-bit kexec-tools on 64-bit ppc64.
+ * So pass KEXEC_ARCH_PPC64 here
+ */
+ info->kexec_flags |= KEXEC_ARCH_PPC64;
+ }
+ else {
+ fprintf(stderr, "Unsupported machine type: %s\n",
+ utsname.machine);
+ return -1;
+ }
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/kexec-ppc64.h kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-ppc64.h
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-ppc64.h 2004-12-17 12:14:42.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-ppc64.h 2006-01-19 18:20:07.000000000 +0530
@@ -1,9 +1,44 @@
#ifndef KEXEC_PPC64_H
#define KEXEC_PPC64_H
+#define MAX_MEMORY_RANGES 256 /* TO FIX - needs to be dynamically set */
+#define MAXBYTES 128
+#define MAX_LINE 160
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
+int setup_memory_ranges(unsigned long kexec_flags);
+
int elf_ppc64_probe(const char *buf, off_t len);
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info);
void elf_ppc64_usage(void);
+void reserve(unsigned long long where, unsigned long long length);
+
+extern unsigned long initrd_base, initrd_size;
+/* boot block version 2 as defined by the linux kernel */
+struct bootblock {
+ unsigned magic,
+ totalsize,
+ off_dt_struct,
+ off_dt_strings,
+ off_mem_rsvmap,
+ version,
+ last_comp_version,
+ boot_physid;
+};
+
+struct arch_options_t {
+ int core_header_type;
+};
+
+struct exclude_range {
+ unsigned long long start, end;
+};
+
+typedef struct mem_rgns {
+ unsigned int size;
+ struct exclude_range ranges[MAX_MEMORY_RANGES];
+} mem_rgns_t;
-#endif /* KEXEC_PPC_H */
+#endif /* KEXEC_PPC64_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/kexec-zImage-ppc64.c kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-zImage-ppc64.c
--- kexec-tools-1.101/kexec/arch/ppc64/kexec-zImage-ppc64.c 2004-12-16 17:47:51.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/kexec-zImage-ppc64.c 2006-02-22 11:30:11.082107808 +0530
@@ -153,7 +153,8 @@ int zImage_ppc64_load(FILE *file, int ar
return -1;
}
mem_offset = p->p_vaddr - load_loc;
- if (fread(segment->buf+mem_offset, p->p_filesz, 1, file) != 1) {
+ if (fread((void *)segment->buf+mem_offset, p->p_filesz, 1,
+ file) != 1) {
perror("read error: ");
return -1;
}
@@ -161,7 +162,7 @@ int zImage_ppc64_load(FILE *file, int ar
segment->mem = (void *) load_loc;
segment->memsz = memsize;
segment->bufsz = filesize;
- *ret_entry = elf.e_entry;
+ *ret_entry = (void *)((uint64_t)elf.e_entry);
*ret_nr_segments = i - 1;
free(ph);
return 0;
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ppc64/Makefile kexec-tools-1.101-kdump/kexec/arch/ppc64/Makefile
--- kexec-tools-1.101/kexec/arch/ppc64/Makefile 2004-12-17 12:05:30.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/ppc64/Makefile 2006-01-19 18:20:06.000000000 +0530
@@ -2,6 +2,10 @@
# kexec ppc64 (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-rel-ppc64.c
-KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/fs2dt.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/crashdump-ppc64.c
KEXEC_S_SRCS+=
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/include/arch/options.h kexec-tools-1.101-kdump/kexec/arch/s390/include/arch/options.h
--- kexec-tools-1.101/kexec/arch/s390/include/arch/options.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/include/arch/options.h 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,11 @@
+#ifndef KEXEC_ARCH_S390_OPTIONS_H
+#define KEXEC_ARCH_S390_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+#endif /* KEXEC_ARCH_S390_OPTIONS_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/kexec-elf-rel-s390.c kexec-tools-1.101-kdump/kexec/arch/s390/kexec-elf-rel-s390.c
--- kexec-tools-1.101/kexec/arch/s390/kexec-elf-rel-s390.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/kexec-elf-rel-s390.c 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,23 @@
+/*
+ * kexec/arch/s390/kexec-elf-rel-s390.c
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ return 0;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
+ void *location, unsigned long address, unsigned long value)
+{
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/kexec-image.c kexec-tools-1.101-kdump/kexec/arch/s390/kexec-image.c
--- kexec-tools-1.101/kexec/arch/s390/kexec-image.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/kexec-image.c 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,137 @@
+/*
+ * kexec/arch/s390/kexec-image.c
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "kexec-s390.h"
+
+#define OPT_APPEND OPT_MAX+0
+#define OPT_RAMDISK OPT_MAX+1
+
+int
+image_s390_load(int argc, char **argv, const char *kernel_buf,
+ off_t kernel_size, struct kexec_info *info)
+{
+ void *krnl_buffer;
+ char *rd_buffer;
+ const char *command_line;
+ const char *ramdisk;
+ int command_line_len;
+ off_t ramdisk_len;
+ unsigned int ramdisk_origin;
+ int opt;
+
+ static const struct option options[] =
+ {
+ KEXEC_OPTIONS
+ {"command-line", 1, 0, OPT_APPEND},
+ {"initrd", 1, 0, OPT_RAMDISK},
+ {0, 0, 0, 0},
+ };
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ ramdisk = NULL;
+ command_line = NULL;
+ ramdisk_len = 0;
+ ramdisk_origin = 0;
+
+ while ((opt = getopt_long(argc,argv,short_options,options,0)) != -1) {
+ switch(opt) {
+ case '?':
+ usage();
+ return -1;
+ break;
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ }
+ }
+
+ /* Process a given command_line: */
+ if (command_line) {
+ command_line_len = strlen(command_line) + 1; /* Remember the '\0' */
+ if (command_line_len > COMMAND_LINESIZE) {
+ fprintf(stderr, "Command line too long.\n");
+ return -1;
+ }
+ }
+
+ /* Add kernel segment */
+ add_segment(info, kernel_buf + IMAGE_READ_OFFSET,
+ kernel_size - IMAGE_READ_OFFSET, IMAGE_READ_OFFSET,
+ kernel_size - IMAGE_READ_OFFSET);
+
+ /* We do want to change the kernel image */
+ krnl_buffer = (void *) kernel_buf + IMAGE_READ_OFFSET;
+
+ /* Load ramdisk if present */
+ if (ramdisk) {
+ rd_buffer = slurp_file(ramdisk, &ramdisk_len);
+ if (rd_buffer == NULL) {
+ fprintf(stderr, "Could not read ramdisk.\n");
+ return -1;
+ }
+ ramdisk_origin = RAMDISK_ORIGIN_ADDR;
+ add_segment(info, rd_buffer, ramdisk_len, RAMDISK_ORIGIN_ADDR, ramdisk_len);
+ }
+
+ /* Register the ramdisk in the kernel. */
+ {
+ unsigned long long *tmp;
+
+ tmp = krnl_buffer + INITRD_START_OFFS;
+ *tmp = (unsigned long long) ramdisk_origin;
+
+ tmp = krnl_buffer + INITRD_SIZE_OFFS;
+ *tmp = (unsigned long long) ramdisk_len;
+ }
+
+ /*
+ * We will write a probably given command line.
+ * First, erase the old area, then setup the new parameters:
+ */
+ if (command_line) {
+ memset(krnl_buffer + COMMAND_LINE_OFFS, 0, COMMAND_LINESIZE);
+ memcpy(krnl_buffer + COMMAND_LINE_OFFS, command_line, strlen(command_line));
+ }
+
+ info->entry = (void *) IMAGE_READ_OFFSET;
+
+ return 0;
+}
+
+int
+image_s390_probe(const char *kernel_buf, off_t kernel_size)
+{
+ /*
+ * Can't reliably tell if an image is valid,
+ * therefore everything is valid.
+ */
+ return 0;
+}
+
+void
+image_s390_usage(void)
+{
+ printf("--command-line=STRING Pass a custom command line STRING to the kernel.\n"
+ "--initrd=FILENAME Use the file FILENAME as a ramdisk.\n"
+ );
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/kexec-s390.c kexec-tools-1.101-kdump/kexec/arch/s390/kexec-s390.c
--- kexec-tools-1.101/kexec/arch/s390/kexec-s390.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/kexec-s390.c 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,104 @@
+/*
+ * kexec/arch/s390/kexec-s390.c
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/utsname.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-s390.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/*
+ * get_memory_ranges:
+ * Return a list of memory ranges by parsing /proc/iomem
+ *
+ * INPUT:
+ * - Pointer to an array of memory_range structures.
+ * - Pointer to an integer with holds the number of memory ranges.
+ *
+ * RETURN:
+ * - 0 on normal execution.
+ * - (-1) if something went wrong.
+ */
+
+int get_memory_ranges(struct memory_range **range, int *ranges)
+{
+ char sys_ram[] = "System RAM\n";
+ char iomem[] = "/proc/iomem";
+ FILE *fp;
+ char line[80];
+ int current_range = 0;
+
+ fp = fopen(iomem,"r");
+ if(fp == 0) {
+ fprintf(stderr,"Unable to open %s: %s\n",iomem,strerror(errno));
+ return -1;
+ }
+
+ /* Setup the compare string properly. */
+ while(fgets(line,sizeof(line),fp) != 0) {
+ unsigned long long start, end;
+ int cons;
+ char *str;
+
+ if (current_range == MAX_MEMORY_RANGES)
+ break;
+
+ sscanf(line,"%Lx-%Lx : %n", &start, &end, &cons);
+ str = line+cons;
+ if(memcmp(str,sys_ram,strlen(sys_ram)) == 0) {
+ memory_range[current_range].start = start;
+ memory_range[current_range].end = end;
+ memory_range[current_range].type = RANGE_RAM;
+ current_range++;
+ }
+ else {
+ continue;
+ }
+ }
+ fclose(fp);
+ *range = memory_range;
+ *ranges = current_range;
+
+ return 0;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ { "image", image_s390_probe, image_s390_load, image_s390_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ return 0;
+}
+
+int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/kexec-s390.h kexec-tools-1.101-kdump/kexec/arch/s390/kexec-s390.h
--- kexec-tools-1.101/kexec/arch/s390/kexec-s390.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/kexec-s390.h 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,25 @@
+/*
+ * kexec/arch/s390/kexec-s390.h
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
+ *
+ */
+
+#ifndef KEXEC_S390_H
+#define KEXEC_S390_H
+
+#define IMAGE_READ_OFFSET 0x10000
+
+#define RAMDISK_ORIGIN_ADDR 0x800000
+#define INITRD_START_OFFS 0x408
+#define INITRD_SIZE_OFFS 0x410
+#define COMMAND_LINE_OFFS 0x480
+#define COMMAND_LINESIZE 896
+
+extern int image_s390_load(int, char **, const char *, off_t, struct kexec_info *);
+extern int image_s390_probe(const char *, off_t);
+extern void image_s390_usage(void);
+
+#endif /* KEXEC_IA64_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/s390/Makefile kexec-tools-1.101-kdump/kexec/arch/s390/Makefile
--- kexec-tools-1.101/kexec/arch/s390/Makefile 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/s390/Makefile 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,6 @@
+#
+# kexec s390 (linux booting linux)
+#
+KEXEC_C_SRCS+= kexec/arch/s390/kexec-s390.c
+KEXEC_C_SRCS+= kexec/arch/s390/kexec-image.c
+KEXEC_C_SRCS+= kexec/arch/s390/kexec-elf-rel-s390.c
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/x86_64/crashdump-x86_64.c kexec-tools-1.101-kdump/kexec/arch/x86_64/crashdump-x86_64.c
--- kexec-tools-1.101/kexec/arch/x86_64/crashdump-x86_64.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/x86_64/crashdump-x86_64.c 2006-02-19 11:13:06.000000000 +0530
@@ -0,0 +1,655 @@
+/*
+ * 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 <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>
+
+extern struct arch_options_t arch_options;
+
+/* Forward Declaration. */
+static int exclude_crash_reserve_region(int *nr_ranges);
+
+/* 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)
+{
+ const char iomem[]= "/proc/iomem";
+ int memory_ranges = 0;
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long long start, end;
+
+ 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 */
+ 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;
+#if 0
+ 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 {
+ continue;
+ }
+
+ /* First 640K already registered */
+ if (start >= 0x00000000 && 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++;
+
+ /* Segregate linearly mapped region. */
+ if ((MAXMEM - 1) >= start && (MAXMEM - 1) <= end) {
+ crash_memory_range[memory_ranges-1].end = MAXMEM -1;
+
+ /* Add segregated region. */
+ crash_memory_range[memory_ranges].start = MAXMEM;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type = type;
+ memory_ranges++;
+ }
+ }
+ fclose(fp);
+ if (exclude_crash_reserve_region(&memory_ranges) < 0)
+ return -1;
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+#if 0
+ 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_crash_reserve_region(int *nr_ranges)
+{
+ int i, j, tidx = -1;
+ unsigned long long cstart, cend;
+ struct memory_range temp_region;
+
+ /* 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) {
+ /* Split memory region */
+ crash_memory_range[i].end = cstart - 1;
+ temp_region.start = cend + 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
+ crash_memory_range[i].start = cend + 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;
+#if 0
+ 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;
+
+ /* 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;
+ }
+#if 0
+ 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, 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);
+ }
+#if 0
+ 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);
+#if 0
+ 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);
+
+#if 0
+ printf("Command line after adding acpi memmap\n");
+ printf("%s\n", cmdline);
+#endif
+ return 0;
+}
+
+/* Prepares the crash memory elf64 headers and stores in supplied buffer. */
+static int prepare_crash_memory_elf64_headers(struct kexec_info *info,
+ void *buf, unsigned long size)
+{
+ Elf64_Ehdr *elf;
+ Elf64_Phdr *phdr;
+ int i;
+ char *bufp;
+ long int nr_cpus = 0;
+ uint64_t notes_addr;
+
+ bufp = (char*) buf;
+
+ /* Setup ELF Header*/
+ elf = (Elf64_Ehdr *) bufp;
+ bufp += sizeof(Elf64_Ehdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELFCLASS64;
+ elf->e_ident[EI_DATA] = ELFDATA2LSB;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = EM_X86_64;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(Elf64_Ehdr);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(Elf64_Ehdr);
+ elf->e_phentsize= sizeof(Elf64_Phdr);
+ elf->e_phnum = 0;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* PT_NOTE program headers. One per cpu*/
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ return -1;
+ }
+
+ /* Need to find a better way to determine per cpu notes section size. */
+#define MAX_NOTE_BYTES 1024
+ for (i = 0; i < nr_cpus; i++) {
+ if (get_crash_notes_per_cpu(i, &notes_addr) < 0) {
+ /* This cpu is not present. Skip it. */
+ continue;
+ }
+
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_NOTE;
+ phdr->p_flags = 0;
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_vaddr = 0;
+ phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+
+ /* Setup PT_LOAD type program header for every system RAM chunk.
+ * A seprate program header for Backup Region*/
+ for (i = 0; i < CRASH_MAX_MEMORY_RANGES; i++) {
+ unsigned long long mstart, mend;
+ if (crash_memory_range[i].type != RANGE_RAM)
+ continue;
+ mstart = crash_memory_range[i].start;
+ mend = crash_memory_range[i].end;
+ if (!mstart && !mend)
+ continue;
+ phdr = (Elf64_Phdr *) bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ if (mstart == BACKUP_START && mend == BACKUP_END)
+ phdr->p_offset = info->backup_start;
+ else
+ phdr->p_offset = mstart;
+
+ /* Handle linearly mapped region.*/
+
+ /* Filling the vaddr conditionally as we have two linearly
+ * mapped regions here. One is __START_KERNEL_map 0 to 40 MB
+ * other one is PAGE_OFFSET */
+
+ if ((mend <= (MAXMEM - 1)) && mstart < KERNEL_TEXT_SIZE)
+ phdr->p_vaddr = mstart + __START_KERNEL_map;
+ else {
+ if (mend <= (MAXMEM - 1))
+ phdr->p_vaddr = mstart + PAGE_OFFSET;
+ else
+ phdr->p_vaddr = -1ULL;
+ }
+ phdr->p_paddr = mstart;
+ phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ }
+ 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, elfcorehdr;
+ int nr_ranges, align = 1024, i;
+ long int nr_cpus = 0;
+ struct memory_range *mem_range, *memmap_p;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 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_START, BACKUP_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*/
+ sz = (BACKUP_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. */
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ fprintf(stderr,"kexec_load (elf header segment)"
+ " failed: %s\n", strerror(errno));
+ return -1;
+ }
+ sz = sizeof(Elf64_Ehdr) + nr_cpus * sizeof(Elf64_Phdr) +
+ nr_ranges * sizeof(Elf64_Phdr);
+ sz = (sz + align - 1) & ~(align -1);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+
+ /* Prepare ELF64 core heaers. */
+ if (prepare_crash_memory_elf64_headers(info, tmp, sz) < 0)
+ return -1;
+
+ /* 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.
+ */
+ elfcorehdr = add_buffer(info, tmp, sz, 16*1024, align, min_base,
+ max_addr, 1);
+ if (delete_memmap(memmap_p, elfcorehdr, sz) < 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)
+ continue;
+ start = mem_range[i].start;
+ end = mem_range[i].end;
+ cmdline_add_memmap_acpi(mod_cmdline, start, end);
+ }
+ return 0;
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/x86_64/crashdump-x86_64.h kexec-tools-1.101-kdump/kexec/arch/x86_64/crashdump-x86_64.h
--- kexec-tools-1.101/kexec/arch/x86_64/crashdump-x86_64.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/x86_64/crashdump-x86_64.h 2006-01-19 11:41:40.000000000 +0530
@@ -0,0 +1,24 @@
+#ifndef CRASHDUMP_X86_64_H
+#define CRASHDUMP_X86_64_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
+#define PAGE_OFFSET 0xffff810000000000UL
+#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 (40UL*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_START 0x00000000
+#define BACKUP_END 0x0009ffff
+#define BACKUP_SIZE (BACKUP_END - BACKUP_START + 1)
+
+#endif /* CRASHDUMP_X86_64_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/x86_64/kexec-elf-x86_64.c kexec-tools-1.101-kdump/kexec/arch/x86_64/kexec-elf-x86_64.c
--- kexec-tools-1.101/kexec/arch/x86_64/kexec-elf-x86_64.c 2005-01-13 18:40:54.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/x86_64/kexec-elf-x86_64.c 2006-01-19 11:41:42.000000000 +0530
@@ -32,10 +32,12 @@
#include <elf.h>
#include <x86/x86-linux.h>
#include "../../kexec.h"
+#include "../../kexec-syscall.h"
#include "../../kexec-elf.h"
#include "../../kexec-elf-boot.h"
#include "../i386/x86-linux-setup.h"
#include "kexec-x86_64.h"
+#include "crashdump-x86_64.h"
#include <arch/options.h>
static const int probe_debug = 0;
@@ -85,7 +87,9 @@ int elf_x86_64_load(int argc, char **arg
{
struct mem_ehdr ehdr;
const char *command_line;
+ char *modified_cmdline;
int command_line_len;
+ int modified_cmdline_len;
const char *ramdisk;
unsigned long entry, max_addr;
int arg_style;
@@ -118,6 +122,8 @@ int elf_x86_64_load(int argc, char **arg
*/
arg_style = ARG_STYLE_ELF;
command_line = 0;
+ modified_cmdline = 0;
+ modified_cmdline_len = 0;
ramdisk = 0;
while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
@@ -156,6 +162,20 @@ int elf_x86_64_load(int argc, char **arg
command_line_len = strlen(command_line) +1;
}
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
+ if (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ modified_cmdline_len = strlen(modified_cmdline);
+ }
+
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len);
@@ -197,6 +217,7 @@ int elf_x86_64_load(int argc, char **arg
const unsigned char *ramdisk_buf;
off_t ramdisk_length;
struct entry64_regs regs;
+ int rc=0;
/* Get the linux parameter header */
hdr = xmalloc(sizeof(*hdr));
@@ -210,9 +231,19 @@ int elf_x86_64_load(int argc, char **arg
/* Add a ramdisk to the current image */
ramdisk_buf = 0;
ramdisk_length = 0;
- if (ramdisk) {
- unsigned char *ramdisk_buf;
+ if (ramdisk)
ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ rc = load_crashdump_segments(info, modified_cmdline,
+ max_addr, 0);
+ if (rc < 0)
+ return -1;
+ /* Use new command line. */
+ command_line = modified_cmdline;
+ command_line_len = strlen(modified_cmdline) + 1;
}
/* Tell the kernel what is going on */
@@ -222,7 +253,7 @@ int elf_x86_64_load(int argc, char **arg
ramdisk_buf, ramdisk_length);
/* Fill in the information bios calls would usually provide */
- setup_linux_system_parameters(&hdr->hdr);
+ setup_linux_system_parameters(&hdr->hdr, info->kexec_flags);
/* Initialize the registers */
elf_rel_get_symbol(&info->rhdr, "entry64_regs", &regs, sizeof(regs));
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/x86_64/kexec-x86_64.c kexec-tools-1.101-kdump/kexec/arch/x86_64/kexec-x86_64.c
--- kexec-tools-1.101/kexec/arch/x86_64/kexec-x86_64.c 2005-02-06 04:55:01.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/x86_64/kexec-x86_64.c 2006-02-19 11:11:37.000000000 +0530
@@ -30,14 +30,15 @@
#include "../../kexec-elf.h"
#include "../../kexec-syscall.h"
#include "kexec-x86_64.h"
+#include "crashdump-x86_64.h"
#include <arch/options.h>
#define MAX_MEMORY_RANGES 64
-#define MAX_LINE 160
static struct memory_range memory_range[MAX_MEMORY_RANGES];
/* Return a sorted list of memory ranges. */
-int get_memory_ranges(struct memory_range **range, int *ranges)
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
{
const char iomem[]= "/proc/iomem";
int memory_ranges = 0;
@@ -79,6 +80,20 @@ int get_memory_ranges(struct memory_rang
else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
type = RANGE_ACPI_NVS;
}
+ else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+ /* Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported
+ * values are subset of user defined values.
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+ continue;
+ }
else {
continue;
}
@@ -124,18 +139,20 @@ void arch_usage(void)
);
}
-static struct {
+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 = {
.reset_vga = 0,
.serial_base = 0x3f8,
.serial_baud = 0,
.console_vga = 0,
.console_serial = 0,
+ .core_header_type = CORE_TYPE_ELF64,
};
int arch_process_options(int argc, char **argv)
@@ -207,7 +224,7 @@ int arch_process_options(int argc, char
return 0;
}
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+int arch_compat_trampoline(struct kexec_info *info)
{
int result;
struct utsname utsname;
@@ -222,7 +239,7 @@ int arch_compat_trampoline(struct kexec_
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_X86_64 here.
*/
- *flags |= KEXEC_ARCH_DEFAULT;
+ info->kexec_flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
@@ -234,6 +251,8 @@ int arch_compat_trampoline(struct kexec_
void arch_update_purgatory(struct kexec_info *info)
{
+ uint8_t panic_kernel = 0;
+
elf_rel_set_symbol(&info->rhdr, "reset_vga",
&arch_options.reset_vga, sizeof(arch_options.reset_vga));
elf_rel_set_symbol(&info->rhdr, "serial_base",
@@ -244,4 +263,12 @@ void arch_update_purgatory(struct kexec_
&arch_options.console_vga, sizeof(arch_options.console_vga));
elf_rel_set_symbol(&info->rhdr, "console_serial",
&arch_options.console_serial, sizeof(arch_options.console_serial));
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ panic_kernel = 1;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &info->backup_start, sizeof(info->backup_start));
+ }
+ elf_rel_set_symbol(&info->rhdr, "panic_kernel",
+ &panic_kernel, sizeof(panic_kernel));
}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/x86_64/Makefile kexec-tools-1.101-kdump/kexec/arch/x86_64/Makefile
--- kexec-tools-1.101/kexec/arch/x86_64/Makefile 2005-02-06 04:55:19.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/arch/x86_64/Makefile 2006-01-19 11:41:40.000000000 +0530
@@ -7,6 +7,7 @@ KEXEC_C_SRCS+= kexec/arch/i386/kexec-mul
KEXEC_C_SRCS+= kexec/arch/i386/kexec-beoboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-nbi.c
KEXEC_C_SRCS+= kexec/arch/i386/x86-linux-setup.c
+KEXEC_C_SRCS+= kexec/arch/x86_64/crashdump-x86_64.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-x86_64.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-elf-x86_64.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-elf-rel-x86_64.c
diff -urNp -X dontdiff kexec-tools-1.101/kexec/crashdump.c kexec-tools-1.101-kdump/kexec/crashdump.c
--- kexec-tools-1.101/kexec/crashdump.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/crashdump.c 2006-01-19 18:19:07.000000000 +0530
@@ -0,0 +1,65 @@
+/*
+ * crashdump.c: Architecture independent code for crashdump support.
+ *
+ * Created by: Vivek Goyal (vgoyal@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "kexec.h"
+#include "crashdump.h"
+
+/* Returns the physical address of start of crash notes buffer for a cpu. */
+int get_crash_notes_per_cpu(int cpu, uint64_t *addr)
+{
+ char crash_notes[PATH_MAX];
+ char line[MAX_LINE];
+ FILE *fp;
+ struct stat cpu_stat;
+ int count;
+ unsigned long long temp;
+
+ sprintf(crash_notes, "/sys/devices/system/cpu/cpu%d/crash_notes", cpu);
+ fp = fopen(crash_notes, "r");
+ if (!fp) {
+ /* Either sysfs is not mounted or CPU is not present*/
+ if (stat("/sys/devices", &cpu_stat))
+ die("Sysfs is not mounted. Try mounting sysfs\n");
+
+ /* CPU is not physically present.*/
+ *addr = 0;
+ return errno;
+ }
+ if (fgets(line, sizeof(line), fp) != 0) {
+ count = sscanf(line, "%Lx", &temp);
+ if (count != 1)
+ die("Cannot parse %s: %s\n", crash_notes,
+ strerror(errno));
+ *addr = (uint64_t) temp;
+ }
+#if 0
+ printf("crash_notes addr = %Lx\n", *addr);
+#endif
+ return 0;
+}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/crashdump.h kexec-tools-1.101-kdump/kexec/crashdump.h
--- kexec-tools-1.101/kexec/crashdump.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/crashdump.h 2006-01-19 18:19:07.000000000 +0530
@@ -0,0 +1,9 @@
+#ifndef CRASHDUMP_H
+#define CRASHDUMP_H
+
+extern int get_crash_notes_per_cpu(int cpu, uint64_t *addr);
+
+/* Need to find a better way to determine per cpu notes section size. */
+#define MAX_NOTE_BYTES 1024
+
+#endif /* CRASHDUMP_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec.c kexec-tools-1.101-kdump/kexec/kexec.c
--- kexec-tools-1.101/kexec/kexec.c 2005-01-13 18:54:29.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/kexec.c 2006-02-01 14:41:52.000000000 +0530
@@ -38,8 +38,8 @@
#include "kexec-elf.h"
#include "kexec-sha256.h"
-static unsigned long long mem_min = 0;
-static unsigned long long mem_max = ULONG_MAX;
+unsigned long long mem_min = 0;
+unsigned long long mem_max = ULONG_MAX;
void die(char *fmt, ...)
{
@@ -187,7 +187,7 @@ unsigned long locate_hole(struct kexec_i
}
/* Compute the free memory ranges */
- max_mem_ranges = memory_ranges + (info->nr_segments -1);
+ max_mem_ranges = memory_ranges + info->nr_segments;
mem_range = malloc(max_mem_ranges *sizeof(struct memory_range));
mem_ranges = 0;
@@ -323,12 +323,17 @@ unsigned long add_buffer(struct kexec_in
{
unsigned long base;
int result;
+ int pagesize;
result = sort_segments(info);
if (result < 0) {
die("sort_segments failed\n");
}
+ /* Round memsz up to a multiple of pagesize */
+ pagesize = getpagesize();
+ memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1);
+
base = locate_hole(info, memsz, buf_align, buf_min, buf_max, buf_end);
if (base == ULONG_MAX) {
die("locate_hole failed\n");
@@ -507,6 +512,8 @@ static int my_load(const char *type, int
info.segment = NULL;
info.nr_segments = 0;
info.entry = NULL;
+ info.backup_start = 0;
+ info.kexec_flags = kexec_flags;
result = 0;
if (argc - fileind <= 0) {
@@ -522,7 +529,8 @@ static int my_load(const char *type, int
kernel_buf, kernel_size);
#endif
- if (get_memory_ranges(&memory_range, &memory_ranges) < 0) {
+ if (get_memory_ranges(&memory_range, &memory_ranges,
+ info.kexec_flags) < 0) {
fprintf(stderr, "Could not get memory layout\n");
return -1;
}
@@ -564,7 +572,7 @@ static int my_load(const char *type, int
return -1;
}
/* If we are not in native mode setup an appropriate trampoline */
- if (arch_compat_trampoline(&info, &kexec_flags) < 0) {
+ if (arch_compat_trampoline(&info) < 0) {
return -1;
}
/* Verify all of the segments load to a valid location in memory */
@@ -585,17 +593,17 @@ static int my_load(const char *type, int
update_purgatory(&info);
#if 0
fprintf(stderr, "kexec_load: entry = %p flags = %lx\n",
- info.entry, kexec_flags);
+ info.entry, info.kexec_flags);
print_segments(stderr, &info);
#endif
result = kexec_load(
- info.entry, info.nr_segments, info.segment, kexec_flags);
+ info.entry, info.nr_segments, info.segment, info.kexec_flags);
if (result != 0) {
/* The load failed, print some debugging information */
fprintf(stderr, "kexec_load failed: %s\n",
strerror(errno));
fprintf(stderr, "entry = %p flags = %lx\n",
- info.entry, kexec_flags);
+ info.entry, info.kexec_flags);
print_segments(stderr, &info);
}
return result;
diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec-elf.c kexec-tools-1.101-kdump/kexec/kexec-elf.c
--- kexec-tools-1.101/kexec/kexec-elf.c 2004-12-20 14:58:30.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/kexec-elf.c 2006-02-22 11:30:11.084107504 +0530
@@ -113,21 +113,21 @@ static int build_mem_elf32_ehdr(const ch
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_entry) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
/* entry is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_entry to large\n");
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_phoff) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
/* phoff is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_phoff to large\n");
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_shoff) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
/* shoff is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_shoff to large\n");
@@ -146,7 +146,7 @@ static int build_mem_elf32_ehdr(const ch
ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
if ((ehdr->e_phnum > 0) &&
- (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr)))
+ (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr)))
{
/* Invalid program header size */
if (probe_debug) {
@@ -185,21 +185,21 @@ static int build_mem_elf64_ehdr(const ch
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_entry) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
/* entry is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_entry to large\n");
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_phoff) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
/* phoff is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_phoff to large\n");
}
return -1;
}
- if (elf32_to_cpu(ehdr, lehdr.e_shoff) > ULONG_MAX) {
+ if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
/* shoff is to large */
if (probe_debug) {
fprintf(stderr, "ELF e_shoff to large\n");
@@ -218,7 +218,7 @@ static int build_mem_elf64_ehdr(const ch
ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
if ((ehdr->e_phnum > 0) &&
- (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr)))
+ (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr)))
{
/* Invalid program header size */
if (probe_debug) {
@@ -302,7 +302,7 @@ static int build_mem_ehdr(const char *bu
return 0;
}
-static int build_mem_elf32_phdr(const char *buf, off_t len,
+static int build_mem_elf32_phdr(const char *buf, off_t len,
struct mem_ehdr *ehdr, int idx)
{
struct mem_phdr *phdr;
@@ -312,12 +312,12 @@ static int build_mem_elf32_phdr(const ch
phdr = &ehdr->e_phdr[idx];
memcpy(&lphdr, pbuf, sizeof(lphdr));
- if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lphdr.p_align) > ULONG_MAX))
+ if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_memsz) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_offset) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_paddr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_vaddr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_align) > UINT32_MAX))
{
fprintf(stderr, "Program segment size out of range\n");
return -1;
@@ -345,12 +345,12 @@ static int build_mem_elf64_phdr(const ch
phdr = &ehdr->e_phdr[idx];
memcpy(&lphdr, pbuf, sizeof(lphdr));
- if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lphdr.p_align) > ULONG_MAX))
+ if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_memsz) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_offset) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_paddr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_vaddr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_align) > UINT64_MAX))
{
fprintf(stderr, "Program segment size out of range\n");
return -1;
@@ -388,7 +388,7 @@ static int build_mem_phdrs(const char *b
fprintf(stderr, "Invalid ei_class?\n");
return -1;
}
- phdr_size *= ehdr->e_phnum;
+ phdr_size *= ehdr->e_phnum;
if (ehdr->e_phoff + phdr_size > len) {
/* The program header did not fit in the file buffer */
if (probe_debug) {
@@ -396,7 +396,7 @@ static int build_mem_phdrs(const char *b
}
return -1;
}
-
+
/* Allocate the e_phdr array */
mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum;
ehdr->e_phdr = xmalloc(mem_phdr_size);
@@ -440,7 +440,7 @@ static int build_mem_phdrs(const char *b
return 0;
}
-static int build_mem_elf32_shdr(const char *buf, off_t len,
+static int build_mem_elf32_shdr(const char *buf, off_t len,
struct mem_ehdr *ehdr, int idx)
{
struct mem_shdr *shdr;
@@ -451,12 +451,12 @@ static int build_mem_elf32_shdr(const ch
shdr = &ehdr->e_shdr[idx];
memcpy(&lshdr, sbuf, sizeof(lshdr));
- if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) ||
- (elf32_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_MAX))
+ if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_addr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_offset) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_size) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_addralign) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_entsize) > UINT32_MAX))
{
fprintf(stderr, "Program section size out of range\n");
return -1;
@@ -510,7 +510,7 @@ static int build_mem_elf32_shdr(const ch
return 0;
}
-static int build_mem_elf64_shdr(const char *buf, off_t len,
+static int build_mem_elf64_shdr(const char *buf, off_t len,
struct mem_ehdr *ehdr, int idx)
{
struct mem_shdr *shdr;
@@ -521,12 +521,12 @@ static int build_mem_elf64_shdr(const ch
shdr = &ehdr->e_shdr[idx];
memcpy(&lshdr, sbuf, sizeof(lshdr));
- if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) ||
- (elf64_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_MAX))
+ if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_addr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_offset) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_size) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_addralign) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_entsize) > UINT64_MAX))
{
fprintf(stderr, "Program section size out of range\n");
return -1;
@@ -608,7 +608,7 @@ static int build_mem_shdrs(const char *b
}
return -1;
}
-
+
/* Allocate the e_shdr array */
mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum;
ehdr->e_shdr = xmalloc(mem_shdr_size);
@@ -635,7 +635,7 @@ static int build_mem_shdrs(const char *b
{
/* The section does not fit in the buffer */
if (probe_debug) {
- fprintf(stderr, "ELF section %d not in file\n",
+ fprintf(stderr, "ELF section %d not in file\n",
i);
}
return -1;
@@ -648,19 +648,19 @@ static int build_mem_shdrs(const char *b
return -1;
}
/* Remember where the section lives in the buffer */
- shdr->sh_data = buf + shdr->sh_offset;
+ shdr->sh_data = (unsigned char *)(buf + shdr->sh_offset);
}
return 0;
}
-static void read_nhdr(const struct mem_ehdr *ehdr,
+static void read_nhdr(const struct mem_ehdr *ehdr,
ElfNN_Nhdr *hdr, const unsigned char *note)
{
memcpy(hdr, note, sizeof(*hdr));
hdr->n_namesz = elf32_to_cpu(ehdr, hdr->n_namesz);
hdr->n_descsz = elf32_to_cpu(ehdr, hdr->n_descsz);
hdr->n_type = elf32_to_cpu(ehdr, hdr->n_type);
-
+
}
static int build_mem_notes(const char *buf, off_t len, struct mem_ehdr *ehdr)
{
@@ -672,7 +672,7 @@ static int build_mem_notes(const char *b
for(i = 0; !note_start && (i < ehdr->e_phnum); i++) {
struct mem_phdr *phdr = &ehdr->e_phdr[i];
if (phdr->p_type == PT_NOTE) {
- note_start = phdr->p_data;
+ note_start = (unsigned char *)phdr->p_data;
note_end = note_start + phdr->p_filesz;
}
}
@@ -686,7 +686,7 @@ static int build_mem_notes(const char *b
if (!note_start) {
return 0;
}
-
+
/* Walk through and count the notes */
ehdr->e_notenum = 0;
for(note = note_start; note < note_end; note+= note_size) {
@@ -708,15 +708,15 @@ static int build_mem_notes(const char *b
note_size += (hdr.n_namesz + 3) & ~3;
desc = note + note_size;
note_size += (hdr.n_descsz + 3) & ~3;
-
+
if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) {
die("Note name is not null termiated");
}
ehdr->e_note[i].n_type = hdr.n_type;
- ehdr->e_note[i].n_name = name;
+ ehdr->e_note[i].n_name = (char *)name;
ehdr->e_note[i].n_desc = desc;
ehdr->e_note[i].n_descsz = hdr.n_descsz;
-
+
}
return 0;
}
diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec-elf-rel.c kexec-tools-1.101-kdump/kexec/kexec-elf-rel.c
--- kexec-tools-1.101/kexec/kexec-elf-rel.c 2005-01-13 18:34:21.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/kexec-elf-rel.c 2006-02-22 11:30:11.085107352 +0530
@@ -155,7 +155,7 @@ int build_elf_rel_info(const char *buf,
if (probe_debug) {
fprintf(stderr, "No ELF section headers\n");
}
- return -1;
+ return -1;
}
if (!machine_verify_elf_rel(ehdr)) {
/* It does not meant the native architecture constraints */
@@ -251,7 +251,7 @@ int elf_rel_load(struct mem_ehdr *ehdr,
/* Allocate where we will put the relocated object */
buf = xmalloc(bufsz);
- buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz,
+ buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz,
buf_align, min, max, end);
ehdr->rel_addr = buf_addr;
ehdr->rel_size = bufsz + bss_pad + bsssz;
@@ -269,7 +269,7 @@ int elf_rel_load(struct mem_ehdr *ehdr,
unsigned long off;
/* Adjust the address */
data_addr = (data_addr + (align - 1)) & ~(align -1);
-
+
/* Update the section */
off = data_addr - buf_addr;
memcpy(buf + off, shdr->sh_data, shdr->sh_size);
@@ -306,7 +306,7 @@ int elf_rel_load(struct mem_ehdr *ehdr,
continue;
}
if ((shdr->sh_info > ehdr->e_shnum) ||
- (shdr->sh_link > ehdr->e_shnum))
+ (shdr->sh_link > ehdr->e_shnum))
{
die("Invalid section number\n");
}
@@ -350,12 +350,12 @@ int elf_rel_load(struct mem_ehdr *ehdr,
/* The final address of that location */
address = section->sh_addr + rel.r_offset;
-
+
/* The relevant symbol */
sym = elf_sym(ehdr, symtab->sh_data + (rel.r_sym * elf_sym_size(ehdr)));
-#if 0
+#ifdef DEBUG
fprintf(stderr, "sym: %10s info: %02x other: %02x shndx: %lx value: %lx size: %lx\n",
- strtab + sym.st_name,
+ strtab + sym.st_name,
sym.st_info,
sym.st_other,
sym.st_shndx,
@@ -364,8 +364,19 @@ int elf_rel_load(struct mem_ehdr *ehdr,
#endif
if (sym.st_shndx == STN_UNDEF) {
- die("Undefined symbol: %s\n",
+ /*
+ * NOTE: ppc64 elf .ro shows up a UNDEF section.
+ * From Elf 1.2 Spec:
+ * Relocation Entries: If the index is STN_UNDEF,
+ * the undefined symbol index, the relocation uses 0
+ * as the "symbol value".
+ * So, is this really an error condition to flag die?
+ */
+ /*
+ die("Undefined symbol: %s\n",
strtab + sym.st_name);
+ */
+ continue;
}
sec_base = 0;
if (sym.st_shndx == SHN_COMMON) {
@@ -383,14 +394,14 @@ int elf_rel_load(struct mem_ehdr *ehdr,
else {
sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr;
}
-#if 0
+#ifdef DEBUG
fprintf(stderr, "sym: %s value: %lx addr: %lx\n",
strtab + sym.st_name, value, address);
#endif
value = sym.st_value;
value += sec_base;
value += rel.r_addend;
- machine_apply_elf_rel(ehdr, rel.r_type,
+ machine_apply_elf_rel(ehdr, rel.r_type,
(void *)location, address, value);
}
}
@@ -399,14 +410,14 @@ int elf_rel_load(struct mem_ehdr *ehdr,
return result;
}
-void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
+void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len, unsigned long min, unsigned long max,
int end)
{
int result;
/* Parse the Elf file */
- result = build_elf_rel_info(purgatory, purgatory_size, ehdr);
+ result = build_elf_rel_info((char *)purgatory, purgatory_size, ehdr);
if (result < 0) {
die("ELF rel parse failed\n");
}
@@ -439,7 +450,7 @@ int elf_rel_find_symbol(struct mem_ehdr
/* Invalid strtab section number? */
continue;
}
- strtab = ehdr->e_shdr[shdr->sh_link].sh_data;
+ strtab = (char *)ehdr->e_shdr[shdr->sh_link].sh_data;
/* Walk through the symbol table and find the symbol */
sym_size = elf_sym_size(ehdr);
sym_end = shdr->sh_data + shdr->sh_size;
@@ -452,8 +463,8 @@ int elf_rel_find_symbol(struct mem_ehdr
if (strcmp(strtab + sym.st_name, name) != 0) {
continue;
}
- if ((sym.st_shndx == STN_UNDEF) ||
- (sym.st_shndx > ehdr->e_shnum))
+ if ((sym.st_shndx == STN_UNDEF) ||
+ (sym.st_shndx > ehdr->e_shnum))
{
die("Symbol: %s has Bad section index %d\n",
name, sym.st_shndx);
@@ -491,7 +502,7 @@ void elf_rel_set_symbol(struct mem_ehdr
result = elf_rel_find_symbol(ehdr, name, &sym);
if (result < 0) {
- die("Symbol: %s not found cannot set\n",
+ die("Symbol: %s not found cannot set\n",
name);
}
if (sym.st_size != size) {
diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec.h kexec-tools-1.101-kdump/kexec/kexec.h
--- kexec-tools-1.101/kexec/kexec.h 2005-01-13 18:33:00.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/kexec.h 2006-01-19 18:19:07.000000000 +0530
@@ -91,6 +91,8 @@ do { \
} while(0)
#endif
+extern unsigned long long mem_min, mem_max;
+
struct kexec_segment {
const void *buf;
size_t bufsz;
@@ -112,10 +114,13 @@ struct kexec_info {
int nr_segments;
void *entry;
struct mem_ehdr rhdr;
+ unsigned long backup_start;
+ unsigned long kexec_flags;
};
void usage(void);
-int get_memory_ranges(struct memory_range **range, int *ranges);
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags);
int valid_memory_range(unsigned long sstart, unsigned long send);
int valid_memory_segment(struct kexec_segment *segment);
void print_segments(FILE *file, struct kexec_info *info);
@@ -188,7 +193,8 @@ extern size_t purgatory_size;
void arch_usage(void);
int arch_process_options(int argc, char **argv);
-int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags);
+int arch_compat_trampoline(struct kexec_info *info);
void arch_update_purgatory(struct kexec_info *info);
+#define MAX_LINE 160
#endif /* KEXEC_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec-syscall.h kexec-tools-1.101-kdump/kexec/kexec-syscall.h
--- kexec-tools-1.101/kexec/kexec-syscall.h 2005-01-06 12:29:50.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/kexec-syscall.h 2006-01-19 11:41:43.000000000 +0530
@@ -37,6 +37,12 @@
#ifdef __x86_64__
#define __NR_kexec_load 246
#endif
+#ifdef __s390x__
+#define __NR_kexec_load 277
+#endif
+#ifdef __s390__
+#define __NR_kexec_load 277
+#endif
#ifndef __NR_kexec_load
#error Unknown processor architecture. Needs a kexec_load syscall number.
#endif
@@ -67,7 +73,8 @@ static inline long kexec_reboot(void)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)
#define KEXEC_ARCH_IA_64 (50 << 16)
+#define KEXEC_ARCH_S390 (22 << 16)
-#define KEXEC_MAX_SEGMENTS 8
+#define KEXEC_MAX_SEGMENTS 16
#endif /* KEXEC_SYSCALL_H */
diff -urNp -X dontdiff kexec-tools-1.101/kexec/Makefile kexec-tools-1.101-kdump/kexec/Makefile
--- kexec-tools-1.101/kexec/Makefile 2004-12-22 01:06:39.000000000 +0530
+++ kexec-tools-1.101-kdump/kexec/Makefile 2006-01-19 18:19:07.000000000 +0530
@@ -15,6 +15,7 @@ KEXEC_C_SRCS+= kexec/kexec-elf.c
KEXEC_C_SRCS+= kexec/kexec-elf-exec.c
KEXEC_C_SRCS+= kexec/kexec-elf-rel.c
KEXEC_C_SRCS+= kexec/kexec-elf-boot.c
+KEXEC_C_SRCS+= kexec/crashdump.c
KEXEC_C_SRCS+= $(PURGATORY_HEX_C)
KEXEC_S_SRCS:=
include kexec/arch/$(ARCH)/Makefile
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/i386/crashdump_backup.c kexec-tools-1.101-kdump/purgatory/arch/i386/crashdump_backup.c
--- kexec-tools-1.101/purgatory/arch/i386/crashdump_backup.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/i386/crashdump_backup.c 2006-01-19 11:41:41.000000000 +0530
@@ -0,0 +1,46 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Vivek goyal (vgoyal@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <stdint.h>
+#include <string.h>
+
+#define BACKUP_REGION_SOURCE 0x00000000
+#define BACKUP_REGION_SIZE 0xa0000
+
+/* Backup region start gets set after /proc/iomem has been parsed. */
+/* We reuse the same code for x86_64 also so changing backup_start to
+ unsigned long */
+unsigned long backup_start = 0;
+
+/* Backup first 640K of memory to backup region as reserved by kexec.
+ * Assuming first 640K has to be present on i386 machines and no address
+ * validity checks have to be performed. */
+
+void crashdump_backup_memory(void)
+{
+ void *dest, *src;
+
+ src = (void *) BACKUP_REGION_SOURCE;
+
+ if (backup_start) {
+ dest = (void *)(backup_start);
+ memcpy(dest, src, BACKUP_REGION_SIZE);
+ }
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/i386/Makefile kexec-tools-1.101-kdump/purgatory/arch/i386/Makefile
--- kexec-tools-1.101/purgatory/arch/i386/Makefile 2005-01-11 06:37:58.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/i386/Makefile 2006-01-19 11:41:27.000000000 +0530
@@ -12,3 +12,4 @@ PURGATORY_C_SRCS+= purgatory/arch/i386/p
PURGATORY_C_SRCS+= purgatory/arch/i386/console-x86.c
PURGATORY_C_SRCS+= purgatory/arch/i386/vga.c
PURGATORY_C_SRCS+= purgatory/arch/i386/pic.c
+PURGATORY_C_SRCS+= purgatory/arch/i386/crashdump_backup.c
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/i386/purgatory-x86.c kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.c
--- kexec-tools-1.101/purgatory/arch/i386/purgatory-x86.c 2004-12-21 21:59:48.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.c 2006-01-19 11:41:27.000000000 +0530
@@ -30,6 +30,7 @@ void x86_setup_cpu(void)
uint8_t reset_vga = 0;
uint8_t legacy_timer = 0;
uint8_t legacy_pic = 0;
+uint8_t panic_kernel = 0;
void setup_arch(void)
{
@@ -38,3 +39,9 @@ void setup_arch(void)
if (legacy_pic) x86_setup_legacy_pic();
/* if (legacy_timer) x86_setup_legacy_timer(); */
}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel) crashdump_backup_memory();
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/i386/purgatory-x86.h kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.h
--- kexec-tools-1.101/purgatory/arch/i386/purgatory-x86.h 2004-12-20 17:52:26.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.h 2006-01-19 11:41:27.000000000 +0530
@@ -4,5 +4,6 @@
void x86_reset_vga(void);
void x86_setup_legacy_pic(void);
void x86_setup_legacy_timer(void);
+void crashdump_backup_memory(void);
#endif /* PURGATORY_X86_H */
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/purgatory-ia64.c kexec-tools-1.101-kdump/purgatory/arch/ia64/purgatory-ia64.c
--- kexec-tools-1.101/purgatory/arch/ia64/purgatory-ia64.c 2004-12-21 04:15:21.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ia64/purgatory-ia64.c 2006-01-19 11:41:27.000000000 +0530
@@ -5,3 +5,9 @@ void setup_arch(void)
{
/* Nothing for now */
}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ /* Nothing for now */
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc/purgatory-ppc.c kexec-tools-1.101-kdump/purgatory/arch/ppc/purgatory-ppc.c
--- kexec-tools-1.101/purgatory/arch/ppc/purgatory-ppc.c 2004-12-21 04:17:43.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc/purgatory-ppc.c 2006-01-19 11:41:27.000000000 +0530
@@ -5,3 +5,9 @@ void setup_arch(void)
{
/* Nothing for now */
}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ /* Nothing for now */
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/console-ppc64.c kexec-tools-1.101-kdump/purgatory/arch/ppc64/console-ppc64.c
--- kexec-tools-1.101/purgatory/arch/ppc64/console-ppc64.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/console-ppc64.c 2006-01-19 18:20:08.000000000 +0530
@@ -0,0 +1,27 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <purgatory.h>
+
+void putchar(int c)
+{
+ return;
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/crashdump_backup.c kexec-tools-1.101-kdump/purgatory/arch/ppc64/crashdump_backup.c
--- kexec-tools-1.101/purgatory/arch/ppc64/crashdump_backup.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/crashdump_backup.c 2006-01-19 18:20:08.000000000 +0530
@@ -0,0 +1,41 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <stdint.h>
+#include <string.h>
+
+#define BACKUP_REGION_SOURCE 0x0
+#define BACKUP_REGION_SIZE 32*1024
+
+extern unsigned long backup_start;
+
+/* Backup first 32KB of memory to backup region reserved by kexec */
+void crashdump_backup_memory(void)
+{
+ void *dest, *src;
+
+ src = (void *)BACKUP_REGION_SOURCE;
+
+ if (backup_start) {
+ dest = (void *)(backup_start);
+ memcpy(dest, src, BACKUP_REGION_SIZE);
+ }
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/Makefile kexec-tools-1.101-kdump/purgatory/arch/ppc64/Makefile
--- kexec-tools-1.101/purgatory/arch/ppc64/Makefile 2004-12-17 11:00:20.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/Makefile 2006-01-19 18:20:08.000000000 +0530
@@ -2,6 +2,7 @@
# Purgatory ppc
#
-PURGATORY_C_SRCS+=
-PURGATORY_S_SRCS+=
-
+PURGATORY_S_SRCS+= purgatory/arch/ppc64/v2wrap.S
+PURGATORY_C_SRCS += purgatory/arch/ppc64/purgatory-ppc64.c
+PURGATORY_C_SRCS += purgatory/arch/ppc64/console-ppc64.c
+PURGATORY_C_SRCS += purgatory/arch/ppc64/crashdump_backup.c
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/purgatory-ppc64.c kexec-tools-1.101-kdump/purgatory/arch/ppc64/purgatory-ppc64.c
--- kexec-tools-1.101/purgatory/arch/ppc64/purgatory-ppc64.c 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/purgatory-ppc64.c 2006-01-19 18:20:08.000000000 +0530
@@ -0,0 +1,41 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <purgatory.h>
+#include "purgatory-ppc64.h"
+
+unsigned int panic_kernel = 0;
+unsigned long backup_start = 0;
+unsigned long stack = 0;
+unsigned long dt_offset = 0;
+unsigned long my_toc = 0;
+unsigned long kernel = 0;
+
+void setup_arch(void)
+{
+ return;
+}
+
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel)
+ crashdump_backup_memory();
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/purgatory-ppc64.h kexec-tools-1.101-kdump/purgatory/arch/ppc64/purgatory-ppc64.h
--- kexec-tools-1.101/purgatory/arch/ppc64/purgatory-ppc64.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/purgatory-ppc64.h 2006-01-19 18:20:08.000000000 +0530
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_PPC64_H
+#define PURGATORY_PPC64_H
+
+void crashdump_backup_memory(void);
+
+#endif /* PURGATORY_PPC64_H */
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ppc64/v2wrap.S kexec-tools-1.101-kdump/purgatory/arch/ppc64/v2wrap.S
--- kexec-tools-1.101/purgatory/arch/ppc64/v2wrap.S 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/ppc64/v2wrap.S 2006-01-19 18:20:08.000000000 +0530
@@ -0,0 +1,128 @@
+#
+# kexec: Linux boots Linux
+#
+# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
+# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation
+#
+# 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.
+#
+
+# v2wrap.S
+# a wrapper to call purgatory code to backup first
+# 32kB of first kernel into the backup region
+# reserved by kexec-tools.
+# Invokes ppc64 kernel with the expected arguments
+# of kernel(device-tree, phys-offset, 0)
+
+#
+# calling convention:
+# r3 = physical number of this cpu (all cpus)
+# r4 = address of this chunk (master only)
+# master enters at purgatory_start (aka first byte of this chunk)
+# slaves (additional cpus), if any, enter a copy of the
+# first 0x100 bytes of this code relocated to 0x0
+#
+# in other words,
+# a copy of the first 0x100 bytes of this code is copied to 0
+# and the slaves are sent to address 0x60
+# with r3 = their physical cpu number.
+
+#define LOADADDR(rn,name) \
+ lis rn,name##@highest; \
+ ori rn,rn,name##@higher; \
+ rldicr rn,rn,32,31; \
+ oris rn,rn,name##@h; \
+ ori rn,rn,name##@l
+
+# look a bit like a Linux kernel here ...
+ .machine ppc64
+ .globl purgatory_start
+purgatory_start: b master
+ tweq 0,0
+master:
+ or 1,1,1 # low priority to let other thread catchup
+ isync
+ mr 17,3 # save cpu id to r17
+ mr 15,4 # save physical address in reg15
+
+ LOADADDR(6,my_toc)
+ ld 2,0(6) #setup toc
+
+ LOADADDR(6,stack)
+ ld 1,0(6) #setup stack
+
+ subi 1,1,112
+ bl .purgatory
+ nop
+
+ b 81f
+ .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys
+slave:
+ # load slave spin code address and branch into that
+ LOADADDR(6,slave_spin)
+ ld 4,0(6)
+ mtctr 4
+ bctr
+
+spin: .long 1
+slave_spin_code:
+ lis 5,spin@ha
+ lwz 5,spin@l(5)
+ cmpwi 0,5,0
+ bne slave_spin_code
+ ba 0x60
+
+81: # master continues here
+ or 3,3,3 # ok back to high, lets boot
+ lis 6,0x1
+ mtctr 6 # delay a bit for slaves to catch up
+83: bdnz 83b # before we overwrite 0-100 again
+
+ LOADADDR(16, dt_offset)
+ ld 3,0(16) # load device-tree address
+ mr 16,3 # save dt address in reg16
+ lwz 6,20(3) # fetch version number
+ cmpwi 0,6,2 # v2 ?
+ blt 80f
+ stw 17,28(3) # save my cpu number as boot_cpu_phys
+80:
+ LOADADDR(6,kernel)
+ ld 4,0(6) # load the kernel address
+
+ addi 5,4,-8 # prepare copy with update form instructions
+ li 6,0x100/8
+ mtctr 6
+ li 6,-8
+85: ldu 7,8(5)
+ stdu 7,8(6)
+ bdnz 85b
+
+ li 5,0 # r5 will be 0 for kernel
+ dcbst 0,5 # store dcache, flush icache
+ dcbst 0,6 # 0 and 0xf8 covers us with 128 byte lines
+ mtctr 4 # prepare branch too
+ sync
+ icbi 0,5
+ icbi 0,6
+ sync
+ isync
+ lis 6,spin@ha
+ li 0,0
+ stw 0,spin@l(6)
+ mr 3,16 # restore dt address
+
+ bctr # start kernel
+
+slave_spin: .llong slave_spin_code
+
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/s390/include/limits.h kexec-tools-1.101-kdump/purgatory/arch/s390/include/limits.h
--- kexec-tools-1.101/purgatory/arch/s390/include/limits.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/s390/include/limits.h 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,54 @@
+#ifndef _LIMITS_H_
+#define _LIMITS_H_
+
+/* Number of bits in a `char'. */
+# define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold. */
+# define SCHAR_MIN (-128)
+# define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+# define UCHAR_MAX 255
+
+# define CHAR_MIN SCHAR_MIN
+# define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold. */
+# define SHRT_MIN (-32768)
+# define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short int' can hold. (Minimum is 0.) */
+# define USHRT_MAX 65535
+
+/* Minimum and maximum values a `signed int' can hold. */
+# define INT_MIN (-INT_MAX - 1)
+# define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+# define UINT_MAX 4294967295U
+
+/* Minimum and maximum values a `signed long int' can hold. */
+#ifdef __s390x__
+# define LONG_MAX 9223372036854775807L
+#else
+# define LONG_MAX 2147483647L
+#endif
+
+# define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long int' can hold. (Minimum is 0.) */
+#ifdef __s390x__
+# define ULONG_MAX 18446744073709551615UL
+#else
+# define ULONG_MAX 4294967295UL
+#endif
+
+/* Minimum and maximum values a `signed long long int' can hold. */
+# define LLONG_MAX 9223372036854775807LL
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+
+/* Maximum value an `unsigned long long int' can hold. (Minimum is 0.) */
+# define ULLONG_MAX 18446744073709551615ULL
+
+#endif /* !_LIMITS_H_ */
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/s390/include/stdint.h kexec-tools-1.101-kdump/purgatory/arch/s390/include/stdint.h
--- kexec-tools-1.101/purgatory/arch/s390/include/stdint.h 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/s390/include/stdint.h 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,24 @@
+#ifndef _STDINT_H
+#define _STDINT_H
+
+typedef unsigned long size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+#ifdef __s390x__
+typedef unsigned long uint64_t;
+#else
+typedef unsigned long long uint64_t;
+#endif
+
+typedef signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+#ifdef __s390x__
+typedef long int64_t;
+#else
+typedef long long int64_t;
+#endif
+
+#endif
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/s390/Makefile kexec-tools-1.101-kdump/purgatory/arch/s390/Makefile
--- kexec-tools-1.101/purgatory/arch/s390/Makefile 1970-01-01 05:30:00.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/s390/Makefile 2006-01-19 11:41:37.000000000 +0530
@@ -0,0 +1,7 @@
+#
+# Purgatory s390
+#
+
+PURGATORY_C_SRCS+=
+PURGATORY_S_SRCS+=
+
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/x86_64/Makefile kexec-tools-1.101-kdump/purgatory/arch/x86_64/Makefile
--- kexec-tools-1.101/purgatory/arch/x86_64/Makefile 2004-12-21 12:43:53.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/x86_64/Makefile 2006-01-19 11:41:41.000000000 +0530
@@ -9,6 +9,7 @@ PURGATORY_S_SRCS+= purgatory/arch/x86_64
PURGATORY_S_SRCS+= purgatory/arch/x86_64/setup-x86_64.S
PURGATORY_S_SRCS+= purgatory/arch/x86_64/stack.S
PURGATORY_C_SRCS+= purgatory/arch/x86_64/purgatory-x86_64.c
+PURGATORY_C_SRCS+= purgatory/arch/i386/crashdump_backup.c
PURGATORY_C_SRCS+= purgatory/arch/i386/console-x86.c
PURGATORY_C_SRCS+= purgatory/arch/i386/vga.c
PURGATORY_C_SRCS+= purgatory/arch/i386/pic.c
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/x86_64/purgatory-x86_64.c kexec-tools-1.101-kdump/purgatory/arch/x86_64/purgatory-x86_64.c
--- kexec-tools-1.101/purgatory/arch/x86_64/purgatory-x86_64.c 2004-12-21 22:07:41.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/arch/x86_64/purgatory-x86_64.c 2006-01-19 11:41:41.000000000 +0530
@@ -4,9 +4,16 @@
uint8_t reset_vga = 0;
uint8_t legacy_pic = 0;
+uint8_t panic_kernel = 0;
void setup_arch(void)
{
if (reset_vga) x86_reset_vga();
if (legacy_pic) x86_setup_legacy_pic();
}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel) crashdump_backup_memory();
+}
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/include/purgatory.h kexec-tools-1.101-kdump/purgatory/include/purgatory.h
--- kexec-tools-1.101/purgatory/include/purgatory.h 2004-12-18 18:42:15.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/include/purgatory.h 2006-01-19 11:41:27.000000000 +0530
@@ -4,5 +4,6 @@
void putchar(int ch);
void printf(const char *fmt, ...);
void setup_arch(void);
+void post_verification_setup_arch(void);
#endif /* PURGATORY_H */
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/Makefile kexec-tools-1.101-kdump/purgatory/Makefile
--- kexec-tools-1.101/purgatory/Makefile 2005-01-09 04:06:32.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/Makefile 2006-01-19 18:20:08.000000000 +0530
@@ -6,6 +6,11 @@
# There is probably a cleaner way to do this but for now this
# should keep us from accidentially include unsafe library functions
# or headers.
+
+ifeq ($(ARCH),ppc64)
+LDFLAGS = -melf64ppc
+endif
+
PCFLAGS:=-Wall -Os \
-I$(shell $(CC) -print-file-name=include) \
-Ipurgatory/include -Ipurgatory/arch/$(ARCH)/include \
@@ -16,11 +21,11 @@ PCFLAGS += $(call cc-option, -fnobuiltin
PCFLAGS += $(call cc-option, -fnostdinc)
PCFLAGS += $(call cc-option, -fno-zero-initialized-in-bss)
-PURGATORY_C_SRCS:=
+PURGATORY_C_SRCS:=
PURGATORY_C_SRCS += purgatory/purgatory.c
PURGATORY_C_SRCS += purgatory/printf.c
PURGATORY_C_SRCS += purgatory/string.c
-PURGATORY_S_OBJS:=
+PURGATORY_S_OBJS:=
include purgatory/arch/$(ARCH)/Makefile
diff -urNp -X dontdiff kexec-tools-1.101/purgatory/purgatory.c kexec-tools-1.101-kdump/purgatory/purgatory.c
--- kexec-tools-1.101/purgatory/purgatory.c 2004-12-22 00:21:03.000000000 +0530
+++ kexec-tools-1.101-kdump/purgatory/purgatory.c 2006-01-19 11:41:27.000000000 +0530
@@ -44,4 +44,5 @@ void purgatory(void)
printf("I'm in purgatory\n");
setup_arch();
verify_sha256_digest();
+ post_verification_setup_arch();
}