[coreboot-gerrit] Change in coreboot[master]: util/cbfstool: Add ARM64 Image support

Patrick Rudolph (Code Review) gerrit at coreboot.org
Mon Feb 26 12:15:46 CET 2018


Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/23869


Change subject: util/cbfstool: Add ARM64 Image support
......................................................................

util/cbfstool: Add ARM64 Image support

Add support for booting Linux ARM64 Image.

* Add new ARM64 trampoline code
* Add ARM64 Image detection
* Parse the Image and mark it relocateable if no loadaddress was given
* Place the trampoline 64 KiB before the Linux Image

Tested on qemu-armv8 and Linux 4.15.

Change-Id: I9a8183a87de4dc3a4c4f038fe5b4912ada5d75f9
---
M payloads/external/linux/Makefile.inc
A payloads/external/linux/arm64image_loader.h
A payloads/external/linux/linux_trampoline_arm64.S
A payloads/external/linux/linux_trampoline_arm64.c
M util/cbfstool/Makefile.inc
M util/cbfstool/cbfs-payload-linux.c
M util/cbfstool/cbfstool.c
M util/cbfstool/common.h
8 files changed, 358 insertions(+), 7 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/69/23869/1

diff --git a/payloads/external/linux/Makefile.inc b/payloads/external/linux/Makefile.inc
index c0a2b0c..e3c5924 100644
--- a/payloads/external/linux/Makefile.inc
+++ b/payloads/external/linux/Makefile.inc
@@ -4,6 +4,7 @@
 ##
 ## Copyright (C) 2009-2010 coresystems GmbH
 ## Copyright (C) 2015 Google Inc.
+## Copyright 2018-present Facebook, Inc.
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
@@ -20,6 +21,14 @@
 
 linuxdir:=$(top)/payloads/external/linux
 
+ifneq ($(GCC_CC_arm64),)
+$(linuxdir)/linux_trampoline_arm64.c: $(linuxdir)/linux_trampoline_arm64.S $(top)/src/arch/arm64/include/*
+	$(MAKE) -C $(linuxdir) linux_trampoline_arm64.c \
+		HOSTCC="$(HOSTCC)" \
+		CC="$(GCC_CC_arm64)" \
+		OBJCOPY="$(OBJCOPY_arm64)" \
+		CFLAGS="$(CFLAGS_arm64) $(armv8_flags) -I$(top)/src/arch/arm64/include"
+endif
 ifneq ($(GCC_CC_x86_32),)
 $(linuxdir)/linux_trampoline_x86.c: $(linuxdir)/linux_trampoline_x86.S $(top)/src/arch/x86/include/*
 	$(MAKE) -C $(linuxdir) linux_trampoline_x86.c \
diff --git a/payloads/external/linux/arm64image_loader.h b/payloads/external/linux/arm64image_loader.h
new file mode 100644
index 0000000..fdc5dee
--- /dev/null
+++ b/payloads/external/linux/arm64image_loader.h
@@ -0,0 +1,20 @@
+/*
+ * This file is part of coreboot project.
+ *
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* trampoline */
+extern unsigned char linux_trampoline_arm64_bin[];
+extern unsigned int linux_trampoline_arm64_bin_len;
+
+#define TRAMPOLINE_SIZE (64 * KiB)
diff --git a/payloads/external/linux/linux_trampoline_arm64.S b/payloads/external/linux/linux_trampoline_arm64.S
new file mode 100644
index 0000000..c28f3a9
--- /dev/null
+++ b/payloads/external/linux/linux_trampoline_arm64.S
@@ -0,0 +1,120 @@
+/*
+ * linux_trampoline
+ *
+ * Copyright (C) 2013 Patrick Georgi <patrick at georgi-clan.de>
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* NOTE: THIS CODE MUST REMAIN POSITION INDEPENDENT
+ *       IT SHOULDN'T USE THE STACK
+ *       AND IN GENERAL EXPECT NOTHING BUT RAM TO WORK
+ */
+
+.data
+#define TRAMPOLINE_SIZE (64 * 1024)
+#define HEADER_SIG 0x4f49424c // LBIO little endian
+#define CB_TAG_FORWARD 0x11
+#define CB_TAG_DEVICETREE 0x0033
+
+/*
+ * Find the devicetree that should have been placed in a coreboot table.
+ * Register usage:
+ * x0:    base pointer
+ * x1:    working register
+ * x2:    working register
+ * x3:    counter, initial value is the number of coreboot tables
+ * x4:    start of trampoline, to calculcate the kernel position
+ */
+trampoline_start:
+   // Store entry point for later use
+   adr    x4, trampoline_start
+   // x0 contains pointer to coreboot tables
+
+   // Verify coreboot table header
+
+   mov    w2, HEADER_SIG >> 16
+   lsl    w2, w2, 16
+   mov    w1, (HEADER_SIG & 0xffff)
+   orr    w2, w2, w1
+
+   // verify signature32 (header magic)
+   ldr    w1, [x0, 0]
+   cmp    w1, w2
+   b.ne   _halt
+
+   // verify header_bytes
+   ldr    w1, [x0, 4]
+   cmp    w1, #0
+   b.eq   _halt
+
+   // verify table_bytes
+   ldr    w1, [x0, 16]
+   cmp    w1, #0
+   b.eq   _halt
+
+   // TODO: Signature check
+
+   // Number of table entries goes into w3
+   ldr    w3, [x0, 20]
+
+   // Set pointer to first entry
+   ldr    w1, [x0, 4]
+   add    x0, x1, x0
+
+   // x0 contains the table entry pointer
+
+tablescan:
+   ldr    w1, [x0, 0]
+   cmp    w1, #CB_TAG_FORWARD
+   b.ne   devicetree
+
+   ldr    w1, [x0, 8]
+   add    x0, x0, x1
+   b      next
+
+devicetree:
+   ldr    w1, [x0, 0]
+   cmp    w1, #CB_TAG_DEVICETREE
+   b.ne   next
+
+   mov    x1, #8
+   add    x0, x0, x1
+   b      done
+
+next:
+   // Increment table entries pointer
+   ldr    w1, [x0, 4]
+   add    x0, x0, x1
+
+   // Decrement table entry counter
+   subs    w3, w3, #1
+
+   b.ne   tablescan
+
+   // not found, call the kernel without devicetree ?
+   b _halt
+done:
+   // devicetree pointer is in x0
+
+   // kernel entry point is trampoline_start + 64 KiB
+   // CBFSTOOL did make sure that the kernel is at the given offset
+   mov    x1, #(TRAMPOLINE_SIZE)
+   add    x1, x1, x4
+
+   // Jump to kernel
+   br     x1
+
+_halt:
+   wfi
+   b _halt
+
+trampoline_end:
diff --git a/payloads/external/linux/linux_trampoline_arm64.c b/payloads/external/linux/linux_trampoline_arm64.c
new file mode 100644
index 0000000..b4bc2c6
--- /dev/null
+++ b/payloads/external/linux/linux_trampoline_arm64.c
@@ -0,0 +1,14 @@
+/* This file is automatically generated. Do not manually change */
+unsigned char linux_trampoline_arm64_bin[] = {
+  0x04, 0x00, 0x00, 0x10, 0x22, 0xe9, 0x89, 0x52, 0x42, 0x3c, 0x10, 0x53, 0x81, 0x49, 0x88, 0x52,
+  0x42, 0x00, 0x01, 0x2a, 0x01, 0x00, 0x40, 0xb9, 0x3f, 0x00, 0x02, 0x6b, 0xc1, 0x03, 0x00, 0x54,
+  0x01, 0x04, 0x40, 0xb9, 0x3f, 0x00, 0x00, 0x71, 0x60, 0x03, 0x00, 0x54, 0x01, 0x10, 0x40, 0xb9,
+  0x3f, 0x00, 0x00, 0x71, 0x00, 0x03, 0x00, 0x54, 0x03, 0x14, 0x40, 0xb9, 0x01, 0x04, 0x40, 0xb9,
+  0x20, 0x00, 0x00, 0x8b, 0x01, 0x00, 0x40, 0xb9, 0x3f, 0x44, 0x00, 0x71, 0x81, 0x00, 0x00, 0x54,
+  0x01, 0x08, 0x40, 0xb9, 0x00, 0x00, 0x01, 0x8b, 0x07, 0x00, 0x00, 0x14, 0x01, 0x00, 0x40, 0xb9,
+  0x3f, 0xcc, 0x00, 0x71, 0x81, 0x00, 0x00, 0x54, 0x01, 0x01, 0x80, 0xd2, 0x00, 0x00, 0x01, 0x8b,
+  0x06, 0x00, 0x00, 0x14, 0x01, 0x04, 0x40, 0xb9, 0x00, 0x00, 0x01, 0x8b, 0x63, 0x04, 0x00, 0x71,
+  0x21, 0xfe, 0xff, 0x54, 0x04, 0x00, 0x00, 0x14, 0x21, 0x00, 0xa0, 0xd2, 0x21, 0x00, 0x04, 0x8b,
+  0x20, 0x00, 0x1f, 0xd6, 0x7f, 0x20, 0x03, 0xd5, 0xff, 0xff, 0xff, 0x17
+};
+unsigned int linux_trampoline_arm64_bin_len = 156;
diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc
index 0575e1d..c1626c4 100644
--- a/util/cbfstool/Makefile.inc
+++ b/util/cbfstool/Makefile.inc
@@ -39,6 +39,7 @@
 cbfsobj += valstr.o
 # linux as payload
 cbfsobj += linux_trampoline_x86.o
+cbfsobj += linux_trampoline_arm64.o
 cbfsobj += cbfs-payload-linux.o
 
 # compression algorithms
diff --git a/util/cbfstool/cbfs-payload-linux.c b/util/cbfstool/cbfs-payload-linux.c
index 17e18ca..7e73db0 100644
--- a/util/cbfstool/cbfs-payload-linux.c
+++ b/util/cbfstool/cbfs-payload-linux.c
@@ -2,6 +2,7 @@
  * cbfs-payload-linux
  *
  * Copyright (C) 2013 Patrick Georgi <patrick at georgi-clan.de>
+ * Copyright     2018-present Facebook, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,18 +21,20 @@
 #include "common.h"
 #include "cbfs.h"
 #include "bzimage_loader.h"
+#include "arm64image_loader.h"
 
 /*
  * Current max number of segments include:
  *
  * 1. parameters
  * 2. kernel
- * 3. trampoline
- * 4. optional cmdline
- * 5. optional initrd
- * 6. terminating entry segment
+ * 3. flags
+ * 4. trampoline
+ * 5. optional cmdline
+ * 6. optional initrd
+ * 7. terminating entry segment
  */
-#define MAX_NUM_SEGMENTS 6
+#define MAX_NUM_SEGMENTS 7
 
 struct bzpayload {
 	/* Input variables. */
@@ -39,6 +42,7 @@
 	struct cbfs_payload_segment segs[MAX_NUM_SEGMENTS];
 	struct buffer parameters;
 	struct buffer kernel;
+	struct buffer flags;
 	struct buffer trampoline;
 	struct buffer cmdline;
 	struct buffer initrd;
@@ -93,6 +97,31 @@
 	       (header->header == BZI_SIGNATURE);
 }
 
+#define ARM64_IMAGE_KERNEL_HEADER_MAGIC  0x644d5241
+
+struct Arm64KernelHeader {
+	u32 code0;
+	u32 code1;
+	u64 text_offset;
+	u64 image_size;
+	u64 flags;
+	u64 res2;
+	u64 res3;
+	u64 res4;
+	u32 magic;
+	u32 res5;
+};
+
+/* Return true if buffer looks like an ARM64 Image */
+int probe_for_ARM64Image_header(const struct buffer *input)
+{
+	const struct Arm64KernelHeader *header =
+	    (const struct Arm64KernelHeader *)input->data;
+	if (input->size < sizeof(*header))
+		return 0;
+	return (header->magic == ARM64_IMAGE_KERNEL_HEADER_MAGIC);
+}
+
 static int bzp_init(struct bzpayload *bzp, enum comp_algo algo)
 {
 	memset(bzp, 0, sizeof(*bzp));
@@ -169,12 +198,23 @@
 	return 0;
 }
 
+static int bzp_add_flags(struct bzpayload *bzp,
+			 struct cbfs_payload_flags *flags)
+{
+	bzp_add_segment(bzp, &bzp->flags, flags, 0);
+	xdr_be.put64(&bzp->flags, flags->f.u);
+	xdr_be.put32(&bzp->flags, flags->align);
+
+	return 0;
+}
+
 static int bzp_init_output(struct bzpayload *bzp, const char *name)
 {
 	size_t sz = 0;
 
 	sz += buffer_size(&bzp->parameters);
 	sz += buffer_size(&bzp->kernel);
+	sz += buffer_size(&bzp->flags);
 	sz += buffer_size(&bzp->trampoline);
 	sz += buffer_size(&bzp->cmdline);
 	sz += buffer_size(&bzp->initrd);
@@ -371,3 +411,137 @@
 	xdr_segs(output, bzp.segs, bzp.num_segments);
 	return 0;
 }
+
+/*
+ * Add an ARM64 Linux Kernel Image as payload.
+ *
+ * The trampoline will locate the devicetree and pass it as argument x0.
+ *
+ *  Memory map
+ *   ...
+ * |                               |
+ * | Usable DRAM                   |
+ * |                               |
+ * --------------------------------| n * 2MiB + text-offset + image-size
+ * |                               |
+ * | Linux Kernel                  |
+ * |                               |
+ * --------------------------------| n * 2MiB + text-offset
+ * |                               |
+ * | Trampoline                    |
+ * |                               |
+ * --------------------------------| n * 2MiB + text-offset - 64 KiB (entry)
+ * |                               |
+ * | Usable DRAM                   |
+ * |                               |
+ * --------------------------------|
+ * | Devicetree                    |
+ * ------------------------------- | >= MAX((n - 256) * 2MiB, 0)
+ * |                               |
+ *    ...
+ */
+
+int parse_ARM64Image_to_payload(const struct buffer *input,
+				struct buffer *output, uint64_t load_address,
+				enum comp_algo algo)
+{
+	const struct Arm64KernelHeader *header =
+	    (const struct Arm64KernelHeader *)input->data;
+	struct buffer tmp;
+	struct bzpayload bzp;
+	uint64_t offset, image_size;
+	struct cbfs_payload_flags flags = {0};
+
+	if (bzp_init(&bzp, algo) != 0)
+		return -1;
+
+	if (linux_trampoline_arm64_bin_len > TRAMPOLINE_SIZE) {
+		ERROR("FIXME: trampoline size is too big\n");
+		return -1;
+	}
+
+	/*
+	 * Prior to v3.17, the endianness of text_offset was not specified. In
+	 * these cases image_size is zero and text_offset is 0x80000 in the
+	 * endianness of the kernel.  Where image_size is non-zero image_size is
+	 * little-endian and must be respected.  Where image_size is zero,
+	 * text_offset can be assumed to be 0x80000.
+	 */
+	if (header->image_size) {
+		offset = header->text_offset;
+		image_size = header->image_size;
+	} else {
+		offset = 0x80000;
+		// FIXME: How much space to reserve ?
+		image_size = input->size + 4 * MiB;
+	}
+	/*
+	* The Image must be placed text_offset bytes from a 2MB aligned base
+	* address anywhere in usable system RAM and called there. The region
+	* between the 2 MB aligned base address and the start of the image has
+	* no special significance to the kernel, and may be used for other
+	* purposes. At least image_size bytes from the start of the image must
+	* be free for use by the kernel.
+	*/
+	load_address = ALIGN_UP(load_address, 2 * MiB);
+
+	/* Reserve some extra space if the trampoline doesn't fit */
+	if (offset < TRAMPOLINE_SIZE)
+		offset += 2 * MiB;
+
+	if (buffer_create(&tmp, image_size + offset, input->name)) {
+		ERROR("Failed to allocate a buffer\n");
+		return -1;
+	}
+
+	/* Clear trampoline memory */
+	memset(tmp.data, 0, tmp.size);
+
+	/* Place trampoline right before kernel */
+	memcpy(tmp.data + offset - TRAMPOLINE_SIZE, linux_trampoline_arm64_bin,
+	       linux_trampoline_arm64_bin_len);
+
+	/* Place kernel at offset, right after trampoline */
+	memcpy(tmp.data + offset, input->data, input->size);
+
+	if (bzp_add_kernel(&bzp, &tmp, 0) != 0)
+		return -1;
+
+	/* Dynamic load address, tell the align */
+	if (!load_address) {
+		flags.f.relocateable = 1;
+		flags.align = 2 * MiB;
+
+		if (bzp_add_flags(&bzp, &flags))
+			return -1;
+	}
+
+	if (bzp_init_output(&bzp, input->name) != 0)
+		return -1;
+
+	if (!load_address) {
+		/* data block */
+		bzp_output_segment(&bzp, &bzp.flags, PAYLOAD_SEGMENT_FLAGS, 0);
+		ERROR("Adding flags\n");
+	}
+
+	/* code block */
+	bzp_output_segment(&bzp, &bzp.kernel, PAYLOAD_SEGMENT_CODE,
+			   load_address);
+
+	/* Terminating entry segment. */
+	bzp_output_segment(&bzp, NULL, PAYLOAD_SEGMENT_ENTRY,
+			   load_address + offset - TRAMPOLINE_SIZE);
+
+	/* Set size of buffer taking into account potential compression. */
+	buffer_set_size(&bzp.output, bzp.offset);
+	/* Make passed-in output buffer be valid. */
+	buffer_clone(output, &bzp.output);
+
+	/* Serialize the segments with the correct encoding. */
+	xdr_segs(output, bzp.segs, bzp.num_segments);
+
+	buffer_delete(&tmp);
+
+	return 0;
+}
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index 91b0290..899e32a 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -5,6 +5,7 @@
  *                 written by Patrick Georgi <patrick.georgi at coresystems.de>
  * Copyright (C) 2012 Google, Inc.
  * Copyright (C) 2016 Siemens AG
+ * Copyright     2018-present Facebook, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -640,6 +641,11 @@
 		ret = parse_bzImage_to_payload(buffer, &output,
 				param.initrd, param.cmdline, param.compression);
 
+	/* Try ARM64 Image */
+	if (ret != 0 && probe_for_ARM64Image_header(buffer))
+		ret = parse_ARM64Image_to_payload(buffer, &output,
+				param.loadaddress, param.compression);
+
 	/* Not a supported payload type */
 	if (ret != 0) {
 		ERROR("Not a supported payload type (ELF / FV).\n");
@@ -1169,7 +1175,7 @@
 	{"add", "H:r:f:n:t:c:b:a:p:yvA:gh?", cbfs_add, true, true},
 	{"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
 				true, true},
-	{"add-payload", "H:r:f:n:t:c:b:C:I:p:vA:gh?", cbfs_add_payload,
+	{"add-payload", "H:r:f:n:t:c:b:C:I:p:l:vA:gh?", cbfs_add_payload,
 				true, true},
 	{"add-stage", "a:H:r:f:n:t:c:b:P:S:p:yvA:gh?", cbfs_add_stage,
 				true, true},
@@ -1299,7 +1305,8 @@
 			"Add a component\n"
 	     " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
 	     "        [-c compression] [-b base-address] \\\n"
-	     "        (linux specific: [-C cmdline] [-I initrd])           "
+	     "        (linux specific: [-C cmdline] [-I initrd]         \\\n"
+	     "         [-l loadaddress])                                   "
 			"Add a payload to the ROM\n"
 	     " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
 	     "        [-c compression] [-b base] [-S section-to-ignore] \\\n"
diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h
index 6e366a1..4361191 100644
--- a/util/cbfstool/common.h
+++ b/util/cbfstool/common.h
@@ -193,6 +193,12 @@
 int parse_bzImage_to_payload(const struct buffer *input,
 			     struct buffer *output, const char *initrd,
 			     char *cmdline, enum comp_algo algo);
+
+int probe_for_ARM64Image_header(const struct buffer *input);
+int parse_ARM64Image_to_payload(const struct buffer *input,
+				struct buffer *output, uint64_t load_address,
+				enum comp_algo algo);
+
 int parse_flat_binary_to_payload(const struct buffer *input,
 				 struct buffer *output,
 				 uint32_t loadaddress,

-- 
To view, visit https://review.coreboot.org/23869
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9a8183a87de4dc3a4c4f038fe5b4912ada5d75f9
Gerrit-Change-Number: 23869
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <patrick.rudolph at 9elements.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180226/73733a19/attachment-0001.html>


More information about the coreboot-gerrit mailing list