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-07-17 00:20:44.000000000 +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-07-17 00:20:43.000000000 +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=],[select directory for object files]), [ OBJDIR="$withval" ], [ OBJDIR="$OBJDIR" ]) diff -urNp -X dontdiff kexec-tools-1.101/kdump/kdump.8 kexec-tools-1.101-kdump/kdump/kdump.8 --- kexec-tools-1.101/kdump/kdump.8 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/kdump/kdump.8 2006-07-17 00:20:49.000000000 +0530 @@ -0,0 +1,39 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH KDUMP 8 "Jul 27, 2005" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +kdump \- This is just a placeholder until real man page has been written +.SH SYNOPSIS +.B kdump +.RI [ options ] " start_address" ... +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBkdump\fP does not have a man page yet. +.SH OPTIONS +.\"These programs follow the usual GNU command line syntax, with long +.\"options starting with two dashes (`-'). +.\"A summary of options is included below. +.\"For a complete description, see the Info files. +.SH SEE ALSO +.SH AUTHOR +kdump was written by Eric Biederman. +.PP +This manual page was written by Khalid Aziz , +for the Debian project (but may be used by others). 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-07-17 00:20:45.000000000 +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/kdump/Makefile kexec-tools-1.101-kdump/kdump/Makefile --- kexec-tools-1.101/kdump/Makefile 2005-02-06 07:10:36.000000000 +0530 +++ kexec-tools-1.101-kdump/kdump/Makefile 2006-07-17 00:20:49.000000000 +0530 @@ -10,6 +10,7 @@ KDUMP_SRCS:= $(KDUMP_C_SRCS) KDUMP_OBJS:= $(KDUMP_C_OBJS) KDUMP_DEPS:= $(KDUMP_C_DEPS) KDUMP:= $(SBINDIR)/kdump +KDUMP_MANPAGE:= $(MANDIR)/man8/kdump.8 include $(KDUMP_DEPS) @@ -25,6 +26,9 @@ $(KDUMP): $(KDUMP_OBJS) mkdir -p $(@D) $(CC) $(CFLAGS) -o $@ $(KDUMP_OBJS) +$(KDUMP_MANPAGE): kdump/kdump.8 + $(MKDIR) -p $(MANDIR)/man8 + cp kdump/kdump.8 $(KDUMP_MANPAGE) echo:: @echo "KDUMP_C_SRCS $(KDUMP_C_SRCS)" @echo "KDUMP_C_DEPS $(KDUMP_C_DEPS)" 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-07-17 00:20:42.000000000 +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-x86.h" +#include "crashdump-x86.h" +#include + +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, ¬es_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, ¬es_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-07-17 00:19:59.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-07-17 00:20:11.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-07-17 00:19:56.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-07-17 00:20:02.000000000 +0530 @@ -32,10 +32,12 @@ #include #include #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 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", ®s, 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-07-17 00:19:56.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-07-17 00:20:25.000000000 +0530 @@ -30,14 +30,14 @@ #include "../../kexec-elf.h" #include "../../kexec-syscall.h" #include "kexec-x86.h" +#include "crashdump-x86.h" #include -#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= 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-07-17 00:19:58.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-07-17 00:19:58.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-07-17 00:20:37.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-07-17 00:19:56.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/crashdump-ia64.c kexec-tools-1.101-kdump/kexec/arch/ia64/crashdump-ia64.c --- kexec-tools-1.101/kexec/arch/ia64/crashdump-ia64.c 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/crashdump-ia64.c 2006-07-17 00:21:02.000000000 +0530 @@ -0,0 +1,351 @@ +/* + * kexec: crashdum support + * Copyright (C) 2005-2006 Zou Nan hai Intel Corp + * + * 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 +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-ia64.h" +#include "crashdump-ia64.h" + +int memory_ranges = 0; +#define LOAD_OFFSET (0xa000000000000000UL + 0x100000000UL - (1UL<<26)) +#define MAX_LINE 160 +/* 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; +unsigned long elfcorehdr; +static unsigned long kernel_code_start; +struct loaded_segment { + unsigned long start; + unsigned long end; + unsigned long reserved; +}; + +#define MAX_LOAD_SEGMENTS 128 +struct loaded_segment loaded_segments[MAX_LOAD_SEGMENTS]; + +unsigned long loaded_segments_num, loaded_segments_base; +static int seg_comp(const void *a, const void *b) +{ + const struct loaded_segment *x = a, *y = b; + /* avoid overflow */ + if (x->start > y->start) return 1; + if (x->start < y->start) return -1; + return 0; +} + +/* purgatory code need this info to patch the EFI memmap + */ +static void add_loaded_segments_info(struct kexec_info *info, + struct mem_ehdr *ehdr, unsigned long max_addr) +{ + int i; + for(i = 0; i < ehdr->e_phnum; i++) { + unsigned long start, end; + struct mem_phdr *phdr; + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + start = phdr->p_paddr; + end = phdr->p_paddr + phdr->p_memsz; + + loaded_segments[loaded_segments_num].start = + start&~(ELF_PAGE_SIZE-1); + loaded_segments[loaded_segments_num].end = + (end + ELF_PAGE_SIZE - 1)&~(ELF_PAGE_SIZE - 1); + loaded_segments[loaded_segments_num].reserved = 0; + loaded_segments_num++; + } +} + +static int get_crash_notes_section_addr(unsigned long *addr, int cpu) +{ + char crash_notes[128]; + char line[MAX_LINE]; + FILE *fp; + sprintf(crash_notes, "/sys/devices/system/cpu/cpu%d/crash_notes", cpu); + fp = fopen(crash_notes, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + crash_notes, strerror(errno)); + fprintf(stderr, "Try mounting sysfs\n"); + return -1; + } + if (fscanf(fp, "%lx", addr) != 1) { + *addr = 0; + return -1; + } + 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 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 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; +} + +static int prepare_crash_memory_elf64_headers(struct kexec_info *info, + void *buf, unsigned long size) +{ + Elf64_Ehdr *elf; + Elf64_Phdr *phdr; + int i; + long int nr_cpus = 0; + char *bufp = buf; + unsigned long notes_addr, notes_offset; + + /* 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_IA_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_section_addr (¬es_addr, i) < 0) + break; + notes_offset = notes_addr; + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = notes_offset; + phdr->p_vaddr = phdr->p_paddr = notes_offset; + 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)++; + } + + for (i = 0; i < memory_ranges; i++) { + unsigned 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; + phdr->p_offset = mstart; + /*add region 5 mapping for kernel*/ + if (kernel_code_start >= mstart && kernel_code_start < mend) { + phdr->p_vaddr = mstart + LOAD_OFFSET; + phdr->p_paddr = mstart; + phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; + phdr->p_align = 0; + (elf->e_phnum)++; + + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = mstart; + } + phdr->p_vaddr = mstart + PAGE_OFFSET; + phdr->p_paddr = mstart; + phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; + phdr->p_align = 0; + (elf->e_phnum)++; + } + return 0; +} + +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + const char iomem[]= "/proc/iomem"; + char line[MAX_LINE]; + FILE *fp; + unsigned long start, end; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + 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); + str = line + consumed; + if (count != 2) + continue; + + 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, "Kernel code\n", 12) == 0) { + kernel_code_start = start; + continue; + }else + continue; + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + } + fclose(fp); + if (exclude_crash_reserve_region(&memory_ranges) < 0) + return -1; + *ranges = memory_ranges; + return 0; +} + +static void +cmdline_add_elfcorehdr(char **cmdline, unsigned long addr) +{ + char *str = *cmdline; + char buf[64]; + size_t len; + sprintf(buf, " elfcorehdr=%ldK", addr/1024); + len = strlen(str) + strlen(buf) + 1; + str = xmalloc(len); + sprintf(str, "%s%s", *cmdline, buf); + *cmdline = str; +} + +int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr, + unsigned long max_addr, unsigned long min_base, + char **cmdline) +{ + //struct memory_range *mem_range, *memmap_p; + struct memory_range *mem_range; + int nr_ranges; + size_t size; + void *tmp; + if (info->kexec_flags & KEXEC_ON_CRASH ) { + if (get_crash_memory_ranges(&mem_range, &nr_ranges) == 0) { + size = sizeof(Elf64_Ehdr) + + (nr_ranges + 1) * sizeof(Elf64_Phdr); + size = (size + EFI_PAGE_SIZE - 1) & ~(EFI_PAGE_SIZE - 1); + tmp = xmalloc(size); + memset(tmp, 0, size); + if (prepare_crash_memory_elf64_headers(info, tmp, size) < 0) + return -1; + elfcorehdr = add_buffer(info, tmp, size, size, EFI_PAGE_SIZE, min_base, + max_addr, -1); + loaded_segments[loaded_segments_num].start = elfcorehdr; + loaded_segments[loaded_segments_num].end = elfcorehdr + size; + loaded_segments[loaded_segments_num].reserved = 1; + loaded_segments_num++; + cmdline_add_elfcorehdr(cmdline, elfcorehdr); + } + } + add_loaded_segments_info(info, ehdr, max_addr); + size = sizeof(struct loaded_segment) * loaded_segments_num; + qsort(loaded_segments, loaded_segments_num, + sizeof(struct loaded_segment), seg_comp); + loaded_segments_base = add_buffer(info, loaded_segments, + size, size, 16, 0, max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__loaded_segments", + &loaded_segments_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num", + &loaded_segments_num, sizeof(long)); + return 0; +} + + diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/crashdump-ia64.h kexec-tools-1.101-kdump/kexec/arch/ia64/crashdump-ia64.h --- kexec-tools-1.101/kexec/arch/ia64/crashdump-ia64.h 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/crashdump-ia64.h 2006-07-17 00:21:02.000000000 +0530 @@ -0,0 +1,13 @@ +#ifndef CRASHDUMP_IA64_H +#define CRASHDUMP_IA64_H + +#define PAGE_OFFSET 0xe000000000000000UL +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +extern int load_crashdump_segments(struct kexec_info *info, + struct mem_ehdr *ehdr, unsigned long max_addr, + unsigned long min_base, char **cmdline); + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) +#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) + +#endif diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/kexec-elf-ia64.c kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-elf-ia64.c --- kexec-tools-1.101/kexec/arch/ia64/kexec-elf-ia64.c 2004-12-22 01:31:37.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-elf-ia64.c 2006-07-17 00:21:02.000000000 +0530 @@ -6,6 +6,7 @@ * Copyright (C) 2004 Silicon Graphics, Inc. * Jesse Barnes * Copyright (C) 2004 Khalid Aziz Hewlett Packard Co + * Copyright (C) 2005 Zou Nan hai Intel Corp * * 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 @@ -34,11 +35,14 @@ #include #include #include +#include #include #include #include #include "../../kexec.h" +#include "../../kexec-syscall.h" #include "../../kexec-elf.h" +#include "crashdump-ia64.h" #include static const int probe_debug = 0; @@ -74,23 +78,53 @@ void elf_ia64_usage(void) { printf( " --command-line=STRING Set the kernel command line to STRING.\n" - " --append=STRING Set the kernel command line to STRING.\n"); + " --append=STRING Set the kernel command line to STRING.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"); +} + +/* Move the crash kerenl physical offset to reserved region + */ +static void move_loaded_segments(struct kexec_info *info, struct mem_ehdr *ehdr) +{ + int i; + long offset; + struct mem_phdr *phdr; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) { + offset = mem_min - phdr->p_paddr; + break; + } + } + ehdr->e_entry += offset; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) + phdr->p_paddr += offset; + } } int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; - const char *command_line; - int command_line_len; - unsigned long entry, max_addr; + const char *command_line, *ramdisk=0; + char *ramdisk_buf = NULL; + off_t ramdisk_size = 0; + unsigned long command_line_len; + unsigned long entry, max_addr, gp_value; + unsigned long command_line_base, ramdisk_base; + unsigned long efi_memmap_base, efi_memmap_size; int result; int opt; + char *efi_memmap_buf; #define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_RAMDISK (OPT_ARCH_MAX+1) static const struct option options[] = { KEXEC_ARCH_OPTIONS {"command-line", 1, 0, OPT_APPEND}, {"append", 1, 0, OPT_APPEND}, + {"initrd", 1, 0, OPT_RAMDISK}, {0, 0, 0, 0}, }; @@ -110,11 +144,14 @@ int elf_ia64_load(int argc, char **argv, case OPT_APPEND: command_line = optarg; break; + case OPT_RAMDISK: + ramdisk = optarg; + break; } } command_line_len = 0; if (command_line) { - command_line_len = strlen(command_line) + 1; + command_line_len = strlen(command_line) + 16; } /* Parse the Elf file */ @@ -124,18 +161,91 @@ int elf_ia64_load(int argc, char **argv, free_elf_info(&ehdr); return result; } + + if (info->kexec_flags & KEXEC_ON_CRASH ) { + if ((mem_min == 0x00) && (mem_max = ULONG_MAX)) { + fprintf(stderr, "Failed to find crash kernel region in /proc/iomem\n"); + return -1; + } + move_loaded_segments(info, &ehdr); + } + entry = ehdr.e_entry; max_addr = elf_max_addr(&ehdr); /* Load the Elf data */ result = elf_exec_load(&ehdr, info); - free_elf_info(&ehdr); if (result < 0) { fprintf(stderr, "ELF load failed\n"); + free_elf_info(&ehdr); return result; } + + + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x0, ULONG_MAX, -1); + + + if (load_crashdump_segments(info, &ehdr, max_addr, 0, + &command_line) < 0) + return -1; + + // reserve 8k for efi_memmap + efi_memmap_size = 1UL<<14; + efi_memmap_buf = xmalloc(efi_memmap_size); + efi_memmap_base = add_buffer(info, efi_memmap_buf, + efi_memmap_size, efi_memmap_size, 4096, 0, + max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base", + &efi_memmap_base, sizeof(long)); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size", + &efi_memmap_size, sizeof(long)); + if (command_line) { + command_line_len = strlen(command_line) + 1; + } + if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) { + char *cmdline = xmalloc(command_line_len); + strcpy(cmdline, command_line); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + char buf[128]; + sprintf(buf," max_addr=%lluM min_addr=%lluM", + mem_max>>20, mem_min>>20); + command_line_len = strlen(cmdline) + strlen(buf) + 1; + cmdline = xrealloc(cmdline, command_line_len); + strcat(cmdline, buf); + } + + command_line_len = (command_line_len + 15)&(~15); + command_line_base = add_buffer(info, cmdline, + command_line_len, command_line_len, + getpagesize(), 0UL, + max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__command_line_len", + &command_line_len, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__command_line", + &command_line_base, sizeof(long)); + } - /* For now we don't have arguments to pass :( */ - info->entry = (void *)entry; + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_size); + ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size, + ramdisk_size, + getpagesize(), 0, max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_base", + &ramdisk_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_size", + &ramdisk_size, sizeof(long)); + } + + gp_value = info->rhdr.rel_addr + 0x200000; + elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value, + sizeof(gp_value)); + + elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry, sizeof(entry)); + free_elf_info(&ehdr); return 0; } diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/kexec-elf-rel-ia64.c kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-elf-rel-ia64.c --- kexec-tools-1.101/kexec/arch/ia64/kexec-elf-rel-ia64.c 2004-12-21 04:13:23.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-elf-rel-ia64.c 2006-07-17 00:21:02.000000000 +0530 @@ -1,8 +1,37 @@ +/* + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.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 + * 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. + */ + +/* pugatory relocation code + * Most of the code in this file is + * based on arch/ia64/kernel/module.c in Linux kernel + */ + + +/* Most of the code in this file is + * based on arch/ia64/kernel/module.c in Linux kernel + */ + #include #include #include "../../kexec.h" #include "../../kexec-elf.h" +#define MAX_LTOFF ((uint64_t) (1 << 22)) + int machine_verify_elf_rel(struct mem_ehdr *ehdr) { if (ehdr->ei_data != ELFDATA2LSB) { @@ -17,12 +46,49 @@ int machine_verify_elf_rel(struct mem_eh return 1; } +static void +ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val) +{ + uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16); +# define insn_mask ((1UL << 41) - 1) + unsigned long shift; + + b0 = b[0]; b1 = b[1]; + shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */ + if (shift >= 64) { + m1 = mask << (shift - 64); + v1 = val << (shift - 64); + } else { + m0 = mask << shift; m1 = mask >> (64 - shift); + v0 = val << shift; v1 = val >> (64 - shift); + b[0] = (b0 & ~m0) | (v0 & m0); + } + b[1] = (b1 & ~m1) | (v1 & m1); +} + +static void +put_unaligned64(unsigned long val, unsigned char *location) +{ + unsigned char *src = (unsigned char *)&val; + int i; + for (i = 0; i < sizeof(long); i++) + *location++ = *src++; +} + +static inline uint64_t +bundle (const uint64_t insn) +{ + return insn & ~0xfUL; +} + void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, void *location, unsigned long address, unsigned long value) { + uint64_t gp_value = ehdr->rel_addr + 0x200000; switch(r_type) { case R_IA64_NONE: break; + case R_IA64_SEGREL64LSB: case R_IA64_DIR64LSB: *((uint64_t *)location) = value; break; @@ -31,15 +97,72 @@ void machine_apply_elf_rel(struct mem_eh if (value != *((uint32_t *)location)) goto overflow; break; - case R_IA64_PCREL21B: + case R_IA64_IMM64: + ia64_patch((uint64_t)location, 0x01fffefe000UL, + /* bit 63 -> 36 */ + (((value & 0x8000000000000000UL) >> 27) + /* bit 21 -> 21 */ + | ((value & 0x0000000000200000UL) << 0) + /* bit 16 -> 22 */ + | ((value & 0x00000000001f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x000000000000ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x000000000000007fUL) << 13))); + ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22); + break; + case R_IA64_IMM22: + if (value + (1 << 21) >= (1 << 22)) + die("value out of IMM22 range\n"); + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + /* bit 21 -> 36 */ + (((value & 0x200000UL) << 15) + /* bit 16 -> 22 */ + | ((value & 0x1f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x00ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x00007fUL) << 13) )); + break; + case R_IA64_PCREL21B: { + uint64_t delta = ((int64_t)value - (int64_t)address)/16; + if (delta + (1 << 20) >= (1 << 21)) + die("value out of IMM21B range\n"); + value = ((int64_t)(value - bundle(address)))/16; + ia64_patch((uint64_t)location, 0x11ffffe000UL, + (((value & 0x100000UL) << 16) /* bit 20 -> 36 */ + | ((value & 0x0fffffUL) << 13) /* bit 0 -> 13 */)); + } + break; + case R_IA64_PCREL64LSB: { + value = value - address; + put_unaligned64(value, location); + } break; + case R_IA64_GPREL22: + case R_IA64_LTOFF22X: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + value -= gp_value; + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + (((value & 0x200000UL) << 15) /* bit 21 -> 36 */ + |((value & 0x1f0000UL) << 6) /* bit 16 -> 22 */ + |((value & 0x00ff80UL) << 20) /* bit 7 -> 27 */ + |((value & 0x00007fUL) << 13) /* bit 0 -> 13 */)); + break; + case R_IA64_LDXMOV: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL); + break; case R_IA64_LTOFF22: - case R_IA64_SEGREL64LSB: + default: - die("Unknown rela relocation: %lu\n", r_type); + die("Unknown rela relocation: 0x%lx 0x%lx\n", + r_type, address); break; } return; - overflow: +overflow: die("overflow in relocation type %lu val %Lx\n", - r_type, value); + r_type, value); } 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-07-17 00:20:52.000000000 +0530 @@ -27,42 +27,87 @@ #include #include #include +#include #include #include "../../kexec.h" #include "../../kexec-syscall.h" #include "kexec-ia64.h" #include -#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; - /* - * /proc/iomem on ia64 does not show where all memory is. If - * that is fixed up, we can make use of that to validate - * the memory range kernel will be loade din. Until then..... - * -- Khalid Aziz - */ - - /* Note that the ia64 architecture mandates all systems will - * have at least 64MB at 0-64M. The SGI altix does not follow - * that restriction, but a reasonable guess is better than nothing - * at all. - * -- Eric Biederman - */ - fprintf(stderr, "Warning assuming memory at 0-64MB is present\n"); - memory_ranges = 0; - memory_range[memory_ranges].start = 0x00010000; - memory_range[memory_ranges].end = 0x10000000; - memory_range[memory_ranges].type = RANGE_RAM; - memory_ranges++; - *range = memory_range; - *ranges = memory_ranges; - return 0; + const char iomem[]= "/proc/iomem"; + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long start, end; + char *str; + int type; + int consumed; + int count; + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%lx-%lx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } + else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } + 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; + /* + * Check if this memory range can be coalesced with + * the previous range + */ + if ((memory_ranges > 0) && + (start == memory_range[memory_ranges-1].end) && + (type == memory_range[memory_ranges-1].type)) { + memory_range[memory_ranges-1].end = end; + } + else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + + return 0; } /* Supported file types and callbacks */ @@ -76,9 +121,6 @@ void arch_usage(void) { } -static struct { -} arch_options = { -}; int arch_process_options(int argc, char **argv) { static const struct option options[] = { @@ -87,8 +129,12 @@ int arch_process_options(int argc, char }; static const char short_options[] = KEXEC_ARCH_OPT_STR; int opt; - unsigned long value; - char *end; + + /* execute from monarch processor */ + cpu_set_t affinity; + CPU_ZERO(&affinity); + CPU_SET(0, &affinity); + sched_setaffinity(0, sizeof(affinity), &affinity); opterr = 0; /* Don't complain about unrecognized options here */ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { @@ -103,7 +149,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,32 +161,7 @@ int arch_compat_trampoline(struct kexec_ } if (strcmp(utsname.machine, "ia64") == 0) { - *flags |= KEXEC_ARCH_X86_64; - } - else { - fprintf(stderr, "Unsupported machine type: %s\n", - utsname.machine); - return -1; - } - return 0; -} - -int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags) -{ - 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, "ia64") == 0) - { - /* For compatibility with older patches - * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here. - */ - *flags |= KEXEC_ARCH_DEFAULT; + info->kexec_flags |= KEXEC_ARCH_IA_64; } else { fprintf(stderr, "Unsupported machine type: %s\n", diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/kexec-ia64.h kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-ia64.h --- kexec-tools-1.101/kexec/arch/ia64/kexec-ia64.h 2004-12-20 05:22:38.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-ia64.h 2006-07-17 00:21:02.000000000 +0530 @@ -1,9 +1,13 @@ #ifndef KEXEC_IA64_H #define KEXEC_IA64_H +#define MAX_MEMORY_RANGES 1024 + int elf_ia64_probe(const char *buf, off_t len); int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info); void elf_ia64_usage(void); - +#define MAX_MEMORY_RANGES 1024 +#define EFI_PAGE_SIZE (1UL<<12) +#define ELF_PAGE_SIZE (1UL<<16) #endif /* KEXEC_IA64_H */ diff -urNp -X dontdiff kexec-tools-1.101/kexec/arch/ia64/Makefile kexec-tools-1.101-kdump/kexec/arch/ia64/Makefile --- kexec-tools-1.101/kexec/arch/ia64/Makefile 2004-12-20 05:41:13.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/ia64/Makefile 2006-07-17 00:21:02.000000000 +0530 @@ -4,3 +4,5 @@ KEXEC_C_SRCS+= kexec/arch/ia64/kexec-ia64.c KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-ia64.c KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-rel-ia64.c +KEXEC_C_SRCS+= kexec/arch/ia64/crashdump-ia64.c + 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-07-17 00:20:25.000000000 +0530 @@ -19,11 +19,11 @@ #include #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-07-17 00:20:57.000000000 +0530 @@ -0,0 +1,473 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#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, }; + +/* + * 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; + +/* 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; + 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) { + /* No space to insert another element. */ + fprintf(stderr, + "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + + start = ((unsigned long long *)buf)[0]; + end = start + ((unsigned long long *)buf)[1]; + if (start == 0 && end >= 0x8000) + start = 0x8000; + + cstart = crash_base; + cend = crash_base + crash_size; + /* + * Exclude the region that lies within crashkernel + */ + if (cstart < end && cend > start) { + if (start < cstart && end > cend) { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + crash_memory_range[memory_ranges].start = cend; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } else if (start < cstart) { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } else if (end > cend){ + crash_memory_range[memory_ranges].start = cend; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } + } else { + 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); + + /* + * If RTAS region is overlapped with crashkernel, need to create ELF + * Program header for the overlapped memory. + */ + if (crash_base < rtas_base + rtas_size && + rtas_base < crash_base + crash_size) { + cstart = rtas_base; + cend = rtas_base + rtas_size; + if (cstart < crash_base) + cstart = crash_base; + if (cend > crash_base + crash_size) + cend = crash_base + crash_size; + crash_memory_range[memory_ranges].start = cstart; + crash_memory_range[memory_ranges++].end = cend; + } + /* + * 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, ¬es_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 +} + +#if 0 +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; + +} +#endif + 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-07-17 00:20:57.000000000 +0530 @@ -0,0 +1,36 @@ +#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); + +#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; +extern unsigned int rtas_base; +extern unsigned int rtas_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-07-17 00:20:55.000000000 +0530 @@ -0,0 +1,464 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#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="); + if (param) { + 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-07-17 00:20:32.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-07-17 00:20:59.000000000 +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 #include "../../kexec.h" #include "../../kexec-elf.h" +#include "../../kexec-syscall.h" #include "kexec-ppc64.h" +#include "crashdump-ppc64.h" +#include #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,98 @@ 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) +#define OPT_ARGS_IGNORE (OPT_ARCH_MAX+3) + + 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 }, + { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, + { 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; + case OPT_ARGS_IGNORE: + 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 +169,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-07-17 00:20:45.000000000 +0530 @@ -1,5 +1,6 @@ #include #include +#include #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-07-17 00:20:57.000000000 +0530 @@ -0,0 +1,598 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-ppc64.h" +#include "crashdump-ppc64.h" +#include + +static struct exclude_range exclude_range[MAX_MEMORY_RANGES]; +static unsigned long long rmo_top; +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; +unsigned int rtas_base, rtas_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) { + fclose(file); + 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 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) { + 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); + } + + 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) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + 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 (rmo_top > 0x30000000UL) + rmo_top = 0x30000000UL; + + fclose(file); + closedir(cdir); + } /* memory */ + + if (strncmp(dentry->d_name, "pci@", 4) == 0) { + strcat(fname, "/linux,tce-base"); + if ((file = fopen(fname, "r")) == NULL) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + 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(); + +#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, " --command-line= command line to append.\n"); + fprintf(stderr, " --append= same as --command-line.\n"); + fprintf(stderr, " --ramdisk= Initial RAM disk.\n"); + fprintf(stderr, " --initrd= same as --ramdisk.\n"); + fprintf(stderr, " --devicetreeblob= 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-07-17 00:20:57.000000000 +0530 @@ -1,9 +1,45 @@ #ifndef KEXEC_PPC64_H #define KEXEC_PPC64_H +#define MAX_MEMORY_RANGES 1024 /* 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-07-17 00:20:45.000000000 +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-07-17 00:20:31.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-07-17 00:20:12.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-07-17 00:20:12.000000000 +0530 @@ -0,0 +1,23 @@ +/* + * kexec/arch/s390/kexec-elf-rel-s390.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Heiko Carstens + * + */ + +#include +#include +#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-07-17 00:20:12.000000000 +0530 @@ -0,0 +1,137 @@ +/* + * kexec/arch/s390/kexec-image.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * Heiko Carstens + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#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-07-17 00:20:12.000000000 +0530 @@ -0,0 +1,104 @@ +/* + * kexec/arch/s390/kexec-s390.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-s390.h" +#include + +#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-07-17 00:20:12.000000000 +0530 @@ -0,0 +1,25 @@ +/* + * kexec/arch/s390/kexec-s390.h + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * + */ + +#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-07-17 00:20:12.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-07-17 00:21:00.000000000 +0530 @@ -0,0 +1,658 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +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 if(memcmp(str,"ACPI Non-volatile Storage\n",26) == 0 ) { + type = RANGE_ACPI_NVS; + } 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, ¬es_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 + || mem_range[i].type == RANGE_ACPI_NVS) ) + continue; + start = mem_range[i].start; + end = mem_range[i].end; + cmdline_add_memmap_acpi(mod_cmdline, start, end); + } + return 0; +} diff -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-07-17 00:20:17.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-07-17 00:20:21.000000000 +0530 @@ -32,10 +32,12 @@ #include #include #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 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", ®s, 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-07-17 00:20:41.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 #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-07-17 00:20:17.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-07-17 00:20:25.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 +#include +#include +#include +#include +#include +#include +#include +#include +#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-07-17 00:20:25.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.8 kexec-tools-1.101-kdump/kexec/kexec.8 --- kexec-tools-1.101/kexec/kexec.8 2004-12-20 03:57:31.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/kexec.8 2006-07-19 00:33:43.000000000 +0530 @@ -1,45 +1,205 @@ -.\" Hey, EMACS: -*- nroff -*- -.\" First parameter, NAME, should be all caps -.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection -.\" other parameters are allowed: see man(7), man(1) -.TH KEXEC-TOOLS 8 "October 13, 2004" -.\" Please adjust this date whenever revising the manpage. +.\" Process this file with +.\" groff -man -Tascii kexec.8 .\" -.\" Some roff macros, for reference: -.\" .nh disable hyphenation -.\" .hy enable hyphenation -.\" .ad l left justify -.\" .ad b justify to both left and right margins -.\" .nf disable filling -.\" .fi enable filling -.\" .br insert line break -.\" .sp insert n+1 empty lines -.\" for manpage-specific macros, see man(7) +.TH kexec 8 "April 2006" Linux "User Manuals" .SH NAME -kexec-tools \- Tool to load a kernel for warm reboot and initiate a warm reboot +kexec \- directly boot into a new kernel .SH SYNOPSIS -.B kexec-tools -.RI [ options ] " files" ... +.B /sbin/kexec +.RI \-l|\-\-load\ kernel-image +.RI "[\-\-append=" command\-line\-options] +.RI "[\-\-initrd=" initrd\-image] +[\-\-mem\-min= addr ] [\-\-mem\-max= addr ] +[-t|\-\-type] + +.B /sbin/kexec +-e|\-\-exec [-f|\-\-force] [-x|\-\-no-ifdown] + +.B /sbin/kexec +-u|\-\-unload + +.B /sbin/kexec +-p|\-\-load-panic + +.B /sbin/kexec +-v|\-\-version + +.B /sbin/kexec +-h|\-\-help + .SH DESCRIPTION +.B kexec +is a utility that enables you to load and boot into another +kernel from the currently running kernel using the kexec_load(2) and reboot(2) system calls. The kexec utility prepares, loads, and starts a new operating environment replacing the currently running kernel. When the new environment is the same kernel that is running, it is effectively a reboot of the existing system. +The primary difference between a standard system boot and a +.B kexec +boot is that the running kernel directly invokes the new kernel instead of calling the BIOS or firmware. Because the firmware is bypassed, any hardware initialization normally performed by the firmware is not performed during a +.B kexec +boot. This has the effect of reducing the time required for a reboot. .PP -.\" TeX users may be more comfortable with the \fB\fP and -.\" \fI\fP escape sequences to invode bold face and italics, -.\" respectively. -\fBkexec-tools\fP does not have a man page yet. Please use "kexec -h" for help. +To use kexec, your running kernel must have been compiled with the +.B CONFIG_KEXEC=y +, this enables the kexec_load(2) system call. +.SH USAGE +Using +.B kexec +consists of + +.RS +(1) loading the kernel to be rebooted into memory. A kernel is loaded with the \fB-l\fR option. The operating environment such as initrd and command line parameters is specified using the optional and \fB\-\-initrd\fR \fB\-\-append\fR options. +.RE +.RS +(2) actually rebooting to the pre-loaded kernel using the \fB-e\fR option. +.RE +.PP + +.I kernel\-image +is the kernel file that you intend to reboot to. The kexec program recognizes several file formats for the linux kernel depending on architecture. For most architectures the perferred format is a linux kernel in elf format (vmlinux). +.PP +When rebooting into a new linux kernel and supplying the \fIkernel\fR parameters, a good staring point are those passed to the existing kernel. They can be found in the file /proc/cmdline. +.PP +If your environment requires the use of an initrd you will most likley need to specify that image using \-\-initrd= +.I \initrd\-image +option. +.PP + +.SH EXAMPLE +.PP +For example, if the kernel image you want to reboot to is +.BR /boot/vmlinux, +and the contents of /proc/cmdline is +.BR "root\=/dev/hda1 vga=0x317 selinux=0" +, and the path to the initrd is +.BR /boot/initrd +, then you would use the following command to load the kernel: + +.RS +.B kexec +\-l\ /boot/vmlinux +\-\-append="root=/dev/hda1 vga=0x317 selinux=0" \-\-initrd=/boot/initrd +.RE +.PP +After this kernel is loaded, it can be booted to at any time using the +command: + +.RS +.BR kexec \ \-e +.RE + .SH OPTIONS -These programs follow the usual GNU command line syntax, with long -options starting with two dashes (`-'). -A summary of options is included below. -For a complete description, see the Info files. -.TP -.B \-h, \-\-help -Show summary of options. -.TP -.B \-v, \-\-version -Show version of program. -.SH SEE ALSO -.SH AUTHOR -kexec-tools was written by Eric Biederman. +.TP +\fB\-e\fR, \fB\-\-exec\fR Run the currently loaded kernel. +.TP +\fB\-f\fR, \fB\-\-force\fR Force an immediate \fBkexec\fR +call, do not call shutdown. +.TP +\fB\-h\fR, \fB\-\-help\fR +Open a help file for \fBkexec\fR. +.TP +\fB\-l\fR, \fB\-\-load\fR \fIkernel\fR +Load the specified \fIkernel\fR into the current kernel. +.TP +\fB\-p\fR, \fB\-\-load\-panic\fR Load the new kernel for use on panic. This option is used by kdump. +.TP +\fB\-t\fR, \fB\-\-type=\fRtype Specify that the new kernel is of this +.I type. +.TP +\fB\-u\fR, \fB\-\-unload\fR Unload the current \fBkexec\fR target kernel. +.TP +\fB\-v\fR, \fB\-\-version\fR Return the version number of the installed utility. +.TP +\fB\-x\fR, \fB\-\-no\-ifdown\fR +Shut down the running kernel, but restore the interface on reload. (If +this option is used, it must be specified last.) +.TP +.BI \-\-mem\-min= addr +Specify the lowest memory address +.I addr +to load code into. +.TP +.BI \-\-mem\-max= addr +Specify the highest memory address +.I addr +to load code into. + +.SH SUPPORTED KERNEL FILE TYPES AND OPTIONS +.B Beoboot-x86 +.RS +.TP +.B \-\-args\-elf +Pass ELF boot notes. +.TP +.B \-\-args\-linux +Pass Linux kernel style options. +.TP +.B \-d|\-\-debug +Enable debugging messages. +.TP +.B \-\-real\-mode +Use the kernel's real mode entry point. +.RE .PP -This manual page was written by Khalid Aziz , -for the Debian project (but may be used by others). +.B elf-x86 +.RS +.TP +.BI \-\-append= string +Append +.I string +to the kernel command line. +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-initrd= file +Use +.I file +as the kernel's initial ramdisk. +.TP +.BI \-\-ramdisk= file +Use +.I file +as the kernel's initial ramdisk. +.RE +.PP +.B multiboot-x86 +.RS +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-module= "mod arg1 arg2 ..." +Load module +.I mod +with command-line arguments +.I "arg1 arg2 ..." +This parameter can be specified multiple times. +.RE + +.SH ARCHITECTURE OPTIONS +.TP +.B \-\-console\-serial +Enable the serial console. +.TP +.B \-\-console\-vga +Enable the VGA console. +.TP +.B \-\-elf32\-core\-headers +Prepare core headers in ELF32 format. +.TP +.B \-\-elf64\-core\-headers +Prepare core headers in ELF64 format. +.TP +.B \-\-reset\-vga +Attempt to reset a standard VGA device. +.TP +.BI \-\-serial= port +Specify the serial +.I port +for debug output. +.TP +.BI \-\-serial\-baud= baud_rate +Specify the +.I baud rate +of the serial port. 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-07-17 00:21:03.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, ...) { @@ -96,6 +96,9 @@ int valid_memory_range(unsigned long sst continue; mstart = memory_range[i].start; mend = memory_range[i].end; + if (i < memory_ranges - 1 && mend == memory_range[i+1].start) + mend = memory_range[i+1].end; + /* Check to see if we are fully contained */ if ((mstart <= sstart) && (mend >= send)) { return 1; @@ -187,14 +190,14 @@ 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; /* Perform a merge on the 2 sorted lists of memory ranges */ for (j = 0, i = 0; i < memory_ranges; i++) { - unsigned long sstart, send; - unsigned long mstart, mend; + unsigned long long sstart, send; + unsigned long long mstart, mend; mstart = memory_range[i].start; mend = memory_range[i].end; if (memory_range[i].type != RANGE_RAM) @@ -232,7 +235,7 @@ unsigned long locate_hole(struct kexec_i if (start < hole_min) { start = hole_min; } - start = (start + hole_align - 1) & ~(hole_align - 1); + start = (start + hole_align - 1) & ~((unsigned long long)hole_align - 1); if (end > mem_max) { end = mem_max; } @@ -250,7 +253,7 @@ unsigned long locate_hole(struct kexec_i hole_base = start; break; } else { - hole_base = (end - hole_size) & ~(hole_align - 1); + hole_base = (end - hole_size) & ~((unsigned long long)hole_align - 1); } } } @@ -323,12 +326,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 +515,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 +532,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 +575,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 +596,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; @@ -687,6 +698,19 @@ void usage(void) printf("\n"); } +int sys_kexec_loaded() +{ + int ret; + FILE *fp; + + fp = fopen("/sys/kernel/kexec_loaded", "r"); + if (fp == NULL) + return -1; + fscanf(fp, "%d", &ret); + fclose(fp); + return ret; +} + int main(int argc, char *argv[]) { int do_load = 1; @@ -796,9 +820,15 @@ int main(int argc, char *argv[]) if ((result == 0) && do_sync) { sync(); } - if ((result == 0) && do_ifdown) { + if ((result == 0) && do_ifdown) { + int ret; extern int ifdown(void); - (void)ifdown(); + ret = sys_kexec_loaded(); + if (!ret) { + fprintf(stderr,"kexec image is not loaded\n"); + result = 1; + } else + (void)ifdown(); } if ((result == 0) && do_exec) { result = my_exec(); 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-07-17 00:20:45.000000000 +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-07-17 00:20:45.000000000 +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-07-17 00:20:25.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 */ Binary files kexec-tools-1.101/kexec/.kexec.h.swp and kexec-tools-1.101-kdump/kexec/.kexec.h.swp differ diff -urNp -X dontdiff kexec-tools-1.101/kexec/kexec-sha256.h kexec-tools-1.101-kdump/kexec/kexec-sha256.h --- kexec-tools-1.101/kexec/kexec-sha256.h 2004-12-22 00:05:01.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/kexec-sha256.h 2006-07-17 00:21:02.000000000 +0530 @@ -6,6 +6,6 @@ struct sha256_region { unsigned long len; }; -#define SHA256_REGIONS 8 +#define SHA256_REGIONS 16 #endif /* KEXEC_SHA256_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-07-17 00:20:23.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-07-17 00:20:49.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 @@ -27,6 +28,7 @@ KEXEC_SRCS:= $(KEXEC_C_SRCS) $(KEXEC_S_S KEXEC_OBJS:= $(KEXEC_C_OBJS) $(KEXEC_S_OBJS) KEXEC_DEPS:= $(KEXEC_C_DEPS) $(KEXEC_S_DEPS) KEXEC:= $(SBINDIR)/kexec +KEXEC_MANPAGE:= $(MANDIR)/man8/kexec.8 include $(KEXEC_DEPS) @@ -50,6 +52,9 @@ $(KEXEC): $(KEXEC_OBJS) $(UTIL_LIB) mkdir -p $(@D) $(CC) $(KCFLAGS) -o $@ $(KEXEC_OBJS) $(UTIL_LIB) $(LIBS) +$(KEXEC_MANPAGE): kexec/kexec.8 + $(MKDIR) -p $(MANDIR)/man8 + cp kexec/kexec.8 $(KEXEC_MANPAGE) echo:: @echo "KEXEC_C_SRCS $(KEXEC_C_SRCS)" @echo "KEXEC_C_DEPS $(KEXEC_C_DEPS)" diff -urNp -X dontdiff kexec-tools-1.101/Makefile kexec-tools-1.101-kdump/Makefile --- kexec-tools-1.101/Makefile 2005-02-18 17:56:09.000000000 +0530 +++ kexec-tools-1.101-kdump/Makefile 2006-07-17 00:20:49.000000000 +0530 @@ -43,6 +43,7 @@ PKGLIBDIR=$(LIBDIR)/$(PACKAGE) PKGINCLUDEIR=$(INCLUDEDIR)/$(PACKAGE) MAN_PAGES:= kexec/kexec.8 +MAN_PAGES+= kdump/kdump.8 BINARIES_i386:= $(SBINDIR)/kexec $(PKGLIBDIR)/kexec_test BINARIES_x86_64:=$(SBINDIR)/kexec $(PKGLIBDIR)/kexec_test BINARIES:=$(SBINDIR)/kexec $(SBINDIR)/kdump $(BINARIES_$(ARCH)) 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-07-17 00:20:19.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 +#include + +#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-07-17 00:19:56.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-07-17 00:19:56.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-07-17 00:19:56.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/console-ia64.c kexec-tools-1.101-kdump/purgatory/arch/ia64/console-ia64.c --- kexec-tools-1.101/purgatory/arch/ia64/console-ia64.c 2004-12-21 04:11:26.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/console-ia64.c 2006-07-17 00:21:02.000000000 +0530 @@ -1,5 +1,47 @@ #include +#include "io.h" + +#define VGABASE UNCACHED(0xb8000) + +/* code based on i386 console code + * TODO add serial support + */ +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +unsigned long current_ypos = 1, current_xpos = 0; + +static void putchar_vga(int ch) +{ + int i, k, j; + + if (current_ypos >= MAX_YPOS) { + /* scroll 1 line up */ + for (k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for (i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for (i = 0; i < MAX_XPOS; i++) + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + current_ypos = MAX_YPOS-1; + } + if (ch == '\n') { + current_xpos = 0; + current_ypos++; + } else if (ch != '\r') { + writew(((0x7 << 8) | (unsigned short) ch), + VGABASE + 2*(MAX_XPOS*current_ypos + + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } +} + void putchar(int ch) { - /* Nothing for now */ + putchar_vga(ch); } diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/entry.S kexec-tools-1.101-kdump/purgatory/arch/ia64/entry.S --- kexec-tools-1.101/purgatory/arch/ia64/entry.S 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/entry.S 2006-07-17 00:21:02.000000000 +0530 @@ -0,0 +1,67 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.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 + * 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. + */ +#define DECLARE_DATA8(name) \ +.global name; \ +.size name, 8; \ +name: data8 0x0 + +.global __dummy_efi_function +.align 32 +.proc __dummy_efi_function +__dummy_efi_function: + mov r8=r0;; + br.ret.sptk.many rp;; +.global __dummy_efi_function_end +__dummy_efi_function_end: +.endp __dummy_efi_function + +.global purgatory_start +.align 32 +.proc purgatory_start +purgatory_start: + movl r2=__gp_value;; + ld8 gp=[r2];; + br.call.sptk.many b0=purgatory + ;; + alloc r2 = ar.pfs, 0, 0, 2, 0 + ;; + mov out0=r28 + movl out1=__ramdisk_base;; + br.call.sptk.many b0=ia64_env_setup + movl r10=__kernel_entry;; + ld8 r14=[r10];; + mov b6=r14;; + mov ar.lc=r0 + mov ar.ec=r0 + cover;; + invala;; + br.call.sptk.many b0=b6 +.endp purgatory_start + +DECLARE_DATA8(__kernel_entry) +DECLARE_DATA8(__ramdisk_base) +DECLARE_DATA8(__ramdisk_size) +DECLARE_DATA8(__command_line) +DECLARE_DATA8(__command_line_len) +DECLARE_DATA8(__efi_memmap_base) +DECLARE_DATA8(__efi_memmap_size) +DECLARE_DATA8(__loaded_segments) +DECLARE_DATA8(__loaded_segments_num) + +DECLARE_DATA8(__gp_value) diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/include/arch/io.h kexec-tools-1.101-kdump/purgatory/arch/ia64/include/arch/io.h --- kexec-tools-1.101/purgatory/arch/ia64/include/arch/io.h 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/include/arch/io.h 2006-07-17 00:20:49.000000000 +0530 @@ -0,0 +1,25 @@ +#ifndef ARCH_IO_H +#define ARCH_IO_H + +#include +/* Helper functions for directly doing I/O */ + +extern inline uint8_t inb(void *port) +{ + volatile unsigned char *addr = (unsigned char *)port; + uint8_t result; + + result = *addr; + asm volatile ("mf.a"::: "memory"); + return result; +} + +extern inline void outb (uint8_t value, void *port) +{ + volatile unsigned char *addr = (unsigned char *)port; + + *addr = value; + asm volatile ("mf.a"::: "memory"); +} + +#endif /* ARCH_IO_H */ diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/io.h kexec-tools-1.101-kdump/purgatory/arch/ia64/io.h --- kexec-tools-1.101/purgatory/arch/ia64/io.h 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/io.h 2006-07-17 00:21:02.000000000 +0530 @@ -0,0 +1,94 @@ +#ifndef IO_H +#define IO_H +#define UNCACHED(x) (void *)((x)|(1UL<<63)) +#define MF() asm volatile ("mf.a" ::: "memory") +#define IO_SPACE_ENCODING(p) ((((p) >> 2) << 12) | (p & 0xfff)) + +static inline void *io_addr (unsigned long port) +{ + unsigned long offset; + unsigned long io_base; + asm volatile ("mov %0=ar.k0":"=r"(io_base)); + offset = IO_SPACE_ENCODING(port); + return UNCACHED(io_base | offset); +} + +static inline unsigned int inb (unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + unsigned char ret; + ret = *addr; + MF(); + return ret; +} + +static inline unsigned int inw (unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + unsigned short ret; + + ret = *addr; + MF(); + return ret; +} + +static inline unsigned int ia64_inl (unsigned long port) +{ + volatile unsigned int *addr = __ia64_mk_io_addr(port); + unsigned int ret; + ret = *addr; + MF(); + return ret; +} + +static inline void outb (unsigned char val, unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + + *addr = val; + MF(); +} + +static inline void outw (unsigned short val, unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + + *addr = val; + MF(); +} + +static inline void outl (unsigned int val, unsigned long port) +{ + volatile unsigned int *addr = io_addr(port); + + *addr = val; + MF(); +} + + +static inline unsigned char readb(const volatile void *addr) +{ + return *(volatile unsigned char *) addr; +} +static inline unsigned short readw(const volatile void *addr) +{ + return *(volatile unsigned short *) addr; +} +static inline unsigned int readl(const volatile void *addr) +{ + return *(volatile unsigned int *) addr; +} + +static inline void writeb(unsigned char b, volatile void *addr) +{ + *(volatile unsigned char *) addr = b; +} +static inline void writew(unsigned short b, volatile void *addr) +{ + *(volatile unsigned short *) addr = b; +} +static inline void writel(unsigned int b, volatile void *addr) +{ + *(volatile unsigned int *) addr = b; +} +#endif diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/Makefile kexec-tools-1.101-kdump/purgatory/arch/ia64/Makefile --- kexec-tools-1.101/purgatory/arch/ia64/Makefile 2004-12-21 04:14:22.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/Makefile 2006-07-17 00:21:02.000000000 +0530 @@ -1,9 +1,9 @@ # # Purgatory ia64 # - -PURGATORY_S_SRCS+= +PCFLAGS += -ffixed-r28 +PURGATORY_S_SRCS+= purgatory/arch/ia64/entry.S PURGATORY_C_SRCS+= purgatory/arch/ia64/purgatory-ia64.c PURGATORY_C_SRCS+= purgatory/arch/ia64/console-ia64.c -PURGATORY_C_SRCS+= +PURGATORY_C_SRCS+= purgatory/arch/ia64/vga.c 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-07-17 00:21:02.000000000 +0530 @@ -1,7 +1,275 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.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 + * 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 +#include +#include #include "purgatory-ia64.h" -void setup_arch(void) +#define PAGE_OFFSET 0xe000000000000000UL + +#define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1UL<efi_memmap_base; + void *src = (void *)boot_param->efi_memmap; + unsigned long len = boot_param->efi_memmap_size; + unsigned long memdesc_size = boot_param->efi_memdesc_size; + uint64_t orig_type; + efi_memory_desc_t *md1, *md2; + void *p1, *p2, *src_end = src + len; + int i; + for (p1 = src, p2 = dest; p1 < src_end; + p1 += memdesc_size, p2 += memdesc_size) { + unsigned long mstart, mend; + md1 = p1; + md2 = p2; + if (md1->num_pages == 0) + continue; + mstart = md1->phys_addr; + mend = md1->phys_addr + (md1->num_pages + << EFI_PAGE_SHIFT); + switch (md1->type) { + case EFI_LOADER_DATA: + *md2 = *md1; + md2->type = EFI_CONVENTIONAL_MEMORY; + break; + default: + *md2 = *md1; + } + // segments are already sorted and aligned to 4K + orig_type = md2->type; + for (i = 0; i < params->loaded_segments_num; i++) { + struct loaded_segment *seg; + seg = ¶ms->loaded_segments[i]; + if (seg->start >= mstart && seg->start < mend) { + unsigned long start_pages, mid_pages, end_pages; + if (seg->end > mend) { + p1 += memdesc_size; + for(; p1 < src_end; + p1 += memdesc_size) { + md1 = p1; + /* TODO check contig and attribute here */ + mend = md1->phys_addr + + (md1->num_pages << EFI_PAGE_SHIFT); + if (seg->end < mend) + break; + } + } + start_pages = (seg->start - mstart) + >> EFI_PAGE_SHIFT; + mid_pages = (seg->end - seg->start) + >> EFI_PAGE_SHIFT; + end_pages = (mend - seg->end) + >> EFI_PAGE_SHIFT; + if (start_pages) { + md2->num_pages = start_pages; + p2 += memdesc_size; + md2 = p2; + *md2 = *md1; + } + md2->phys_addr = seg->start; + md2->num_pages = mid_pages; + md2->type = seg->reserved ? + EFI_UNUSABLE_MEMORY:EFI_LOADER_DATA; + if (end_pages) { + p2 += memdesc_size; + md2 = p2; + *md2 = *md1; + md2->phys_addr = seg->end; + md2->num_pages = end_pages; + md2->type = orig_type; + mstart = seg->end; + } else + break; + } + } + } + + boot_param->efi_memmap_size = p2 - dest; +} + +void +flush_icache_range(char *start, unsigned long len) +{ + unsigned long i; + for (i = 0;i < len; i += 32) + asm volatile("fc.i %0"::"r"(start + i):"memory"); + asm volatile (";;sync.i;;":::"memory"); + asm volatile ("srlz.i":::"memory"); +} + +extern char __dummy_efi_function[], __dummy_efi_function_end[]; + + +void +ia64_env_setup(struct ia64_boot_param *boot_param, + struct kexec_boot_params *params) +{ + unsigned long len; + efi_system_table_t *systab; + efi_runtime_services_t *runtime; + unsigned long *set_virtual_address_map; + char *command_line = (char *)params->command_line; + uint64_t command_line_len = params->command_line_len; + + // patch efi_runtime->set_virtual_address_map to a + // dummy function + len = __dummy_efi_function_end - __dummy_efi_function; + memcpy(command_line + command_line_len, + __dummy_efi_function, len); + systab = (efi_system_table_t *)boot_param->efi_systab; + runtime = (efi_runtime_services_t *)PA(systab->runtime); + set_virtual_address_map = + (unsigned long *)PA(runtime->set_virtual_address_map); + *(set_virtual_address_map) = + (unsigned long)(command_line + command_line_len); + flush_icache_range(command_line + command_line_len, len); + + patch_efi_memmap(params, boot_param); + + boot_param->efi_memmap = params->efi_memmap_base; + + boot_param->command_line = params->command_line; + boot_param->console_info.orig_x = 0; + boot_param->console_info.orig_y = 0; + boot_param->initrd_start = params->ramdisk_base; + boot_param->initrd_size = params->ramdisk_size; +} + +/* 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/ia64/purgatory-ia64.h kexec-tools-1.101-kdump/purgatory/arch/ia64/purgatory-ia64.h --- kexec-tools-1.101/purgatory/arch/ia64/purgatory-ia64.h 2004-12-21 04:14:55.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/purgatory-ia64.h 2006-07-17 00:21:02.000000000 +0530 @@ -1,6 +1,5 @@ #ifndef PURGATORY_IA64_H #define PURGATORY_IA64_H -/* nothing yet */ - +void reset_vga(void); #endif /* PURGATORY_IA64_H */ diff -urNp -X dontdiff kexec-tools-1.101/purgatory/arch/ia64/vga.c kexec-tools-1.101-kdump/purgatory/arch/ia64/vga.c --- kexec-tools-1.101/purgatory/arch/ia64/vga.c 1970-01-01 05:30:00.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/ia64/vga.c 2006-07-17 00:21:02.000000000 +0530 @@ -0,0 +1,143 @@ +#include "io.h" +void reset_vga(void) +{ + /* Hello */ + inb(0x3da); + outb(0, 0x3c0); + + /* Sequencer registers */ + outw(0x0300, 0x3c4); + outw(0x0001, 0x3c4); + outw(0x0302, 0x3c4); + outw(0x0003, 0x3c4); + outw(0x0204, 0x3c4); + + /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */ + outw(0x0e11, 0x3d4); + /* CRTC registers */ + outw(0x5f00, 0x3d4); + outw(0x4f01, 0x3d4); + outw(0x5002, 0x3d4); + outw(0x8203, 0x3d4); + outw(0x5504, 0x3d4); + outw(0x8105, 0x3d4); + outw(0xbf06, 0x3d4); + outw(0x1f07, 0x3d4); + outw(0x0008, 0x3d4); + outw(0x4f09, 0x3d4); + outw(0x200a, 0x3d4); + outw(0x0e0b, 0x3d4); + outw(0x000c, 0x3d4); + outw(0x000d, 0x3d4); + outw(0x010e, 0x3d4); + outw(0xe00f, 0x3d4); + outw(0x9c10, 0x3d4); + outw(0x8e11, 0x3d4); + outw(0x8f12, 0x3d4); + outw(0x2813, 0x3d4); + outw(0x1f14, 0x3d4); + outw(0x9615, 0x3d4); + outw(0xb916, 0x3d4); + outw(0xa317, 0x3d4); + outw(0xff18, 0x3d4); + + /* Graphic registers */ + outw(0x0000, 0x3ce); + outw(0x0001, 0x3ce); + outw(0x0002, 0x3ce); + outw(0x0003, 0x3ce); + outw(0x0004, 0x3ce); + outw(0x1005, 0x3ce); + outw(0x0e06, 0x3ce); + outw(0x0007, 0x3ce); + outw(0xff08, 0x3ce); + + /* Attribute registers */ + inb(0x3da); + outb(0x00, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x01, 0x3c0); + outb(0x01, 0x3c0); + + inb(0x3da); + outb(0x02, 0x3c0); + outb(0x02, 0x3c0); + + inb(0x3da); + outb(0x03, 0x3c0); + outb(0x03, 0x3c0); + + inb(0x3da); + outb(0x04, 0x3c0); + outb(0x04, 0x3c0); + + inb(0x3da); + outb(0x05, 0x3c0); + outb(0x05, 0x3c0); + + inb(0x3da); + outb(0x06, 0x3c0); + outb(0x14, 0x3c0); + + inb(0x3da); + outb(0x07, 0x3c0); + outb(0x07, 0x3c0); + + inb(0x3da); + outb(0x08, 0x3c0); + outb(0x38, 0x3c0); + + inb(0x3da); + outb(0x09, 0x3c0); + outb(0x39, 0x3c0); + + inb(0x3da); + outb(0x0a, 0x3c0); + outb(0x3a, 0x3c0); + + inb(0x3da); + outb(0x0b, 0x3c0); + outb(0x3b, 0x3c0); + + inb(0x3da); + outb(0x0c, 0x3c0); + outb(0x3c, 0x3c0); + + inb(0x3da); + outb(0x0d, 0x3c0); + outb(0x3d, 0x3c0); + + inb(0x3da); + outb(0x0e, 0x3c0); + outb(0x3e, 0x3c0); + + inb(0x3da); + outb(0x0f, 0x3c0); + outb(0x3f, 0x3c0); + + inb(0x3da); + outb(0x10, 0x3c0); + outb(0x0c, 0x3c0); + + inb(0x3da); + outb(0x11, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x12, 0x3c0); + outb(0x0f, 0x3c0); + + inb(0x3da); + outb(0x13, 0x3c0); + outb(0x08, 0x3c0); + + inb(0x3da); + outb(0x14, 0x3c0); + outb(0x00, 0x3c0); + + /* Goodbye */ + inb(0x3da); + outb(0x20, 0x3c0); +} 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-07-17 00:19:56.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-07-17 00:20:34.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 + +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-07-17 00:20:34.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 +#include + +#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-07-17 00:20:34.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-07-17 00:20:34.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 +#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-07-17 00:20:34.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-07-17 00:20:34.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-07-17 00:20:12.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-07-17 00:20:12.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-07-17 00:20:12.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-07-17 00:20:19.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-07-17 00:20:19.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-07-17 00:19:56.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-07-17 00:20:34.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-07-17 00:19:56.000000000 +0530 @@ -44,4 +44,5 @@ void purgatory(void) printf("I'm in purgatory\n"); setup_arch(); verify_sha256_digest(); + post_verification_setup_arch(); }