[PATCH] powerpc: dtb and purgatory support for ppc32
Simon Horman
horms at verge.net.au
Tue Oct 7 23:03:26 EDT 2008
On Wed, Oct 01, 2008 at 11:20:24PM +0200, Sebastian Andrzej Siewior wrote:
> From: Sebastian Siewior <bigeasy at linutronix.de>
>
> 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
Can you just use WITH_GAMECUBE inside elf_ppc_load() and remove
the need for go_purgatory, or do you plan to make go_purgatory
switchable at run-time at some point in the future?
> +
> 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
>
>
> _______________________________________________
> kexec mailing list
> kexec at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
--
Simon Horman
VA Linux Systems Japan K.K., Sydney, Australia Satellite Office
H: www.vergenet.net/~horms/ W: www.valinux.co.jp/en
More information about the kexec
mailing list