From: Sebastian Siewior <bigeasy@xxxxxxxxxxxxx> Some code dtb scanning & filling has been borrowed from ppc64. The old behavior is still available if compiled with GameCube, other PowerPC platform use the can purgatory and specify a new dtb. The purgatory is disabled because somehting is wrong and I dunno the reason. Booting a self containd cuImage (incl. dtb / wiuthout the need for a bd sturct) can be booted. Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de> --- kexec/arch/ppc/Makefile | 1 + kexec/arch/ppc/kexec-elf-ppc.c | 167 ++++++++++---- kexec/arch/ppc/kexec-elf-rel-ppc.c | 4 + kexec/arch/ppc/kexec-ppc.c | 469 +++++++++++++++++++++++++++++++++++- purgatory/arch/ppc/Makefile | 1 + purgatory/arch/ppc/purgatory-ppc.c | 4 + purgatory/arch/ppc/v2wrap.S | 54 ++++ 7 files changed, 652 insertions(+), 48 deletions(-) create mode 100644 purgatory/arch/ppc/v2wrap.S diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile index 1550c20..ac05bf8 100644 --- a/kexec/arch/ppc/Makefile +++ b/kexec/arch/ppc/Makefile @@ -7,6 +7,7 @@ ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-rel-ppc.c ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-dol-ppc.c ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S +ppc_KEXEC_SRCS += kexec/arch/ppc64/fs2dt.c dist += kexec/arch/ppc/Makefile $(ppc_KEXEC_SRCS) \ kexec/arch/ppc/kexec-ppc.h kexec/arch/ppc/ppc_asm.h \ diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c index 530e501..d4cfb93 100644 --- a/kexec/arch/ppc/kexec-elf-ppc.c +++ b/kexec/arch/ppc/kexec-elf-ppc.c @@ -26,6 +26,15 @@ #include "config.h" +/* these are here to keep arch/ppc64/fs2dt.c happy and are not implemented */ +#include "../ppc64/kexec-ppc64.h" +mem_rgns_t usablemem_rgns = {0, NULL}; +unsigned char reuse_initrd = 0; +uint64_t initrd_base, initrd_size; +/* */ + +int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *, + char *); static const int probe_debug = 0; #define MAX_COMMAND_LINE 256 @@ -91,16 +100,6 @@ int elf_ppc_probe(const char *buf, off_t len) return result; } -void elf_ppc_usage(void) -{ - printf - ( - " --command-line=STRING Set the kernel command line to STRING.\n" - " --append=STRING Set the kernel command line to STRING.\n" - " --gamecube=1|0 Enable/disable support for ELFs with changed\n" - " addresses suitable for the GameCube.\n"); -} - static void gamecube_hack_addresses(struct mem_ehdr *ehdr) { struct mem_phdr *phdr, *phdr_end; @@ -122,6 +121,36 @@ static void gamecube_hack_addresses(struct mem_ehdr *ehdr) } } +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_GAMECUBE (OPT_ARCH_MAX+1) +#define OPT_DTB (OPT_ARCH_MAX+2) +static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"command-line", 1, 0, OPT_APPEND}, + {"append", 1, 0, OPT_APPEND}, + {"gamecube", 1, 0, OPT_GAMECUBE}, + {"dtb", 1, 0, OPT_DTB}, + {0, 0, 0, 0}, +}; +static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; + +void elf_ppc_usage(void) +{ + printf( + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --gamecube=1|0 Enable/disable support for ELFs with changed\n" + " addresses suitable for the GameCube.\n" + " --devicetreeblob=<filename> Specify device tree blob file.\n" + ); +} + +#ifdef WITH_GAMECUBE +static int go_purgatory = 0; +#else +static int go_purgatory = 1; +#endif + int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { @@ -131,10 +160,11 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, unsigned long arg_base; struct boot_notes *notes; size_t note_bytes; - const char *command_line; + char *command_line; int command_line_len; unsigned char *setup_start; uint32_t setup_size; + char *dtb; int result; #ifdef WITH_GAMECUBE int target_is_gamecube = 1; @@ -142,19 +172,9 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, int target_is_gamecube = 0; #endif int opt; -#define OPT_APPEND (OPT_ARCH_MAX+0) -#define OPT_GAMECUBE (OPT_ARCH_MAX+1) - static const struct option options[] = { - KEXEC_ARCH_OPTIONS - {"command-line", 1, 0, OPT_APPEND}, - {"append", 1, 0, OPT_APPEND}, - {"gamecube", 1, 0, OPT_GAMECUBE}, - {0, 0, 0, 0}, - }; - static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; - - command_line = 0; + command_line = NULL; + dtb = NULL; while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch (opt) { default: @@ -171,6 +191,10 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, case OPT_GAMECUBE: target_is_gamecube = atoi(optarg); break; + + case OPT_DTB: + dtb = optarg; + break; } } command_line_len = 0; @@ -194,31 +218,86 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, return result; } - if (target_is_gamecube) { - setup_start = setup_dol_start; - setup_size = setup_dol_size; - setup_dol_regs.spr8 = ehdr.e_entry; /* Link Register */ + /* + * In case of a toy we take the hardcoded things and an easy setup via + * one of the assembly startups. Every thing else should be grown up + * and go through the purgatory. + */ + if (!go_purgatory) { + if (target_is_gamecube) { + setup_start = setup_dol_start; + setup_size = setup_dol_size; + setup_dol_regs.spr8 = ehdr.e_entry; /* Link Register */ + } else { + setup_start = setup_simple_start; + setup_size = setup_simple_size; + setup_simple_regs.spr8 = ehdr.e_entry; /* Link Register */ + } + + note_bytes = sizeof(elf_boot_notes) + ((command_line_len + 3) & ~3); + arg_bytes = note_bytes + ((setup_size + 3) & ~3); + + arg_buf = xmalloc(arg_bytes); + arg_base = add_buffer(info, arg_buf, arg_bytes, arg_bytes, 4, + 0, elf_max_addr(&ehdr), 1); + + notes = (struct boot_notes *)(arg_buf + ((setup_size + 3) & ~3)); + + memcpy(arg_buf, setup_start, setup_size); + memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes)); + memcpy(notes->command_line, command_line, command_line_len); + notes->hdr.b_size = note_bytes; + notes->cmd_hdr.n_descsz = command_line_len; + notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes); + + info->entry = (void *)arg_base; + } else { - setup_start = setup_simple_start; - setup_size = setup_simple_size; - setup_simple_regs.spr8 = ehdr.e_entry; /* Link Register */ - } - note_bytes = sizeof(elf_boot_notes) + ((command_line_len + 3) & ~3); - arg_bytes = note_bytes + ((setup_size + 3) & ~3); + unsigned char *seg_buf; + unsigned long seg_size; + unsigned int addr; + + elf_rel_build_load(info, &info->rhdr, (const char *)purgatory, + purgatory_size, 0, elf_max_addr(&ehdr), 1, 0); - arg_buf = xmalloc(arg_bytes); - arg_base = add_buffer(info, - arg_buf, arg_bytes, arg_bytes, 4, 0, elf_max_addr(&ehdr), 1); + if (dtb) { + char *blob_buf; + off_t blob_size = 0; - notes = (struct boot_notes *)(arg_buf + ((setup_size + 3) & ~3)); + /* Grab device tree from buffer */ + blob_buf = slurp_file(dtb, &blob_size); + add_buffer(info, blob_buf, blob_size, blob_size, 0, 0, + elf_max_addr(&ehdr), -1); + } else { + seg_buf = NULL; + seg_size = 0; + create_flatten_tree(info, &seg_buf, &seg_size, command_line); + add_buffer(info, seg_buf, seg_size, seg_size, + 0, 0, elf_max_addr(&ehdr), -1); + } + /* set various variables for the purgatory */ + addr = ehdr.e_entry; + elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr)); + + addr = (unsigned int)info->segment[info->nr_segments - 1].mem; + elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr)); + +#define PUL_STACK_SIZE (16 * 1024) + addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, elf_max_addr(&ehdr), 1); + addr += PUL_STACK_SIZE; + elf_rel_set_symbol(&info->rhdr, "pul_stack", &addr, sizeof(addr)); +#undef PUL_STACK_SIZE - memcpy(arg_buf, setup_start, setup_size); - memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes)); - memcpy(notes->command_line, command_line, command_line_len); - notes->hdr.b_size = note_bytes; - notes->cmd_hdr.n_descsz = command_line_len; - notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes); + addr = elf_rel_get_addr(&info->rhdr, "purgatory_start"); + info->entry = (void *)addr; - info->entry = (void *)arg_base; + elf_rel_get_symbol(&info->rhdr, "pul_stack", &addr, sizeof(addr)); + printf("Stack is: %08x\n", addr); + elf_rel_get_symbol(&info->rhdr, "kernel", &addr, sizeof(addr)); + printf("Kernel is entry: %08x\n", addr); + elf_rel_get_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr)); + printf("dtb is: %08x\n", addr); + + } return 0; } diff --git a/kexec/arch/ppc/kexec-elf-rel-ppc.c b/kexec/arch/ppc/kexec-elf-rel-ppc.c index e711f3b..9a66bed 100644 --- a/kexec/arch/ppc/kexec-elf-rel-ppc.c +++ b/kexec/arch/ppc/kexec-elf-rel-ppc.c @@ -31,6 +31,10 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, *(uint16_t *)location = value; break; + case R_PPC_ADDR16_HI: + *(uint16_t *)location = (value>>16) & 0xffff; + break; + case R_PPC_ADDR16_HA: /* Sign-adjusted lower 16 bits: PPC ELF ABI says: (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c index ef4fe35..183b4e5 100644 --- a/kexec/arch/ppc/kexec-ppc.c +++ b/kexec/arch/ppc/kexec-ppc.c @@ -12,6 +12,12 @@ #include <stdint.h> #include <string.h> #include <getopt.h> +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> + #include "../../kexec.h" #include "../../kexec-syscall.h" #include "kexec-ppc.h" @@ -19,14 +25,13 @@ #include "config.h" +#ifdef WITH_GAMECUBE #define MAX_MEMORY_RANGES 64 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, +static int get_memory_ranges_gc(struct memory_range **range, int *ranges, unsigned long kexec_flags) { -#ifdef WITH_GAMECUBE int memory_ranges = 0; /* RAM - lowmem used by DOLs - framebuffer */ @@ -37,9 +42,465 @@ int get_memory_ranges(struct memory_range **range, int *ranges, *range = memory_range; *ranges = memory_ranges; return 0; +} #else - fprintf(stderr, "%s(): Unsupported platform\n", __func__); +static int use_new_dtb; +static int max_memory_ranges; +static int nr_memory_ranges, nr_exclude_ranges; +static struct memory_range *exclude_range; +static struct memory_range *memory_range; +static struct memory_range *base_memory_range; +static uint64_t memory_max; +static uint64_t rmo_top; +unsigned int rtas_base, rtas_size; + +/* + * Count the memory nodes under /proc/device-tree and populate the + * max_memory_ranges variable. This variable replaces MAX_MEMORY_RANGES + * macro used earlier. + */ +static int count_memory_ranges(void) +{ + char device_tree[256] = "/proc/device-tree/"; + struct dirent *dentry; + DIR *dir; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + max_memory_ranges++; + } + + /* need to add extra region for retained initrd */ + if (use_new_dtb) { + max_memory_ranges++; + } + + closedir(dir); + return 0; + +} + + static void cleanup_memory_ranges(void) + { + free(memory_range); + free(base_memory_range); + free(exclude_range); + } + +/* + * Allocate memory for various data structures used to hold + * values of different memory ranges + */ +static int alloc_memory_ranges(void) +{ + int memory_range_len; + + memory_range_len = sizeof(struct memory_range) * max_memory_ranges; + + memory_range = malloc(memory_range_len); + if (!memory_range) + return -1; + + base_memory_range = malloc(memory_range_len); + if (!base_memory_range) + goto err1; + + exclude_range = malloc(memory_range_len); + if (!exclude_range) + goto err1; + +#if 0 + usablemem_rgns.ranges = (struct memory_range *) + malloc(memory_range_len); + if (!(usablemem_rgns.ranges)) + goto err1; + + memset(usablemem_rgns.ranges, 0, memory_range_len); +#endif + memset(memory_range, 0, memory_range_len); + memset(base_memory_range, 0, memory_range_len); + memset(exclude_range, 0, memory_range_len); + return 0; + +err1: + fprintf(stderr, "memory range structure allocation failure\n"); + cleanup_memory_ranges(); return -1; +} + +/* Sort the exclude ranges in memory */ +static int sort_ranges(void) +{ + int i, j; + uint64_t 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; +} + +/* Sort the base ranges in memory - this is useful for ensuring that our + * ranges are in ascending order, even if device-tree read of memory nodes + * is done differently. Also, could be used for other range coalescing later + */ +static int sort_base_ranges(void) +{ + int i, j; + unsigned long long tstart, tend; + + for (i = 0; i < nr_memory_ranges - 1; i++) { + for (j = 0; j < nr_memory_ranges - i - 1; j++) { + if (base_memory_range[j].start > base_memory_range[j+1].start) { + tstart = base_memory_range[j].start; + tend = base_memory_range[j].end; + base_memory_range[j].start = base_memory_range[j+1].start; + base_memory_range[j].end = base_memory_range[j+1].end; + base_memory_range[j+1].start = tstart; + base_memory_range[j+1].end = tend; + } + } + } + return 0; +} + + +#define MAXBYTES 128 + +/* Get base memory ranges */ +static int get_base_ranges(void) +{ + int local_memory_ranges = 0; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + char buf[MAXBYTES]; + DIR *dir, *dmem; + FILE *file; + struct dirent *dentry, *mentry; + int n; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((dmem = opendir(fname)) == NULL) { + perror(fname); + closedir(dir); + return -1; + } + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(dmem); + closedir(dir); + return -1; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + fclose(file); + closedir(dmem); + closedir(dir); + return -1; + } + if (local_memory_ranges >= max_memory_ranges) { + fclose(file); + break; + } + base_memory_range[local_memory_ranges].start = + ((uint64_t *)buf)[0]; + base_memory_range[local_memory_ranges].end = + base_memory_range[local_memory_ranges].start + + ((uint64_t *)buf)[1]; + base_memory_range[local_memory_ranges].type = RANGE_RAM; + local_memory_ranges++; + dbgprintf("%016llx-%016llx : %x\n", + base_memory_range[local_memory_ranges-1].start, + base_memory_range[local_memory_ranges-1].end, + base_memory_range[local_memory_ranges-1].type); + fclose(file); + } + closedir(dmem); + } + closedir(dir); + nr_memory_ranges = local_memory_ranges; + sort_base_ranges(); + memory_max = base_memory_range[nr_memory_ranges - 1].end; +#ifdef DEBUG + fprintf(stderr, "get base memory ranges:%d\n", nr_memory_ranges); +#endif + return 0; +} + +/* Get devtree details and create exclude_range array + * Also create usablemem_ranges for KEXEC_ON_CRASH + */ +static int get_devtree_details(unsigned long kexec_flags) +{ + uint64_t rmo_base; + char buf[MAXBYTES]; + 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@", 7) && + strcmp(dentry->d_name, "memory") && + strncmp(dentry->d_name, "rtas", 4)) + continue; + + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((cdir = opendir(fname)) == NULL) { + perror(fname); + goto error_opendir; + } + + if (strncmp(dentry->d_name, "rtas", 4) == 0) { + strcat(fname, "/linux,rtas-base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + 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); + goto error_opencdir; + } + if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + 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@", 7) || + !strcmp(dentry->d_name, "memory")) { + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == 8) { + rmo_base = ((uint32_t *)buf)[0]; + rmo_top = rmo_base + ((uint32_t *)buf)[1]; + } else if (n == 16) { + rmo_base = ((uint64_t *)buf)[0]; + rmo_top = rmo_base + ((uint64_t *)buf)[1]; + } else { + fprintf(stderr, "Mem node has invalid size: %d\n", n); + goto error_openfile; + } + if (rmo_top > 0x30000000UL) + rmo_top = 0x30000000UL; + + fclose(file); + closedir(cdir); + } /* memory */ + } + closedir(dir); + + nr_exclude_ranges = i; + + sort_ranges(); + +#ifdef DEBUG + int k; + for (k = 0; k < i; k++) + fprintf(stderr, "exclude_range sorted exclude_range[%d] " + "start:%llx, end:%llx\n", k, exclude_range[k].start, + exclude_range[k].end); +#endif + return 0; + +error_openfile: + fclose(file); +error_opencdir: + closedir(cdir); +error_opendir: + closedir(dir); + return -1; +} + + +/* Setup a sorted list of memory ranges. */ +static int setup_memory_ranges(unsigned long kexec_flags) +{ + int i, j = 0; + + /* Get the base list of memory ranges from /proc/device-tree/memory + * nodes. Build list of ranges to be excluded from valid memory + */ + + if (get_base_ranges()) + goto out; + if (get_devtree_details(kexec_flags)) + goto out; + + for (i = 0; i < nr_exclude_ranges; i++) { + /* If first exclude range does not start with 0, include the + * first hole of valid memory from 0 - exclude_range[0].start + */ + if (i == 0) { + if (exclude_range[i].start != 0) { + memory_range[j].start = 0; + memory_range[j].end = exclude_range[i].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + } + } /* i == 0 */ + /* If the last exclude range does not end at memory_max, include + * the last hole of valid memory from exclude_range[last].end - + * memory_max + */ + if (i == nr_exclude_ranges - 1) { + if (exclude_range[i].end < memory_max) { + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = memory_max; + memory_range[j].type = RANGE_RAM; + j++; + /* Limit the end to rmo_top */ + if (memory_range[j-1].start >= rmo_top) { + j--; + break; + } + if ((memory_range[j-1].start < rmo_top) && + (memory_range[j-1].end >= rmo_top)) { + memory_range[j-1].end = rmo_top; + break; + } + continue; + } + } /* i == nr_exclude_ranges - 1 */ + /* contiguous exclude ranges - skip */ + if (exclude_range[i+1].start == exclude_range[i].end + 1) + continue; + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = exclude_range[i+1].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + /* Limit range to rmo_top */ + if (memory_range[j-1].start >= rmo_top) { + j--; + break; + } + if ((memory_range[j-1].start < rmo_top) && + (memory_range[j-1].end >= rmo_top)) { + memory_range[j-1].end = rmo_top; + break; + } + } + nr_memory_ranges = j; + + +#ifdef DEBUG + int k; + for (k = 0; k < j; k++) + fprintf(stderr, "setup_memory_ranges memory_range[%d] " + "start:%llx, end:%llx\n", k, memory_range[k].start, + memory_range[k].end); +#endif + return 0; + +out: + cleanup_memory_ranges(); + return -1; +} + + +/* Return a list of valid memory ranges */ +int get_memory_ranges_dt(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + if (count_memory_ranges()) + return -1; + if (alloc_memory_ranges()) + return -1; + if (setup_memory_ranges(kexec_flags)) + return -1; + + /* fixup in case we have no exclude regions */ + if (!nr_memory_ranges) { + memory_range[0].start = 0x0ULL; + memory_range[0].end = rmo_top; + memory_range[0].type = RANGE_RAM; + nr_memory_ranges = 1; + } + + *range = memory_range; + *ranges = nr_memory_ranges; +#if 0 + { + int i; + + for (i = 0; i < nr_memory_ranges; i++) + printf("%d:: %016llx - %016llx\n", + i, + memory_range[i].start, + memory_range[i].end); + + } +#endif + fprintf(stderr, "get memory ranges:%d\n", nr_memory_ranges); + return 0; +} +#endif + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ +#ifdef WITH_GAMECUBE + return get_memory_ranges_gc(range, ranges, kexec_flags); +#else + return get_memory_ranges_dt(range, ranges, kexec_flags); #endif } diff --git a/purgatory/arch/ppc/Makefile b/purgatory/arch/ppc/Makefile index 69fd46c..0dd18b6 100644 --- a/purgatory/arch/ppc/Makefile +++ b/purgatory/arch/ppc/Makefile @@ -2,6 +2,7 @@ # Purgatory ppc # +ppc_PURGATORY_SRCS += purgatory/arch/ppc/v2wrap.S ppc_PURGATORY_SRCS += purgatory/arch/ppc/misc.S ppc_PURGATORY_SRCS += purgatory/arch/ppc/purgatory-ppc.c ppc_PURGATORY_SRCS += purgatory/arch/ppc/console-ppc.c diff --git a/purgatory/arch/ppc/purgatory-ppc.c b/purgatory/arch/ppc/purgatory-ppc.c index 077f495..369f7d7 100644 --- a/purgatory/arch/ppc/purgatory-ppc.c +++ b/purgatory/arch/ppc/purgatory-ppc.c @@ -1,6 +1,10 @@ #include <purgatory.h> #include "purgatory-ppc.h" +unsigned long pul_stack = 0; +unsigned long dt_offset = 0; +unsigned long kernel = 0; + void setup_arch(void) { /* Nothing for now */ diff --git a/purgatory/arch/ppc/v2wrap.S b/purgatory/arch/ppc/v2wrap.S new file mode 100644 index 0000000..35803e7 --- /dev/null +++ b/purgatory/arch/ppc/v2wrap.S @@ -0,0 +1,54 @@ +# +# kexec: Linux boots Linux +# +# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation +# Copyright (C) 2006, Mohan Kumar M (mohan at in.ibm.com), IBM Corporation +# Copyright (C) 2008, Sebastian Andrzej Siewior (bigeasy at linutronix.de), linutronix +# +# 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 "ppc_asm.h" + +# v2wrap.S +# a wrapper to call purgatory code +# Invokes ppc kernel with the expected arguments +# of kernel(device-tree) + +# calling convention: +# no register are considred +# + +#define LOADADDR(rn,name) \ + lis rn,name##@h; \ + ori rn,rn,name##@l; \ + + .globl purgatory_start +purgatory_start: + + LOADADDR(r6,pul_stack) +## lwz r1,0(r6) #setup stack + + subi r1, r1, 112 + bl purgatory + nop + + LOADADDR(r6,kernel) + lwz r4,0(r6) # load the kernel address + mtlr r4 # prepare branch too + + LOADADDR(r6, dt_offset) + lwz r3, 0(r6) # load device-tree address + + blr # start kernel -- 1.5.6.5