diff mbox

powerpc: dtb and purgatory support for ppc32

Message ID 1222896024-22172-4-git-send-email-sebastian@breakpoint.cc (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Sebastian Andrzej Siewior Oct. 1, 2008, 9:20 p.m. UTC
From: Sebastian Siewior <bigeasy@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@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

Comments

Kumar Gala Oct. 2, 2008, 3:54 a.m. UTC | #1
On Oct 1, 2008, at 4:20 PM, Sebastian Andrzej Siewior wrote:

> From: Sebastian Siewior <bigeasy@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@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

are you trying to get this accepted?

- k
Sebastian Andrzej Siewior Oct. 2, 2008, 7:50 a.m. UTC | #2
Kumar Gala wrote:

> are you trying to get this accepted?

If the reviewer don't have any objections yes. You sound
like there is something terrible wrong. Do you want it in smaller pieces?

> - k
Sebastian
Simon Horman Oct. 2, 2008, 10:05 a.m. UTC | #3
On Thu, Oct 02, 2008 at 09:50:16AM +0200, Sebastian Siewior wrote:
> Kumar Gala wrote:
> 
> > are you trying to get this accepted?
> 
> If the reviewer don't have any objections yes. You sound
> like there is something terrible wrong. Do you want it in smaller pieces?

I'm not speaking on behalf of Kumar Gala, just on behalf of myself.

I would like to get some review of this patch by if possible.
Also, I'm a little unclear of how it will interact with the
patches that Mohan Kumar sent this week. But at this point
I don't see any particular need to break the patch up into
smaller pieices.
Kumar Gala Oct. 2, 2008, 12:52 p.m. UTC | #4
On Oct 1, 2008, at 4:20 PM, Sebastian Andrzej Siewior wrote:

> From: Sebastian Siewior <bigeasy@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@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

I think I might have realized some of my confusion.. is this patch to  
the kernel or to some kexec tool?   If its to the kernel I don't  
answer at all.  If its to some kexec tool than that wasn't clear at all.

- k
Sebastian Andrzej Siewior Oct. 2, 2008, 1:03 p.m. UTC | #5
Kumar Gala wrote:
> 
> On Oct 1, 2008, at 4:20 PM, Sebastian Andrzej Siewior wrote:
> 
>> From: Sebastian Siewior <bigeasy@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@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
> 
> I think I might have realized some of my confusion.. is this patch to 
> the kernel or to some kexec tool?   If its to the kernel I don't answer 
> at all.  If its to some kexec tool than that wasn't clear at all.

Patch 1 and 2 were for kernel side support. Patch 3 is kexec userland 
only. I wrote this down in the patch first email (should be 0/3) but now I 
see that git did not enumerate them. Sorry for that, I see that I have to 
work on git skills....

The two patches tagged with [RFC] is kernel side support. The third patch 
tagged with [PATCH] is also meant to be [RFC] and is the kexec userland 
part against [1].


[1] git://git.kernel.org/pub/scm/linux/kernel/git/horms/kexec-tools.git

> - k

Sebastian
Mohan Kumar M Oct. 3, 2008, 5:44 a.m. UTC | #6
Simon Horman wrote:
> On Thu, Oct 02, 2008 at 09:50:16AM +0200, Sebastian Siewior wrote:

> 
> I'm not speaking on behalf of Kumar Gala, just on behalf of myself.
>

Hi Horms,

> I would like to get some review of this patch by if possible.
> Also, I'm a little unclear of how it will interact with the
> patches that Mohan Kumar sent this week. But at this point
> I don't see any particular need to break the patch up into
> smaller pieices.
> 

My patches are meant for PPC64 only. The patch implements support for 
relocatable kdump kernel on PPC64 platform.

So I don't think Sebastian's patches need to support this relocation 
support in the kexec-tools as of now.

Regards,
Mohan.
Simon Horman Oct. 8, 2008, 3:03 a.m. UTC | #7
On Wed, Oct 01, 2008 at 11:20:24PM +0200, Sebastian Andrzej Siewior wrote:
> From: Sebastian Siewior <bigeasy@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@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@in.ibm.com), IBM Corporation
> +#  Copyright (C) 2008, Sebastian Andrzej Siewior (bigeasy@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@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
Sebastian Andrzej Siewior Oct. 12, 2008, 1:39 p.m. UTC | #8
* Simon Horman | 2008-10-08 14:03:26 [+1100]:

>> +#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?
For the first shot I would prefer to use WITH_GAMECUBE inside of
elf_ppc_load(). In longterm I don't see any reason why GameCube can't
use the purgatory code like the other archs and get the memory maps from
the device tree. However I'm not sure if GameCube still runs on a recent
kernel: now that arc/ppc isn't available anymore GameCube has to pass a
dtb somehow and this isn't the case. So therefore I would like the keep
#ifdef and the exisiting behavior until someone clears this up.

>Simon Horman

Sebastian
Simon Horman Oct. 15, 2008, 3:37 a.m. UTC | #9
On Sun, Oct 12, 2008 at 03:39:55PM +0200, Sebastian Andrzej Siewior wrote:
> * Simon Horman | 2008-10-08 14:03:26 [+1100]:
> 
> >> +#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?

> For the first shot I would prefer to use WITH_GAMECUBE inside of
> elf_ppc_load(). In longterm I don't see any reason why GameCube can't
> use the purgatory code like the other archs and get the memory maps from
> the device tree. However I'm not sure if GameCube still runs on a recent
> kernel: now that arc/ppc isn't available anymore GameCube has to pass a
> dtb somehow and this isn't the case. So therefore I would like the keep
> #ifdef and the exisiting behavior until someone clears this up.

Ok, in this case I would like to request that you remove
go_purgatory (for now) and just use #ifdef WITH_GAMECUBE instead.
I think that it will make things cleaner (for now).
diff mbox

Patch

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@in.ibm.com), IBM Corporation
+#  Copyright (C) 2008, Sebastian Andrzej Siewior (bigeasy@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