Ejemplo n.º 1
0
/* Patch stub to reference function and correct r2 value. */
static inline int create_stub(Elf64_Shdr *sechdrs,
			      struct ppc64_stub_entry *entry,
			      unsigned long addr,
			      struct module *me)
{
	long reladdr;

	memcpy(entry->jump, ppc64_stub_insns, sizeof(ppc64_stub_insns));

	/* Stub uses address relative to r2. */
	reladdr = (unsigned long)entry - my_r2(sechdrs, me);
	if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
		pr_err("%s: Address %p of stub out of range of %p.\n",
		       me->name, (void *)reladdr, (void *)my_r2);
		return 0;
	}
	pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);

	entry->jump[0] |= PPC_HA(reladdr);
	entry->jump[1] |= PPC_LO(reladdr);
	entry->funcdata = func_desc(addr);
	return 1;
}
void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
	void *location, unsigned long address, unsigned long value)
{
	switch(r_type) {
	case R_PPC64_ADDR32:
		/* Simply set it */
		*(uint32_t *)location = value;
		break;

	case R_PPC64_ADDR64:
	case R_PPC64_REL64:
		/* Simply set it */
		*(uint64_t *)location = value;
		break;

	case R_PPC64_REL32:
		*(uint32_t *)location = value - (uint32_t)(uint64_t)location;
		break;

	case R_PPC64_TOC:
		*(uint64_t *)location = my_r2(ehdr);
		break;

	case R_PPC64_TOC16_DS:
		/* Subtact TOC pointer */
		value -= my_r2(ehdr);
		if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
			die("bad TOC16_DS relocation (%lu)\n", value);
		}
		*((uint16_t *) location)
			= (*((uint16_t *) location) & ~0xfffc)
			| (value & 0xfffc);
		break;

	case R_PPC64_REL24:
		/* Convert value to relative */
		value -= address;
		if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
			die("REL24 %li out of range!\n",
				(long int)value);
		}

		/* Only replace bits 2 through 26 */
		*(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc)
					| (value & 0x03fffffc);
		break;

	case R_PPC64_ADDR16_LO:
		*(uint16_t *)location = value & 0xffff;
		break;

	case R_PPC64_ADDR16_HI:
		*(uint16_t *)location = (value>>16) & 0xffff;
		break;

	case R_PPC64_ADDR16_HA:
		*(uint16_t *)location = (((value+0x8000)>>16)  & 0xffff);
		break;

	case R_PPC64_ADDR16_HIGHEST:
		*(uint16_t *)location = (((uint64_t)value>>48)  & 0xffff);
		break;
	case R_PPC64_ADDR16_HIGHER:
		*(uint16_t *)location = (((uint64_t)value>>32)  & 0xffff);
		break;

	default:
		die("Unknown rela relocation: %lu\n", r_type);
		break;
	}
	return;
}
Ejemplo n.º 3
0
void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *sym,
	unsigned long r_type, void *location, unsigned long address,
	unsigned long value)
{
	switch(r_type) {
	case R_PPC64_ADDR32:
		/* Simply set it */
		*(uint32_t *)location = value;
		break;

	case R_PPC64_ADDR64:
	case R_PPC64_REL64:
		/* Simply set it */
		*(uint64_t *)location = value;
		break;

	case R_PPC64_REL32:
		*(uint32_t *)location = value - (uint32_t)(uint64_t)location;
		break;

	case R_PPC64_TOC:
		*(uint64_t *)location = my_r2(ehdr);
		break;

	case R_PPC64_TOC16:
		do_relative_toc(value - my_r2(ehdr), location, 0xffff, 1);
		break;

	case R_PPC64_TOC16_DS:
		do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 1);
		break;

	case R_PPC64_TOC16_LO:
		do_relative_toc(value - my_r2(ehdr), location, 0xffff, 0);
		break;

	case R_PPC64_TOC16_LO_DS:
		do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 0);
		break;

	case R_PPC64_TOC16_HI:
		do_relative_toc((value - my_r2(ehdr)) >> 16, location,
			0xffff, 0);
		break;

	case R_PPC64_TOC16_HA:
		do_relative_toc((value - my_r2(ehdr) + 0x8000) >> 16, location,
			0xffff, 0);
		break;

	case R_PPC64_REL24:
		value += local_entry_offset(sym);
		/* Convert value to relative */
		value -= address;
		if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0) {
			die("REL24 %li out of range!\n", (long int)value);
		}

		/* Only replace bits 2 through 26 */
		*(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc) |
					(value & 0x03fffffc);
		break;

	case R_PPC64_ADDR16_LO:
		*(uint16_t *)location = value & 0xffff;
		break;

	case R_PPC64_ADDR16_HI:
		*(uint16_t *)location = (value >> 16) & 0xffff;
		break;

	case R_PPC64_ADDR16_HA:
		*(uint16_t *)location = (((value + 0x8000) >> 16) & 0xffff);
		break;

	case R_PPC64_ADDR16_HIGHER:
		*(uint16_t *)location = (((uint64_t)value >> 32) & 0xffff);
		break;

	case R_PPC64_ADDR16_HIGHEST:
		*(uint16_t *)location = (((uint64_t)value >> 48) & 0xffff);
		break;

		/* R_PPC64_REL16_HA and R_PPC64_REL16_LO are handled to support
		 * ABIv2 r2 assignment based on r12 for PIC executable.
		 * Here address is know so replace
		 *	0:	addis 2,12,.TOC.-0b@ha
		 *		addi 2,2,.TOC.-0b@l
		 * by
		 *		lis 2,.TOC.@ha
		 *		addi 2,2,.TOC.@l
		 */
	case R_PPC64_REL16_HA:
		/* check that we are dealing with the addis 2,12 instruction */
		if (((*(uint32_t*)location) & 0xffff0000) != 0x3c4c0000)
			die("Unexpected instruction for  R_PPC64_REL16_HA");
		value += my_r2(ehdr);
		/* replacing by lis 2 */
		*(uint32_t *)location = 0x3c400000 + ((value >> 16) & 0xffff);
		break;

	case R_PPC64_REL16_LO:
		/* check that we are dealing with the addi 2,2 instruction */
		if (((*(uint32_t*)location) & 0xffff0000) != 0x38420000)
			die("Unexpected instruction for R_PPC64_REL16_LO");

		value += my_r2(ehdr) - 4;
		*(uint16_t *)location = value & 0xffff;
		break;

	default:
		die("Unknown rela relocation: %lu\n", r_type);
		break;
	}
}
Ejemplo n.º 4
0
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
			struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	char *cmdline, *modified_cmdline = NULL;
	const char *devicetreeblob;
	int cmdline_len, modified_cmdline_len;
	uint64_t max_addr, hole_addr;
	unsigned char *seg_buf = NULL;
	off_t seg_size = 0;
	struct mem_phdr *phdr;
	size_t size;
	uint64_t *rsvmap_ptr;
	struct bootblock *bb_ptr;
	unsigned int nr_segments, i;
	int result, opt;
	uint64_t my_kernel, my_dt_offset;
	unsigned int my_panic_kernel;
	uint64_t my_stack, my_backup_start;
	uint64_t toc_addr;
	unsigned int slave_code[256/sizeof (unsigned int)], master_entry;

#define OPT_APPEND     (OPT_ARCH_MAX+0)
#define OPT_RAMDISK     (OPT_ARCH_MAX+1)
#define OPT_DEVICETREEBLOB     (OPT_ARCH_MAX+2)
#define OPT_ARGS_IGNORE		(OPT_ARCH_MAX+3)

	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",       1, NULL, OPT_APPEND },
		{ "append",             1, NULL, OPT_APPEND },
		{ "ramdisk",            1, NULL, OPT_RAMDISK },
		{ "initrd",             1, NULL, OPT_RAMDISK },
		{ "devicetreeblob",     1, NULL, OPT_DEVICETREEBLOB },
		{ "args-linux",		0, NULL, OPT_ARGS_IGNORE },
		{ 0,                    0, NULL, 0 },
	};

	static const char short_options[] = KEXEC_OPT_STR "";

	/* Parse command line arguments */
	initrd_base = 0;
	initrd_size = 0;
	cmdline = 0;
	ramdisk = 0;
	devicetreeblob = 0;
	max_addr = 0xFFFFFFFFFFFFFFFFUL;
	hole_addr = 0;

	while ((opt = getopt_long(argc, argv, short_options,
					options, 0)) != -1) {
		switch (opt) {
		default:
			/* Ignore core options */
			if (opt < OPT_ARCH_MAX)
				break;
		case '?':
			usage();
			return -1;
		case OPT_APPEND:
			cmdline = optarg;
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_DEVICETREEBLOB:
			devicetreeblob = optarg;
			break;
		case OPT_ARGS_IGNORE:
			break;
		}
	}

	cmdline_len = 0;
	if (cmdline)
		cmdline_len = strlen(cmdline) + 1;
	else
		fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n");

	if (ramdisk && reuse_initrd)
		die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");

	setup_memory_ranges(info->kexec_flags);

	/* Need to append some command line parameters internally in case of
	 * taking crash dumps.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
		memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
		if (cmdline) {
			strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}
		modified_cmdline_len = strlen(modified_cmdline);
	}

	/* Parse the Elf file */
	result = build_elf_exec_info(buf, len, &ehdr, 0);
	if (result < 0) {
		free_elf_info(&ehdr);
		return result;
	}

	/* Load the Elf data. Physical load addresses in elf64 header do not
	 * show up correctly. Use user supplied address for now to patch the
	 * elf header
	 */

	phdr = &ehdr.e_phdr[0];
	size = phdr->p_filesz;
	if (size > phdr->p_memsz)
		size = phdr->p_memsz;

	hole_addr = (uint64_t)locate_hole(info, size, 0, 0,
			max_addr, 1);
	ehdr.e_phdr[0].p_paddr = hole_addr;
	result = elf_exec_load(&ehdr, info);
	if (result < 0) {
		free_elf_info(&ehdr);
		return result;
	}

	/* If panic kernel is being loaded, additional segments need
	 * to be created.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		result = load_crashdump_segments(info, modified_cmdline,
						max_addr, 0);
		if (result < 0)
			return -1;
		/* Use new command line. */
		cmdline = modified_cmdline;
		cmdline_len = strlen(modified_cmdline) + 1;
	}

	/* Add v2wrap to the current image */
	seg_buf = NULL;
	seg_size = 0;

	seg_buf = (unsigned char *) malloc(purgatory_size);
	if (seg_buf == NULL) {
		free_elf_info(&ehdr);
		return -1;
	}
	memcpy(seg_buf, purgatory, purgatory_size);
	seg_size = purgatory_size;
	elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
				purgatory_size, 0, max_addr, 1, 0);

	/* Add a ram-disk to the current image
	 * Note: Add the ramdisk after elf_rel_build_load
	 */
	if (ramdisk) {
		if (devicetreeblob) {
			fprintf(stderr,
			"Can't use ramdisk with device tree blob input\n");
			return -1;
		}
		seg_buf = (unsigned char *)slurp_file(ramdisk, &seg_size);
		add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, 1);
		hole_addr = (uint64_t)
			info->segment[info->nr_segments-1].mem;
		initrd_base = hole_addr;
		initrd_size = (uint64_t)
			info->segment[info->nr_segments-1].memsz;
	} /* ramdisk */

	if (devicetreeblob) {
		unsigned char *blob_buf = NULL;
		off_t blob_size = 0;

		/* Grab device tree from buffer */
		blob_buf =
			(unsigned char *)slurp_file(devicetreeblob, &blob_size);
		add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
				max_addr, -1);

	} else {
		/* create from fs2dt */
		seg_buf = NULL;
		seg_size = 0;
		create_flatten_tree(info, (unsigned char **)&seg_buf,
				(unsigned long *)&seg_size,cmdline);
		add_buffer(info, seg_buf, seg_size, seg_size,
				0, 0, max_addr, -1);
	}

	/* patch reserve map address for flattened device-tree
	 * find last entry (both 0) in the reserve mem list.  Assume DT
	 * entry is before this one
	 */
	bb_ptr = (struct bootblock *)(
		(unsigned char *)info->segment[(info->nr_segments)-1].buf);
	rsvmap_ptr = (uint64_t *)(
		(unsigned char *)info->segment[(info->nr_segments)-1].buf +
		bb_ptr->off_mem_rsvmap);
	while (*rsvmap_ptr || *(rsvmap_ptr+1))
		rsvmap_ptr += 2;
	rsvmap_ptr -= 2;
	*rsvmap_ptr = (uint64_t)(
		info->segment[(info->nr_segments)-1].mem);
	rsvmap_ptr++;
	*rsvmap_ptr = (uint64_t)bb_ptr->totalsize;

	nr_segments = info->nr_segments;

	/* Set kernel */
	my_kernel = (uint64_t)info->segment[0].mem;
	elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));

	/* Set dt_offset */
	my_dt_offset = (uint64_t)info->segment[nr_segments-1].mem;
	elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
				sizeof(my_dt_offset));

	/* get slave code from new kernel, put in purgatory */
	elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code,
			sizeof(slave_code));
	master_entry = slave_code[0];
	memcpy(slave_code, info->segment[0].buf, sizeof(slave_code));
	slave_code[0] = master_entry;
	elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code,
				sizeof(slave_code));

	if (info->kexec_flags & KEXEC_ON_CRASH) {
		my_panic_kernel = 1;
		/* Set panic flag */
		elf_rel_set_symbol(&info->rhdr, "panic_kernel",
				&my_panic_kernel, sizeof(my_panic_kernel));

		/* Set backup address */
		my_backup_start = info->backup_start;
		elf_rel_set_symbol(&info->rhdr, "backup_start",
				&my_backup_start, sizeof(my_backup_start));
	}

	/* Set stack address */
	my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1);
	my_stack += 16*1024;
	elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));

	/* Set toc */
	toc_addr = (unsigned long) my_r2(&info->rhdr);
	elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr));

#ifdef DEBUG
	my_kernel = 0;
	my_dt_offset = 0;
	my_panic_kernel = 0;
	my_backup_start = 0;
	my_stack = 0;
	toc_addr = 0;

	elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
	elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
				sizeof(my_dt_offset));
	elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel,
				sizeof(my_panic_kernel));
	elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start,
				sizeof(my_backup_start));
	elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
	elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr,
				sizeof(toc_addr));

	fprintf(stderr, "info->entry is %p\n", info->entry);
	fprintf(stderr, "kernel is %lx\n", my_kernel);
	fprintf(stderr, "dt_offset is %lx\n", my_dt_offset);
	fprintf(stderr, "panic_kernel is %x\n", my_panic_kernel);
	fprintf(stderr, "backup_start is %lx\n", my_backup_start);
	fprintf(stderr, "stack is %lx\n", my_stack);
	fprintf(stderr, "toc_addr is %lx\n", toc_addr);
	fprintf(stderr, "purgatory size is %lu\n", purgatory_size);
#endif

	for (i = 0; i < nr_segments; i++)
		fprintf(stderr, "segment[%d].mem:%p memsz:%ld\n", i,
			info->segment[i].mem, info->segment[i].memsz);

	return 0;
}
Ejemplo n.º 5
0
int apply_relocate_add(Elf64_Shdr *sechdrs,
		       const char *strtab,
		       unsigned int symindex,
		       unsigned int relsec,
		       struct module *me)
{
	unsigned int i;
	Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
	Elf64_Sym *sym;
	unsigned long *location;
	unsigned long value;

	pr_debug("Applying ADD relocate section %u to %u\n", relsec,
	       sechdrs[relsec].sh_info);

	/* First time we're called, we can fix up .TOC. */
	if (!me->arch.toc_fixed) {
		sym = find_dot_toc(sechdrs, strtab, symindex);
		/* It's theoretically possible that a module doesn't want a
		 * .TOC. so don't fail it just for that. */
		if (sym)
			sym->st_value = my_r2(sechdrs, me);
		me->arch.toc_fixed = true;
	}

	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
		/* This is where to make the change */
		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
			+ rela[i].r_offset;
		/* This is the symbol it is referring to */
		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
			+ ELF64_R_SYM(rela[i].r_info);

		pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
		       location, (long)ELF64_R_TYPE(rela[i].r_info),
		       strtab + sym->st_name, (unsigned long)sym->st_value,
		       (long)rela[i].r_addend);

		/* `Everything is relative'. */
		value = sym->st_value + rela[i].r_addend;

		switch (ELF64_R_TYPE(rela[i].r_info)) {
		case R_PPC64_ADDR32:
			/* Simply set it */
			*(u32 *)location = value;
			break;

		case R_PPC64_ADDR64:
			/* Simply set it */
			*(unsigned long *)location = value;
			break;

		case R_PPC64_TOC:
			*(unsigned long *)location = my_r2(sechdrs, me);
			break;

		case R_PPC64_TOC16:
			/* Subtract TOC pointer */
			value -= my_r2(sechdrs, me);
			if (value + 0x8000 > 0xffff) {
				pr_err("%s: bad TOC16 relocation (0x%lx)\n",
				       me->name, value);
				return -ENOEXEC;
			}
			*((uint16_t *) location)
				= (*((uint16_t *) location) & ~0xffff)
				| (value & 0xffff);
			break;

		case R_PPC64_TOC16_LO:
			/* Subtract TOC pointer */
			value -= my_r2(sechdrs, me);
			*((uint16_t *) location)
				= (*((uint16_t *) location) & ~0xffff)
				| (value & 0xffff);
			break;

		case R_PPC64_TOC16_DS:
			/* Subtract TOC pointer */
			value -= my_r2(sechdrs, me);
			if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
				pr_err("%s: bad TOC16_DS relocation (0x%lx)\n",
				       me->name, value);
				return -ENOEXEC;
			}
			*((uint16_t *) location)
				= (*((uint16_t *) location) & ~0xfffc)
				| (value & 0xfffc);
			break;

		case R_PPC64_TOC16_LO_DS:
			/* Subtract TOC pointer */
			value -= my_r2
Ejemplo n.º 6
0
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
			struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	char *cmdline, *modified_cmdline = NULL;
	const char *devicetreeblob;
	uint64_t max_addr, hole_addr;
	char *seg_buf = NULL;
	off_t seg_size = 0;
	struct mem_phdr *phdr;
	size_t size;
#ifdef NEED_RESERVE_DTB
	uint64_t *rsvmap_ptr;
	struct bootblock *bb_ptr;
#endif
	int result, opt;
	uint64_t my_kernel, my_dt_offset;
	uint64_t my_opal_base = 0, my_opal_entry = 0;
	unsigned int my_panic_kernel;
	uint64_t my_stack, my_backup_start;
	uint64_t toc_addr;
	uint32_t my_run_at_load;
	unsigned int slave_code[256/sizeof (unsigned int)], master_entry;

	/* See options.h -- add any more there, too. */
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",       1, NULL, OPT_APPEND },
		{ "append",             1, NULL, OPT_APPEND },
		{ "ramdisk",            1, NULL, OPT_RAMDISK },
		{ "initrd",             1, NULL, OPT_RAMDISK },
		{ "devicetreeblob",     1, NULL, OPT_DEVICETREEBLOB },
		{ "dtb",                1, NULL, OPT_DEVICETREEBLOB },
		{ "args-linux",		0, NULL, OPT_ARGS_IGNORE },
		{ 0,                    0, NULL, 0 },
	};

	static const char short_options[] = KEXEC_OPT_STR "";

	if (info->file_mode)
		return elf_ppc64_load_file(argc, argv, info);

	/* Parse command line arguments */
	initrd_base = 0;
	initrd_size = 0;
	cmdline = 0;
	ramdisk = 0;
	devicetreeblob = 0;
	max_addr = 0xFFFFFFFFFFFFFFFFULL;
	hole_addr = 0;

	while ((opt = getopt_long(argc, argv, short_options,
					options, 0)) != -1) {
		switch (opt) {
		default:
			/* Ignore core options */
			if (opt < OPT_ARCH_MAX)
				break;
		case OPT_APPEND:
			cmdline = optarg;
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_DEVICETREEBLOB:
			devicetreeblob = optarg;
			break;
		case OPT_ARGS_IGNORE:
			break;
		}
	}

	if (!cmdline)
		fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n");

	if (ramdisk && reuse_initrd)
		die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");

	/* Need to append some command line parameters internally in case of
	 * taking crash dumps.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
		memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
		if (cmdline) {
			strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}
	}

	/* Parse the Elf file */
	result = build_elf_exec_info(buf, len, &ehdr, 0);
	if (result < 0) {
		free_elf_info(&ehdr);
		return result;
	}

	/* Load the Elf data. Physical load addresses in elf64 header do not
	 * show up correctly. Use user supplied address for now to patch the
	 * elf header
	 */

	phdr = &ehdr.e_phdr[0];
	size = phdr->p_filesz;
	if (size > phdr->p_memsz)
		size = phdr->p_memsz;

	my_kernel = hole_addr = locate_hole(info, size, 0, 0, max_addr, 1);
	ehdr.e_phdr[0].p_paddr = hole_addr;
	result = elf_exec_load(&ehdr, info);
	if (result < 0) {
		free_elf_info(&ehdr);
		return result;
	}

	/* If panic kernel is being loaded, additional segments need
	 * to be created.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		result = load_crashdump_segments(info, modified_cmdline,
						max_addr, 0);
		if (result < 0)
			return -1;
		/* Use new command line. */
		cmdline = modified_cmdline;
	}

	/* Add v2wrap to the current image */
	elf_rel_build_load(info, &info->rhdr, purgatory,
				purgatory_size, 0, max_addr, 1, 0);

	/* Add a ram-disk to the current image
	 * Note: Add the ramdisk after elf_rel_build_load
	 */
	if (ramdisk) {
		if (devicetreeblob) {
			fprintf(stderr,
			"Can't use ramdisk with device tree blob input\n");
			return -1;
		}
		seg_buf = slurp_file(ramdisk, &seg_size);
		hole_addr = add_buffer(info, seg_buf, seg_size, seg_size,
			0, 0, max_addr, 1);
		initrd_base = hole_addr;
		initrd_size = seg_size;
	} /* ramdisk */

	if (devicetreeblob) {
		/* Grab device tree from buffer */
		seg_buf = slurp_file(devicetreeblob, &seg_size);
	} else {
		/* create from fs2dt */
		create_flatten_tree(&seg_buf, &seg_size, cmdline);
	}

	result = fixup_dt(&seg_buf, &seg_size);
	if (result < 0)
		return result;

	my_dt_offset = add_buffer(info, seg_buf, seg_size, seg_size,
				0, 0, max_addr, -1);

#ifdef NEED_RESERVE_DTB
	/* patch reserve map address for flattened device-tree
	 * find last entry (both 0) in the reserve mem list.  Assume DT
	 * entry is before this one
	 */
	bb_ptr = (struct bootblock *)(seg_buf);
	rsvmap_ptr = (uint64_t *)(seg_buf + be32_to_cpu(bb_ptr->off_mem_rsvmap));
	while (*rsvmap_ptr || *(rsvmap_ptr+1))
		rsvmap_ptr += 2;
	rsvmap_ptr -= 2;
	*rsvmap_ptr = cpu_to_be64(my_dt_offset);
	rsvmap_ptr++;
	*rsvmap_ptr = cpu_to_be64((uint64_t)be32_to_cpu(bb_ptr->totalsize));
#endif

	if (read_prop("/proc/device-tree/ibm,opal/opal-base-address",
		      &my_opal_base, sizeof(my_opal_base)) == 0) {
		my_opal_base = be64_to_cpu(my_opal_base);
		elf_rel_set_symbol(&info->rhdr, "opal_base",
				   &my_opal_base, sizeof(my_opal_base));
	}

	if (read_prop("/proc/device-tree/ibm,opal/opal-entry-address",
		      &my_opal_entry, sizeof(my_opal_entry)) == 0) {
		my_opal_entry = be64_to_cpu(my_opal_entry);
		elf_rel_set_symbol(&info->rhdr, "opal_entry",
				   &my_opal_entry, sizeof(my_opal_entry));
	}

	/* Set kernel */
	elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));

	/* Set dt_offset */
	elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
				sizeof(my_dt_offset));

	/* get slave code from new kernel, put in purgatory */
	elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code,
			sizeof(slave_code));
	master_entry = slave_code[0];
	memcpy(slave_code, phdr->p_data, sizeof(slave_code));
	slave_code[0] = master_entry;
	elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code,
				sizeof(slave_code));

	if (info->kexec_flags & KEXEC_ON_CRASH) {
		my_panic_kernel = 1;
		/* Set panic flag */
		elf_rel_set_symbol(&info->rhdr, "panic_kernel",
				&my_panic_kernel, sizeof(my_panic_kernel));

		/* Set backup address */
		my_backup_start = info->backup_start;
		elf_rel_set_symbol(&info->rhdr, "backup_start",
				&my_backup_start, sizeof(my_backup_start));

		/* Tell relocatable kernel to run at load address
		 * via word before slave code in purgatory
		 */

		elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
				sizeof(my_run_at_load));
		if (my_run_at_load == KERNEL_RUN_AT_ZERO_MAGIC)
			my_run_at_load = 1;
			/* else it should be a fixed offset image */
		elf_rel_set_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
				sizeof(my_run_at_load));
	}

	/* Set stack address */
	my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1);
	my_stack += 16*1024;
	elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));

	/* Set toc */
	toc_addr = my_r2(&info->rhdr);
	elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr));

	/* Set debug */
	elf_rel_set_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug));

	my_kernel = 0;
	my_dt_offset = 0;
	my_panic_kernel = 0;
	my_backup_start = 0;
	my_stack = 0;
	toc_addr = 0;
	my_run_at_load = 0;
	my_debug = 0;
	my_opal_base = 0;
	my_opal_entry = 0;

	elf_rel_get_symbol(&info->rhdr, "opal_base", &my_opal_base,
			   sizeof(my_opal_base));
	elf_rel_get_symbol(&info->rhdr, "opal_entry", &my_opal_entry,
			   sizeof(my_opal_entry));
	elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel));
	elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
				sizeof(my_dt_offset));
	elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load,
				sizeof(my_run_at_load));
	elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel,
				sizeof(my_panic_kernel));
	elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start,
				sizeof(my_backup_start));
	elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack));
	elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr,
				sizeof(toc_addr));
	elf_rel_get_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug));

	dbgprintf("info->entry is %p\n", info->entry);
	dbgprintf("kernel is %llx\n", (unsigned long long)my_kernel);
	dbgprintf("dt_offset is %llx\n",
		(unsigned long long)my_dt_offset);
	dbgprintf("run_at_load flag is %x\n", my_run_at_load);
	dbgprintf("panic_kernel is %x\n", my_panic_kernel);
	dbgprintf("backup_start is %llx\n",
		(unsigned long long)my_backup_start);
	dbgprintf("stack is %llx\n", (unsigned long long)my_stack);
	dbgprintf("toc_addr is %llx\n", (unsigned long long)toc_addr);
	dbgprintf("purgatory size is %zu\n", purgatory_size);
	dbgprintf("debug is %d\n", my_debug);
	dbgprintf("opal_base is %llx\n", (unsigned long long) my_opal_base);
	dbgprintf("opal_entry is %llx\n", (unsigned long long) my_opal_entry);

	return 0;
}