Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/examples/versal_vmk180.config
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ ELF?=1
# Native gzip decompression for FIT subimages (set GZIP=0 to disable)
GZIP?=1

# FIT ramdisk (initramfs) extraction plus the /chosen/linux,initrd-{start,end}
# fixup. A stock PetaLinux image.ub carries a ramdisk sub-image that a U-Boot
# "bootm" loads; without this wolfBoot ignores it.
FIT_RAMDISK?=1

# Toolchain
USE_GCC=1
CROSS_COMPILE=aarch64-none-elf-
Expand Down
5 changes: 5 additions & 0 deletions config/examples/versal_vmk180_sdcard.config
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ ELF?=1
# Native gzip decompression for FIT subimages (set GZIP=0 to disable)
GZIP?=1

# FIT ramdisk (initramfs) extraction plus the /chosen/linux,initrd-{start,end}
# fixup. A stock PetaLinux image.ub carries a ramdisk sub-image that a U-Boot
# "bootm" loads; without this wolfBoot ignores it.
FIT_RAMDISK?=1

# Boot Benchmarking (optional)
BOOT_BENCHMARK?=1

Expand Down
39 changes: 39 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -4968,6 +4968,12 @@ sf erase 0x800000 +${filesize}
sf write ${loadaddr} 0x800000 ${filesize}
```

##### Ramdisk (initramfs)

A stock PetaLinux `image.ub` carries a `ramdisk` sub-image that `bootm` passes to the kernel via `/chosen/linux,initrd-{start,end}`. wolfBoot does the same with `FIT_RAMDISK=1`, enabled by default in both Versal example configs. Add the node plus a `ramdisk = "ramdisk-1";` reference to your configuration node; `hal/versal.its` carries a commented example.

`WOLFBOOT_LOAD_RAMDISK_ADDRESS` defaults to 0, which uses the ramdisk in place inside the staged FIT. Set it to a DDR address clear of the kernel, DTB and staging area if the payload needs a fixed location.

**DTB Fixup for Root Filesystem**

wolfBoot automatically modifies the device tree to set the kernel command line (`bootargs`). The default configuration mounts the root filesystem from SD card partition 2:
Expand Down Expand Up @@ -5030,6 +5036,39 @@ PetaLinux 2024.2 xilinx-vmk180 ttyAMA0
xilinx-vmk180 login:
```

**Example Linux Boot Output (`GZIP=1` + `FIT_RAMDISK=1`)**

```
Decompressing Image kernel-1 (gzip): 0x100000E4 -> 0x200000 (10623118 bytes)
Decompressed kernel-1: 24617472 bytes (7363 ms)
Loading DTS: 0x1000 -> 0x1000 (39384 bytes)
Loaded ramdisk: 0x10A2B540 (5647828 bytes)
FDT: Set chosen (28076), linux,initrd-start=279098688
FDT: Set chosen (28076), linux,initrd-end=284746516
Booting at 0x200000
do_boot: EL2->EL1 via ERET
[ 0.856319] Freeing initrd memory: 5512K
[ 3.315791] Run /init as init process

xilinx-vmk180-20242 login:
```

Inflating a 24 MB kernel takes about 7 s on the A72, spent entirely between the `Decompressing` and `Decompressed` lines with no intermediate output. Do not mistake that gap for a hang.

**Diagnosing a boot that stops with no message**

wolfBoot runs at EL2 on Versal, where any abort is fatal. With `DEBUG_UART=1` (the default in both Versal example configs) the handlers print the syndrome:

```
*** SYNCHRONOUS EXCEPTION ***
ESR_EL2: 0x0000000096000006
ELR_EL2: 0x0000000008005910
FAR_EL2: 0x00000000F9200000
*** SYSTEM HALTED ***
```

`ESR_EL2[31:26]` is the exception class, `[5:0]` the fault status, and `FAR_EL2` the faulting address. A stop with no such banner is not an exception - check the PLM's `PMC EAM` output on the same UART. Error IDs `0xA`/`0xB` are `DDRMB_CR`/`DDRMB_NCR`, DDR controller correctable and uncorrectable errors, which the PLM logs without acting on.

**Boot Performance**

Typical boot timing with ECC384/SHA384 signing:
Expand Down
25 changes: 21 additions & 4 deletions hal/versal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,18 @@ void hal_init(void)
#endif
"========================================\n");
wolfBoot_printf("Current EL: %d\n", current_el());

/* BL31 enters wolfBoot with all of DAIF masked (SPSR 0x3c9), so an
* asynchronous external abort - e.g. a DDR uncorrectable ECC error
* returned to the A72 - stays pending and invisible while the boot
* dies downstream. Unmask SError now that the console is up so it is
* taken at EL2 and reported by SErrorInterrupt() instead. */
#if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
if (current_el() == 2) {
__asm__ volatile("msr daifclr, #4");
__asm__ volatile("isb");
}
#endif
#endif

#ifdef EXT_FLASH
Expand Down Expand Up @@ -1306,14 +1318,19 @@ int hal_dts_fixup(void* dts_addr, uint32_t capacity)
off = fdt_add_subnode(&ctx, 0, "chosen");
}

if (off >= 0) {
/* Set bootargs property */
fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS);
} else {
if (off < 0) {
wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off);
return off;
}

/* Set bootargs property - overrides the PetaLinux default root= with
* the wolfBoot partition layout. */
ret = fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS);
if (ret < 0) {
wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret);
return ret;
}

return 0;
}
#endif /* __WOLFBOOT */
Expand Down
18 changes: 18 additions & 0 deletions hal/versal.its
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@
algo = "sha256";
};
};
/* Ramdisk (initramfs) sub-image; requires FIT_RAMDISK=1 (default in
* the Versal example configs). wolfBoot uses it in place and patches
* /chosen/linux,initrd-{start,end}. Add the "ramdisk = ..." reference
* to the configuration node below to enable it.
*
* ramdisk-1 {
* description = "petalinux-image-minimal";
* data = /incbin/("../rootfs.cpio.gz");
* type = "ramdisk";
* arch = "arm64";
* os = "linux";
* compression = "none";
* hash-1 {
* algo = "sha256";
* };
* };
*/
/* FPGA bitstream sub-image (requires FPGA_BITSTREAM=1). Add the
* "fpga = ..." reference to the configuration node below. The
* bitstream must be a bootgen .bin staged to its load address.
Expand Down Expand Up @@ -60,6 +77,7 @@
description = "Linux kernel and FDT blob";
kernel = "kernel-1";
fdt = "fdt-1";
/* ramdisk = "ramdisk-1"; */
/* fpga = "fpga-1"; */
hash-1 {
algo = "sha256";
Expand Down
17 changes: 14 additions & 3 deletions src/boot_aarch64.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset)
uintptr_t dts = 0;
#endif
wolfBoot_printf("do_boot: EL2->EL1 via ERET\n");
/* Clean the EL2 D-cache and drop the MMU before the ERET: Linux
* enters at EL1 with SCTLR_EL1.M/C clear and reads memory uncached,
* so the kernel and DTB must be clean to PoC (ARM64 booting.rst).
* hal_prepare_boot() cleans only a fixed window at
* WOLFBOOT_LOAD_ADDRESS, which on a FIT boot is the staging buffer,
* not the load destinations. */
el2_flush_and_disable_mmu();
el2_to_el1_boot((uintptr_t)app_offset, dts);
}
#else
Expand Down Expand Up @@ -273,7 +280,7 @@ void RAMFUNCTION arch_reboot(void)
#endif

/* ============================================================================
* Exception Handlers for EL2 (optional DEBUG_HARDFAULT)
* Exception Handlers for EL2 / EL3
* ============================================================================
*/

Expand Down Expand Up @@ -307,7 +314,11 @@ void FIQInterrupt(void)
void SErrorInterrupt(void)
{ print_exception_info_el3("SERROR"); while (1) { __asm__ volatile("wfi"); } }

#elif defined(DEBUG_HARDFAULT) && defined(DEBUG_UART) && defined(EL2_HYPERVISOR)
#elif defined(DEBUG_UART) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1

/* EL2 counterpart of the EL3 block above. Gated on DEBUG_UART alone: without
* it an abort taken at EL2 (ZynqMP, Versal) lands in the silent wfi stub and
* the boot just stops with no output. */

#define READ_SYSREG(_out, _reg) __asm__ volatile("mrs %0, " #_reg : "=r"(_out))

Expand Down Expand Up @@ -374,4 +385,4 @@ void SynchronousInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void IRQInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void FIQInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void SErrorInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
#endif /* DEBUG_HARDFAULT && DEBUG_UART && EL2_HYPERVISOR */
#endif /* exception handler variants */
33 changes: 32 additions & 1 deletion src/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@
#define WOLFBOOT_FIT_MAX_DECOMP (256U * 1024U * 1024U)
#endif

/* Start of wolfBoot's own image (linker script _start_text). Weak so hosted
* builds (sim, unit tests) without the symbol resolve it to NULL and skip the
* bound derived from it. */
extern char _start_text[] __attribute__((weak));
#define wolfboot_start_text ((void*)_start_text)

/* ------------------------------------------------------------------ */
/* Byte order */
/* ------------------------------------------------------------------ */
Expand Down Expand Up @@ -1299,6 +1305,10 @@ int fdt_fixup_initrd(fdt_ctx* ctx, uint64_t start, uint64_t size)
if (ret < 0) {
return ret;
}
if (start + size < start) {
/* Wrapped initrd end: linux,initrd-end would precede -start. */
return -FDT_ERR_BADARG;
}
ret = fdt_fixup_val64(ctx, off, "chosen", "linux,initrd-end",
start + size);
if (ret < 0) {
Expand Down Expand Up @@ -1609,12 +1619,33 @@ static void* fit_load_image_inner(fdt_ctx* ctx, const char* image, int* lenp,
if (is_gzip) {
#ifdef WOLFBOOT_GZIP
uint32_t out_len = 0;
uint32_t gz_max = out_max;
uintptr_t gap;
int rc;
/* out_max is only a sanity ceiling. Cap the output below
* anything above the destination that must survive the
* inflate: the staged compressed input, and wolfBoot's own
* image (a corrupted stream can emit garbage at full match
* speed, and without this cap the window reaches straight
* through the bootloader before the decoder trips on an
* invalid code). Conservative lower bounds. */
if ((uintptr_t)data > (uintptr_t)load) {
gap = (uintptr_t)data - (uintptr_t)load;
if (gap < (uintptr_t)gz_max) {
gz_max = (uint32_t)gap;
}
}
if ((uintptr_t)wolfboot_start_text > (uintptr_t)load) {
gap = (uintptr_t)wolfboot_start_text - (uintptr_t)load;
if (gap < (uintptr_t)gz_max) {
gz_max = (uint32_t)gap;
}
}
wolfBoot_printf("Decompressing Image %s (gzip): "
"%p -> %p (%d bytes)\n", image, data, load, len);
BENCHMARK_START();
rc = wolfBoot_gunzip((const uint8_t*)data,
(uint32_t)len, (uint8_t*)load, out_max, &out_len);
(uint32_t)len, (uint8_t*)load, gz_max, &out_len);
if (rc != 0) {
wolfBoot_printf("FIT gunzip failed for %s: rc=%d "
"(wrote %u bytes)\n", image, rc, out_len);
Expand Down
14 changes: 14 additions & 0 deletions tools/unit-tests/unit-fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,19 @@ START_TEST(test_fdt_fixup_initrd)
}
END_TEST

/* A start+size that wraps must be rejected: linux,initrd-end would
* otherwise precede linux,initrd-start. */
START_TEST(test_fdt_fixup_initrd_rejects_wrapped_end)
{
static uint8_t buf[0x800];
fdt_ctx ctx;

(void)build_compat_fdt(buf, sizeof(buf), (const uint8_t *)"abc\0", 4);
ck_assert_int_eq(fdt_open(&ctx, buf, (uint32_t)sizeof(buf)), 0);
ck_assert_int_lt(fdt_fixup_initrd(&ctx, ~0ULL - 1U, 0x1000ULL), 0);
}
END_TEST

/* ------------------------------------------------------------------ */
/* fdt_peek_size */
/* ------------------------------------------------------------------ */
Expand Down Expand Up @@ -935,6 +948,7 @@ static Suite *fdt_suite(void)
tcase_add_test(tc, test_fdt_add_subnode_bounded_by_capacity);
tcase_add_test(tc, test_fdt_setprop_resizes_existing_property);
tcase_add_test(tc, test_fdt_fixup_initrd);
tcase_add_test(tc, test_fdt_fixup_initrd_rejects_wrapped_end);
tcase_add_test(tc, test_fdt_peek_size_header_only);
tcase_add_test(tc, test_fit_find_images_rejects_unterminated_image_name);
suite_add_tcase(s, tc);
Expand Down
3 changes: 2 additions & 1 deletion tools/unit-tests/unit-update-disk-fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,9 +682,10 @@ uint32_t wolfBoot_get_blob_version(uint8_t *blob)
return version;
}

int wolfBoot_get_dts_size(void *dts_addr)
int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity)
{
(void)dts_addr;
(void)capacity;
return -1;
}

Expand Down
Loading