diff options
-rw-r--r-- | kexec/arch/ppc64/fs2dt.c | 8 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-elf-ppc64.c | 176 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-elf-rel-ppc64.c | 41 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-zImage-ppc64.c | 3 | ||||
-rw-r--r-- | kexec/kexec-elf-rel.c | 38 | ||||
-rw-r--r-- | kexec/kexec-elf.c | 88 | ||||
-rw-r--r-- | purgatory/Makefile | 11 | ||||
-rw-r--r-- | purgatory/arch/ppc64/Makefile | 5 | ||||
-rw-r--r-- | purgatory/arch/ppc64/console-ppc64.c | 27 | ||||
-rw-r--r-- | purgatory/arch/ppc64/crashdump_backup.c | 41 | ||||
-rw-r--r-- | purgatory/arch/ppc64/purgatory-ppc64.c | 41 | ||||
-rw-r--r-- | purgatory/arch/ppc64/purgatory-ppc64.h | 6 | ||||
-rw-r--r-- | purgatory/arch/ppc64/v2wrap.S | 102 |
13 files changed, 398 insertions, 189 deletions
diff --git a/kexec/arch/ppc64/fs2dt.c b/kexec/arch/ppc64/fs2dt.c index 447da64..2b81402 100644 --- a/kexec/arch/ppc64/fs2dt.c +++ b/kexec/arch/ppc64/fs2dt.c @@ -374,7 +374,7 @@ void putnode(void) } int create_flatten_tree(struct kexec_info *info, unsigned char **bufp, - unsigned long *sizep, char *cmdline) + unsigned long *sizep, char *cmdline) { unsigned long len; unsigned long tlen; @@ -420,10 +420,10 @@ int create_flatten_tree(struct kexec_info *info, unsigned char **bufp, reserve(me, bb->totalsize); /* patched later in kexec_load */ - buf = (unsigned char *) realloc(*bufp, *sizep + bb->totalsize); + buf = (unsigned char *) malloc(bb->totalsize); *bufp = buf; - memcpy(buf+(*sizep), bb, bb->off_mem_rsvmap); - tlen = *sizep + bb->off_mem_rsvmap; + memcpy(buf, bb, bb->off_mem_rsvmap); + tlen = bb->off_mem_rsvmap; memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap); tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap); memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct); diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c index 08106ce..50a59f5 100644 --- a/kexec/arch/ppc64/kexec-elf-ppc64.c +++ b/kexec/arch/ppc64/kexec-elf-ppc64.c @@ -4,6 +4,7 @@ * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) * Copyright (C) 2004 IBM Corp. * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +46,7 @@ unsigned long initrd_base, initrd_size; int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *, char *); +int my_r2(struct mem_ehdr *ehdr); int elf_ppc64_probe(const char *buf, off_t len) { @@ -83,6 +85,10 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, struct bootblock *bb_ptr; unsigned int nr_segments, i; int result, opt; + unsigned long my_kernel, my_dt_offset; + unsigned int my_panic_kernel; + unsigned long my_stack, my_backup_start; + unsigned long toc_addr; #define OPT_APPEND (OPT_ARCH_MAX+0) #define OPT_RAMDISK (OPT_ARCH_MAX+1) @@ -167,7 +173,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, size = phdr->p_memsz; hole_addr = (unsigned long)locate_hole(info, size, 0, 0, - 0xFFFFFFFFFFFFFFFFUL, 1); + max_addr, 1); ehdr.e_phdr[0].p_paddr = hole_addr; result = elf_exec_load(&ehdr, info); if (result < 0) { @@ -175,24 +181,6 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, return result; } - /* 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; - } - /* If panic kernel is being loaded, additional segments need * to be created. */ @@ -207,68 +195,136 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, } /* 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; + seg_buf = NULL; + seg_size = 0; - v2wrap_buf = (char *) malloc(purgatory_size); - if (v2wrap_buf == NULL) { + seg_buf = (char *) malloc(purgatory_size); + if (seg_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; + memcpy(seg_buf, purgatory, purgatory_size); + seg_size = purgatory_size; + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0, max_addr, 1); - /* Grab device tree from buffer */ - blob_buf = slurp_file(devicetreeblob, &blob_size); + /* Add a ram-disk to the current image + * Note: Add the ramdisk after elf_rel_build_load + */ + if (ramdisk) { + if (devicetreeblob) { + fprintf(stderr, + "Can't use ramdisk with device tree blob input\n"); + return -1; + } + seg_buf = slurp_file(ramdisk, &seg_size); + add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, 1); + hole_addr = (unsigned long long) + info->segment[info->nr_segments-1].mem; + initrd_base = hole_addr; + initrd_size = (unsigned long long) + info->segment[info->nr_segments-1].memsz; + } /* ramdisk */ + + if (devicetreeblob) { + unsigned char *blob_buf = NULL; + off_t blob_size = 0; - /* 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; + /* Grab device tree from buffer */ + blob_buf = slurp_file(devicetreeblob, &blob_size); + add_buffer(info, blob_buf, blob_size, blob_size, 0, 0, + max_addr, -1); } else { - /* create from fs2dt */ - create_flatten_tree(info, &v2wrap_buf, &v2wrap_size); + /* create from fs2dt */ + seg_buf = NULL; + seg_size = 0; + create_flatten_tree(info, &seg_buf, &seg_size, cmdline); + add_buffer(info, seg_buf, seg_size, seg_size, + 0, 0, max_addr, -1); } - 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 */ + * 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); + (unsigned char *)info->segment[(info->nr_segments)-1].buf); 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)){ + bb_ptr->off_mem_rsvmap); + while (*rsvmap_ptr || *(rsvmap_ptr+1)) rsvmap_ptr += 2; - } rsvmap_ptr -= 2; *rsvmap_ptr = (unsigned long long)( - info->segment[(info->nr_segments)-1].mem + 0x100); - rsvmap_ptr++; - *rsvmap_ptr = (unsigned long long)bb_ptr->totalsize; + info->segment[(info->nr_segments)-1].mem); + 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; + /* Set kernel */ + my_kernel = (unsigned long )info->segment[0].mem; + elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); + + /* Set dt_offset */ + my_dt_offset = (unsigned long )info->segment[nr_segments-1].mem; + elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset, + sizeof(my_dt_offset)); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + my_panic_kernel = 1; + /* Set panic flag */ + elf_rel_set_symbol(&info->rhdr, "panic_kernel", + &my_panic_kernel, sizeof(my_panic_kernel)); + + /* Set backup address */ + my_backup_start = info->backup_start; + elf_rel_set_symbol(&info->rhdr, "backup_start", + &my_backup_start, sizeof(my_backup_start)); + } + + /* Set stack address */ + my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1); + my_stack += 16*1024; + elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); + + /* Set toc */ + toc_addr = (unsigned long) my_r2(&info->rhdr); + elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); + +#ifdef DEBUG + my_kernel = 0; + my_dt_offset = 0; + my_panic_kernel = 0; + my_backup_start = 0; + my_stack = 0; + toc_addr = 0; + + elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); + elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset, + sizeof(my_dt_offset)); + elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, + sizeof(my_panic_kernel)); + elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start, + sizeof(my_backup_start)); + elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); + elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr, + sizeof(toc_addr)); + + fprintf(stderr, "info->entry is %p\n", info->entry); + fprintf(stderr, "kernel is %Lx\n", my_kernel); + fprintf(stderr, "dt_offset is %Lx\n", my_dt_offset); + fprintf(stderr, "panic_kernel is %x\n", my_panic_kernel); + fprintf(stderr, "backup_start is %Lx\n", my_backup_start); + fprintf(stderr, "stack is %Lx\n", my_stack); + fprintf(stderr, "toc_addr is %Lx\n", toc_addr); + fprintf(stderr, "purgatory size is %d\n", purgatory_size); +#endif + for (i = 0; i < nr_segments; i++) - printf("segment[i].mem:%lx\n", info->segment[i].mem); + fprintf(stderr, "segment[%d].mem:%p memsz:%ld\n", i, + info->segment[i].mem, info->segment[i].memsz); return 0; } diff --git a/kexec/arch/ppc64/kexec-elf-rel-ppc64.c b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c index b50f266..a62552c 100644 --- a/kexec/arch/ppc64/kexec-elf-rel-ppc64.c +++ b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c @@ -1,5 +1,6 @@ #include <stdio.h> #include <elf.h> +#include <string.h> #include "../../kexec.h" #include "../../kexec-elf.h" @@ -21,20 +22,19 @@ static struct mem_shdr *toc_section(const struct mem_ehdr *ehdr) { struct mem_shdr *shdr, *shdr_end; unsigned char *strtab; - strtab = ehdr->e_shdr[ehdr->e_shstrndx].sh_data; + strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data; shdr_end = &ehdr->e_shdr[ehdr->e_shnum]; - for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { - if (strcmp(shdr->sh_name, ".toc") == 0) { + for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) + if ( shdr->sh_size && + strcmp(&strtab[shdr->sh_name], ".toc") == 0) return shdr; - } - } return NULL; } /* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this gives the value maximum span in an instruction which uses a signed offset) */ -static unsigned long my_r2(const struct mem_ehdr *ehdr) +unsigned long my_r2(const struct mem_ehdr *ehdr) { struct mem_shdr *shdr; shdr = toc_section(ehdr); @@ -53,7 +53,7 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, /* Simply set it */ *(uint32_t *)location = value; break; - + case R_PPC64_ADDR64: /* Simply set it */ *(uint64_t *)location = value; @@ -78,15 +78,34 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, /* Convert value to relative */ value -= address; if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){ - die("REL24 %li out of range!\n", + die("REL24 %li out of range!\n", (long int)value); } /* Only replace bits 2 through 26 */ - *(uint32_t *)location - = (*(uint32_t *)location & ~0x03fffffc) - | (value & 0x03fffffc); + *(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc) + | (value & 0x03fffffc); + break; + + case R_PPC64_ADDR16_LO: + *(uint16_t *)location = value & 0xffff; + break; + + case R_PPC64_ADDR16_HI: + *(uint16_t *)location = (value>>16) & 0xffff; break; + + case R_PPC64_ADDR16_HA: + *(uint16_t *)location = ((value>>16) & 0xffff); + break; + + case R_PPC64_ADDR16_HIGHEST: + *(uint16_t *)location = ((value>>48) & 0xffff); + break; + case R_PPC64_ADDR16_HIGHER: + *(uint16_t *)location = ((value>>32) & 0xffff); + break; + default: die("Unknown rela relocation: %lu\n", r_type); break; diff --git a/kexec/arch/ppc64/kexec-zImage-ppc64.c b/kexec/arch/ppc64/kexec-zImage-ppc64.c index 2b5e00f..697a4db 100644 --- a/kexec/arch/ppc64/kexec-zImage-ppc64.c +++ b/kexec/arch/ppc64/kexec-zImage-ppc64.c @@ -153,7 +153,8 @@ int zImage_ppc64_load(FILE *file, int argc, char **argv, void **ret_entry, return -1; } mem_offset = p->p_vaddr - load_loc; - if (fread(segment->buf+mem_offset, p->p_filesz, 1, file) != 1) { + if (fread((void *)segment->buf+mem_offset, p->p_filesz, 1, + file) != 1) { perror("read error: "); return -1; } diff --git a/kexec/kexec-elf-rel.c b/kexec/kexec-elf-rel.c index 560c659..1f5cdf1 100644 --- a/kexec/kexec-elf-rel.c +++ b/kexec/kexec-elf-rel.c @@ -155,7 +155,7 @@ int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr) if (probe_debug) { fprintf(stderr, "No ELF section headers\n"); } - return -1; + return -1; } if (!machine_verify_elf_rel(ehdr)) { /* It does not meant the native architecture constraints */ @@ -251,7 +251,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, /* Allocate where we will put the relocated object */ buf = xmalloc(bufsz); - buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz, + buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz, buf_align, min, max, end); ehdr->rel_addr = buf_addr; ehdr->rel_size = bufsz + bss_pad + bsssz; @@ -269,7 +269,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, unsigned long off; /* Adjust the address */ data_addr = (data_addr + (align - 1)) & ~(align -1); - + /* Update the section */ off = data_addr - buf_addr; memcpy(buf + off, shdr->sh_data, shdr->sh_size); @@ -306,7 +306,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, continue; } if ((shdr->sh_info > ehdr->e_shnum) || - (shdr->sh_link > ehdr->e_shnum)) + (shdr->sh_link > ehdr->e_shnum)) { die("Invalid section number\n"); } @@ -350,12 +350,12 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, /* The final address of that location */ address = section->sh_addr + rel.r_offset; - + /* The relevant symbol */ sym = elf_sym(ehdr, symtab->sh_data + (rel.r_sym * elf_sym_size(ehdr))); -#if 0 +#ifdef DEBUG fprintf(stderr, "sym: %10s info: %02x other: %02x shndx: %lx value: %lx size: %lx\n", - strtab + sym.st_name, + strtab + sym.st_name, sym.st_info, sym.st_other, sym.st_shndx, @@ -364,8 +364,18 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, #endif if (sym.st_shndx == STN_UNDEF) { - die("Undefined symbol: %s\n", + /* + * NOTE: ppc64 elf .ro shows up a UNDEF section. + * From Elf 1.2 Spec: + * Relocation Entries: If the index is STN_UNDEF, + * the undefined symbol index, the relocation uses 0 + * as the "symbol value". + * So, is this really an error condition to flag die? + */ + /* + die("Undefined symbol: %s\n", strtab + sym.st_name); + */ } sec_base = 0; if (sym.st_shndx == SHN_COMMON) { @@ -383,14 +393,14 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, else { sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr; } -#if 0 +#ifdef DEBUG fprintf(stderr, "sym: %s value: %lx addr: %lx\n", strtab + sym.st_name, value, address); #endif value = sym.st_value; value += sec_base; value += rel.r_addend; - machine_apply_elf_rel(ehdr, rel.r_type, + machine_apply_elf_rel(ehdr, rel.r_type, (void *)location, address, value); } } @@ -399,7 +409,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, return result; } -void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, +void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, const char *buf, off_t len, unsigned long min, unsigned long max, int end) { @@ -452,8 +462,8 @@ int elf_rel_find_symbol(struct mem_ehdr *ehdr, if (strcmp(strtab + sym.st_name, name) != 0) { continue; } - if ((sym.st_shndx == STN_UNDEF) || - (sym.st_shndx > ehdr->e_shnum)) + if ((sym.st_shndx == STN_UNDEF) || + (sym.st_shndx > ehdr->e_shnum)) { die("Symbol: %s has Bad section index %d\n", name, sym.st_shndx); @@ -491,7 +501,7 @@ void elf_rel_set_symbol(struct mem_ehdr *ehdr, result = elf_rel_find_symbol(ehdr, name, &sym); if (result < 0) { - die("Symbol: %s not found cannot set\n", + die("Symbol: %s not found cannot set\n", name); } if (sym.st_size != size) { diff --git a/kexec/kexec-elf.c b/kexec/kexec-elf.c index e4d07f4..021bca9 100644 --- a/kexec/kexec-elf.c +++ b/kexec/kexec-elf.c @@ -113,21 +113,21 @@ static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_entry) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) { /* entry is to large */ if (probe_debug) { fprintf(stderr, "ELF e_entry to large\n"); } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_phoff) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) { /* phoff is to large */ if (probe_debug) { fprintf(stderr, "ELF e_phoff to large\n"); } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_shoff) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) { /* shoff is to large */ if (probe_debug) { fprintf(stderr, "ELF e_shoff to large\n"); @@ -146,7 +146,7 @@ static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx); if ((ehdr->e_phnum > 0) && - (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr))) + (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr))) { /* Invalid program header size */ if (probe_debug) { @@ -185,21 +185,21 @@ static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_entry) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) { /* entry is to large */ if (probe_debug) { fprintf(stderr, "ELF e_entry to large\n"); } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_phoff) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) { /* phoff is to large */ if (probe_debug) { fprintf(stderr, "ELF e_phoff to large\n"); } return -1; } - if (elf32_to_cpu(ehdr, lehdr.e_shoff) > ULONG_MAX) { + if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) { /* shoff is to large */ if (probe_debug) { fprintf(stderr, "ELF e_shoff to large\n"); @@ -218,7 +218,7 @@ static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx); if ((ehdr->e_phnum > 0) && - (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr))) + (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr))) { /* Invalid program header size */ if (probe_debug) { @@ -302,7 +302,7 @@ static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) return 0; } -static int build_mem_elf32_phdr(const char *buf, off_t len, +static int build_mem_elf32_phdr(const char *buf, off_t len, struct mem_ehdr *ehdr, int idx) { struct mem_phdr *phdr; @@ -312,12 +312,12 @@ static int build_mem_elf32_phdr(const char *buf, off_t len, phdr = &ehdr->e_phdr[idx]; memcpy(&lphdr, pbuf, sizeof(lphdr)); - if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lphdr.p_align) > ULONG_MAX)) + if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_memsz) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_offset) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_paddr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_vaddr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_align) > UINT32_MAX)) { fprintf(stderr, "Program segment size out of range\n"); return -1; @@ -345,12 +345,12 @@ static int build_mem_elf64_phdr(const char *buf, off_t len, phdr = &ehdr->e_phdr[idx]; memcpy(&lphdr, pbuf, sizeof(lphdr)); - if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lphdr.p_align) > ULONG_MAX)) + if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_memsz) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_offset) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_paddr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_vaddr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_align) > UINT64_MAX)) { fprintf(stderr, "Program segment size out of range\n"); return -1; @@ -388,7 +388,7 @@ static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) fprintf(stderr, "Invalid ei_class?\n"); return -1; } - phdr_size *= ehdr->e_phnum; + phdr_size *= ehdr->e_phnum; if (ehdr->e_phoff + phdr_size > len) { /* The program header did not fit in the file buffer */ if (probe_debug) { @@ -396,7 +396,7 @@ static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) } return -1; } - + /* Allocate the e_phdr array */ mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum; ehdr->e_phdr = xmalloc(mem_phdr_size); @@ -440,7 +440,7 @@ static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) return 0; } -static int build_mem_elf32_shdr(const char *buf, off_t len, +static int build_mem_elf32_shdr(const char *buf, off_t len, struct mem_ehdr *ehdr, int idx) { struct mem_shdr *shdr; @@ -451,12 +451,12 @@ static int build_mem_elf32_shdr(const char *buf, off_t len, shdr = &ehdr->e_shdr[idx]; memcpy(&lshdr, sbuf, sizeof(lshdr)); - if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) || - (elf32_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_MAX)) + if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_addr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_offset) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_size) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_addralign) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_entsize) > UINT32_MAX)) { fprintf(stderr, "Program section size out of range\n"); return -1; @@ -510,7 +510,7 @@ static int build_mem_elf32_shdr(const char *buf, off_t len, return 0; } -static int build_mem_elf64_shdr(const char *buf, off_t len, +static int build_mem_elf64_shdr(const char *buf, off_t len, struct mem_ehdr *ehdr, int idx) { struct mem_shdr *shdr; @@ -521,12 +521,12 @@ static int build_mem_elf64_shdr(const char *buf, off_t len, shdr = &ehdr->e_shdr[idx]; memcpy(&lshdr, sbuf, sizeof(lshdr)); - if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) || - (elf64_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_MAX)) + if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_addr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_offset) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_size) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_addralign) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_entsize) > UINT64_MAX)) { fprintf(stderr, "Program section size out of range\n"); return -1; @@ -608,7 +608,7 @@ static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) } return -1; } - + /* Allocate the e_shdr array */ mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum; ehdr->e_shdr = xmalloc(mem_shdr_size); @@ -635,7 +635,7 @@ static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) { /* The section does not fit in the buffer */ if (probe_debug) { - fprintf(stderr, "ELF section %d not in file\n", + fprintf(stderr, "ELF section %d not in file\n", i); } return -1; @@ -653,14 +653,14 @@ static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr) return 0; } -static void read_nhdr(const struct mem_ehdr *ehdr, +static void read_nhdr(const struct mem_ehdr *ehdr, ElfNN_Nhdr *hdr, const unsigned char *note) { memcpy(hdr, note, sizeof(*hdr)); hdr->n_namesz = elf32_to_cpu(ehdr, hdr->n_namesz); hdr->n_descsz = elf32_to_cpu(ehdr, hdr->n_descsz); hdr->n_type = elf32_to_cpu(ehdr, hdr->n_type); - + } static int build_mem_notes(const char *buf, off_t len, struct mem_ehdr *ehdr) { @@ -686,7 +686,7 @@ static int build_mem_notes(const char *buf, off_t len, struct mem_ehdr *ehdr) if (!note_start) { return 0; } - + /* Walk through and count the notes */ ehdr->e_notenum = 0; for(note = note_start; note < note_end; note+= note_size) { @@ -708,7 +708,7 @@ static int build_mem_notes(const char *buf, off_t len, struct mem_ehdr *ehdr) note_size += (hdr.n_namesz + 3) & ~3; desc = note + note_size; note_size += (hdr.n_descsz + 3) & ~3; - + if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) { die("Note name is not null termiated"); } @@ -716,7 +716,7 @@ static int build_mem_notes(const char *buf, off_t len, struct mem_ehdr *ehdr) ehdr->e_note[i].n_name = name; ehdr->e_note[i].n_desc = desc; ehdr->e_note[i].n_descsz = hdr.n_descsz; - + } return 0; } diff --git a/purgatory/Makefile b/purgatory/Makefile index 5dd03e7..50ce99f 100644 --- a/purgatory/Makefile +++ b/purgatory/Makefile @@ -21,13 +21,11 @@ PCFLAGS += $(call cc-option, -fnobuiltin) PCFLAGS += $(call cc-option, -fnostdinc) PCFLAGS += $(call cc-option, -fno-zero-initialized-in-bss) -PURGATORY_C_SRCS:= -ifneq ($(ARCH),ppc64) +PURGATORY_C_SRCS:= PURGATORY_C_SRCS += purgatory/purgatory.c PURGATORY_C_SRCS += purgatory/printf.c PURGATORY_C_SRCS += purgatory/string.c -endif -PURGATORY_S_OBJS:= +PURGATORY_S_OBJS:= include purgatory/arch/$(ARCH)/Makefile @@ -61,12 +59,7 @@ $(PURGATORY_S_OBJS): $(OBJDIR)/%.o: %.S $(OBJDIR)/%.d $(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 --git a/purgatory/arch/ppc64/Makefile b/purgatory/arch/ppc64/Makefile index 6916c5f..82b1654 100644 --- a/purgatory/arch/ppc64/Makefile +++ b/purgatory/arch/ppc64/Makefile @@ -2,6 +2,7 @@ # Purgatory ppc # -PURGATORY_C_SRCS+= PURGATORY_S_SRCS+= purgatory/arch/ppc64/v2wrap.S - +PURGATORY_C_SRCS += purgatory/arch/ppc64/purgatory-ppc64.c +PURGATORY_C_SRCS += purgatory/arch/ppc64/console-ppc64.c +PURGATORY_C_SRCS += purgatory/arch/ppc64/crashdump_backup.c diff --git a/purgatory/arch/ppc64/console-ppc64.c b/purgatory/arch/ppc64/console-ppc64.c new file mode 100644 index 0000000..d6da7b3 --- /dev/null +++ b/purgatory/arch/ppc64/console-ppc64.c @@ -0,0 +1,27 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <purgatory.h> + +void putchar(int c) +{ + return; +} diff --git a/purgatory/arch/ppc64/crashdump_backup.c b/purgatory/arch/ppc64/crashdump_backup.c new file mode 100644 index 0000000..07f7847 --- /dev/null +++ b/purgatory/arch/ppc64/crashdump_backup.c @@ -0,0 +1,41 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <string.h> + +#define BACKUP_REGION_SOURCE 0x0 +#define BACKUP_REGION_SIZE 32*1024 + +extern unsigned long backup_start; + +/* Backup first 32KB of memory to backup region reserved by kexec */ +void crashdump_backup_memory(void) +{ + void *dest, *src; + + src = (void *)BACKUP_REGION_SOURCE; + + if (backup_start) { + dest = (void *)(backup_start); + memcpy(dest, src, BACKUP_REGION_SIZE); + } +} diff --git a/purgatory/arch/ppc64/purgatory-ppc64.c b/purgatory/arch/ppc64/purgatory-ppc64.c new file mode 100644 index 0000000..93f28d2 --- /dev/null +++ b/purgatory/arch/ppc64/purgatory-ppc64.c @@ -0,0 +1,41 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <purgatory.h> +#include "purgatory-ppc64.h" + +unsigned int panic_kernel = 0; +unsigned long backup_start = 0; +unsigned long stack = 0; +unsigned long dt_offset = 0; +unsigned long my_toc = 0; +unsigned long kernel = 0; + +void setup_arch(void) +{ + return; +} + +void post_verification_setup_arch(void) +{ + if (panic_kernel) + crashdump_backup_memory(); +} diff --git a/purgatory/arch/ppc64/purgatory-ppc64.h b/purgatory/arch/ppc64/purgatory-ppc64.h new file mode 100644 index 0000000..52eaf43 --- /dev/null +++ b/purgatory/arch/ppc64/purgatory-ppc64.h @@ -0,0 +1,6 @@ +#ifndef PURGATORY_PPC64_H +#define PURGATORY_PPC64_H + +void crashdump_backup_memory(void); + +#endif /* PURGATORY_PPC64_H */ diff --git a/purgatory/arch/ppc64/v2wrap.S b/purgatory/arch/ppc64/v2wrap.S index 6e5cdce..7514d68 100644 --- a/purgatory/arch/ppc64/v2wrap.S +++ b/purgatory/arch/ppc64/v2wrap.S @@ -2,6 +2,7 @@ # kexec: Linux boots Linux # # Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation +# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,14 +19,17 @@ # # v2wrap.S -# a wrapper to place in front of a v2 device tree -# to call a ppc64 kernel with the expected arguments +# a wrapper to call purgatory code to backup first +# 32kB of first kernel into the backup region +# reserved by kexec-tools. +# Invokes ppc64 kernel with the expected arguments # of kernel(device-tree, phys-offset, 0) + # # calling convention: # r3 = physical number of this cpu (all cpus) # r4 = address of this chunk (master only) -# master enters at start (aka first byte of this chunk) +# master enters at purgatory_start (aka first byte of this chunk) # slaves (additional cpus), if any, enter a copy of the # first 0x100 bytes of this code relocated to 0x0 # @@ -34,50 +38,50 @@ # and the slaves are sent to address 0x60 # with r3 = their physical cpu number. +#define LOADADDR(rn,name) \ + lis rn,name##@highest; \ + ori rn,rn,name##@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name##@h; \ + ori rn,rn,name##@l # look a bit like a Linux kernel here ... .machine ppc64 - .org 0 -start: b master + .globl purgatory_start +purgatory_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 + mr 17,3 # save cpu id to r17 + mr 15,4 # save physical address in reg15 + + LOADADDR(6,my_toc) + ld 2,0(6) #setup toc - # 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 + LOADADDR(6,stack) + ld 1,0(6) #setup stack + subi 1,1,112 + bl .purgatory + nop + b 81f + .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys +slave: + # load slave spin code address and branch into that + LOADADDR(6,slave_spin) + ld 4,0(6) + mtctr 4 + bctr - .long 0 # just an eye-catcher, delete if space needed - .long 0 # just an eye-catcher, delete if space needed +spin: .long 1 +slave_spin_code: + lis 5,spin@ha + lwz 5,spin@l(5) + cmpwi 0,5,0 + bne slave_spin_code + ba 0x60 81: # master continues here or 3,3,3 # ok back to high, lets boot @@ -85,7 +89,17 @@ slave: ld 4,secondary_hold@l(0); 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 + LOADADDR(16, dt_offset) + ld 3,0(16) # load device-tree address + mr 16,3 # save dt address in reg16 + lwz 6,20(3) # fetch version number + cmpwi 0,6,2 # v2 ? + blt 80f + stw 17,28(3) # save my cpu number as boot_cpu_phys +80: + LOADADDR(6,kernel) + ld 4,0(6) # load the kernel address + addi 5,4,-8 # prepare copy with update form instructions li 6,0x100/8 mtctr 6 @@ -103,12 +117,12 @@ slave: ld 4,secondary_hold@l(0); icbi 0,6 sync isync - std 6,-16(3) # send slaves back down + lis 6,spin@ha + li 0,0 + stw 0,spin@l(6) + mr 3,16 # restore dt address + bctr # start kernel - .org 0xf0 -copied: .llong 0 -kernel: .llong 0 - .org 0x100 -__end_stub: - .equ boot_block, . - start +slave_spin: .llong slave_spin_code + |