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 {
int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len,
	struct kexec_info *info)
/* Marshal up a multiboot-style kernel */
{
	struct multiboot_info *mbi;
	void   *mbi_buf;
	struct mod_list *modp;
	unsigned long freespace;
	unsigned long long mem_lower = 0, mem_upper = 0;
	struct mem_ehdr ehdr;
	unsigned long mbi_base;
	struct entry32_regs regs;
	size_t mbi_bytes, mbi_offset;
	char *command_line = NULL;
	char *imagename, *cp, *append = NULL;;
	struct memory_range *range;
	int ranges;
	struct AddrRangeDesc *mmap;
	int command_line_len;
	int i;
	uint32_t u;
	int opt;
	int modules, mod_command_line_space;
	/* See options.h -- add any more there, too. */
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",		1, 0, OPT_CL },
		{ "append",			1, 0, OPT_CL },
		{ "reuse-cmdline",		0, 0, OPT_REUSE_CMDLINE },
		{ "module",			1, 0, OPT_MOD },
		{ 0, 				0, 0, 0 },
	};
	static const char short_options[] = KEXEC_ARCH_OPT_STR "";
	
	/* Probe for the MB header if it's not already found */
	if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) 
	{
		fprintf(stderr, "Cannot find a loadable multiboot header.\n");
		return -1;
	}

	
	/* Parse the command line */
	command_line = "";
	command_line_len = 0;
	modules = 0;
	mod_command_line_space = 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_CL:
			append = optarg;
			break;
		case OPT_REUSE_CMDLINE:
			command_line = get_command_line();
			break;
		case OPT_MOD:
			modules++;
			mod_command_line_space += strlen(optarg) + 1;
			break;
		}
	}
	imagename = argv[optind];
	command_line = concat_cmdline(command_line, append);
	command_line_len = strlen(command_line) + strlen(imagename) + 2;


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

	/* Load the setup code */
	elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0,
				ULONG_MAX, 1, 0);
	
	/* The first segment will contain the multiboot headers:
	 * =============
	 * multiboot information (mbi)
	 * -------------
	 * kernel command line
	 * -------------
	 * bootloader name
	 * -------------
	 * module information entries
	 * -------------
	 * module command lines
	 * ==============
	 */
	mbi_bytes = (sizeof(*mbi) + command_line_len 
		     + strlen (BOOTLOADER " " BOOTLOADER_VERSION) + 1
		     + 3) & ~3;
	mbi_buf = xmalloc(mbi_bytes);
	mbi = mbi_buf;
	memset(mbi, 0, sizeof(*mbi));
	sprintf(((char *)mbi) + sizeof(*mbi), "%s %s",
		imagename, command_line);
	sprintf(((char *)mbi) + sizeof(*mbi) + command_line_len, "%s",
		BOOTLOADER " " BOOTLOADER_VERSION);
	mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME;
	/* We'll relocate these to absolute addresses later. For now,
	 * all addresses within the first segment are relative to the
	 * start of the MBI. */
	mbi->cmdline = sizeof(*mbi); 
	mbi->boot_loader_name = sizeof(*mbi) + command_line_len; 

	/* Memory map */
	if ((get_memory_ranges(&range, &ranges, info->kexec_flags) < 0)
			|| ranges == 0) {
		fprintf(stderr, "Cannot get memory information\n");
		return -1;
	}
	mmap = xmalloc(ranges * sizeof(*mmap));
	for (i=0; i<ranges; i++) {
		unsigned long long length;
		length = range[i].end - range[i].start;
		/* Translate bzImage mmap to multiboot-speak */
		mmap[i].size = sizeof(mmap[i]) - 4;
		mmap[i].base_addr_low  = range[i].start & 0xffffffff;
		mmap[i].base_addr_high  = range[i].start >> 32;
		mmap[i].length_low     = length & 0xffffffff;
		mmap[i].length_high    = length >> 32;
		if (range[i].type == RANGE_RAM) {
			mmap[i].Type = 1; /* RAM */
			/* Is this the "low" memory? */
			if ((range[i].start == 0)
			    && (range[i].end > mem_lower))
				mem_lower = range[i].end;
			/* Is this the "high" memory? */
			if ((range[i].start <= 0x100000)
			    && (range[i].end > mem_upper + 0x100000))
				mem_upper = range[i].end - 0x100000;
		}
		else
		mmap[i].Type = 0xbad;  /* Not RAM */
	}


	if (mbh->flags & MULTIBOOT_MEMORY_INFO) { 
		/* Provide a copy of the memory map to the kernel */

		mbi->flags |= MB_INFO_MEMORY | MB_INFO_MEM_MAP;
		
		freespace = add_buffer(info,
			mmap, ranges * sizeof(*mmap), ranges * sizeof(*mmap),
			4, 0, 0xFFFFFFFFUL, 1);

		mbi->mmap_addr   = freespace;
		mbi->mmap_length = ranges * sizeof(*mmap);

		/* For kernels that care naught for fancy memory maps
		 * and just want the size of low and high memory */
		mbi->mem_lower = MIN(mem_lower>>10, 0xffffffff);
		mbi->mem_upper = MIN(mem_upper>>10, 0xffffffff);
			
		/* done */
	}