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