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 {
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;
}
Exemple #4
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;
	}