[PATCH 2/7] powerpc: dtb and purgatory support for ppc32

Sebastian Andrzej Siewior sebastian at breakpoint.cc
Wed Mar 31 04:24:13 EDT 2010


From: Sebastian Andrzej 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.
Booting a self contained elf image (incl. dtb / without the need
for a bd sturct or the like) can be booted. The dtb support is currently
optional. That means if the elf image does not contain a dtb file then
the user has to supply a complete dtb (including mem size, command line,
bus freq., mac addr, ...)

Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
---
 kexec/arch/ppc/kexec-elf-ppc.c     |  108 +++++++---
 kexec/arch/ppc/kexec-elf-rel-ppc.c |    4 +
 kexec/arch/ppc/kexec-ppc.c         |  449 +++++++++++++++++++++++++++++++++++-
 kexec/arch/ppc/kexec-ppc.h         |    7 +
 purgatory/arch/ppc/Makefile        |    1 +
 purgatory/arch/ppc/purgatory-ppc.c |    6 +
 purgatory/arch/ppc/v2wrap.S        |   66 ++++++
 7 files changed, 608 insertions(+), 33 deletions(-)
 create mode 100644 purgatory/arch/ppc/v2wrap.S

diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c
index 530e501..03b8a94 100644
--- a/kexec/arch/ppc/kexec-elf-ppc.c
+++ b/kexec/arch/ppc/kexec-elf-ppc.c
@@ -91,16 +91,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,39 +112,56 @@ 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"
+	     "     --dtb=<filename> Specify device tree blob file.\n"
+	     );
+}
+
 int elf_ppc_load(int argc, char **argv,	const char *buf, off_t len, 
 	struct kexec_info *info)
 {
 	struct mem_ehdr ehdr;
+	char *command_line;
+	int command_line_len;
+	char *dtb;
+	int result;
+#ifdef WITH_GAMECUBE
+	int target_is_gamecube = 1;
 	char *arg_buf;
 	size_t arg_bytes;
 	unsigned long arg_base;
 	struct boot_notes *notes;
 	size_t note_bytes;
-	const char *command_line;
-	int command_line_len;
 	unsigned char *setup_start;
 	uint32_t setup_size;
-	int result;
-#ifdef WITH_GAMECUBE
-	int target_is_gamecube = 1;
 #else
 	int target_is_gamecube = 0;
+	unsigned int addr;
+	unsigned long dtb_addr;
 #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 +178,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,6 +205,12 @@ int elf_ppc_load(int argc, char **argv,	const char *buf, off_t len,
 		return result;
 	}
 
+	/*
+	 * 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.
+	 */
+#ifdef WITH_GAMECUBE
 	if (target_is_gamecube) {
 		setup_start = setup_dol_start;
 		setup_size = setup_dol_size;
@@ -220,5 +237,42 @@ int elf_ppc_load(int argc, char **argv,	const char *buf, off_t len,
 	notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes);
 
 	info->entry = (void *)arg_base;
+#else
+	elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
+			purgatory_size, 0, elf_max_addr(&ehdr), 1, 0);
+
+	if (dtb) {
+		char *blob_buf;
+		off_t blob_size = 0;
+
+		/* Grab device tree from buffer */
+		blob_buf = slurp_file(dtb, &blob_size);
+		dtb_addr = add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
+				KERNEL_ACCESS_TOP, -1);
+		if (command_line)
+			die("Don't consider command line because dtb is supplied\n");
+	} else {
+		die("Missing dtb.\n");
+	}
+
+	/* set various variables for the purgatory */
+	addr = ehdr.e_entry;
+	elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
+
+	addr = dtb_addr;
+	elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr));
+
+	addr = rmo_top;
+	elf_rel_set_symbol(&info->rhdr, "mem_size", &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
+
+	addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
+	info->entry = (void *)addr;
+#endif
 	return 0;
 }
diff --git a/kexec/arch/ppc/kexec-elf-rel-ppc.c b/kexec/arch/ppc/kexec-elf-rel-ppc.c
index 7aa92db..90a66f4 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 *UNUSED(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 a77804f..ac0daad 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"
@@ -22,13 +28,10 @@
 #ifdef WITH_GAMECUBE
 #define MAX_MEMORY_RANGES  64
 static struct memory_range memory_range[MAX_MEMORY_RANGES];
-#endif
 
-/* Return a sorted list of memory ranges. */
-int get_memory_ranges(struct memory_range **UNUSED(range), int *UNUSED(ranges),
-		      unsigned long UNUSED(kexec_flags))
+static int get_memory_ranges_gc(struct memory_range **range, int *ranges,
+					unsigned long UNUSED(kexec_flags))
 {
-#ifdef WITH_GAMECUBE
 	int memory_ranges = 0;
 
 	/* RAM - lowmem used by DOLs - framebuffer */
@@ -39,9 +42,443 @@ int get_memory_ranges(struct memory_range **UNUSED(range), int *UNUSED(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;
+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;
+
+	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 =
+				((uint32_t *)buf)[0];
+			base_memory_range[local_memory_ranges].end  =
+				base_memory_range[local_memory_ranges].start +
+				((uint32_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;
+		}
+	}
+
+	/* fixup in case we have no exclude regions */
+	if (!j) {
+		memory_range[0].start = base_memory_range[0].start;
+		memory_range[0].end = rmo_top;
+		memory_range[0].type = RANGE_RAM;
+		nr_memory_ranges = 1;
+	} else
+		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;
+
+	*range = memory_range;
+	*ranges = 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/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
index 6a40cc8..1b2b015 100644
--- a/kexec/arch/ppc/kexec-ppc.h
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -10,6 +10,7 @@ extern struct {
 
 extern unsigned char setup_dol_start[];
 extern uint32_t setup_dol_size;
+extern uint64_t rmo_top;
 
 extern struct {
 	uint32_t spr8;
@@ -25,4 +26,10 @@ int dol_ppc_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info);
 void dol_ppc_usage(void);
 
+/*
+ * During inital setup the kernel does not map the whole memory but a part of
+ * it. On Book-E that is 64MiB, 601 24MiB or 256MiB (if possible).
+ */
+#define KERNEL_ACCESS_TOP (24 * 1024 * 1024)
+
 #endif /* KEXEC_PPC_H */
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..01d0f38 100644
--- a/purgatory/arch/ppc/purgatory-ppc.c
+++ b/purgatory/arch/ppc/purgatory-ppc.c
@@ -1,6 +1,12 @@
 #include <purgatory.h>
 #include "purgatory-ppc.h"
 
+unsigned int pul_stack = 0;
+unsigned int dt_offset = 0;
+unsigned int kernel = 0;
+unsigned int epapr_magic = 0;
+unsigned int mem_size = 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..79d188f
--- /dev/null
+++ b/purgatory/arch/ppc/v2wrap.S
@@ -0,0 +1,66 @@
+#
+#  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 arguments according to ePAPR v1.0
+# It assumes that the MSR is allready correct.
+
+# 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
+
+	li	r4, 0
+	li	r5, 0
+
+	LOADADDR(r6, epapr_magic)	# ePAPR magic value
+	lwz	r6, 0(r6)
+
+	LOADADDR(r7, mem_size)		# the Initial Mapped Area
+	lwz	r7, 0(r6)
+
+	li	r8, 0
+	li	r9, 0
+
+	blr				# start kernel
-- 
1.6.5.2




More information about the kexec mailing list