[PATCH 5/5] powerpc32: add support for uImage

Sebastian Andrzej Siewior sebastian at breakpoint.cc
Tue Jan 19 17:59:45 EST 2010


From: Sebastian Andrzej Siewior <bigeasy at linutronix.de>

On PowerPC the uImage usually contains the compressed "final" kernel and
not a tiny wrapper which relocates itself und uncomprosses the final
kernel to its final position. Instead we uncompress the gzip image and
put it the its final position.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
---
 kexec/arch/ppc/Makefile           |    1 +
 kexec/arch/ppc/kexec-ppc.c        |    1 +
 kexec/arch/ppc/kexec-ppc.h        |    5 +
 kexec/arch/ppc/kexec-uImage-ppc.c |  336 +++++++++++++++++++++++++++++++++++++
 4 files changed, 343 insertions(+), 0 deletions(-)
 create mode 100644 kexec/arch/ppc/kexec-uImage-ppc.c

diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile
index 90dfa5b..a6f9a04 100644
--- a/kexec/arch/ppc/Makefile
+++ b/kexec/arch/ppc/Makefile
@@ -7,6 +7,7 @@ ppc_KEXEC_SRCS =  kexec/arch/ppc/kexec-ppc.c
 ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-ppc.c
 ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-rel-ppc.c
 ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-dol-ppc.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-uImage-ppc.c
 ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S
 ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S
 
diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
index ef22c3d..9708b8c 100644
--- a/kexec/arch/ppc/kexec-ppc.c
+++ b/kexec/arch/ppc/kexec-ppc.c
@@ -485,6 +485,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
 struct file_type file_type[] = {
 	{"elf-ppc", elf_ppc_probe, elf_ppc_load, elf_ppc_usage},
 	{"dol-ppc", dol_ppc_probe, dol_ppc_load, dol_ppc_usage},
+	{"uImage-ppc", uImage_ppc_probe, uImage_ppc_load, uImage_ppc_usage },
 };
 int file_types = sizeof(file_type) / sizeof(file_type[0]);
 
diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
index 1b2b015..6cec467 100644
--- a/kexec/arch/ppc/kexec-ppc.h
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -21,6 +21,11 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info);
 void elf_ppc_usage(void);
 
+int uImage_ppc_probe(const char *buf, off_t len);
+int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len,
+	struct kexec_info *info);
+void uImage_ppc_usage(void);
+
 int dol_ppc_probe(const char *buf, off_t len);
 int dol_ppc_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info);
diff --git a/kexec/arch/ppc/kexec-uImage-ppc.c b/kexec/arch/ppc/kexec-uImage-ppc.c
new file mode 100644
index 0000000..8232d0e
--- /dev/null
+++ b/kexec/arch/ppc/kexec-uImage-ppc.c
@@ -0,0 +1,336 @@
+/*
+ * uImage support for PowerPC
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <zlib.h>
+#include <image.h>
+#include <getopt.h>
+#include <arch/options.h>
+#include "../../kexec.h"
+#include "kexec-ppc.h"
+#include "fixup_dtb.h"
+
+#define OPT_APPEND      (OPT_ARCH_MAX+0)
+#define OPT_DTB         (OPT_ARCH_MAX+1)
+#define OPT_NODES       (OPT_ARCH_MAX+2)
+static const struct option options[] = {
+	KEXEC_ARCH_OPTIONS
+	{"command-line",	1, 0, OPT_APPEND},
+	{"append",	1, 0, OPT_APPEND},
+	{"dtb",		1, 0, OPT_DTB},
+	{"reuse-node",	1, 0, OPT_NODES},
+	{0, 0, 0, 0},
+};
+static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+void uImage_ppc_usage(void)
+{
+	printf(
+			"    --command-line=STRING Set the kernel command line to STRING.\n"
+			"    --append=STRING       Set the kernel command line to STRING.\n"
+			"     --dtb=<filename>     Specify device tree blob file.\n"
+			"     --reuse-node=node    Specify nodes which should be taken from /proc/device-tree.\n"
+			"                          Can be set multiple times.\n"
+	);
+}
+
+int uImage_ppc_probe(const char *buf, off_t len)
+{
+	struct image_header header;
+	unsigned int crc;
+	unsigned int hcrc;
+
+	if (len < sizeof(header))
+		return -1;
+
+	memcpy(&header, buf, sizeof(header));
+
+	if (cpu_to_be32(header.ih_magic) != IH_MAGIC)
+		return -1;
+
+	hcrc = be32_to_cpu(header.ih_hcrc);
+	header.ih_hcrc = 0;
+	crc = crc32(0, (void *)&header, sizeof(header));
+	if (crc != hcrc) {
+		printf("Header checksum of the uImage does not match\n");
+		return -1;
+	}
+
+	if (header.ih_type != IH_TYPE_KERNEL) {
+		printf("uImage type %d unsupported\n", header.ih_type);
+		return -1;
+	}
+
+	if (header.ih_os != IH_OS_LINUX) {
+		printf("uImage os %d unsupported\n", header.ih_os);
+		return -1;
+	}
+
+	if (header.ih_arch != IH_ARCH_PPC) {
+		printf("uImage arch %d unsupported\n", header.ih_arch);
+		return -1;
+	}
+
+	switch (header.ih_comp) {
+	case IH_COMP_NONE:
+	case IH_COMP_GZIP:
+		break;
+
+	case IH_COMP_BZIP2:
+	case IH_COMP_LZMA:
+	case IH_COMP_LZO:
+	default:
+		printf("uImage uses unsupported compression method\n");
+		return -1;
+	}
+
+	crc = crc32(0, (void *)buf + sizeof(header), len - sizeof(header));
+	if (crc != be32_to_cpu(header.ih_dcrc)) {
+		printf("The data CRC does not match. Computed: %08x expected %08x\n",
+			crc, be32_to_cpu(header.ih_dcrc));
+		return -1;
+	}
+	return 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;
+}
+
+/* gzip flag byte */
+#define ASCII_FLAG	0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC	0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD	0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME	0x08 /* bit 3 set: original file name present */
+#define COMMENT		0x10 /* bit 4 set: file comment present */
+#define RESERVED	0xE0 /* bits 5..7: reserved */
+
+static int uImage_gz_load(int argc, char **argv, const char *buf, off_t len,
+		struct kexec_info *info, unsigned int load_addr,
+		unsigned int ep)
+{
+	int ret;
+	z_stream strm;
+	unsigned int skip;
+	unsigned int flags;
+	unsigned char *uncomp_buf;
+	unsigned int mem_alloc;
+
+	mem_alloc = 10 * 1024 * 1024;
+	uncomp_buf = malloc(mem_alloc);
+	if (!uncomp_buf)
+		return -1;
+
+	memset(&strm, 0, sizeof(strm));
+
+	/* Skip magic, method, time, flags, os code ... */
+	skip = 10;
+
+	/* check GZ magic */
+	if (buf[0] != 0x1f || buf[1] != 0x8b)
+		return -1;
+
+	flags = buf[3];
+	if (buf[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
+		puts ("Error: Bad gzipped data\n");
+		return -1;
+	}
+
+	if (flags & EXTRA_FIELD) {
+		skip += 2;
+		skip += buf[10];
+		skip += buf[11] << 8;
+	}
+	if (flags & ORIG_NAME) {
+		while (buf[skip++])
+			;
+	}
+	if (flags & COMMENT) {
+		while (buf[skip++])
+			;
+	}
+	if (flags & HEAD_CRC)
+		skip += 2;
+
+	strm.avail_in = len - skip;
+	strm.next_in = (void *)buf + skip;
+
+	/* - activates parsing gz headers */
+	ret = inflateInit2(&strm, -MAX_WBITS);
+	if (ret != Z_OK)
+		return -1;
+
+	strm.next_out = uncomp_buf;
+	strm.avail_out = mem_alloc;
+
+	do {
+		ret = inflate(&strm, Z_FINISH);
+		if (ret == Z_STREAM_END)
+			break;
+
+		if (ret == Z_OK || ret == Z_BUF_ERROR) {
+			void *new_buf;
+			int inc_buf = 5 * 1024 * 1024;
+
+			mem_alloc += inc_buf;
+			new_buf = realloc(uncomp_buf, mem_alloc);
+			if (!new_buf) {
+				inflateEnd(&strm);
+				free(uncomp_buf);
+				return -1;
+			}
+
+			strm.next_out = uncomp_buf + mem_alloc - inc_buf;
+			strm.avail_out = inc_buf;
+			uncomp_buf = new_buf;
+		} else {
+			printf("Error during decompression %d\n", ret);
+			return -1;
+		}
+	} while (1);
+
+	inflateEnd(&strm);
+
+	ret = ppc_load_bare_bits(argc, argv, (char *)uncomp_buf,
+			mem_alloc - strm.avail_out, info,
+			load_addr, ep);
+
+	/* leak uncomp_buf since the buffer has to remain past this function */
+	return ret;
+}
+
+int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len,
+		struct kexec_info *info)
+{
+	const struct image_header *header = (const struct image_header *)buf;
+	const char *img_buf = buf + sizeof(struct image_header);
+	off_t img_len = len - sizeof(struct image_header);
+	unsigned int img_base = cpu_to_be32(header->ih_load);
+	unsigned int img_entry = cpu_to_be32(header->ih_ep);
+
+	switch (header->ih_comp) {
+	case IH_COMP_NONE:
+		return ppc_load_bare_bits(argc, argv, img_buf, img_len, info,
+				img_base, img_entry);
+		break;
+
+	case IH_COMP_GZIP:
+		return uImage_gz_load(argc, argv, img_buf, img_len, info,
+				img_base, img_entry);
+		break;
+
+	case IH_COMP_BZIP2:
+	case IH_COMP_LZMA:
+	case IH_COMP_LZO:
+	default:
+	printf("%s(%d)\n", __func__, __LINE__);
+		return -1;
+	}
+}
-- 
1.6.5.2




More information about the kexec mailing list