예제 #1
0
void arch_update_purgatory(struct kexec_info *info)
{
	uint8_t panic_kernel = 0;

	elf_rel_set_symbol(&info->rhdr, "reset_vga",
		&arch_options.reset_vga, sizeof(arch_options.reset_vga));
	elf_rel_set_symbol(&info->rhdr, "serial_base",
		&arch_options.serial_base, sizeof(arch_options.serial_base));
	elf_rel_set_symbol(&info->rhdr, "serial_baud",
		&arch_options.serial_baud, sizeof(arch_options.serial_baud));
	elf_rel_set_symbol(&info->rhdr, "console_vga",
		&arch_options.console_vga, sizeof(arch_options.console_vga));
	elf_rel_set_symbol(&info->rhdr, "console_serial",
		&arch_options.console_serial, sizeof(arch_options.console_serial));
	elf_rel_set_symbol(&info->rhdr, "backup_src_start",
		&info->backup_src_start, sizeof(info->backup_src_start));
	elf_rel_set_symbol(&info->rhdr, "backup_src_size",
		&info->backup_src_size, sizeof(info->backup_src_size));
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		panic_kernel = 1;
		elf_rel_set_symbol(&info->rhdr, "backup_start",
				&info->backup_start, sizeof(info->backup_start));
	}
	elf_rel_set_symbol(&info->rhdr, "panic_kernel",
		&panic_kernel, sizeof(panic_kernel));
}
int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr,
                            unsigned long max_addr, unsigned long min_base,
			    const char **cmdline)
{
	struct memory_range *mem_range;
	int nr_ranges;
	unsigned long sz;
	size_t size;
	void *tmp;
	if (info->kexec_flags & KEXEC_ON_CRASH &&
	    get_crash_memory_ranges(&mem_range, &nr_ranges) == 0) {
		int i;

		info->kern_paddr_start = kernel_code_start;
		for (i=0; i < nr_ranges; i++) {
			unsigned long long mstart = crash_memory_range[i].start;
			unsigned long long mend = crash_memory_range[i].end;
			if (!mstart && !mend)
				continue;
			if (kernel_code_start >= mstart &&
			    kernel_code_start < mend) {
				info->kern_vaddr_start = mstart + LOAD_OFFSET;
				break;
			}
		}
		info->kern_size = kernel_code_end - kernel_code_start + 1;
		if (crash_create_elf64_headers(info, &elf_info,
					       crash_memory_range, nr_ranges,
					       &tmp, &sz, EFI_PAGE_SIZE) < 0)
			return -1;

		elfcorehdr = add_buffer(info, tmp, sz, sz, EFI_PAGE_SIZE,
					min_base, max_addr, -1);
		loaded_segments[loaded_segments_num].start = elfcorehdr;
		loaded_segments[loaded_segments_num].end = elfcorehdr + sz;
		loaded_segments_num++;
		cmdline_add_elfcorehdr(cmdline, elfcorehdr);
	}
	add_loaded_segments_info(info, ehdr, max_addr);
	size = sizeof(struct loaded_segment) * loaded_segments_num;
	qsort(loaded_segments, loaded_segments_num,
                        sizeof(struct loaded_segment), seg_comp);
        loaded_segments_base = add_buffer(info, loaded_segments,
                        size, size, 16, 0, max_addr, -1);

        elf_rel_set_symbol(&info->rhdr, "__loaded_segments",
                        &loaded_segments_base, sizeof(long));
        elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num",
                         &loaded_segments_num, sizeof(long));
	return 0;
}
예제 #3
0
static void update_purgatory(struct kexec_info *info)
{
	static const uint8_t null_buf[256];
	sha256_context ctx;
	sha256_digest_t digest;
	struct sha256_region region[SHA256_REGIONS];
	int i, j;
	/* Don't do anything if we are not using purgatory */
	if (!info->rhdr.e_shdr) {
		return;
	}
	arch_update_purgatory(info);
	memset(region, 0, sizeof(region));
	sha256_starts(&ctx);
	/* Compute a hash of the loaded kernel */
	for(j = i = 0; i < info->nr_segments; i++) {
		unsigned long nullsz;
		/* Don't include purgatory in the checksum.  The stack
		 * in the bss will definitely change, and the .data section
		 * will also change when we poke the sha256_digest in there.
		 * A very clever/careful person could probably improve this.
		 */
		if (info->segment[i].mem == (void *)info->rhdr.rel_addr) {
			continue;
		}
		sha256_update(&ctx, info->segment[i].buf,
			      info->segment[i].bufsz);
		nullsz = info->segment[i].memsz - info->segment[i].bufsz;
		while(nullsz) {
			unsigned long bytes = nullsz;
			if (bytes > sizeof(null_buf)) {
				bytes = sizeof(null_buf);
			}
			sha256_update(&ctx, null_buf, bytes);
			nullsz -= bytes;
		}
		region[j].start = info->segment[i].mem;
		region[j].len   = info->segment[i].memsz;
		j++;
	}
	sha256_finish(&ctx, digest);
	elf_rel_set_symbol(&info->rhdr, "sha256_regions", &region,
			   sizeof(region));
	elf_rel_set_symbol(&info->rhdr, "sha256_digest", &digest,
			   sizeof(digest));
}
예제 #4
0
int arch_compat_trampoline(struct kexec_info *info)
{
	if ((info->kexec_flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_X86_64)
	{
		if (!info->rhdr.e_shdr) {
			fprintf(stderr,
				"A trampoline is required for cross architecture support\n");
			return -1;
		}
		elf_rel_set_symbol(&info->rhdr, "compat_x86_64_entry32",
			&info->entry, sizeof(info->entry));

		info->entry = (void *)elf_rel_get_addr(&info->rhdr, "compat_x86_64");
	}
	return 0;
}
int elf_x86_load(int argc, char **argv, const char *buf, off_t len, 
	struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	char *command_line = NULL, *modified_cmdline = NULL;
	const char *append = NULL;
	char *tmp_cmdline = NULL;
	char *error_msg = NULL;
	int result;
	int command_line_len;
	const char *ramdisk;
	unsigned long entry, max_addr;
	int arg_style;
#define ARG_STYLE_ELF   0
#define ARG_STYLE_LINUX 1
#define ARG_STYLE_NONE  2
	int opt;

	/* 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 },
		{ "reuse-cmdline",	0, NULL, OPT_REUSE_CMDLINE },
		{ "initrd",		1, NULL, OPT_RAMDISK },
		{ "ramdisk",		1, NULL, OPT_RAMDISK },
		{ "args-elf",		0, NULL, OPT_ARGS_ELF },
		{ "args-linux",		0, NULL, OPT_ARGS_LINUX },
		{ "args-none",		0, NULL, OPT_ARGS_NONE },
		{ 0, 			0, NULL, 0 },
	};

	static const char short_options[] = KEXEC_OPT_STR "";

	/*
	 * Parse the command line arguments
	 */
	arg_style = ARG_STYLE_ELF;
	ramdisk = 0;
	result = 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:
			append = optarg;
			break;
		case OPT_REUSE_CMDLINE:
			tmp_cmdline = get_command_line();
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_ARGS_ELF: 
			arg_style = ARG_STYLE_ELF;
			break;
		case OPT_ARGS_LINUX:
			arg_style = ARG_STYLE_LINUX;
			break;
		case OPT_ARGS_NONE:
#ifdef __i386__
			arg_style = ARG_STYLE_NONE;
#else
			die("--args-none only works on arch i386\n");
#endif
			break;
		}
	}
	command_line = concat_cmdline(tmp_cmdline, append);
	if (tmp_cmdline) {
		free(tmp_cmdline);
	}
	command_line_len = 0;
	if (command_line) {
		command_line_len = strlen(command_line) +1;
	} else {
	    command_line = strdup("\0");
	    command_line_len = 1;
	}

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

	/* Load the ELF executable */
	elf_exec_build_load(info, &ehdr, buf, len, 0);

	entry = ehdr.e_entry;
	max_addr = elf_max_addr(&ehdr);

	/* Do we want arguments? */
	if (arg_style != ARG_STYLE_NONE) {
		/* Load the setup code */
		elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
			0, ULONG_MAX, 1, 0);
	}
	if (arg_style == ARG_STYLE_NONE) {
		info->entry = (void *)entry;

	}
	else if (arg_style == ARG_STYLE_ELF) {
		unsigned long note_base;
		struct entry32_regs regs;
		uint32_t arg1, arg2;

		/* Setup the ELF boot notes */
		note_base = elf_boot_notes(info, max_addr,
					   command_line, command_line_len);

		/* Initialize the stack arguments */
		arg2 = 0; /* No return address */
		arg1 = note_base;
		elf_rel_set_symbol(&info->rhdr, "stack_arg32_1", &arg1, sizeof(arg1));
		elf_rel_set_symbol(&info->rhdr, "stack_arg32_2", &arg2, sizeof(arg2));
		
		/* Initialize the registers */
		elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
		regs.eip = entry;       /* The entry point */
		regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2");
		elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));

		if (ramdisk) {
			error_msg = "Ramdisks not supported with generic elf arguments";
			goto out;
		}
	}
	else if (arg_style == ARG_STYLE_LINUX) {
		struct x86_linux_faked_param_header *hdr;
		unsigned long param_base;
		const char *ramdisk_buf;
		off_t ramdisk_length;
		struct entry32_regs regs;
		int rc = 0;

		/* Get the linux parameter header */
		hdr = xmalloc(sizeof(*hdr));

		/* Hack: With some ld versions, vmlinux program headers show
		 * a gap of two pages between bss segment and data segment
		 * but effectively kernel considers it as bss segment and
		 * overwrites the any data placed there. Hence bloat the
		 * memsz of parameter segment to 16K to avoid being placed
		 * in such gaps.
		 * This is a makeshift solution until it is fixed in kernel
		 */
		param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024,
			16, 0, max_addr, 1);

		/* Initialize the parameter header */
		memset(hdr, 0, sizeof(*hdr));
		init_linux_parameters(&hdr->hdr);

		/* Add a ramdisk to the current image */
		ramdisk_buf = NULL;
		ramdisk_length = 0;
		if (ramdisk) {
			ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
		}

		/* If panic kernel is being loaded, additional segments need
		 * to be created. */
		if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) {
			rc = load_crashdump_segments(info, modified_cmdline,
						max_addr, 0);
			if (rc < 0) {
				result = -1;
				goto out;
			}
			/* Use new command line. */
			free(command_line);
			command_line = modified_cmdline;
			command_line_len = strlen(modified_cmdline) + 1;
			modified_cmdline = NULL;
		}

		/* Tell the kernel what is going on */
		setup_linux_bootloader_parameters(info, &hdr->hdr, param_base, 
			offsetof(struct x86_linux_faked_param_header, command_line),
			command_line, command_line_len,
			ramdisk_buf, ramdisk_length);

		/* Fill in the information bios calls would usually provide */
		setup_linux_system_parameters(info, &hdr->hdr);

		/* Initialize the registers */
		elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
		regs.ebx = 0;		/* Bootstrap processor */
		regs.esi = param_base;	/* Pointer to the parameters */
		regs.eip = entry;	/* The entry point */
		regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
		elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
	}
	else {
예제 #6
0
int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
	struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	const char *command_line, *ramdisk=0, *vmm=0, *kernel_buf;
	char *ramdisk_buf = NULL;
	off_t ramdisk_size = 0, kernel_size;
	unsigned long command_line_len;
	unsigned long entry, max_addr, gp_value;
	unsigned long command_line_base, ramdisk_base, image_base;
	unsigned long efi_memmap_base, efi_memmap_size;
	unsigned long boot_param_base;
	unsigned long noio=0;
	int result;
	int opt;
	char *efi_memmap_buf, *boot_param;
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{"command-line", 1, 0, OPT_APPEND},
		{"append",       1, 0, OPT_APPEND},
		{"initrd",       1, 0, OPT_RAMDISK},
		{"noio",         0, 0, OPT_NOIO},
		{"vmm",          1, 0, OPT_VMM},
		{0, 0, 0, 0},
	};

	static const char short_options[] = KEXEC_ARCH_OPT_STR "";

	command_line = 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:
			command_line = optarg;
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_NOIO:	/* disable PIO and MMIO in purgatory code*/
			noio = 1;
			break;
		case OPT_VMM:
			vmm = optarg;
			break;
		}
	}
	command_line_len = 0;
	if (command_line) {
		command_line_len = strlen(command_line) + 16;
	}

	if (vmm)
		kernel_buf = slurp_decompress_file(vmm, &kernel_size);
	else {
		kernel_buf = buf;
		kernel_size = len;
	}

	/* Parse the Elf file */
	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
	if (result < 0) {
		fprintf(stderr, "ELF parse failed\n");
		free_elf_info(&ehdr);
		return result;
	}

	if (info->kexec_flags & KEXEC_ON_CRASH ) {
		if ((mem_min == 0x00) && (mem_max == ULONG_MAX)) {
			fprintf(stderr, "Failed to find crash kernel region "
				"in %s\n", proc_iomem());
			free_elf_info(&ehdr);
			return -1;
		}
		move_loaded_segments(info, &ehdr, mem_min);
	} else if (update_loaded_segments(info, &ehdr) < 0) {
		fprintf(stderr, "Failed to place kernel\n");
		return -1;
	}

	entry = ehdr.e_entry;
	max_addr = elf_max_addr(&ehdr);

	/* Load the Elf data */
	result = elf_exec_load(&ehdr, info);
	if (result < 0) {
		fprintf(stderr, "ELF load failed\n");
		free_elf_info(&ehdr);
		return result;
	}


	/* Load the setup code */
	elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
			0x0, ULONG_MAX, -1, 0);


	if (load_crashdump_segments(info, &ehdr, max_addr, 0,
				&command_line) < 0)
		return -1;

	// reverve 4k for ia64_boot_param
	boot_param = xmalloc(4096);
        boot_param_base = add_buffer(info, boot_param, 4096, 4096, 4096, 0,
                        max_addr, -1);

	elf_rel_set_symbol(&info->rhdr, "__noio",
			   &noio, sizeof(long));

        elf_rel_set_symbol(&info->rhdr, "__boot_param_base",
                        &boot_param_base, sizeof(long));

	// reserve efi_memmap of actual size allocated in production kernel
	efi_memmap_size = saved_efi_memmap_size;
	efi_memmap_buf = xmalloc(efi_memmap_size);
	efi_memmap_base = add_buffer(info, efi_memmap_buf,
			efi_memmap_size, efi_memmap_size, 4096, 0,
			max_addr, -1);

	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base",
			&efi_memmap_base, sizeof(long));

	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size",
			&efi_memmap_size, sizeof(long));
	if (command_line) {
		command_line_len = strlen(command_line) + 1;
	}
	if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) {
		char *cmdline = xmalloc(command_line_len);
		strcpy(cmdline, command_line);

		if (info->kexec_flags & KEXEC_ON_CRASH) {
			char buf[128];
			sprintf(buf," max_addr=%lluM min_addr=%lluM",
					mem_max>>20, mem_min>>20);
			command_line_len = strlen(cmdline) + strlen(buf) + 1;
			cmdline = xrealloc(cmdline, command_line_len);
			strcat(cmdline, buf);
		}
예제 #7
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;
}
예제 #8
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;
}
예제 #9
0
static int ppc_load_bare_bits(int argc, char **argv, const char *buf,
		off_t len, struct kexec_info *info, unsigned int load_addr,
		unsigned int ep)
{
	char *command_line;
	int command_line_len;
	char *dtb;
	unsigned int addr;
	unsigned long dtb_addr;
#define FIXUP_ENTRYS    (20)
	char *fixup_nodes[FIXUP_ENTRYS + 1];
	int cur_fixup = 0;
	int opt;
	int ret;

	command_line = NULL;
	dtb = NULL;

	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:
			command_line = optarg;
			break;

		case OPT_DTB:
			dtb = optarg;
			break;

		case OPT_NODES:
			if (cur_fixup >= FIXUP_ENTRYS) {
				fprintf(stderr, "The number of entries for the fixup is too large\n");
				exit(1);
			}
			fixup_nodes[cur_fixup] = optarg;
			cur_fixup++;
			break;
		}
	}

	command_line_len = 0;
	if (command_line)
		command_line_len = strlen(command_line) + 1;

	fixup_nodes[cur_fixup] = NULL;

	/*
	 * len contains the length of the whole kernel image except the bss
	 * section. The 3 MiB should cover it. The purgatory and the dtb are
	 * allocated from memtop down towards zero so we should never get too
	 * close to the bss :)
	 */
	ret = valid_memory_range(info, load_addr, len + 3 * 1024 * 1024);
	if (!ret) {
		printf("Can't add kernel to addr 0x%08x len %ld\n",
				load_addr, len + 3 * 1024 * 1024);
		return -1;
	}
	add_segment(info, buf, len, load_addr, len + 3 * 1024 * 1024);
	if (dtb) {
		char *blob_buf;
		off_t blob_size = 0;

		/* Grab device tree from buffer */
		blob_buf = slurp_file(dtb, &blob_size);
		if (!blob_buf || !blob_size)
			die("Device tree seems to be an empty file.\n");
		blob_buf = fixup_dtb_nodes(blob_buf, &blob_size, fixup_nodes, command_line);

		dtb_addr = add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
				KERNEL_ACCESS_TOP, -1);
	} else {
		dtb_addr = 0;
	}

	elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
			purgatory_size, 0, -1, -1, 0);

	/* set various variables for the purgatory */
	addr = ep;
	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, -1, 1);
	addr += PUL_STACK_SIZE;
	elf_rel_set_symbol(&info->rhdr, "pul_stack", &addr, sizeof(addr));
	/* No allocation past here in order not to overwrite the stack */
#undef PUL_STACK_SIZE

	addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
	info->entry = (void *)addr;
	return 0;
}
예제 #10
0
int do_bzImage_load(struct kexec_info *info,
	const char *kernel, off_t kernel_len,
	const char *command_line, off_t command_line_len,
	const char *initrd, off_t initrd_len,
	int real_mode_entry, int debug)
{
	struct x86_linux_header setup_header;
	struct x86_linux_param_header *real_mode;
	int setup_sects;
	char *kernel_version;
	size_t size;
	int kern16_size;
	unsigned long setup_base, setup_size;
	struct entry32_regs regs32;
	struct entry16_regs regs16;
	unsigned int relocatable_kernel = 0;
	unsigned long kernel32_load_addr;
	char *modified_cmdline;

	/*
	 * Find out about the file I am about to load.
	 */
	if (kernel_len < sizeof(setup_header)) {
		return -1;
	}
	memcpy(&setup_header, kernel, sizeof(setup_header));
	setup_sects = setup_header.setup_sects;
	if (setup_sects == 0) {
		setup_sects = 4;
	}

	kern16_size = (setup_sects +1) *512;
	kernel_version = ((unsigned char *)&setup_header) + 512 + setup_header.kver_addr;
	if (kernel_len < kern16_size) {
		fprintf(stderr, "BzImage truncated?\n");
		return -1;
	}

	if (setup_header.protocol_version >= 0x0205) {
		relocatable_kernel = setup_header.relocatable_kernel;
		dfprintf(stdout, "bzImage is relocatable\n");
	}

	/* Can't use bzImage for crash dump purposes with real mode entry */
	if((info->kexec_flags & KEXEC_ON_CRASH) && real_mode_entry) {
		fprintf(stderr, "Can't use bzImage for crash dump purposes"
				" with real mode entry\n");
		return -1;
	}

	if((info->kexec_flags & KEXEC_ON_CRASH) && !relocatable_kernel) {
		fprintf(stderr, "BzImage is not relocatable. Can't be used"
				" as capture kernel.\n");
		return -1;
	}

	/* 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 (command_line) {
			strncpy(modified_cmdline, command_line,
					COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}

		/* If panic kernel is being loaded, additional segments need
		 * to be created. load_crashdump_segments will take care of
		 * loading the segments as high in memory as possible, hence
		 * in turn as away as possible from kernel to avoid being
		 * stomped by the kernel.
		 */
		if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0)
			return -1;

		/* Use new command line buffer */
		command_line = modified_cmdline;
		command_line_len = strlen(command_line) +1;
	}

	/* Load the trampoline.  This must load at a higher address
	 * the the argument/parameter segment or the kernel will stomp
	 * it's gdt.
	 *
	 * x86_64 purgatory code has got relocations type R_X86_64_32S
	 * that means purgatory got to be loaded within first 2G otherwise
	 * overflow takes place while applying relocations.
	 */
	if (!real_mode_entry && relocatable_kernel)
		elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
					0x3000, 0x7fffffff, -1, 0);
	else
		elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
					0x3000, 640*1024, -1, 0);
	dfprintf(stdout, "Loaded purgatory at addr 0x%lx\n",
				info->rhdr.rel_addr);
	/* The argument/parameter segment */
	setup_size = kern16_size + command_line_len;
	real_mode = xmalloc(setup_size);
	memcpy(real_mode, kernel, kern16_size);

	if (info->kexec_flags & KEXEC_ON_CRASH) {
		/* If using bzImage for capture kernel, then we will not be
		 * executing real mode code. setup segment can be loaded
		 * anywhere as we will be just reading command line.
		 */
		setup_base = add_buffer(info, real_mode, setup_size, setup_size,
			16, 0x3000, -1, 1);
	}
	else if (real_mode->protocol_version >= 0x0200) {
		/* Careful setup_base must be greater than 8K */
		setup_base = add_buffer(info, real_mode, setup_size, setup_size,
			16, 0x3000, 640*1024, 1);
	} else {
		add_segment(info, real_mode, setup_size, SETUP_BASE, setup_size);
		setup_base = SETUP_BASE;
	}
	dfprintf(stdout, "Loaded real-mode code and command line at 0x%lx\n",
			setup_base);

	/* Verify purgatory loads higher than the parameters */
	if (info->rhdr.rel_addr < setup_base) {
		die("Could not put setup code above the kernel parameters\n");
	}
	
	/* The main kernel segment */
	size = kernel_len - kern16_size;

	if (real_mode->protocol_version >=0x0205 && relocatable_kernel) {
		/* Relocatable bzImage */
		unsigned long kern_align = real_mode->kernel_alignment;
		unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX;

		if (real_mode->protocol_version >= 0x0203) {
			if (kernel32_max_addr > real_mode->initrd_addr_max)
				kernel32_max_addr = real_mode->initrd_addr_max;
		}

		kernel32_load_addr = add_buffer(info, kernel + kern16_size,
						size, size, kern_align,
						0x100000, kernel32_max_addr,
						1);
	}
	else {
		kernel32_load_addr = KERN32_BASE;
		add_segment(info, kernel + kern16_size, size,
				kernel32_load_addr, size);
	}
		
	dfprintf(stdout, "Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr);

	/* Tell the kernel what is going on */
	setup_linux_bootloader_parameters(info, real_mode, setup_base,
		kern16_size, command_line, command_line_len,
		initrd, initrd_len);

	/* Get the initial register values */
	elf_rel_get_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
	elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
	/*

	 * Initialize the 32bit start information.
	 */
	regs32.eax = 0; /* unused */
	regs32.ebx = 0; /* 0 == boot not AP processor start */
	regs32.ecx = 0; /* unused */
	regs32.edx = 0; /* unused */
	regs32.esi = setup_base; /* kernel parameters */
	regs32.edi = 0; /* unused */
	regs32.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* stack, unused */
	regs32.ebp = 0; /* unused */
	regs32.eip = kernel32_load_addr; /* kernel entry point */

	/*
	 * Initialize the 16bit start information.
	 */
	regs16.cs = (setup_base>>4) + 0x20;
	regs16.ip = 0;
	regs16.ss = (elf_rel_get_addr(&info->rhdr, "stack_end") - 64*1024) >> 4;
	regs16.esp = 0xFFFC;
	if (real_mode_entry) {
		printf("Starting the kernel in real mode\n");
		regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
	}
	if (real_mode_entry && debug) {
		unsigned long entry16_debug, pre32, first32;
		uint32_t old_first32;
		/* Find the location of the symbols */
		entry16_debug = elf_rel_get_addr(&info->rhdr, "entry16_debug");
		pre32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_pre32");
		first32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_first32");
		
		/* Hook all of the linux kernel hooks */
		real_mode->rmode_switch_cs = entry16_debug >> 4;
		real_mode->rmode_switch_ip = pre32 - entry16_debug;
		old_first32 = real_mode->kernel_start;
		real_mode->kernel_start = first32;
		elf_rel_set_symbol(&info->rhdr, "entry16_debug_old_first32",
			&old_first32, sizeof(old_first32));
	
		regs32.eip = entry16_debug;
	}