diff -Nurp kexec-tools-1.101-orig/kexec/arch/i386/crashdump-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.c --- kexec-tools-1.101-orig/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-08-24 15:46:08.636240824 +0530 @@ -0,0 +1,723 @@ +/* + * 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 "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-x86.h" +#include "crashdump-x86.h" +#include + +#define MAX_LINE 160 + +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; +} + +/* Returns the virtual address of start of crash notes section. */ +static int get_crash_notes_section_addr(unsigned long *addr) +{ + const char crash_notes[]= "/sys/kernel/crash_notes"; + char line[MAX_LINE]; + FILE *fp; + + fp = fopen(crash_notes, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + crash_notes, strerror(errno)); + fprintf(stderr, "Try mounting sysfs\n"); + return -1; + } + + if (fgets(line, sizeof(line), fp) != 0) { + int count; + count = sscanf(line, "%lx", addr); + if (count != 1) { + *addr = 0; + return -1; + } +#if 0 + printf("crash_notes addr = %lx\n", *addr); +#endif + } + return 0; +} + +/* Prepares the crash memory elf64 headers and stores in supplied buffer. */ +static int prepare_crash_memory_elf64_headers(struct kexec_info *info, + void *buf, unsigned long size) +{ + Elf64_Ehdr *elf; + Elf64_Phdr *phdr; + int i; + char *bufp; + long int nr_cpus = 0; + unsigned long notes_addr, notes_offset; + + 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; + } + + /* Need to find a better way to determine per cpu notes section size. */ +#define MAX_NOTE_BYTES 1024 + if (get_crash_notes_section_addr (¬es_addr) < 0) { + return -1; + } + notes_offset = __pa(notes_addr); + for (i = 0; i < nr_cpus; i++) { + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = notes_offset; + phdr->p_vaddr = phdr->p_paddr = notes_offset; + phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + notes_offset += MAX_NOTE_BYTES; + + /* 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; + unsigned long notes_addr, notes_offset; + + 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 + if (get_crash_notes_section_addr (¬es_addr) < 0) { + return -1; + } + notes_offset = __pa(notes_addr); + for (i = 0; i < nr_cpus; i++) { + phdr = (Elf32_Phdr *) bufp; + bufp += sizeof(Elf32_Phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = notes_offset; + phdr->p_vaddr = phdr->p_paddr = notes_offset; + phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + notes_offset += MAX_NOTE_BYTES; + + /* 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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/crashdump-x86.h kexec-tools-1.101-kdump/kexec/arch/i386/crashdump-x86.h --- kexec-tools-1.101-orig/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-08-24 15:45:43.461068032 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/include/arch/options.h kexec-tools-1.101-kdump/kexec/arch/i386/include/arch/options.h --- kexec-tools-1.101-orig/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-08-24 15:45:35.249316408 +0530 @@ -7,6 +7,8 @@ #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+6) +#define OPT_ELF64_CORE (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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/kexec-bzImage.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-bzImage.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.197388464 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/kexec-elf-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-elf-x86.c --- kexec-tools-1.101-orig/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-08-24 15:46:03.167072264 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/kexec-multiboot-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-multiboot-x86.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.014416280 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/kexec-x86.c kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.c --- kexec-tools-1.101-orig/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-08-24 15:45:35.223320360 +0530 @@ -30,14 +30,16 @@ #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 +81,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 +136,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 +211,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 +225,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 +243,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 +268,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 +280,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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/kexec-x86.h kexec-tools-1.101-kdump/kexec/arch/i386/kexec-x86.h --- kexec-tools-1.101-orig/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-08-24 15:45:35.247316712 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/Makefile kexec-tools-1.101-kdump/kexec/arch/i386/Makefile --- kexec-tools-1.101-orig/kexec/arch/i386/Makefile 2005-02-06 04:53:58.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/i386/Makefile 2005-08-24 15:45:35.248316560 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/x86-linux-setup.c kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.039412480 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/i386/x86-linux-setup.h kexec-tools-1.101-kdump/kexec/arch/i386/x86-linux-setup.h --- kexec-tools-1.101-orig/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-08-24 15:45:28.172392264 +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 -Nurp kexec-tools-1.101-orig/kexec/arch/ia64/kexec-ia64.c kexec-tools-1.101-kdump/kexec/arch/ia64/kexec-ia64.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.040412328 +0530 @@ -38,7 +38,8 @@ 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 +104,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 +116,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 +126,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 +141,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 -Nurp kexec-tools-1.101-orig/kexec/arch/ppc/kexec-ppc.c kexec-tools-1.101-kdump/kexec/arch/ppc/kexec-ppc.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.066408376 +0530 @@ -23,7 +23,8 @@ 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 +121,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 +136,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 -Nurp kexec-tools-1.101-orig/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-orig/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-08-24 15:46:08.634241128 +0530 @@ -222,7 +222,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 -Nurp kexec-tools-1.101-orig/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-orig/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-08-24 15:46:08.635240976 +0530 @@ -37,7 +37,8 @@ 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; @@ -124,7 +125,7 @@ void arch_usage(void) ); } -static struct { +struct { uint8_t reset_vga; uint16_t serial_base; uint32_t serial_baud; @@ -207,7 +208,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 +223,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", diff -Nurp kexec-tools-1.101-orig/kexec/arch/x86_64/Makefile kexec-tools-1.101-kdump/kexec/arch/x86_64/Makefile --- kexec-tools-1.101-orig/kexec/arch/x86_64/Makefile 2005-02-06 04:55:19.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/arch/x86_64/Makefile 2005-08-24 15:46:08.634241128 +0530 @@ -1,6 +1,7 @@ # # kexec x86_64 (linux booting linux) # +KEXEC_C_SRCS+= kexec/arch/i386/crashdump-x86.c KEXEC_C_SRCS+= kexec/arch/i386/kexec-elf-x86.c KEXEC_C_SRCS+= kexec/arch/i386/kexec-bzImage.c KEXEC_C_SRCS+= kexec/arch/i386/kexec-multiboot-x86.c diff -Nurp kexec-tools-1.101-orig/kexec/kexec.c kexec-tools-1.101-kdump/kexec/kexec.c --- kexec-tools-1.101-orig/kexec/kexec.c 2005-01-13 18:54:29.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/kexec.c 2005-08-24 15:45:57.670907808 +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 -Nurp kexec-tools-1.101-orig/kexec/kexec.h kexec-tools-1.101-kdump/kexec/kexec.h --- kexec-tools-1.101-orig/kexec/kexec.h 2005-01-13 18:33:00.000000000 +0530 +++ kexec-tools-1.101-kdump/kexec/kexec.h 2005-08-24 15:45:28.093404272 +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,7 @@ 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); #endif /* KEXEC_H */ diff -Nurp kexec-tools-1.101-orig/purgatory/arch/i386/crashdump_backup.c kexec-tools-1.101-kdump/purgatory/arch/i386/crashdump_backup.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.094404120 +0530 @@ -0,0 +1,44 @@ +/* + * 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. */ +uint32_t 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 -Nurp kexec-tools-1.101-orig/purgatory/arch/i386/Makefile kexec-tools-1.101-kdump/purgatory/arch/i386/Makefile --- kexec-tools-1.101-orig/purgatory/arch/i386/Makefile 2005-01-11 06:37:58.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/arch/i386/Makefile 2005-08-24 15:45:28.119400320 +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 -Nurp kexec-tools-1.101-orig/purgatory/arch/i386/purgatory-x86.c kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.119400320 +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 -Nurp kexec-tools-1.101-orig/purgatory/arch/i386/purgatory-x86.h kexec-tools-1.101-kdump/purgatory/arch/i386/purgatory-x86.h --- kexec-tools-1.101-orig/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-08-24 15:45:28.120400168 +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 -Nurp kexec-tools-1.101-orig/purgatory/arch/ia64/purgatory-ia64.c kexec-tools-1.101-kdump/purgatory/arch/ia64/purgatory-ia64.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.145396368 +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 -Nurp kexec-tools-1.101-orig/purgatory/arch/ppc/purgatory-ppc.c kexec-tools-1.101-kdump/purgatory/arch/ppc/purgatory-ppc.c --- kexec-tools-1.101-orig/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-08-24 15:45:28.170392568 +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 -Nurp kexec-tools-1.101-orig/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-orig/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-08-24 15:45:28.171392416 +0530 @@ -10,3 +10,9 @@ 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) +{ + /* Nothing for now */ +} diff -Nurp kexec-tools-1.101-orig/purgatory/include/purgatory.h kexec-tools-1.101-kdump/purgatory/include/purgatory.h --- kexec-tools-1.101-orig/purgatory/include/purgatory.h 2004-12-18 18:42:15.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/include/purgatory.h 2005-08-24 15:45:28.145396368 +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 -Nurp kexec-tools-1.101-orig/purgatory/purgatory.c kexec-tools-1.101-kdump/purgatory/purgatory.c --- kexec-tools-1.101-orig/purgatory/purgatory.c 2004-12-22 00:21:03.000000000 +0530 +++ kexec-tools-1.101-kdump/purgatory/purgatory.c 2005-08-24 15:45:28.144396520 +0530 @@ -44,4 +44,5 @@ void purgatory(void) printf("I'm in purgatory\n"); setup_arch(); verify_sha256_digest(); + post_verification_setup_arch(); }