コード例 #1
0
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
	struct kexec_info *info)
{
	unsigned long base;
	unsigned int atag_offset = 0x1000; /* 4k offset from memory start */
	unsigned int offset = 0x8000;      /* 32k offset from memory start */
	unsigned int opt_ramdisk_addr;
	unsigned int opt_atags_addr;
	const char *command_line;
	char *modified_cmdline = NULL;
	off_t command_line_len;
	const char *ramdisk;
	char *ramdisk_buf;
	int opt;
	char *endptr;
	int use_dtb;
	const char *dtb_file;
	char *dtb_buf;
	off_t dtb_length;
	off_t dtb_offset;
	struct arm_mach *mach;

	/* See options.h -- add any more there, too. */
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",	1, 0, OPT_APPEND },
		{ "append",		1, 0, OPT_APPEND },
		{ "initrd",		1, 0, OPT_RAMDISK },
		{ "ramdisk",		1, 0, OPT_RAMDISK },
		{ "dtb",		2, 0, OPT_DTB },
		{ "rd-addr",		1, 0, OPT_RD_ADDR },
		{ "atags-addr",		1, 0, OPT_ATAGS_ADDR },
		{ "boardname",  1, 0, OPT_BOARDNAME },
		{ 0, 			0, 0, 0 },
	};
	static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:d::i:g:b:";

	/*
	 * Parse the command line arguments
	 */
	command_line = 0;
	command_line_len = 0;
	ramdisk = 0;
	ramdisk_buf = 0;
	use_dtb = 0;
	dtb_file = NULL;
	opt_ramdisk_addr = 0;
	opt_atags_addr = 0;
	mach = 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_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_DTB:
			use_dtb = 1;
			if(optarg)
				dtb_file = optarg;
			break;
		case OPT_RD_ADDR:
			opt_ramdisk_addr = strtoul(optarg, &endptr, 0);
			if (*endptr) {
				fprintf(stderr,
					"Bad option value in --rd-addr=%s\n",
					optarg);
				usage();
				return -1;
			}
			break;
		case OPT_ATAGS_ADDR:
			opt_atags_addr = strtoul(optarg, &endptr, 0);
			if (*endptr) {
				fprintf(stderr,
					"Bad option value in --atag-addr=%s\n",
					optarg);
				usage();
				return -1;
			}
			break;
		case OPT_BOARDNAME:
			mach = arm_mach_choose(optarg);
			if(!mach)
			{
				fprintf(stderr, "Unknown boardname '%s'!\n", optarg);
				return -1;
			}
			break;
		}
	}
	if (command_line) {
		command_line_len = strlen(command_line) + 1;
		if (command_line_len > COMMAND_LINE_SIZE)
			command_line_len = COMMAND_LINE_SIZE;
	}
	if (ramdisk) {
		ramdisk_buf = slurp_file(ramdisk, &initrd_size);
	}

	/*
	 * If we are loading a dump capture kernel, we need to update kernel
	 * command line and also add some additional segments.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		uint64_t start, end;

		modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
		if (!modified_cmdline)
			return -1;

		if (command_line) {
			(void) strncpy(modified_cmdline, command_line,
				       COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}

		if (load_crashdump_segments(info, modified_cmdline) < 0) {
			free(modified_cmdline);
			return -1;
		}

		command_line = modified_cmdline;
		command_line_len = strlen(command_line) + 1;

		/*
		 * We put the dump capture kernel at the start of crashkernel
		 * reserved memory.
		 */
		if (parse_iomem_single("Crash kernel\n", &start, &end)) {
			/*
			 * No crash kernel memory reserved. We cannot do more
			 * but just bail out.
			 */
			return -1;
		}
		base = start;
	} else {
		base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX);
	}

	if (base == ULONG_MAX)
		return -1;

	/* assume the maximum kernel compression ratio is 4,
	 * and just to be safe, place ramdisk after that
	 */
	if(opt_ramdisk_addr == 0)
		initrd_base = _ALIGN(base + len * 4, getpagesize());
	else
		initrd_base = opt_ramdisk_addr;

	if(!use_dtb)
	{
		if (atag_arm_load(info, base + atag_offset,
				command_line, command_line_len,
				ramdisk_buf, initrd_size, initrd_base) == -1)
			return -1;
	}
	else
	{
		char *dtb_img = NULL;
		off_t dtb_img_len = 0;
		int free_dtb_img = 0;
		int choose_res = 0;

		if(!mach)
		{
			fprintf(stderr, "DTB: --boardname was not specified.\n");
			return -1;
		}

		if(dtb_file)
		{
			if(!load_dtb_image(dtb_file, &dtb_img, &dtb_img_len))
				return -1;

			printf("DTB: Using DTB from file %s\n", dtb_file);
			free_dtb_img = 1;
		}
		else
		{
			if(!get_appended_dtb(buf, len, &dtb_img, &dtb_img_len))
				return -1;

			printf("DTB: Using DTB appended to zImage\n");
		}

		choose_res = (mach->choose_dtb)(dtb_img, dtb_img_len, &dtb_buf, &dtb_length);

		if(free_dtb_img)
			free(dtb_img);

		if(choose_res)
		{
			int ret, off;

			dtb_length = fdt_totalsize(dtb_buf) + DTB_PAD_SIZE;
			dtb_buf = xrealloc(dtb_buf, dtb_length);
			ret = fdt_open_into(dtb_buf, dtb_buf, dtb_length);
			if(ret)
				die("DTB: fdt_open_into failed");

			ret = (mach->add_extra_regs)(dtb_buf);
			if (ret < 0)
			{
				fprintf(stderr, "DTB: error while adding mach-specific extra regs\n");
				return -1;
			}

			if (command_line) {
				const char *node_name = "/chosen";
				const char *prop_name = "bootargs";

				/* check if a /choosen subnode already exists */
				off = fdt_path_offset(dtb_buf, node_name);

				if (off == -FDT_ERR_NOTFOUND)
					off = fdt_add_subnode(dtb_buf, off, node_name);

				if (off < 0) {
					fprintf(stderr, "DTB: Error adding %s node.\n", node_name);
					return -1;
				}

				if (fdt_setprop(dtb_buf, off, prop_name,
						command_line, strlen(command_line) + 1) != 0) {
					fprintf(stderr, "DTB: Error setting %s/%s property.\n",
						node_name, prop_name);
					return -1;
				}
			}

			if(ramdisk)
			{
				const char *node_name = "/chosen";
				uint32_t initrd_start, initrd_end;

				/* check if a /choosen subnode already exists */
				off = fdt_path_offset(dtb_buf, node_name);

				if (off == -FDT_ERR_NOTFOUND)
					off = fdt_add_subnode(dtb_buf, off, node_name);

				if (off < 0) {
					fprintf(stderr, "DTB: Error adding %s node.\n", node_name);
					return -1;
				}

				initrd_start = cpu_to_fdt32(initrd_base);
				initrd_end = cpu_to_fdt32(initrd_base + initrd_size);

				ret = fdt_setprop(dtb_buf, off, "linux,initrd-start", &initrd_start, sizeof(initrd_start));
				if (ret)
					die("DTB: Error setting %s/linux,initrd-start property.\n", node_name);

				ret = fdt_setprop(dtb_buf, off, "linux,initrd-end", &initrd_end, sizeof(initrd_end));
				if (ret)
					die("DTB: Error setting %s/linux,initrd-end property.\n", node_name);
			}

			fdt_pack(dtb_buf);
		}
		else
		{
			/*
			* Extract the DTB from /proc/device-tree.
			*/
			printf("DTB: Failed to load dtb from zImage or dtb.img, using /proc/device-tree. This is unlikely to work.\n");
			create_flatten_tree(&dtb_buf, &dtb_length, command_line);
		}

		if(ramdisk)
		{
			add_segment(info, ramdisk_buf, initrd_size, initrd_base,
				initrd_size);
		}

		if(opt_atags_addr != 0)
			dtb_offset = opt_atags_addr;
		else
		{
			dtb_offset = initrd_base + initrd_size + getpagesize();
			dtb_offset = _ALIGN_DOWN(dtb_offset, getpagesize());
		}

		printf("DTB: add dtb segment 0x%x\n", (unsigned int)dtb_offset);
		add_segment(info, dtb_buf, dtb_length,
		            dtb_offset, dtb_length);
	}

	add_segment(info, buf, len, base + offset, len);

	info->entry = (void*)base + offset;

	return 0;
}
コード例 #2
0
ファイル: kexec-elf-ppc64.c プロジェクト: horms/kexec-tools
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;
}
コード例 #3
0
ファイル: kexec-elf-ppc64.c プロジェクト: OPSF/uClinux
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;
}
コード例 #4
0
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len,
	struct kexec_info *info)
{
	unsigned long base;
	unsigned int atag_offset = 0x1000; /* 4k offset from memory start */
	unsigned int offset = 0x8000;      /* 32k offset from memory start */
	const char *command_line;
	char *modified_cmdline = NULL;
	off_t command_line_len;
	const char *ramdisk;
	char *ramdisk_buf;
	int opt;
	int use_atags;
	char *dtb_buf;
	off_t dtb_length;
	char *dtb_file;
	off_t dtb_offset;
	char *end;

	/* See options.h -- add any more there, too. */
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",	1, 0, OPT_APPEND },
		{ "append",		1, 0, OPT_APPEND },
		{ "initrd",		1, 0, OPT_RAMDISK },
		{ "ramdisk",		1, 0, OPT_RAMDISK },
		{ "dtb",		1, 0, OPT_DTB },
		{ "atags",		0, 0, OPT_ATAGS },
		{ "image-size",		1, 0, OPT_IMAGE_SIZE },
		{ "atags-file",		1, 0, OPT_ATAGS },
		{ 0, 			0, 0, 0 },
	};
	static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:";

	/*
	 * Parse the command line arguments
	 */
	command_line = 0;
	command_line_len = 0;
	ramdisk = 0;
	ramdisk_buf = 0;
	initrd_size = 0;
	use_atags = 0;
	dtb_file = 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 OPT_APPEND:
			command_line = optarg;
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_DTB:
			dtb_file = optarg;
			break;
		case OPT_ATAGS:
			use_atags = 1;
			break;
		case OPT_IMAGE_SIZE:
			kexec_arm_image_size = strtoul(optarg, &end, 0);
			break;
		case OPT_ATAGS_FILE:
			atags_file = optarg;
			break;
		}
	}

	if (use_atags && dtb_file) {
		fprintf(stderr, "You can only use ATAGs if you don't specify a "
		        "dtb file.\n");
		return -1;
	}

	if (command_line) {
		command_line_len = strlen(command_line) + 1;
		if (command_line_len > COMMAND_LINE_SIZE)
			command_line_len = COMMAND_LINE_SIZE;
	}
	if (ramdisk) {
		ramdisk_buf = slurp_file(ramdisk, &initrd_size);
	}

	/*
	 * If we are loading a dump capture kernel, we need to update kernel
	 * command line and also add some additional segments.
	 */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		uint64_t start, end;

		modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
		if (!modified_cmdline)
			return -1;

		if (command_line) {
			(void) strncpy(modified_cmdline, command_line,
				       COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}

		if (load_crashdump_segments(info, modified_cmdline) < 0) {
			free(modified_cmdline);
			return -1;
		}

		command_line = modified_cmdline;
		command_line_len = strlen(command_line) + 1;

		/*
		 * We put the dump capture kernel at the start of crashkernel
		 * reserved memory.
		 */
		if (parse_iomem_single("Crash kernel\n", &start, &end)) {
			/*
			 * No crash kernel memory reserved. We cannot do more
			 * but just bail out.
			 */
			return -1;
		}
		base = start;
	} else {
		base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX);
	}

	if (base == ULONG_MAX)
		return -1;

	printf("Kernel segment stats: %lx (%ld)\n", base, len);

	if (kexec_arm_image_size) {
		/* If the image size was passed as command line argument,
		 * use that value for determining the address for initrd,
		 * atags and dtb images. page-align the given length.*/
		initrd_base = base + _ALIGN(kexec_arm_image_size, getpagesize());
	} else {
		/* Otherwise, assume the maximum kernel compression ratio
		 * is 4, and just to be safe, place ramdisk after that */
		initrd_base = base + _ALIGN(len * 4, getpagesize());
	}

	if (use_atags) {
		/*
		 * use ATAGs from /proc/atags
		 */
		if (atag_arm_load(info, base + atag_offset,
		                  command_line, command_line_len,
		                  ramdisk_buf, initrd_size, initrd_base) == -1)
			return -1;
	} else {
		/*
		 * Read a user-specified DTB file.
		 */
		if (dtb_file) {
			dtb_buf = slurp_file(dtb_file, &dtb_length);

			if (fdt_check_header(dtb_buf) != 0) {
				fprintf(stderr, "Invalid FDT buffer.\n");
				return -1;
			}

			if (command_line) {
				/*
				 *  Error should have been reported so
				 *  directly return -1
				 */
				if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen",
						"bootargs", command_line,
						strlen(command_line) + 1))
					return -1;
			}
		} else {
			/*
			 * Extract the DTB from /proc/device-tree.
			 */
			create_flatten_tree(&dtb_buf, &dtb_length, command_line);
		}

		if (base + atag_offset + dtb_length > base + offset) {
			fprintf(stderr, "DTB too large!\n");
			return -1;
		}

		if (ramdisk) {
			add_segment(info, ramdisk_buf, initrd_size,
			            initrd_base, initrd_size);

			unsigned long start, end;
			start = cpu_to_be32((unsigned long)(initrd_base));
			end = cpu_to_be32((unsigned long)(initrd_base + initrd_size));

			if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen",
					"linux,initrd-start", &start,
					sizeof(start)))
				return -1;
			if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen",
					"linux,initrd-end", &end,
					sizeof(end)))
				return -1;
		}

		/* Stick the dtb at the end of the initrd and page
		 * align it.
		 */
		dtb_offset = initrd_base + initrd_size + getpagesize();
		dtb_offset = _ALIGN_DOWN(dtb_offset, getpagesize());

		add_segment(info, dtb_buf, dtb_length,
		            dtb_offset, dtb_length);
	}

	printf("Kernel segment info: %lx (%d)\n", base+offset, len);
	add_segment(info, buf, len, base + offset, len);

	info->entry = (void*)base + offset;

	return 0;
}