summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/lib/payloads/fit.md8
-rw-r--r--payloads/Kconfig4
-rw-r--r--src/arch/arm/Makefile.inc1
-rw-r--r--src/arch/arm/fit_payload.c90
4 files changed, 101 insertions, 2 deletions
diff --git a/Documentation/lib/payloads/fit.md b/Documentation/lib/payloads/fit.md
index c6ccc7b868..ef5e892c36 100644
--- a/Documentation/lib/payloads/fit.md
+++ b/Documentation/lib/payloads/fit.md
@@ -5,6 +5,7 @@
## Supported architectures
+* aarch32
* aarch64
* riscv
@@ -26,6 +27,13 @@ The section must be named in order to be found by the FIT parser:
The FIT parser needs architecure support.
+### aarch32
+The source code can be found in `src/arch/arm/fit_payload.c`.
+
+On aarch32 the kernel (a section named 'kernel') must be in **Image**
+format and it needs a devicetree (a section named 'fdt') to boot.
+The kernel will be placed close to "*DRAMSTART*".
+
### aarch64
The source code can be found in `src/arch/arm64/fit_payload.c`.
diff --git a/payloads/Kconfig b/payloads/Kconfig
index cfb28d6e81..627bb95c9c 100644
--- a/payloads/Kconfig
+++ b/payloads/Kconfig
@@ -30,7 +30,7 @@ config PAYLOAD_ELF
config PAYLOAD_FIT
bool "A FIT payload"
- depends on ARCH_ARM64 || ARCH_RISCV
+ depends on ARCH_ARM64 || ARCH_RISCV || ARCH_ARM
select PAYLOAD_FIT_SUPPORT
help
Select this option if you have a payload image (a FIT file) which
@@ -97,7 +97,7 @@ config PAYLOAD_FIT_SUPPORT
bool "FIT support"
default n
default y if PAYLOAD_LINUX && (ARCH_ARM || ARCH_ARM64 || ARCH_RISCV)
- depends on ARCH_ARM64 || ARCH_RISCV
+ depends on ARCH_ARM64 || ARCH_RISCV || ARCH_ARM
select FLATTENED_DEVICE_TREE
help
Select this option if your payload is of type FIT.
diff --git a/src/arch/arm/Makefile.inc b/src/arch/arm/Makefile.inc
index eef2650685..241bfe5051 100644
--- a/src/arch/arm/Makefile.inc
+++ b/src/arch/arm/Makefile.inc
@@ -119,6 +119,7 @@ ramstage-y += memset.S
ramstage-y += memcpy.S
ramstage-y += memmove.S
ramstage-y += clock.c
+ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) += fit_payload.c
rmodules_arm-y += memset.S
rmodules_arm-y += memcpy.S
diff --git a/src/arch/arm/fit_payload.c b/src/arch/arm/fit_payload.c
new file mode 100644
index 0000000000..f5470071d4
--- /dev/null
+++ b/src/arch/arm/fit_payload.c
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <console/console.h>
+#include <bootmem.h>
+#include <program_loading.h>
+#include <fit.h>
+#include <symbols.h>
+
+/**
+ * Place the region in free memory range.
+ */
+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;
+
+ /* Linux 4.15 doesn't like 4KiB alignment. Align to 1 MiB for now. */
+ start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), 1 * MiB);
+
+ 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;
+
+ /**
+ * The kernel ARM documentation recommends loading the kernel above 32MiB
+ * in order to avoid the need to need to relocate prior to decompression.
+ */
+ kernel->offset = (uintptr_t)_dram + 32 * MiB;
+
+ /**
+ * 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.
+ */
+ 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);
+
+ /**
+ * To ensure the fdt is not overwritten by the kernel decompressor, place
+ * the fdt above the 128 MB from the start of RAM, as recommended by the
+ * kernel documentation.
+ */
+ fdt->offset = (uintptr_t)_dram + 128 * MiB;
+
+ 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);
+
+ /* Place INITRD */
+ if (config->ramdisk) {
+ initrd->offset = fdt->offset + fdt->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);
+ }
+
+ /* Kernel expects FDT as argument */
+ arg = (void *)fdt->offset;
+
+ prog_set_entry(payload, (void *)kernel->offset, arg);
+
+ bootmem_dump_ranges();
+
+ return true;
+}