summaryrefslogtreecommitdiff
path: root/src/arch/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/riscv')
-rw-r--r--src/arch/riscv/Makefile.inc1
-rw-r--r--src/arch/riscv/fit_payload.c141
2 files changed, 142 insertions, 0 deletions
diff --git a/src/arch/riscv/Makefile.inc b/src/arch/riscv/Makefile.inc
index 01168593f1..0039fab180 100644
--- a/src/arch/riscv/Makefile.inc
+++ b/src/arch/riscv/Makefile.inc
@@ -147,6 +147,7 @@ ramstage-y += boot.c
ramstage-y += tables.c
ramstage-y += payload.c
ramstage-$(ARCH_RISCV_PMP) += pmp.c
+ramstage-y += fit_payload.c
ramstage-y += \
$(top)/src/lib/memchr.c \
$(top)/src/lib/memcmp.c \
diff --git a/src/arch/riscv/fit_payload.c b/src/arch/riscv/fit_payload.c
new file mode 100644
index 0000000000..60a4bc0557
--- /dev/null
+++ b/src/arch/riscv/fit_payload.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyright 2018 Facebook, Inc.
+ * Copyright 2019 9elements Agency GmbH <patrick.rudolph@9elements.com>
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <console/console.h>
+#include <bootmem.h>
+#include <stdlib.h>
+#include <program_loading.h>
+#include <string.h>
+#include <commonlib/compression.h>
+#include <commonlib/cbfs_serialized.h>
+#include <lib.h>
+#include <fit.h>
+#include <endian.h>
+
+/* Implements a Berkley Boot Loader (BBL) compatible payload loading */
+
+#define MAX_KERNEL_SIZE (64*MiB)
+
+#if CONFIG(ARCH_RISCV_RV32)
+#define SECTION_ALIGN (4 * MiB)
+#endif
+#if CONFIG(ARCH_RISCV_RV64)
+#define SECTION_ALIGN (2 * MiB)
+#endif
+
+static size_t get_kernel_size(const struct fit_image_node *node)
+{
+ /*
+ * Since we don't have a way to determine the uncompressed size of the
+ * kernel, we have to keep as much memory as possible free for use by
+ * the kernel immediately after the end of the kernel image. The amount
+ * of space required will vary depending on selected features, and is
+ * effectively unbound.
+ */
+
+ printk(BIOS_INFO,
+ "FIT: Leaving additional %u MiB of free space after kernel.\n",
+ MAX_KERNEL_SIZE >> 20);
+
+ return node->size + MAX_KERNEL_SIZE;
+}
+
+/**
+ * Place the region in free memory range.
+ *
+ * The caller has to set region->offset to the minimum allowed address.
+ */
+static bool fit_place_mem(const struct range_entry *r, void *arg)
+{
+ struct region *region = arg;
+ resource_t start;
+
+ if (range_entry_tag(r) != BM_MEM_RAM)
+ return true;
+
+ /* Section must be aligned at page boundary */
+ start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), SECTION_ALIGN);
+
+ if (start + region->size < range_entry_end(r)) {
+ region->offset = (size_t)start;
+ return false;
+ }
+
+ return true;
+}
+
+bool fit_payload_arch(struct prog *payload, struct fit_config_node *config,
+ struct region *kernel,
+ struct region *fdt,
+ struct region *initrd)
+{
+ void *arg = NULL;
+
+ if (!config->fdt || !fdt) {
+ printk(BIOS_CRIT, "CRIT: Providing a valid FDT is mandatory to "
+ "boot a RISC-V kernel!\n");
+ return false;
+ /* TODO: Fall back to the ROM FDT? */
+ }
+
+ /* Update kernel size from image header, if possible */
+ kernel->size = get_kernel_size(config->kernel);
+ printk(BIOS_DEBUG, "FIT: Using kernel size of 0x%zx bytes\n",
+ kernel->size);
+
+ /*
+ * The code assumes that bootmem_walk provides a sorted list of memory
+ * regions, starting from the lowest address.
+ * The order of the calls here doesn't matter, as the placement is
+ * enforced in the called functions.
+ * For details check code on top.
+ */
+ kernel->offset = 0;
+ if (!bootmem_walk(fit_place_mem, kernel))
+ return false;
+
+ /* Mark as reserved for future allocations. */
+ bootmem_add_range(kernel->offset, kernel->size, BM_MEM_PAYLOAD);
+
+ /* Place FDT and INITRD after kernel. */
+
+ /* Place INITRD */
+ if (config->ramdisk) {
+ initrd->offset = kernel->offset + kernel->size;
+
+ if (!bootmem_walk(fit_place_mem, initrd))
+ return false;
+ /* Mark as reserved for future allocations. */
+ bootmem_add_range(initrd->offset, initrd->size, BM_MEM_PAYLOAD);
+ }
+
+ /* Place FDT */
+ fdt->offset = kernel->offset + kernel->size;
+
+ if (!bootmem_walk(fit_place_mem, fdt))
+ return false;
+ /* Mark as reserved for future allocations. */
+ bootmem_add_range(fdt->offset, fdt->size, BM_MEM_PAYLOAD);
+
+ /* Kernel expects FDT as argument */
+ arg = (void *)fdt->offset;
+
+ prog_set_entry(payload, (void *)kernel->offset, arg);
+
+ bootmem_dump_ranges();
+
+ return true;
+}