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@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@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@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,