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 2005-12-14 16:00:40.000000000 +0530 @@ -1384,12 +1384,18 @@ case $host_cpu in powerpc ) host_cpu="ppc" ;; + powerpc64 ) + host_cpu="ppc64" + ;; + s390x ) + host_cpu="s390" + ;; * ) host_cpu="$host_cpu" ;; esac case $host_cpu in - i386|ppc|x86_64|alpha|ppc64|ia64) + i386|ppc|x86_64|alpha|ppc64|ia64|s390) ;; * ) { { echo "$as_me:$LINENO: error: unsupported architecture $host_cpu" >&5 @@ -1421,6 +1427,10 @@ if test "${with_gamecube+set}" = set; th EXTRA_CFLAGS="$EXTRA_CFLAGS -DCONFIG_GAMECUBE=1" fi; +# Check whether ppc64. Add -m64 for building 64-bit binary +if test "$ARCH" = ppc64; then + EXTRA_CFLAGS="$EXTRA_CFLAGS -m64" +fi; # Check whether --with-zlib or --without-zlib was given. if test "${with_zlib+set}" = set; then 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 2005-12-15 16:51:43.720392560 +0530 @@ -0,0 +1,724 @@ +/* + * 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; + 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 + 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; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (!mstart && !mend) + break; + 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 2005-12-14 15:57:17.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 2005-12-14 16:00:38.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 2005-12-14 15:57:15.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 2005-12-14 15:57:18.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 2005-12-14 15:57:15.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 2005-12-15 11:55:22.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 2005-12-14 15:57:16.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 2005-12-14 15:57:16.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 2005-12-14 15:57:15.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) { 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 2005-12-14 15:57:15.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 2005-12-15 11:55:22.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 2005-12-15 11:55:22.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/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 2005-12-14 15:57:18.000000000 +0530 @@ -0,0 +1,340 @@ +/* + * 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-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]; + +extern unsigned long initrd_base; +extern unsigned long initrd_size; +static int initrd_found = 0; + +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,initrd-start")) { + if (initrd_base) + *(unsigned long long *) data = initrd_base; + base = *(unsigned long long *)data; + initrd_found = 1; + } + 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; + else if (!strcmp(name, "linux,initrd-end")) { + if (initrd_size) { + *(unsigned long long *) data = initrd_base + + initrd_size; + size = initrd_size; + } else + end = *(unsigned long long *)data; + initrd_found = 1; + } + 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; +} + +/* put all properties (files) in the property structure */ +void putprops(char *fn, DIR *dir) +{ + struct dirent *dp; + + while ((dp = readdir(dir)) != NULL) { + strcpy(fn, dp->d_name); + + if (lstat(pathname, statbuf)) + err(pathname, ERR_STAT); + + /* skip initrd entries if 2nd kernel does not need them */ + if (!initrd_base && !strcmp(fn,"linux,initrd-end")) + continue; + + if (!initrd_base && !strcmp(fn,"linux,initrd-start")) + 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, "htab_base") || + !strcmp(dp->d_name, "htab_size") || + !strcmp(dp->d_name, "kernel_end")) + continue; + + if (S_ISREG(statbuf[0].st_mode)) { + int fd, len = statbuf[0].st_size; + + *dt++ = 3; + *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); + close(fd); + + checkprop(fn, dt); + + dt += (len + 3)/4; + } + } + fn[0] = '\0'; + if(errno == ENOSYS) + errno = 0; + if (errno) + err(pathname, ERR_READDIR); + checkprop(pathname, NULL); +} + +/* + * put a node (directory) in the property structure. first properties + * then children. + */ +void putnode(void) +{ + DIR *dir; + char *dn; + struct dirent *dp; + char *basename; + + *dt++ = 1; + strcpy((void *)dt, *pathstart ? pathstart : "/"); + while(*dt) + dt++; + if (dt[-1] & 0xff) + dt++; + + dir = opendir(pathname); + + if (!dir) + err(pathname, ERR_OPENDIR); + + basename = strrchr(pathname,'/'); + + strcat(pathname, "/"); + dn = pathname + strlen(pathname); + + putprops(dn, dir); + + /* 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,8); + dt += (len + 3)/4; + + reserve(initrd_base, initrd_size); + } + + rewinddir(dir); + + while ((dp = readdir(dir)) != NULL) { + strcpy(dn, dp->d_name); + + 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; + closedir(dir); + dn[-1] = '\0'; +} + +struct bootblock bb[1]; + +int create_flatten_tree(struct kexec_info *info, unsigned char **bufp, unsigned long *sizep) +{ + 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; + + 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 *) realloc(*bufp, *sizep + bb->totalsize); + *bufp = buf; + memcpy(buf+(*sizep), bb, bb->off_mem_rsvmap); + tlen = *sizep + 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/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 2005-12-14 16:00:37.000000000 +0530 @@ -3,6 +3,7 @@ * * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) * Copyright (C) 2004 IBM Corp. + * Copyright (C) 2005 R Sharada (sharada@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 @@ -33,45 +34,15 @@ #include "../../kexec.h" #include "../../kexec-elf.h" #include "kexec-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, - }, -}; +int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *); +int parse_options(char *); +int setup_memory_ranges(void); int elf_ppc64_probe(const char *buf, off_t len) { @@ -94,12 +65,69 @@ int elf_ppc64_probe(const char *buf, off return result; } -int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, +int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; + const char *command_line; + const char *input_options; + int command_line_len; + const char *ramdisk; + const char *devicetreeblob; + unsigned long *lp; + int result; + int opt; +#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 + { "append", 1, NULL, OPT_APPEND }, + { "ramdisk", 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; + command_line = 0; + input_options = 0; + ramdisk = 0; + devicetreeblob = 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: + input_options = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_DEVICETREEBLOB: + devicetreeblob = optarg; + break; + } + } + + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) + 1; + } + + if (input_options) + parse_options(input_options); /* Parse the Elf file */ result = build_elf_exec_info(buf, len, &ehdr); @@ -109,15 +137,180 @@ int elf_ppc64_load(int argc, char **argv } /* Load the Elf data */ + setup_memory_ranges(); + /* 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 + */ + unsigned long long base_addr; + struct mem_phdr *phdr; + size_t size; + + phdr = &ehdr.e_phdr[0]; + size = phdr->p_filesz; + if (size > phdr->p_memsz) { + size = phdr->p_memsz; + } + + base_addr = (unsigned long)locate_hole(info, size, 0, 0, + 0xFFFFFFFFFFFFFFFFUL, 1); + ehdr.e_phdr[0].p_paddr = base_addr; result = elf_exec_load(&ehdr, info); if (result < 0) { free_elf_info(&ehdr); return result; } - return 1; + + /* Add a ram-disk to the current image */ + if (ramdisk) { + if (devicetreeblob) { + fprintf(stderr, "Can't use ramdisk with device tree blob input\n"); + return -1; + } + unsigned char *ramdisk_buf = NULL; + off_t ramdisk_size = 0; + unsigned long long ramdisk_addr; + + ramdisk_buf = slurp_file(ramdisk, &ramdisk_size); + add_buffer(info, ramdisk_buf, ramdisk_size, ramdisk_size, 0, 0, + 0xFFFFFFFFFFFFFFFFUL, 1); + ramdisk_addr = (unsigned long long)info->segment[info->nr_segments-1].mem; + initrd_base = ramdisk_addr; + initrd_size = ramdisk_size; + } + + /* Add v2wrap to the current image */ + unsigned char *v2wrap_buf = NULL; + off_t v2wrap_size = 0; + unsigned long long *rsvmap_ptr; + struct bootblock *bb_ptr; + unsigned int devtree_size; + + v2wrap_buf = (char *) malloc(purgatory_size); + if (v2wrap_buf == NULL) { + free_elf_info(&ehdr); + return -1; + } + memcpy(v2wrap_buf, purgatory, purgatory_size); + v2wrap_size = purgatory_size; + if (devicetreeblob) { + unsigned char *blob_buf = NULL; + off_t blob_size = 0; + unsigned char *tmp_buf = NULL; + + /* Grab device tree from buffer */ + blob_buf = slurp_file(devicetreeblob, &blob_size); + + /* Append to purgatory */ + tmp_buf = (unsigned char *) realloc(v2wrap_buf, v2wrap_size + blob_size); + v2wrap_buf = tmp_buf; + memcpy(v2wrap_buf+v2wrap_size, blob_buf, blob_size); + v2wrap_size += blob_size; + + } else { + /* create from fs2dt */ + create_flatten_tree(info, &v2wrap_buf, &v2wrap_size); + } + add_buffer(info, v2wrap_buf, v2wrap_size, v2wrap_size, 0, 0, + 0xFFFFFFFFFFFFFFFFUL, -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 + + 0x100); + rsvmap_ptr = (long long *)( + (unsigned char *)info->segment[(info->nr_segments)-1].buf + + bb_ptr->off_mem_rsvmap + 0x100); + while (*rsvmap_ptr || *(rsvmap_ptr+1)){ + rsvmap_ptr += 2; + } + rsvmap_ptr -= 2; + *rsvmap_ptr = (unsigned long long)( + info->segment[(info->nr_segments)-1].mem + 0x100); + rsvmap_ptr++; + *rsvmap_ptr = (unsigned long long)bb_ptr->totalsize; + + unsigned int nr_segments; + nr_segments = info->nr_segments; + lp = info->segment[nr_segments-1].buf + 0x100; + lp--; + *lp = info->segment[0].mem; + info->entry = info->segment[nr_segments-1].mem; + + unsigned int i; + for (i = 0; i < nr_segments; i++) + printf("segment[i].mem:%lx\n", info->segment[i].mem); + + return 0; } void elf_ppc64_usage(void) { fprintf(stderr, "elf support is still broken\n"); } + +struct param_struct { + const char *name; + void *val; +}; +struct param_struct params; + +static char *next_arg(char *args, char **param, char **val) +{ + unsigned int i, equals = 0; + char *next; + + /* Chew any extra spaces */ + while (*args == ' ') args++; + for (i = 0; args[i]; i++) { + if (args[i] == ' ') + break; + if (equals == 0) { + if (args[i] == '=') + equals = i; + } + } + *param = args; + if (!equals) + *val = NULL; + else { + args[equals] = '\0'; + *val = args + equals + 1; + } + + if (args[i]) { + args[i] = '\0'; + next = args + i + 1; + } else + next = args + i; + return next; +} + +static int add_arg(char *param, char*val) +{ + int ret = 0; + if (strcmp(param, "initrd-base")==0) + initrd_base=strtoul(val, NULL, 0); + else if (strcmp(param, "initrd-size")==0) + initrd_size=strtoul(val, NULL, 0); + else { + printf("invalid option\n"); + ret = 1; + } + return ret; +} + +int parse_options(char *options) +{ + char *param, *val; + /* initrd-addr , initrd-size */ + while (*options) { + int ret; + options = next_arg(options, ¶m, &val); + ret = add_arg(param, val); + } + /* All parsed OK */ + return 0; +} 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 2005-12-14 15:57:19.000000000 +0530 @@ -22,7 +22,7 @@ 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]; + shdr_end = &ehdr->e_shdr[ehdr->e_shnum]; for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { if (strcmp(shdr->sh_name, ".toc") == 0) { return shdr; 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 2005-12-14 16:00:37.000000000 +0530 @@ -0,0 +1,527 @@ +/* + * 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 + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 +#define MAXBYTES 128 +/* Platforms supported by kexec on PPC64 */ +#define PLATFORM_PSERIES 0x0100 +#define PLATFORM_PSERIES_LPAR 0x0101 + +struct exclude_range { + unsigned long long start, end; +}; +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; +static int nr_exclude_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; + unsigned long long start, end; + + 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++; +#if 0 + 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); + } + memory_max = base_memory_range[local_memory_ranges - 1].end; + closedir(dmem); + } + closedir(dir); + nr_memory_ranges = local_memory_ranges; + fprintf(stderr, "get base memory ranges:%d\n", nr_memory_ranges); + 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 */ +static int get_devtree_details() +{ + unsigned long long rmo_base; + unsigned long long tce_base; + unsigned int tce_size; + 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, "/kernel_end"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(cdir); + closedir(dir); + return -1; + } + unsigned long long kernel_end; + if (fread(&kernel_end, sizeof(unsigned long), 1, file) != 1) { + perror(fname); + fclose(file); + closedir(cdir); + closedir(dir); + return -1; + } + /* Add kernel memory to exclude_range */ + exclude_range[i].start = 0x0UL; + exclude_range[i].end = kernel_end; + i++; + /* 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, "/htab_base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(cdir); + closedir(dir); + return -1; + } + unsigned long long htab_base, htab_size; + 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, "/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; + } + unsigned int rtas_base, rtas_size; + 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++; + } /* 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 (memory_max > 0x40000000UL? (rmo_top = 0x40000000UL) : (rmo_top = memory_max)); + 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++; + closedir(cdir); + } /* pci */ + } + closedir(dir); + + nr_exclude_ranges = i; + + sort_ranges(); +#if 0 + 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(void) +{ + 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(); + + 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; + +#if 0 + int k; + for (k = 0; k < j; k++) + fprintf(stderr, "seup_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) +{ + setup_memory_ranges(); + *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"); +} + +static struct { +} arch_options = { +}; + +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; + } + } + /* 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, unsigned long *flags) +{ + int result; + struct utsname utsname; + result = uname(&utsname); + if (result < 0) { + fprintf(stderr, "uname failed: %s\n", + strerror(errno)); + return -1; + } + if (strcmp(utsname.machine, "ppc64") == 0) + { + /* We are running a 32-bit kexec-tools on 64-bit ppc64. + * So pass KEXEC_ARCH_PPC64 here + */ + *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 2005-12-14 15:57:19.000000000 +0530 @@ -6,4 +6,17 @@ int elf_ppc64_load(int argc, char **argv struct kexec_info *info); void elf_ppc64_usage(void); -#endif /* KEXEC_PPC_H */ +unsigned long initrd_base; +unsigned long 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; +}; +#endif /* KEXEC_PPC64_H */ 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 2005-12-14 15:57:18.000000000 +0530 @@ -2,6 +2,9 @@ # 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_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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-15 16:57:30.415686864 +0530 @@ -0,0 +1,766 @@ +/* + * 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; + 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.*/ + + /* 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; +} + +/* 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_X86_64; + 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_per_cpu(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; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (!mstart && !mend) + break; + 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.*/ + + /* 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 = 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, 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; + } + 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); + + /* 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 2005-12-14 16:00:44.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/include/arch/options.h kexec-tools-1.101-kdump/kexec/arch/x86_64/include/arch/options.h --- kexec-tools-1.101/kexec/arch/x86_64/include/arch/options.h 2004-12-22 02:24:02.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/x86_64/include/arch/options.h 2005-12-14 16:00:44.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/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 2005-12-14 16:00:58.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 2005-12-15 11:55:22.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; } @@ -121,21 +136,25 @@ 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 { +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) @@ -199,6 +218,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 */ @@ -207,7 +232,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 +247,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 +259,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 +271,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 2005-12-14 16:00:44.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 2005-12-15 16:56:46.389379880 +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 2005-12-15 11:55:22.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 2005-12-14 15:57:17.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, ...) { @@ -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.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 2005-12-15 11:55:22.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 2005-12-15 11:09:09.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 2005-12-15 11:55:22.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 2005-12-14 16:00:56.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 2005-12-14 15:57:15.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 2005-12-14 15:57:15.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 2005-12-14 15:57:15.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 2005-12-14 15:57:15.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 2005-12-14 15:57:15.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/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 2005-12-14 15:57:19.000000000 +0530 @@ -3,5 +3,5 @@ # PURGATORY_C_SRCS+= -PURGATORY_S_SRCS+= +PURGATORY_S_SRCS+= purgatory/arch/ppc64/v2wrap.S 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 2005-12-14 15:57:19.000000000 +0530 @@ -0,0 +1,114 @@ +# +# kexec: Linux boots Linux +# +# Copyright (C) 2004 - 2005, Milton D Miller II, 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 place in front of a v2 device tree +# to call a 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 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. + + +# look a bit like a Linux kernel here ... + .machine ppc64 + .org 0 +start: b master + tweq 0,0 +secondary_hold: + .llong 0 + + .org 0x20 # need a bit more space than after slave, +master: + std 4,secondary_hold@l(0) # bring slaves up here to this copy + sync # try to get the slaves to see this + or 1,1,1 # low priority to let other thread catchup + isync + mr 5,3 # save cpu id to r5 + addi 3,4,0x100 # r3 = boot param block + lwz 6,20(3) # fetch version number + cmpwi 0,6,2 # v2 ? + blt 80f + stw 5,28(3) # save my cpu number as boot_cpu_phys +80: b 81f + + .org 0x60 # ABI: slaves start at 60 with r3=phys +slave: ld 4,secondary_hold@l(0); + cmpdi 0,4,0 + beq slave + + # ahh, master told us where he is running from + # jump into our copy of the code up there so this code can change + addi 5,4,1f-start + mtctr 5 + bctr + + # ok, now wait for the master to tell is to go back to the new block +1: ld 5,copied@l(4) + cmpdi 0,5,0 + beq 1b + ba 0x60 + + + + .long 0 # just an eye-catcher, delete if space needed + .long 0 # just an eye-catcher, delete if space needed + +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 + + ld 4,-8(3) # kernel pointer is at -8(bb) by loader + 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 + std 6,-16(3) # send slaves back down + bctr # start kernel + + .org 0xf0 +copied: .llong 0 +kernel: .llong 0 + .org 0x100 +__end_stub: + .equ boot_block, . - start 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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:40.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 2005-12-14 16:00:56.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 2005-12-14 16:00:56.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 2005-12-14 15:57:15.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 2005-12-14 15:57:19.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 \ @@ -17,9 +22,11 @@ PCFLAGS += $(call cc-option, -fnostdinc) PCFLAGS += $(call cc-option, -fno-zero-initialized-in-bss) PURGATORY_C_SRCS:= +ifneq ($(ARCH),ppc64) PURGATORY_C_SRCS += purgatory/purgatory.c PURGATORY_C_SRCS += purgatory/printf.c PURGATORY_C_SRCS += purgatory/string.c +endif PURGATORY_S_OBJS:= include purgatory/arch/$(ARCH)/Makefile @@ -54,7 +61,12 @@ $(PURGATORY_S_OBJS): $(OBJDIR)/%.o: %.S $(PURGATORY): $(PURGATORY_OBJS) $(UTIL_LIB) $(MKDIR) -p $(@D) +ifneq ($(ARCH),ppc64) $(LD) $(LDFLAGS) --no-undefined -e purgatory_start -r -o $@ $(PURGATORY_OBJS) $(UTIL_LIB) +else + $(LD) -Ttext=0 -e 0 -o $(OBJDIR)/purgatory/v2wrap.elf $(PURGATORY_OBJS) + objcopy -O binary $(OBJDIR)/purgatory/v2wrap.elf $@ +endif echo:: @echo "PURGATORY_C_SRCS $(PURGATORY_C_SRCS)" 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 2005-12-14 15:57:15.000000000 +0530 @@ -44,4 +44,5 @@ void purgatory(void) printf("I'm in purgatory\n"); setup_arch(); verify_sha256_digest(); + post_verification_setup_arch(); }