mirror of https://github.com/wolfSSL/wolfBoot.git
versal: report EL2 exceptions, fix FIT ramdisk fixup and the EL2->EL1 cache handoff
parent
24c23e767b
commit
a2639e88f8
|
|
@ -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-
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
25
hal/versal.c
25
hal/versal.c
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ SECTIONS
|
|||
PROVIDE (_MEMORY_SIZE = LENGTH(psu_ocm_ram_0_MEM_0));
|
||||
|
||||
.text : {
|
||||
/* Exported for the gzip inflate bound in src/fdt.c (weak _start_text):
|
||||
* marks the start of wolfBoot's own image so a runaway decompression
|
||||
* fails closed instead of overwriting the bootloader. */
|
||||
_start_text = .;
|
||||
KEEP (*(.vectors))
|
||||
*(.boot)
|
||||
*(.text)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ SECTIONS
|
|||
PROVIDE (_MEMORY_SIZE = LENGTH(psu_ocm_ram_0_MEM_0));
|
||||
|
||||
.text : {
|
||||
/* Exported for the gzip inflate bound in src/fdt.c (weak _start_text):
|
||||
* marks the start of wolfBoot's own image so a runaway decompression
|
||||
* fails closed instead of overwriting the bootloader. */
|
||||
_start_text = .;
|
||||
KEEP (*(.vectors))
|
||||
*(.boot)
|
||||
*(.text)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -273,7 +280,7 @@ void RAMFUNCTION arch_reboot(void)
|
|||
#endif
|
||||
|
||||
/* ============================================================================
|
||||
* Exception Handlers for EL2 (optional DEBUG_HARDFAULT)
|
||||
* Exception Handlers for EL2 / EL3
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
|
|
@ -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))
|
||||
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
40
src/fdt.c
40
src/fdt.c
|
|
@ -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 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -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) {
|
||||
|
|
@ -1609,12 +1619,40 @@ 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;
|
||||
}
|
||||
}
|
||||
else if (wolfboot_start_text == NULL) {
|
||||
/* Linker script does not export _start_text: the
|
||||
* bootloader-image bound cannot be applied. Say so
|
||||
* rather than skipping it silently. */
|
||||
wolfBoot_printf("gzip: no _start_text, output not "
|
||||
"bounded by the wolfBoot image\n");
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue