mirror of https://github.com/wolfSSL/wolfBoot.git
fdt: boot a FIT with no kernel load/entry and no device tree
parent
aef57a113d
commit
d490e36d2e
|
|
@ -1332,12 +1332,35 @@ jobs:
|
|||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180.config
|
||||
|
||||
versal_vmk180_highddr_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180_highddr.config
|
||||
|
||||
versal_vmk180_sdcard_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180_sdcard.config
|
||||
|
||||
# FIT read from a filesystem with no kernel load/entry and no fdt
|
||||
# sub-image, so the staging addresses come from the build.
|
||||
versal_vmk180_sdcard_fit_file_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180_sdcard.config
|
||||
make-args: DISK_FS=fat32 FIT_RAMDISK=1 WOLFBOOT_LOAD_KERNEL_ADDRESS=0x200000 WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40000000
|
||||
|
||||
# Same, with every address moved into a high DDR aperture.
|
||||
versal_vmk180_sdcard_fit_file_highddr_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180_sdcard.config
|
||||
make-args: DISK_FS=fat32 FIT_RAMDISK=1 DDR_HIGH_BASE=0x40000000000 DDR_HIGH_SIZE=0x10000000000 NO_DDR_LOW=1 WOLFBOOT_ORIGIN=0x40000800000 WOLFBOOT_LOAD_ADDRESS=0x40010000000 WOLFBOOT_LOAD_DTS_ADDRESS=0x40000001000 WOLFBOOT_LOAD_KERNEL_ADDRESS=0x40002000000 WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40030000000
|
||||
|
||||
versal_vmk180_sdcard_fs_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -887,8 +887,6 @@ include/target.h: $(TARGET_H_TEMPLATE) FORCE
|
|||
sed -e "s/@WOLFBOOT_DTS_UPDATE_ADDRESS@/$(WOLFBOOT_DTS_UPDATE_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_RAMDISK_ADDRESS@/$(WOLFBOOT_LOAD_RAMDISK_ADDRESS)/g" | \
|
||||
sed -e "s/@WOLFBOOT_LOAD_FPGA_ADDRESS@/$(WOLFBOOT_LOAD_FPGA_ADDRESS)/g" | \
|
||||
sed -e "s|@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@|$(if $(strip $(WOLFBOOT_RAMBOOT_MAX_SIZE)),#define WOLFBOOT_RAMBOOT_MAX_SIZE $(WOLFBOOT_RAMBOOT_MAX_SIZE),/* WOLFBOOT_RAMBOOT_MAX_SIZE undefined */)|g" | \
|
||||
sed -e "s/@WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@/$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS)/g" \
|
||||
> $@
|
||||
|
|
|
|||
|
|
@ -47,12 +47,11 @@ WOLFTPM?=0
|
|||
EXT_FLASH?=1
|
||||
NO_XIP=1
|
||||
|
||||
# Boot Exception Level
|
||||
# Choose the exception level for booting applications (mutually exclusive):
|
||||
# BOOT_EL1: Transition applications to EL1 (default - for Linux, most bare-metal)
|
||||
# BOOT_EL2: Boot applications at EL2 (for hypervisors, RTOSes that expect EL2)
|
||||
# Default is BOOT_EL1 if neither is specified.
|
||||
# Uncomment one of the following to override:
|
||||
# Boot Exception Level (mutually exclusive)
|
||||
# BOOT_EL1: drop to EL1 via ERET before entering the application.
|
||||
# BOOT_EL2: enter the application at the EL2 wolfBoot runs at (default).
|
||||
# EL2 is the default: PSCI brings the secondary cores up at EL2, so dropping
|
||||
# only the boot core leaves Linux with "CPUs started in inconsistent modes".
|
||||
#BOOT_EL1=1
|
||||
#BOOT_EL2=1
|
||||
|
||||
|
|
@ -78,6 +77,13 @@ GZIP?=1
|
|||
# "bootm" loads; without this wolfBoot ignores it.
|
||||
FIT_RAMDISK?=1
|
||||
|
||||
# Load address for the FIT kernel sub-image, overriding the FIT's own
|
||||
# `load`/`entry`. Needed when the ITS omits them. 0 honors the FIT.
|
||||
#WOLFBOOT_LOAD_KERNEL_ADDRESS?=0x200000
|
||||
|
||||
# Likewise for the ramdisk: 0 honors the FIT's own `load`.
|
||||
#WOLFBOOT_LOAD_RAMDISK_ADDRESS?=0x40000000
|
||||
|
||||
# Toolchain
|
||||
USE_GCC=1
|
||||
CROSS_COMPILE=aarch64-none-elf-
|
||||
|
|
@ -118,17 +124,25 @@ WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x6000000
|
|||
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000
|
||||
|
||||
# High DDR aperture: for designs whose DDR lives above the default address map
|
||||
# (e.g. DDR at 0x400_0000_0000). These must stay BELOW the defaults above and
|
||||
# use '=' so they override them. Base and size are 512 GB aligned with no
|
||||
# UL/ULL suffix (the assembler consumes them); the FIT ITS must then use
|
||||
# (e.g. DDR at 0x400_0000_0000). config/examples/versal_vmk180_highddr.config
|
||||
# is this recipe already applied. These must stay BELOW the defaults above
|
||||
# and use '=' so they override them. Base and size are 512 GB aligned with
|
||||
# no UL/ULL suffix (the assembler consumes them); the FIT ITS must then use
|
||||
# #address-cells = <2> with two-cell load/entry values, and the bootgen BIF
|
||||
# and BL31 BL33 entry must match WOLFBOOT_ORIGIN.
|
||||
#
|
||||
# Note 0x40000800000 is 0x400_0080_0000 (8 MB in), not 0x400_0800_0000.
|
||||
# The kernel and ramdisk destinations must clear the FIT staging buffer at
|
||||
# WOLFBOOT_LOAD_ADDRESS, which the sub-images are read out of, and
|
||||
# WOLFBOOT_PARTITION_SIZE must fit the whole signed FIT.
|
||||
#WOLFBOOT_ORIGIN=0x40000800000
|
||||
#WOLFBOOT_LOAD_ADDRESS=0x40010000000
|
||||
#WOLFBOOT_LOAD_DTS_ADDRESS=0x40000001000
|
||||
#CFLAGS_EXTRA+=-DVERSAL_DDR_HIGH_BASE=0x40000000000
|
||||
#CFLAGS_EXTRA+=-DVERSAL_DDR_HIGH_SIZE=0x10000000000
|
||||
#CFLAGS_EXTRA+=-DVERSAL_NO_DDR_LOW
|
||||
#WOLFBOOT_LOAD_KERNEL_ADDRESS=0x40002000000
|
||||
#WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40030000000
|
||||
#DDR_HIGH_BASE=0x40000000000
|
||||
#DDR_HIGH_SIZE=0x10000000000
|
||||
#NO_DDR_LOW=1
|
||||
WOLFBOOT_DTS_BOOT_ADDRESS?=0x7B0000
|
||||
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x39B0000
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
# wolfBoot configuration for AMD Versal - DDR Boot with a high DDR aperture
|
||||
# Versal with DDR mapped at 0x400_0000_0000 instead of the default map.
|
||||
#
|
||||
# PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL2)
|
||||
#
|
||||
# Derived from versal_vmk180.config; see that file for the knobs this one
|
||||
# does not change. Every address here lives inside the aperture declared by
|
||||
# DDR_HIGH_BASE/DDR_HIGH_SIZE.
|
||||
#
|
||||
# Three things outside this file have to agree with it: the bootgen BIF
|
||||
# (wolfboot.elf at WOLFBOOT_ORIGIN, raw DTB at WOLFBOOT_LOAD_DTS_ADDRESS),
|
||||
# BL31's PRELOADED_BL33_BASE = WOLFBOOT_ORIGIN, and the FIT ITS using
|
||||
# #address-cells = <2>.
|
||||
|
||||
ARCH?=AARCH64
|
||||
TARGET?=versal
|
||||
|
||||
WOLFBOOT_VERSION?=1
|
||||
|
||||
# ECC-384 with SHA-384 (good balance of security and performance)
|
||||
SIGN?=ECC384
|
||||
HASH?=SHA384
|
||||
IMAGE_HEADER_SIZE?=512
|
||||
|
||||
# RSA 4096-bit with SHA3-384 (alternative)
|
||||
#SIGN?=RSA4096
|
||||
#HASH?=SHA3
|
||||
#IMAGE_HEADER_SIZE?=1024
|
||||
|
||||
# Debug options
|
||||
DEBUG?=1
|
||||
DEBUG_SYMBOLS=1
|
||||
DEBUG_UART=1
|
||||
|
||||
# Boot Benchmarking (optional):
|
||||
# Enables timing of boot operations (flash read, integrity, signature).
|
||||
BOOT_BENCHMARK?=1
|
||||
|
||||
VTOR?=1
|
||||
CORTEX_M0?=0
|
||||
NO_ASM?=0
|
||||
ALLOW_DOWNGRADE?=0
|
||||
NVM_FLASH_WRITEONCE?=0
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=0
|
||||
DUALBANK_SWAP?=0
|
||||
PKA?=0
|
||||
WOLFTPM?=0
|
||||
|
||||
# Flash configuration
|
||||
EXT_FLASH?=1
|
||||
NO_XIP=1
|
||||
|
||||
# Boot Exception Level (mutually exclusive)
|
||||
# BOOT_EL1: drop to EL1 via ERET before entering the application.
|
||||
# BOOT_EL2: enter the application at the EL2 wolfBoot runs at (default).
|
||||
# EL2 is the default: PSCI brings the secondary cores up at EL2, so dropping
|
||||
# only the boot core leaves Linux with "CPUs started in inconsistent modes".
|
||||
#BOOT_EL1=1
|
||||
#BOOT_EL2=1
|
||||
|
||||
# ELF loading support
|
||||
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
|
||||
|
||||
# WOLFBOOT_LOAD_KERNEL_ADDRESS / WOLFBOOT_LOAD_RAMDISK_ADDRESS override the
|
||||
# FIT's own `load`/`entry`, so a FIT built without them boots. Set below.
|
||||
|
||||
# Toolchain
|
||||
USE_GCC=1
|
||||
CROSS_COMPILE=aarch64-none-elf-
|
||||
|
||||
# ============================================================================
|
||||
# Boot Memory Layout
|
||||
# ============================================================================
|
||||
# wolfBoot runs from DDR inside the high aperture, at EL2 non-secure.
|
||||
# WOLFBOOT_ORIGIN is set with the rest of the layout further down.
|
||||
|
||||
# Optional debugging with OCRAM
|
||||
# Versal Gen 1 (VMK180): OCM is 256KB at 0xFFFC0000 - 0xFFFFFFFF
|
||||
# Versal Gen 2: OCM is 2MB at 0xFFE00000 - 0xFFFFFFFF
|
||||
#WOLFBOOT_ORIGIN=0xFFFC0000
|
||||
|
||||
# Flash Sector Size (QSPI)
|
||||
WOLFBOOT_SECTOR_SIZE=0x20000
|
||||
|
||||
# Application Partition Size (44MB)
|
||||
WOLFBOOT_PARTITION_SIZE=0x2C00000
|
||||
|
||||
# Location in Flash for Primary Boot Partition
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x800000
|
||||
|
||||
# Load Partition to RAM Address (Linux kernel loads here)
|
||||
WOLFBOOT_LOAD_ADDRESS?=0x10000000
|
||||
|
||||
# Location in Flash for Secondary Partition (update image)
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3400000
|
||||
|
||||
# Location to store wolfBoot state/swap
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x6000000
|
||||
|
||||
# DTS (Device Tree) - matches addresses from BOOT.BIN analysis
|
||||
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000
|
||||
|
||||
# The aperture itself. Base and size are 512 GB aligned with no UL/ULL
|
||||
# suffix - the assembler consumes them. NO_DDR_LOW drops the low mapping.
|
||||
DDR_HIGH_BASE=0x40000000000
|
||||
DDR_HIGH_SIZE=0x10000000000
|
||||
NO_DDR_LOW=1
|
||||
|
||||
# DDR layout inside the aperture, using '=' to override the '?=' defaults
|
||||
# above. Each region is distinct: the kernel and ramdisk destinations must
|
||||
# clear the FIT staging buffer the sub-images are read out of.
|
||||
#
|
||||
# 0x400_0000_1000 DTB (WOLFBOOT_LOAD_DTS_ADDRESS)
|
||||
# 0x400_0080_0000 wolfBoot (WOLFBOOT_ORIGIN)
|
||||
# 0x400_0200_0000 kernel (WOLFBOOT_LOAD_KERNEL_ADDRESS)
|
||||
# 0x400_1000_0000 FIT staging (WOLFBOOT_LOAD_ADDRESS)
|
||||
# 0x400_3000_0000 ramdisk (WOLFBOOT_LOAD_RAMDISK_ADDRESS)
|
||||
WOLFBOOT_ORIGIN=0x40000800000
|
||||
WOLFBOOT_LOAD_DTS_ADDRESS=0x40000001000
|
||||
WOLFBOOT_LOAD_KERNEL_ADDRESS=0x40002000000
|
||||
WOLFBOOT_LOAD_ADDRESS=0x40010000000
|
||||
WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40030000000
|
||||
|
||||
# Cap the decompressed kernel at the 224 MB gap to the staging buffer above,
|
||||
# which is tighter than the 256 MB WOLFBOOT_FIT_MAX_DECOMP default.
|
||||
CFLAGS_EXTRA+=-DWOLFBOOT_FIT_MAX_KERNEL=0xE000000
|
||||
WOLFBOOT_DTS_BOOT_ADDRESS?=0x7B0000
|
||||
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x39B0000
|
||||
|
||||
# Speed up reads by using larger blocks
|
||||
CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096
|
||||
|
||||
# UART Configuration - UART0 for APU console
|
||||
CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0
|
||||
|
||||
# QSPI Reference Clock: Ref (300MHz default for Versal)
|
||||
#CFLAGS_EXTRA+=-DGQSPI_CLK_REF=300000000
|
||||
|
||||
# QSPI Bus Divisor: (2 << div) = BUS (0=div2, 1=div4, 2=div8)
|
||||
# MT25QU01G max: 133MHz Quad Read (0x6C) with 8 dummy cycles
|
||||
# div=0: 300MHz/2 = 150MHz (above spec but tested working)
|
||||
# div=1: 300MHz/4 = 75MHz (within spec, default)
|
||||
# div=2: 300MHz/8 = 37.5MHz (conservative)
|
||||
#CFLAGS_EXTRA+=-DGQSPI_CLK_DIV=1
|
||||
|
||||
# QSPI flash options (uncomment to enable)
|
||||
#CFLAGS_EXTRA+=-DDEBUG_QSPI # Enable QSPI debug logging
|
||||
#CFLAGS_EXTRA+=-DGQSPI_MODE_IO # Use polling instead of DMA (slower)
|
||||
#CFLAGS_EXTRA+=-DTEST_EXT_FLASH # Run flash erase/write/read test
|
||||
|
|
@ -103,6 +103,20 @@ CFLAGS_EXTRA+=-DBOOT_PART_B=2
|
|||
#DISK_FS=both
|
||||
#CFLAGS_EXTRA+=-DBOOT_FILE_A='"/boot/fitImage_A.itb"'
|
||||
#CFLAGS_EXTRA+=-DBOOT_FILE_B='"/boot/fitImage_B.itb"'
|
||||
#
|
||||
# A U-Boot "fatload mmc 0:1 <addr> image.ub ; bootm <addr> <addr>
|
||||
# ${fdtcontroladdr}" flow maps onto DISK_FS=fat32 plus BOOT_FILE_A, with
|
||||
# wolfBoot reading the file itself. If that FIT declares no `load`/`entry`
|
||||
# and carries no `fdt`, add the staging addresses below and point
|
||||
# WOLFBOOT_LOAD_DTS_ADDRESS at the DTB the bootgen BIF leaves.
|
||||
#DISK_FS=fat32
|
||||
#CFLAGS_EXTRA+=-DBOOT_FILE_A='"/image.ub"'
|
||||
#FIT_RAMDISK=1
|
||||
#WOLFBOOT_LOAD_KERNEL_ADDRESS=0x200000
|
||||
#WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40000000
|
||||
#
|
||||
# For DDR above the default address map see the "High DDR aperture" block
|
||||
# in config/examples/versal_vmk180.config; the ITS then needs 2 cells.
|
||||
# Select the partitions by name instead of by index. The GPT partition label
|
||||
# is matched first, then the filesystem volume label (which is what makes
|
||||
# this work on MBR disks, where partitions have no names).
|
||||
|
|
|
|||
|
|
@ -4601,7 +4601,7 @@ qemu-system-aarch64 -machine xlnx-zcu102 -cpu cortex-a53 -serial stdio -display
|
|||
|
||||
Use `config/examples/zynqmp_sdcard.config`. This uses the Arasan SDHCI controller (SD1 - external SD card slot on ZCU102) and an **MBR** partitioned SD card.
|
||||
|
||||
On the direct-jump handoff path, wolfBoot flushes the EL2 D-cache/I-cache and disables the EL2 MMU via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S` when `BOOT_EL1` is not enabled and the current exception level is EL2. The ERET-to-EL1 handoff path is different, so this cleanup is not unconditional.
|
||||
Both handoff paths flush the EL2 D-cache/I-cache and disable the EL2 MMU via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`, under different conditions: the ERET-to-EL1 path whenever `BOOT_EL1` and `EL2_HYPERVISOR=1` are both set, and the direct-jump path when `EL2_HYPERVISOR=1` and the current exception level is EL2.
|
||||
|
||||
**Partition layout**
|
||||
| Partition | Name | Size | Type | Contents |
|
||||
|
|
@ -5253,11 +5253,13 @@ AMD Versal Prime Series VMK180 Evaluation Kit - Versal Prime XCVM1802-2MSEVSVA21
|
|||
|
||||
wolfBoot replaces U-Boot in the Versal boot flow:
|
||||
```
|
||||
PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL1)
|
||||
PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL2)
|
||||
```
|
||||
|
||||
wolfBoot runs from DDR at `0x8000000` (EL2, non-secure). All clock, MIO, and DDR initialization is handled by PLM/PSM before wolfBoot starts.
|
||||
|
||||
The application is entered at **EL2** by default (`BOOT_EL2` in `hal/versal.h`). PSCI brings the secondary cores up at EL2, so dropping only the boot core to EL1 leaves Linux with `CPUs started in inconsistent modes`. Set `BOOT_EL1=1` for a payload that requires EL1 - that was the previous default, so it now has to be stated explicitly.
|
||||
|
||||
This target supports **two boot paths**:
|
||||
- **QSPI boot** (primary, production-style): `config/examples/versal_vmk180.config`
|
||||
- **SD card boot** (MBR, A/B images): `config/examples/versal_vmk180_sdcard.config`
|
||||
|
|
@ -5274,7 +5276,7 @@ Note: If using QSPI there are bootgen issues with 2025.1+, so recommend 2024.1 o
|
|||
### Common Notes
|
||||
|
||||
- Debugging with OCRAM (OCM): set `WOLFBOOT_ORIGIN=0xFFFC0000` (OCM is 256KB at `0xFFFC0000 - 0xFFFFFFFF`).
|
||||
- **High DDR apertures**: designs whose DDR window lives above the default map (for example DDR at `0x400_0000_0000`) add `CFLAGS_EXTRA+=-DVERSAL_DDR_HIGH_BASE=...` and `-DVERSAL_DDR_HIGH_SIZE=...` (512 GB aligned, plain hex literals with no `UL` suffix) to map the window in the translation table, plus `-DVERSAL_NO_DDR_LOW` when nothing remains at `0x0`. Move `WOLFBOOT_ORIGIN`, `WOLFBOOT_LOAD_ADDRESS` and `WOLFBOOT_LOAD_DTS_ADDRESS` into the window (`hal/versal.ld` follows `WOLFBOOT_ORIGIN` from the config), update the bootgen BIF load/exec and the BL31 BL33 entry to match, and switch the FIT ITS to `#address-cells = <2>` with two-cell `load`/`entry` values since 32-bit cells cannot hold addresses above 4 GB. A commented recipe is in `config/examples/versal_vmk180.config`; the DDR aperture itself must be routed to the APU by the design's PDI.
|
||||
- **High DDR apertures**: designs whose DDR window lives above the default map (for example DDR at `0x400_0000_0000`) set `DDR_HIGH_BASE=...` and `DDR_HIGH_SIZE=...` (512 GB aligned, plain hex with no `UL` suffix) to map the window in the translation table, plus `NO_DDR_LOW=1` when nothing remains at `0x0`. These are AArch64-generic, and the assembler rejects a window outside the VA region the target's TCR covers; the older `-DVERSAL_DDR_HIGH_*` spellings are still accepted. Move `WOLFBOOT_ORIGIN`, `WOLFBOOT_LOAD_ADDRESS` and `WOLFBOOT_LOAD_DTS_ADDRESS` into the window (`hal/versal.ld` follows `WOLFBOOT_ORIGIN` from the config), update the bootgen BIF load/exec and the BL31 BL33 entry to match, and switch the FIT ITS to `#address-cells = <2>` with two-cell `load`/`entry` values since 32-bit cells cannot hold addresses above 4 GB. A commented recipe is in `config/examples/versal_vmk180.config`; the DDR aperture itself must be routed to the APU by the design's PDI.
|
||||
- Test application uses generic `boot_arm64_start.S` and `AARCH64.ld` and prints EL + version.
|
||||
- Entry point: `_start` (in `boot_arm64_start.S`) which sets up stack, clears BSS, and calls `main()`
|
||||
|
||||
|
|
@ -5500,6 +5502,43 @@ A stock PetaLinux `image.ub` carries a `ramdisk` sub-image that `bootm` passes t
|
|||
|
||||
`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.
|
||||
|
||||
##### FIT images built without `load`/`entry`
|
||||
|
||||
Some producers emit a FIT whose kernel node declares neither `load` nor `entry` - Yocto's `kernel-fitimage` class omits both unless `UBOOT_LOADADDRESS` and `UBOOT_ENTRYPOINT` are set - and leave it to U-Boot to place the image. wolfBoot needs a destination: a `gzip` kernel has nowhere to decompress to, and an uncompressed one would be entered in place inside the staged FIT at whatever alignment the FIT gives it.
|
||||
|
||||
Either add the properties to the ITS, or set `WOLFBOOT_LOAD_KERNEL_ADDRESS` to the address the kernel should be staged at; a nonzero value overrides the FIT's `load`/`entry` and is returned as the entry point. `WOLFBOOT_LOAD_RAMDISK_ADDRESS` does the same for the `ramdisk` node.
|
||||
|
||||
`WOLFBOOT_FIT_MAX_KERNEL` bounds how much may be written at that address. It defaults to `WOLFBOOT_FIT_MAX_DECOMP` (256 MB) and is set with `CFLAGS_EXTRA+=-DWOLFBOOT_FIT_MAX_KERNEL=...`; `WOLFBOOT_FIT_MAX_RAMDISK` and `WOLFBOOT_FIT_MAX_FPGA` are the equivalents for those nodes. Like the other `WOLFBOOT_FIT_MAX_*` caps it is a compile-time sanity ceiling and not a description of your DDR map, so lower it to the staging window if you want a tight bound. wolfBoot narrows it further at run time to the space left below the staged FIT and below wolfBoot's own image, and a kernel that does not fit there is rejected with a message rather than copied or decompressed over either - both the uncompressed and the `gzip` path make that check.
|
||||
|
||||
For a DDR aperture above 4 GB the ITS must use `#address-cells = <2>` and two-cell values:
|
||||
|
||||
```dts
|
||||
/ {
|
||||
#address-cells = <2>;
|
||||
images {
|
||||
kernel-1 {
|
||||
data = /incbin/("Image.gz");
|
||||
compression = "gzip";
|
||||
load = <0x00000400 0x08000000>;
|
||||
entry = <0x00000400 0x08000000>;
|
||||
hash-1 { algo = "sha256"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
##### FIT images with no device tree
|
||||
|
||||
A kernel-only FIT - no `fdt` sub-image, and no `fdt` property on the configuration node - takes its device tree from the boot firmware instead, which is the arrangement U-Boot describes with `fdtcontroladdr`. Point `WOLFBOOT_LOAD_DTS_ADDRESS` at wherever the earlier stage left the blob; for a bootgen raw partition that is the `load` address in the BIF:
|
||||
|
||||
```
|
||||
{ type=raw, load=0x40000001000, file=system-top.dtb }
|
||||
```
|
||||
|
||||
wolfBoot then relocates and validates that DTB exactly as it does for a non-FIT payload, and hands it to the kernel in `x0`.
|
||||
|
||||
Such a DTB is outside the FIT, so the signature does not cover it: `/chosen/bootargs` from the blob is **not** trusted and `LINUX_BOOTARGS` replaces it. On the RAM/flash loader (`src/update_ram.c`) a digest bound with `sign --dts` authenticates it and honors its bootargs, and `WOLFBOOT_REQUIRE_SIGNED_DTB=1` makes a missing digest fatal. The disk loader (`src/update_disk.c`) has no DTB-digest machinery, so put the DTB in the FIT as an `fdt` sub-image to bring it inside the root of trust there.
|
||||
|
||||
**Kernel Command Line (bootargs)**
|
||||
|
||||
If the FIT's DTB carries `/chosen/bootargs`, wolfBoot keeps them by default - an image boots with the arguments its kernel was validated with. Setting `LINUX_BOOTARGS` or `LINUX_BOOTARGS_ROOT` in the config replaces the DTB's value (the replaced value is logged); `CFLAGS_EXTRA+=-DLINUX_BOOTARGS_OVERRIDE=0` demotes an explicit `LINUX_BOOTARGS` to a fallback used only when the DTB has none.
|
||||
|
|
@ -5608,7 +5647,7 @@ Typical boot timing with ECC384/SHA384 signing:
|
|||
|
||||
Use `config/examples/versal_vmk180_sdcard.config`. This uses the Arasan SDHCI controller and an **MBR** partitioned SD card.
|
||||
|
||||
Versal defaults to `BOOT_EL1` — the handoff goes through `el2_to_el1_boot` (ERET to EL1). Custom `BOOT_EL2` Versal configs get the same EL2 cache/MMU teardown as ZynqMP via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`, so no extra config flag is needed to boot Linux directly at EL2.
|
||||
Versal enters the application at `BOOT_EL2` by default, with the same EL2 cache/MMU teardown as ZynqMP via `el2_flush_and_disable_mmu` in `src/boot_aarch64_start.S`, so no extra config flag is needed to boot Linux directly at EL2. Set `BOOT_EL1=1` to hand off through `el2_to_el1_boot` (ERET to EL1) instead.
|
||||
|
||||
**Partition layout**
|
||||
| Partition | Name | Size | Type | Contents |
|
||||
|
|
|
|||
|
|
@ -284,6 +284,15 @@ int hal_flash_test_dualbank(void)
|
|||
|
||||
#endif /* TEST_FLASH */
|
||||
|
||||
#ifdef MMU
|
||||
/* Weak default for targets with no configured device tree; some HALs also
|
||||
* compile their own out under WOLFBOOT_NO_PARTITIONS. */
|
||||
WEAKFUNCTION void* hal_get_dts_address(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* MMU */
|
||||
|
||||
WEAKFUNCTION int RAMFUNCTION hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
(void)address;
|
||||
|
|
|
|||
45
hal/versal.h
45
hal/versal.h
|
|
@ -78,19 +78,18 @@
|
|||
* BL31 handles GIC initialization, so skip gicv2_init_secure().
|
||||
*
|
||||
* Boot Exception Level Configuration (mutually exclusive):
|
||||
* BOOT_EL1: wolfBoot runs at EL2, but applications (Linux, most bare-metal)
|
||||
* are transitioned to EL1 before jumping (default)
|
||||
* BOOT_EL2: Applications boot at EL2 (for hypervisors/RTOSes that
|
||||
* expect to run at EL2)
|
||||
* BOOT_EL1: transition the application to EL1 before jumping
|
||||
* BOOT_EL2: enter the application at the EL2 wolfBoot runs at (default)
|
||||
*
|
||||
* Default: BOOT_EL1 (most applications expect EL1)
|
||||
* EL2 is the default: PSCI brings the secondary cores up at EL2, so dropping
|
||||
* only the boot core leaves Linux with "CPUs started in inconsistent modes".
|
||||
*/
|
||||
#ifndef SKIP_GIC_INIT
|
||||
#define SKIP_GIC_INIT 1
|
||||
#endif
|
||||
|
||||
#if !defined(BOOT_EL1) && !defined(BOOT_EL2)
|
||||
#define BOOT_EL1 1
|
||||
#define BOOT_EL2 1
|
||||
#endif
|
||||
|
||||
#if defined(BOOT_EL1) && defined(BOOT_EL2)
|
||||
|
|
@ -123,24 +122,32 @@
|
|||
/* DDR defines for MMU table setup (used by boot_aarch64_start.S)
|
||||
* These macros enable proper DDR mapping in the page tables.
|
||||
* Without these, the MMU tables would have DDR_0_REG=0 and no DDR mapped!
|
||||
* VERSAL_NO_DDR_LOW leaves the low window (0x0-0x7FFFFFFF) unmapped, for
|
||||
* designs whose DDR aperture lives entirely above 4 GB; pair it with
|
||||
* VERSAL_DDR_HIGH_BASE/VERSAL_DDR_HIGH_SIZE (512 GB aligned, plain hex
|
||||
* literals with no UL/ULL suffix - they are consumed by the assembler),
|
||||
* which map an additional Normal-memory window in the translation table. */
|
||||
#if defined(VERSAL_DDR_HIGH_BASE) && !defined(VERSAL_DDR_HIGH_SIZE)
|
||||
#error "VERSAL_DDR_HIGH_BASE requires VERSAL_DDR_HIGH_SIZE"
|
||||
* Set from a .config with DDR_HIGH_BASE / DDR_HIGH_SIZE / NO_DDR_LOW=1
|
||||
* (see options.mk); those are AArch64-generic, not Versal-specific. */
|
||||
|
||||
/* Deprecated VERSAL_ spellings, kept because they shipped in v2.10.0. */
|
||||
#if defined(VERSAL_DDR_HIGH_BASE) && !defined(WOLFBOOT_DDR_HIGH_BASE)
|
||||
#define WOLFBOOT_DDR_HIGH_BASE VERSAL_DDR_HIGH_BASE
|
||||
#endif
|
||||
#if defined(VERSAL_DDR_HIGH_SIZE) && !defined(VERSAL_DDR_HIGH_BASE)
|
||||
#error "VERSAL_DDR_HIGH_SIZE requires VERSAL_DDR_HIGH_BASE"
|
||||
#if defined(VERSAL_DDR_HIGH_SIZE) && !defined(WOLFBOOT_DDR_HIGH_SIZE)
|
||||
#define WOLFBOOT_DDR_HIGH_SIZE VERSAL_DDR_HIGH_SIZE
|
||||
#endif
|
||||
#if defined(VERSAL_NO_DDR_LOW) && !defined(VERSAL_DDR_HIGH_BASE)
|
||||
#error "VERSAL_NO_DDR_LOW without VERSAL_DDR_HIGH_BASE maps no DDR at all"
|
||||
#if defined(VERSAL_NO_DDR_LOW) && !defined(WOLFBOOT_NO_DDR_LOW)
|
||||
#define WOLFBOOT_NO_DDR_LOW
|
||||
#endif
|
||||
#ifndef VERSAL_NO_DDR_LOW
|
||||
|
||||
#if defined(WOLFBOOT_DDR_HIGH_BASE) && !defined(WOLFBOOT_DDR_HIGH_SIZE)
|
||||
#error "DDR_HIGH_BASE requires DDR_HIGH_SIZE"
|
||||
#endif
|
||||
#if defined(WOLFBOOT_DDR_HIGH_SIZE) && !defined(WOLFBOOT_DDR_HIGH_BASE)
|
||||
#error "DDR_HIGH_SIZE requires DDR_HIGH_BASE"
|
||||
#endif
|
||||
#if defined(WOLFBOOT_NO_DDR_LOW) && !defined(WOLFBOOT_DDR_HIGH_BASE)
|
||||
#error "NO_DDR_LOW without DDR_HIGH_BASE maps no DDR at all"
|
||||
#endif
|
||||
/* WOLFBOOT_NO_DDR_LOW is honored in src/boot_aarch64_start.S. */
|
||||
#define XPAR_PSU_DDR_0_S_AXI_BASEADDR VERSAL_DDR_0_BASE
|
||||
#define XPAR_PSU_DDR_0_S_AXI_HIGHADDR VERSAL_DDR_0_HIGH
|
||||
#endif
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
|
|
|
|||
|
|
@ -415,11 +415,15 @@ void* fit_load_image_ex(fdt_ctx* ctx, const char* image, int* lenp,
|
|||
void* fit_load_image_to(fdt_ctx* ctx, const char* image, void* dst,
|
||||
uint32_t dst_max, int* lenp);
|
||||
|
||||
/* Load the FIT kernel, relocated to WOLFBOOT_LOAD_KERNEL_ADDRESS when that
|
||||
* is nonzero. Returns the entry address, or NULL on failure. */
|
||||
void* fit_load_kernel(fdt_ctx* ctx, const char* kernel_node, int* lenp);
|
||||
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* Load a FIT ramdisk sub-image (optionally relocated to
|
||||
* WOLFBOOT_LOAD_RAMDISK_ADDRESS) and patch /chosen/linux,initrd-* in
|
||||
* `dts`. Pass dts as NULL to skip the fixup. Returns 0 on success, -1
|
||||
* on load failure. Callers typically log and continue. */
|
||||
* `dts`. Returns 0 on success, -1 on load failure or when `dts` is NULL,
|
||||
* since a staged ramdisk is useless unless the tree names it. */
|
||||
int fit_load_ramdisk(fdt_ctx* ctx, const char* ramdisk_node, fdt_ctx* dts);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -260,15 +260,4 @@
|
|||
@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@
|
||||
#define WOLFBOOT_LOAD_DTS_ADDRESS @WOLFBOOT_LOAD_DTS_ADDRESS@
|
||||
|
||||
/* Load address for FIT ramdisk extraction (used when WOLFBOOT_FIT_RAMDISK
|
||||
* is set; otherwise unused). 0 means "use the FIT image's `load` property
|
||||
* verbatim — do not relocate". */
|
||||
#define WOLFBOOT_LOAD_RAMDISK_ADDRESS @WOLFBOOT_LOAD_RAMDISK_ADDRESS@
|
||||
|
||||
/* DDR staging address for the (decompressed) FPGA bitstream (used when
|
||||
* WOLFBOOT_FPGA_BITSTREAM is set; otherwise unused). 0 means "use the FIT
|
||||
* image's `load` property verbatim". */
|
||||
#define WOLFBOOT_LOAD_FPGA_ADDRESS @WOLFBOOT_LOAD_FPGA_ADDRESS@
|
||||
|
||||
|
||||
#endif /* !H_TARGETS_TARGET_ */
|
||||
|
|
|
|||
49
options.mk
49
options.mk
|
|
@ -1149,6 +1149,25 @@ FIT_RAMDISK ?= 0
|
|||
ifeq ($(FIT_RAMDISK),1)
|
||||
CFLAGS+=-DWOLFBOOT_FIT_RAMDISK
|
||||
endif
|
||||
# Destination for the (decompressed) ramdisk. 0 honors the FIT's own `load`.
|
||||
#
|
||||
# This and the two load addresses below are CFLAGS, not include/target.h.in
|
||||
# entries: target.h is generated by three paths and only this Makefile
|
||||
# substitutes them. Defaulted here and in tools/config.mk, since the host
|
||||
# tools include options.mk on its own.
|
||||
WOLFBOOT_LOAD_RAMDISK_ADDRESS ?= 0
|
||||
ifneq ($(WOLFBOOT_LOAD_RAMDISK_ADDRESS),0)
|
||||
CFLAGS+=-DWOLFBOOT_LOAD_RAMDISK_ADDRESS=$(WOLFBOOT_LOAD_RAMDISK_ADDRESS)
|
||||
endif
|
||||
|
||||
# Load address for the FIT kernel sub-image. 0 (the default) honors the
|
||||
# FIT's own `load`. Set this when the FIT was built without `load`/`entry`
|
||||
# on the kernel node; pick a region clear of WOLFBOOT_LOAD_ADDRESS and of
|
||||
# the DTB/ramdisk addresses.
|
||||
WOLFBOOT_LOAD_KERNEL_ADDRESS ?= 0
|
||||
ifneq ($(WOLFBOOT_LOAD_KERNEL_ADDRESS),0)
|
||||
CFLAGS+=-DWOLFBOOT_LOAD_KERNEL_ADDRESS=$(WOLFBOOT_LOAD_KERNEL_ADDRESS)
|
||||
endif
|
||||
|
||||
# FPGA_BITSTREAM=1 enables loading an "fpga" sub-image from a FIT and
|
||||
# programming the PL before booting (Xilinx ZynqMP/Zynq-7000; Versal is
|
||||
|
|
@ -1162,8 +1181,36 @@ endif
|
|||
# it is decompressed to this address before the PL is programmed. 0 (the
|
||||
# default) means "honor the FIT's own `load` property instead". Pick a
|
||||
# region clear of the FIT staging area (WOLFBOOT_LOAD_ADDRESS) and the
|
||||
# kernel/DTB/ramdisk load addresses.
|
||||
# kernel/DTB/ramdisk load addresses. A CFLAG for the same reason as above.
|
||||
WOLFBOOT_LOAD_FPGA_ADDRESS ?= 0
|
||||
ifneq ($(WOLFBOOT_LOAD_FPGA_ADDRESS),0)
|
||||
CFLAGS+=-DWOLFBOOT_LOAD_FPGA_ADDRESS=$(WOLFBOOT_LOAD_FPGA_ADDRESS)
|
||||
endif
|
||||
# High DDR aperture (AArch64, shared table in src/boot_aarch64_start.S).
|
||||
# DDR_HIGH_BASE/DDR_HIGH_SIZE add a Normal-memory window; NO_DDR_LOW=1
|
||||
# drops the default low-DDR mapping. Both 512 GB aligned, plain hex with
|
||||
# no UL/ULL suffix - the assembler consumes them and bounds-checks them.
|
||||
DDR_HIGH_BASE ?=
|
||||
DDR_HIGH_SIZE ?=
|
||||
NO_DDR_LOW ?= 0
|
||||
ifneq ($(DDR_HIGH_BASE),)
|
||||
ifeq ($(DDR_HIGH_SIZE),)
|
||||
$(error DDR_HIGH_BASE requires DDR_HIGH_SIZE)
|
||||
endif
|
||||
CFLAGS+=-DWOLFBOOT_DDR_HIGH_BASE=$(DDR_HIGH_BASE)
|
||||
CFLAGS+=-DWOLFBOOT_DDR_HIGH_SIZE=$(DDR_HIGH_SIZE)
|
||||
else
|
||||
ifneq ($(DDR_HIGH_SIZE),)
|
||||
$(error DDR_HIGH_SIZE requires DDR_HIGH_BASE)
|
||||
endif
|
||||
endif
|
||||
ifeq ($(NO_DDR_LOW),1)
|
||||
ifeq ($(DDR_HIGH_BASE),)
|
||||
$(error NO_DDR_LOW requires DDR_HIGH_BASE, or no DDR is mapped at all)
|
||||
endif
|
||||
CFLAGS+=-DWOLFBOOT_NO_DDR_LOW
|
||||
endif
|
||||
|
||||
# FPGA_NONFATAL=1 downgrades a failed PL load from fatal (panic) to a
|
||||
# logged warning that continues the boot.
|
||||
FPGA_NONFATAL ?= 0
|
||||
|
|
|
|||
|
|
@ -161,10 +161,10 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset, const uint32_t* dts_offset)
|
|||
void RAMFUNCTION do_boot(const uint32_t *app_offset)
|
||||
#endif
|
||||
{
|
||||
wolfBoot_printf("do_boot: entry=0x%08x, EL=%d\n",
|
||||
(uint32_t)(uintptr_t)app_offset, current_el());
|
||||
wolfBoot_printf("do_boot: entry=%p, EL=%d\n", (void*)app_offset,
|
||||
current_el());
|
||||
#ifdef MMU
|
||||
wolfBoot_printf("do_boot: dts=0x%08x\n", (uint32_t)(uintptr_t)dts_offset);
|
||||
wolfBoot_printf("do_boot: dts=%p\n", (void*)dts_offset);
|
||||
/* WOLFBOOT_DTS_MAX_SIZE is this target's DTS staging-window size
|
||||
* (see include/fdt.h); it bounds the fixups below. */
|
||||
if (hal_dts_fixup((uint32_t*)dts_offset, WOLFBOOT_DTS_MAX_SIZE) != 0) {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,16 @@
|
|||
#include "hal/imx8qm.h"
|
||||
#endif
|
||||
|
||||
/* VA region the TCR_EL{1,2,3} T0SZ values below cover. A high DDR window
|
||||
* must fit inside it; keep in step with those TCR literals. */
|
||||
#ifndef WOLFBOOT_AARCH64_VA_LIMIT
|
||||
#ifdef TARGET_versal
|
||||
#define WOLFBOOT_AARCH64_VA_LIMIT 0x100000000000 /* 2^44, TCR 0x80843514 */
|
||||
#else
|
||||
#define WOLFBOOT_AARCH64_VA_LIMIT 0x10000000000 /* 2^40, TCR 0x80823518 */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* GICv2 Register Offsets */
|
||||
#ifndef GICD_BASE
|
||||
#define GICD_BASE 0xF9010000
|
||||
|
|
@ -573,34 +583,35 @@ MMUTableL0:
|
|||
.set SECT, MMUTableL1+0x1000 /* 0x80_0000_0000 - 0xFF_FFFF_FFFF */
|
||||
.8byte SECT + 0x3
|
||||
|
||||
#if defined(TARGET_versal) && defined(VERSAL_DDR_HIGH_BASE)
|
||||
#ifdef WOLFBOOT_DDR_HIGH_BASE
|
||||
/* Optional high DDR window (designs whose DDR aperture sits above 1 TB).
|
||||
* One L0 descriptor per 512 GB, pointing at MMUTableL1_HI pages below.
|
||||
* Base and size must be 512 GB aligned and end within the 44-bit space. */
|
||||
.if (VERSAL_DDR_HIGH_BASE % 0x8000000000) != 0
|
||||
.error "VERSAL_DDR_HIGH_BASE must be 512GB aligned"
|
||||
* Base and size must be 512 GB aligned and the window must end inside the
|
||||
* VA region this target's TCR covers. */
|
||||
.if (WOLFBOOT_DDR_HIGH_BASE % 0x8000000000) != 0
|
||||
.error "WOLFBOOT_DDR_HIGH_BASE must be 512GB aligned"
|
||||
.endif
|
||||
.if VERSAL_DDR_HIGH_SIZE < 0x8000000000
|
||||
.error "VERSAL_DDR_HIGH_SIZE must be at least 512GB (one L0 granule)"
|
||||
.if WOLFBOOT_DDR_HIGH_SIZE < 0x8000000000
|
||||
.error "WOLFBOOT_DDR_HIGH_SIZE must be at least 512GB (one L0 granule)"
|
||||
.endif
|
||||
.if (VERSAL_DDR_HIGH_SIZE % 0x8000000000) != 0
|
||||
.error "VERSAL_DDR_HIGH_SIZE must be a multiple of 512GB"
|
||||
.if (WOLFBOOT_DDR_HIGH_SIZE % 0x8000000000) != 0
|
||||
.error "WOLFBOOT_DDR_HIGH_SIZE must be a multiple of 512GB"
|
||||
.endif
|
||||
.if (VERSAL_DDR_HIGH_BASE + VERSAL_DDR_HIGH_SIZE) > 0x100000000000
|
||||
.error "VERSAL_DDR_HIGH window exceeds the 44-bit address space"
|
||||
.if (WOLFBOOT_DDR_HIGH_BASE + WOLFBOOT_DDR_HIGH_SIZE) > WOLFBOOT_AARCH64_VA_LIMIT
|
||||
.error "DDR_HIGH window exceeds the VA space this target's TCR covers"
|
||||
.endif
|
||||
.if VERSAL_DDR_HIGH_BASE < 0x10000000000
|
||||
.error "VERSAL_DDR_HIGH_BASE must be at least 1TB (the first two L0 entries are fixed)"
|
||||
.if WOLFBOOT_DDR_HIGH_BASE < 0x10000000000
|
||||
.error "WOLFBOOT_DDR_HIGH_BASE must be at least 1TB (the first two L0 entries are fixed)"
|
||||
.endif
|
||||
.rept ((VERSAL_DDR_HIGH_BASE / 0x8000000000) - 2)
|
||||
.rept ((WOLFBOOT_DDR_HIGH_BASE / 0x8000000000) - 2)
|
||||
.8byte 0 /* invalid up to the high window */
|
||||
.endr
|
||||
.set SECT, MMUTableL1_HI
|
||||
.rept (VERSAL_DDR_HIGH_SIZE / 0x8000000000)
|
||||
.rept (WOLFBOOT_DDR_HIGH_SIZE / 0x8000000000)
|
||||
.8byte SECT + 0x3
|
||||
.set SECT, SECT + 0x1000
|
||||
.endr
|
||||
#endif /* TARGET_versal && VERSAL_DDR_HIGH_BASE */
|
||||
#endif /* WOLFBOOT_DDR_HIGH_BASE */
|
||||
|
||||
.section .mmu_tbl1,"a"
|
||||
|
||||
|
|
@ -670,16 +681,16 @@ MMUTableL1:
|
|||
.endr
|
||||
|
||||
|
||||
#if defined(TARGET_versal) && defined(VERSAL_DDR_HIGH_BASE)
|
||||
#ifdef WOLFBOOT_DDR_HIGH_BASE
|
||||
/* 1 GB Normal-memory blocks covering the high DDR window. */
|
||||
.balign 4096
|
||||
MMUTableL1_HI:
|
||||
.set SECT, VERSAL_DDR_HIGH_BASE
|
||||
.rept (VERSAL_DDR_HIGH_SIZE / 0x40000000)
|
||||
.set SECT, WOLFBOOT_DDR_HIGH_BASE
|
||||
.rept (WOLFBOOT_DDR_HIGH_SIZE / 0x40000000)
|
||||
.8byte SECT + Memory
|
||||
.set SECT, SECT + 0x40000000
|
||||
.endr
|
||||
#endif /* TARGET_versal && VERSAL_DDR_HIGH_BASE */
|
||||
#endif /* WOLFBOOT_DDR_HIGH_BASE */
|
||||
|
||||
.section .mmu_tbl2,"a"
|
||||
|
||||
|
|
@ -695,7 +706,9 @@ MMUTableL2:
|
|||
.set SECT, SECT+0x200000
|
||||
.endr
|
||||
#else
|
||||
#ifdef XPAR_PSU_DDR_0_S_AXI_BASEADDR
|
||||
/* WOLFBOOT_NO_DDR_LOW drops this window for designs whose DDR lives
|
||||
* entirely in a high aperture. */
|
||||
#if defined(XPAR_PSU_DDR_0_S_AXI_BASEADDR) && !defined(WOLFBOOT_NO_DDR_LOW)
|
||||
.set DDR_0_START, XPAR_PSU_DDR_0_S_AXI_BASEADDR
|
||||
.set DDR_0_END, XPAR_PSU_DDR_0_S_AXI_HIGHADDR
|
||||
.set DDR_0_SIZE, (DDR_0_END - DDR_0_START)+1
|
||||
|
|
|
|||
141
src/fdt.c
141
src/fdt.c
|
|
@ -47,6 +47,12 @@
|
|||
#include "gzip.h"
|
||||
#endif
|
||||
|
||||
/* Largest address a pointer can hold. Overridable so the two-cell
|
||||
* rejection below can be exercised at 32-bit width on a 64-bit host. */
|
||||
#ifndef FDT_PTR_MAX
|
||||
#define FDT_PTR_MAX UINTPTR_MAX
|
||||
#endif
|
||||
|
||||
/* Coarse upper bound on a single FIT subimage's decompressed size.
|
||||
* This is a sanity ceiling, not a per-destination memory-safety
|
||||
* bound. Authenticity of the FIT bytes is provided by the outer
|
||||
|
|
@ -812,7 +818,15 @@ void* fdt_getprop_address(const fdt_ctx* ctx, int nodeoffset, const char* name)
|
|||
return NULL;
|
||||
}
|
||||
if (len == 8) {
|
||||
return (void*)(uintptr_t)fdt_rd64u(val);
|
||||
uint64_t addr64 = fdt_rd64u(val);
|
||||
/* Reject rather than truncate: dropping the high cell would turn
|
||||
* an out-of-range address into an unrelated low one. */
|
||||
#if FDT_PTR_MAX < UINT64_MAX
|
||||
if (addr64 > (uint64_t)FDT_PTR_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return (void*)(uintptr_t)addr64;
|
||||
}
|
||||
if (len == 4) {
|
||||
return (void*)(uintptr_t)fdt_rd32(val);
|
||||
|
|
@ -1674,6 +1688,28 @@ const char* fit_get_compatible(fdt_ctx* ctx, const char* image)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* 0 honors the FIT's own `load` property. */
|
||||
#ifndef WOLFBOOT_LOAD_KERNEL_ADDRESS
|
||||
#define WOLFBOOT_LOAD_KERNEL_ADDRESS 0
|
||||
#endif
|
||||
|
||||
/* Upper bound on the decompressed kernel size. */
|
||||
#ifndef WOLFBOOT_FIT_MAX_KERNEL
|
||||
#define WOLFBOOT_FIT_MAX_KERNEL WOLFBOOT_FIT_MAX_DECOMP
|
||||
#endif
|
||||
|
||||
/* Stage the kernel at WOLFBOOT_LOAD_KERNEL_ADDRESS when nonzero, bypassing
|
||||
* the FIT's `load`/`entry` so a FIT declaring neither still boots. */
|
||||
void* fit_load_kernel(fdt_ctx* ctx, const char* kernel_node, int* lenp)
|
||||
{
|
||||
if (WOLFBOOT_LOAD_KERNEL_ADDRESS != 0) {
|
||||
return fit_load_image_to(ctx, kernel_node,
|
||||
(void*)WOLFBOOT_LOAD_KERNEL_ADDRESS,
|
||||
(uint32_t)WOLFBOOT_FIT_MAX_KERNEL, lenp);
|
||||
}
|
||||
return fit_load_image(ctx, kernel_node, lenp);
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* Defensive fallback: targets without a fixed relocation address
|
||||
* leave WOLFBOOT_LOAD_RAMDISK_ADDRESS at 0, in which case the
|
||||
|
|
@ -1697,12 +1733,12 @@ const char* fit_get_compatible(fdt_ctx* ctx, const char* image)
|
|||
* straight into the override (with the override capacity acting as
|
||||
* the safety bound). Otherwise the address fit_load_image returned
|
||||
* (FIT-specified or in-FIT pointer) is used as-is. Caller passes the
|
||||
* DTB context for the initrd fixup, or NULL to skip the fixup.
|
||||
* DTB context the initrd fixup writes into.
|
||||
*
|
||||
* Returns 0 on success, -1 if the ramdisk node was found but the
|
||||
* load failed. The current callers ignore the return value
|
||||
* (log-and-continue), so a missing/failed ramdisk does not abort
|
||||
* the boot. */
|
||||
* Returns 0 on success, -1 if the ramdisk node was found but the load
|
||||
* failed, or if `dts` is NULL - a ramdisk the kernel is never told
|
||||
* about is not a success. Callers log and continue, so this does not
|
||||
* abort the boot. */
|
||||
int fit_load_ramdisk(fdt_ctx* ctx, const char* ramdisk_node, fdt_ctx* dts)
|
||||
{
|
||||
int rd_size = 0;
|
||||
|
|
@ -1733,7 +1769,14 @@ int fit_load_ramdisk(fdt_ctx* ctx, const char* ramdisk_node, fdt_ctx* dts)
|
|||
wolfBoot_printf("Loaded ramdisk: %p (%d bytes)\n", rd_dst, rd_size);
|
||||
}
|
||||
|
||||
if (dts != NULL) {
|
||||
if (dts == NULL) {
|
||||
/* Staged but unreachable: without a tree to write
|
||||
* /chosen/linux,initrd-* into, the kernel never learns of it. */
|
||||
wolfBoot_printf("FIT: no device tree for the initrd fixup; "
|
||||
"kernel will not see initrd\n");
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
int frc = fdt_fixup_initrd(dts, (uint64_t)(uintptr_t)rd_dst,
|
||||
(uint64_t)rd_size);
|
||||
if (frc != 0) {
|
||||
|
|
@ -1773,6 +1816,46 @@ int __attribute__((weak)) wolfBoot_fit_check_dest(void *dst, uint32_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Narrow `out_max` to what may actually be written at `load`: anything above
|
||||
* it that has to survive the load caps the window - the staged FIT, whose
|
||||
* header and structure block the later `fdt`/`ramdisk` lookups still read,
|
||||
* and wolfBoot's own image. A gzip stream can emit garbage at full match
|
||||
* speed and a raw subimage is copied just as blindly, so both paths need the
|
||||
* same bound. Conservative lower bounds. */
|
||||
static uint32_t fit_dest_bound(const fdt_ctx* ctx, const void* load,
|
||||
const void* data, uint32_t out_max)
|
||||
{
|
||||
uintptr_t gap;
|
||||
|
||||
if ((uintptr_t)ctx->blob > (uintptr_t)load) {
|
||||
gap = (uintptr_t)ctx->blob - (uintptr_t)load;
|
||||
if (gap < (uintptr_t)out_max) {
|
||||
out_max = (uint32_t)gap;
|
||||
}
|
||||
}
|
||||
/* A destination inside the blob but below `data` is past the bound
|
||||
* above, so the data pointer still has to cap it. */
|
||||
if ((uintptr_t)data > (uintptr_t)load) {
|
||||
gap = (uintptr_t)data - (uintptr_t)load;
|
||||
if (gap < (uintptr_t)out_max) {
|
||||
out_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)out_max) {
|
||||
out_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("FIT: no _start_text, load not bounded by the "
|
||||
"wolfBoot image\n");
|
||||
}
|
||||
return out_max;
|
||||
}
|
||||
|
||||
/* Inner implementation shared by fit_load_image_ex and fit_load_image_to.
|
||||
* When dst_override is non-NULL it replaces the FIT image's `load`
|
||||
* property as the destination, so a compressed (gzip) payload is
|
||||
|
|
@ -1855,35 +1938,10 @@ 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;
|
||||
/* out_max is only a sanity ceiling; the inflate window
|
||||
* must also stop below anything that has to survive it. */
|
||||
uint32_t gz_max = fit_dest_bound(ctx, load, data, out_max);
|
||||
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");
|
||||
}
|
||||
if (wolfBoot_fit_check_dest(load, gz_max) != 0) {
|
||||
wolfBoot_printf("FIT: %s decompression destination %p "
|
||||
"rejected\n", image, load);
|
||||
|
|
@ -1921,9 +1979,14 @@ static void* fit_load_image_inner(fdt_ctx* ctx, const char* image, int* lenp,
|
|||
return NULL;
|
||||
}
|
||||
else {
|
||||
if (len < 0 || (uint32_t)len > out_max) {
|
||||
/* Same bound as the inflate above: a raw subimage too
|
||||
* large for the space below the staged FIT or below
|
||||
* wolfBoot itself would be copied over them. */
|
||||
uint32_t cp_max = fit_dest_bound(ctx, load, data, out_max);
|
||||
|
||||
if (len < 0 || (uint32_t)len > cp_max) {
|
||||
wolfBoot_printf("FIT: subimage '%s' size %d exceeds "
|
||||
"staging bound %u\n", image, len, out_max);
|
||||
"staging bound %u\n", image, len, cp_max);
|
||||
return NULL;
|
||||
}
|
||||
wolfBoot_printf("Loading Image %s: %p -> %p "
|
||||
|
|
@ -1959,7 +2022,9 @@ static void* fit_load_image_inner(fdt_ctx* ctx, const char* image, int* lenp,
|
|||
wolfBoot_printf("FIT: subimage '%s' declares "
|
||||
"compression=\"%s\" but has no distinct load "
|
||||
"destination (load=%p, data=%p); refusing to pass "
|
||||
"compressed bytes through as raw\n",
|
||||
"compressed bytes through as raw. Add `load` to the "
|
||||
"FIT node, or set this sub-image's "
|
||||
"WOLFBOOT_LOAD_*_ADDRESS\n",
|
||||
image, compstr, load, data);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -677,6 +677,11 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
uint32_t dts_size = 0;
|
||||
/* Validated view of the FIT staged at load_address. */
|
||||
fdt_ctx fit_ctx;
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* FIT ramdisk node, set once the FIT parses. The initrd fixup is
|
||||
* deferred until dts_addr is final. */
|
||||
const char *fit_ramdisk = NULL;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
|
|
@ -753,7 +758,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
load_address = (uint32_t *)((((uintptr_t)_end_wb) + 0xf) & ~0xf);
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Load address %p\r\n", load_address);
|
||||
wolfBoot_printf("Load address %p\r\n", (void*)load_address);
|
||||
|
||||
/* Upper bound on anything the media may claim about the image size.
|
||||
* The payload is copied into the load region before its signature is
|
||||
|
|
@ -1179,7 +1184,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
(void)fpga;
|
||||
#endif
|
||||
if (kernel != NULL) {
|
||||
void *new_load = fit_load_image(fit, kernel, NULL);
|
||||
void *new_load = fit_load_kernel(fit, kernel, NULL);
|
||||
if (new_load == NULL) {
|
||||
wolfBoot_printf("FIT: failed to load kernel '%s'\r\n",
|
||||
kernel);
|
||||
|
|
@ -1230,20 +1235,16 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
wolfBoot_panic();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Do not fall through to the firmware DTB silently: it is
|
||||
* a different device tree than the FIT names. */
|
||||
wolfBoot_printf("FIT: '%s' is not a usable DTB (%d); "
|
||||
"trying the boot firmware's instead\r\n", flat_dt,
|
||||
parsed);
|
||||
}
|
||||
}
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
if (ramdisk != NULL) {
|
||||
fdt_ctx dts_ctx;
|
||||
fdt_ctx* dts_for_initrd = NULL;
|
||||
|
||||
/* The relocated DTB sits in the staging window, so that is
|
||||
* the capacity the initrd fixup may grow into. */
|
||||
if (dts_addr != NULL &&
|
||||
fdt_open(&dts_ctx, dts_addr, WOLFBOOT_DTS_MAX_SIZE) == 0) {
|
||||
dts_for_initrd = &dts_ctx;
|
||||
}
|
||||
(void)fit_load_ramdisk(fit, ramdisk, dts_for_initrd);
|
||||
}
|
||||
fit_ramdisk = ramdisk;
|
||||
#else
|
||||
(void)ramdisk;
|
||||
#endif
|
||||
|
|
@ -1259,7 +1260,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Booting at %p\r\n", load_address);
|
||||
wolfBoot_printf("Booting at %p\r\n", (void*)load_address);
|
||||
|
||||
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
|
||||
(void)hal_hsm_disconnect();
|
||||
|
|
@ -1292,6 +1293,55 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
if (dts_addr == NULL) {
|
||||
dts_addr = (uint8_t*)hal_get_boot_dts();
|
||||
}
|
||||
if (dts_addr == NULL) {
|
||||
/* Fall back to the firmware DTB, forwarded unauthenticated: no
|
||||
* digest check here, and only a mapped HAL pointer is usable. */
|
||||
uint8_t *cfg_dts = (uint8_t*)hal_get_dts_address();
|
||||
int cfg_len;
|
||||
|
||||
if (cfg_dts != NULL) {
|
||||
cfg_len = wolfBoot_get_dts_size(cfg_dts, WOLFBOOT_DTS_MAX_SIZE);
|
||||
if (cfg_len < (int)WOLFBOOT_DTS_MIN_SIZE ||
|
||||
(uint32_t)cfg_len > WOLFBOOT_DTS_MAX_SIZE) {
|
||||
wolfBoot_printf("DTB parse/size check failed - ignoring\r\n");
|
||||
}
|
||||
else if ((uintptr_t)cfg_dts ==
|
||||
(uintptr_t)WOLFBOOT_LOAD_DTS_ADDRESS) {
|
||||
/* Usual case: the HAL returns the staging address. */
|
||||
dts_addr = cfg_dts;
|
||||
}
|
||||
else if (wolfBoot_fit_memcpy((void*)WOLFBOOT_LOAD_DTS_ADDRESS,
|
||||
cfg_dts, (uint32_t)cfg_len) == 0) {
|
||||
/* Relocate first: the initrd fixup grows the tree in place
|
||||
* against WOLFBOOT_DTS_MAX_SIZE of writable space. */
|
||||
dts_addr = (uint8_t*)WOLFBOOT_LOAD_DTS_ADDRESS;
|
||||
}
|
||||
else {
|
||||
wolfBoot_printf("DTB relocation failed - ignoring\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(WOLFBOOT_FDT) && defined(WOLFBOOT_FIT_RAMDISK)
|
||||
/* Deferred until dts_addr is final so the fixup lands on the tree the
|
||||
* kernel is handed. Outside the MMU guard for the MPFS E51 boot. */
|
||||
if (fit_ramdisk != NULL) {
|
||||
fdt_ctx dts_ctx;
|
||||
fdt_ctx* dts_for_initrd = NULL;
|
||||
|
||||
/* The DTB is in the staging window; that is the growth capacity. */
|
||||
if (dts_addr != NULL &&
|
||||
fdt_open(&dts_ctx, dts_addr, WOLFBOOT_DTS_MAX_SIZE) == 0) {
|
||||
dts_for_initrd = &dts_ctx;
|
||||
}
|
||||
else if (dts_addr != NULL) {
|
||||
wolfBoot_printf("FIT: DTB at %p is not usable for the initrd "
|
||||
"fixup\n", dts_addr);
|
||||
}
|
||||
if (fit_load_ramdisk(&fit_ctx, fit_ramdisk, dts_for_initrd) != 0) {
|
||||
wolfBoot_printf("FIT: booting without an initrd\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Deferred from just after verification (see NOTE above): close the boot
|
||||
* disk now that all env / DTB reads and writes are done, before handoff. */
|
||||
|
|
|
|||
|
|
@ -302,6 +302,11 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
uint32_t dts_size = 0;
|
||||
/* Validated view of the FIT staged at load_address. */
|
||||
fdt_ctx fit_ctx;
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* FIT ramdisk node, set once the FIT parses. The initrd fixup is
|
||||
* deferred until dts_addr is final. */
|
||||
const char *fit_ramdisk = NULL;
|
||||
#endif
|
||||
/* HDR_DEVICE_TREE_DIGEST snapshot, taken before the raw DTB is loaded. */
|
||||
uint8_t dts_digest[WOLFBOOT_SHA_DIGEST_SIZE];
|
||||
uint8_t *dts_tlv = NULL;
|
||||
|
|
@ -642,7 +647,7 @@ backup_on_failure:
|
|||
(void)fpga;
|
||||
#endif
|
||||
if (kernel != NULL) {
|
||||
void *new_load = fit_load_image(fit, kernel, NULL);
|
||||
void *new_load = fit_load_kernel(fit, kernel, NULL);
|
||||
if (new_load == NULL) {
|
||||
wolfBoot_printf("FIT: failed to load kernel '%s'\n", kernel);
|
||||
wolfBoot_panic();
|
||||
|
|
@ -687,25 +692,23 @@ backup_on_failure:
|
|||
fdt_set_dtb_authenticated(1);
|
||||
memcpy(dts_addr, dts_ptr, dts_size);
|
||||
}
|
||||
else {
|
||||
/* Do not fall through to the firmware DTB silently: it is
|
||||
* a different device tree than the FIT names. */
|
||||
wolfBoot_printf("FIT: '%s' is not a usable DTB (%d); "
|
||||
"trying the boot firmware's instead\n", flat_dt,
|
||||
parsed);
|
||||
}
|
||||
}
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
if (ramdisk != NULL) {
|
||||
fdt_ctx dts_ctx;
|
||||
fdt_ctx* dts_for_initrd = NULL;
|
||||
|
||||
/* The relocated DTB sits in the staging window, so that is
|
||||
* the capacity the initrd fixup may grow into. */
|
||||
if (dts_addr != NULL &&
|
||||
fdt_open(&dts_ctx, dts_addr, WOLFBOOT_DTS_MAX_SIZE) == 0) {
|
||||
dts_for_initrd = &dts_ctx;
|
||||
}
|
||||
(void)fit_load_ramdisk(fit, ramdisk, dts_for_initrd);
|
||||
}
|
||||
fit_ramdisk = ramdisk;
|
||||
#else
|
||||
(void)ramdisk;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* Also reached when a FIT parsed but carried no usable `fdt`: the
|
||||
* device tree then comes from the boot firmware. */
|
||||
if (dts_addr == NULL) {
|
||||
/* Prefer the HAL's memory-mapped DTB (unchanged for XIP targets); fall
|
||||
* back to external flash at WOLFBOOT_DTS_BOOT_ADDRESS when the HAL has
|
||||
* no usable address (NULL, or a flash offset on NO_XIP targets). */
|
||||
|
|
@ -717,6 +720,11 @@ backup_on_failure:
|
|||
wolfBoot_printf("DTB parse/size check failed - ignoring\n");
|
||||
dts_addr = NULL; /* never forward an unvalidated address */
|
||||
}
|
||||
else if ((uintptr_t)dts_addr ==
|
||||
(uintptr_t)WOLFBOOT_LOAD_DTS_ADDRESS) {
|
||||
/* Usual case: the HAL returns the staging address. */
|
||||
dts_size = (uint32_t)ret;
|
||||
}
|
||||
else {
|
||||
dts_size = (uint32_t)ret;
|
||||
memcpy((void*)WOLFBOOT_LOAD_DTS_ADDRESS, dts_addr, dts_size);
|
||||
|
|
@ -786,6 +794,27 @@ backup_on_failure:
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* Deferred until dts_addr is final so the fixup lands on the tree the
|
||||
* kernel is handed. */
|
||||
if (fit_ramdisk != NULL) {
|
||||
fdt_ctx dts_ctx;
|
||||
fdt_ctx* dts_for_initrd = NULL;
|
||||
|
||||
/* The DTB is in the staging window; that is the growth capacity. */
|
||||
if (dts_addr != NULL &&
|
||||
fdt_open(&dts_ctx, dts_addr, WOLFBOOT_DTS_MAX_SIZE) == 0) {
|
||||
dts_for_initrd = &dts_ctx;
|
||||
}
|
||||
else if (dts_addr != NULL) {
|
||||
wolfBoot_printf("FIT: DTB at %p is not usable for the initrd "
|
||||
"fixup\n", dts_addr);
|
||||
}
|
||||
if (fit_load_ramdisk(&fit_ctx, fit_ramdisk, dts_for_initrd) != 0) {
|
||||
wolfBoot_printf("FIT: booting without an initrd\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* MMU */
|
||||
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ endif
|
|||
# Defaulted globally (outside the CI ifeq block above) so FIT_RAMDISK=1
|
||||
# can be toggled on any target without forcing an explicit address.
|
||||
WOLFBOOT_LOAD_RAMDISK_ADDRESS?=0
|
||||
WOLFBOOT_LOAD_KERNEL_ADDRESS?=0
|
||||
|
||||
# Hardware watchdog, off by default. Defaulted globally (outside the CI ifeq
|
||||
# block above) so any target can enable it without editing its config.
|
||||
|
|
@ -130,6 +131,9 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO
|
|||
WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS \
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS WOLFBOOT_LOAD_ADDRESS \
|
||||
WOLFBOOT_LOAD_DTS_ADDRESS WOLFBOOT_LOAD_RAMDISK_ADDRESS \
|
||||
WOLFBOOT_LOAD_KERNEL_ADDRESS \
|
||||
DDR_HIGH_BASE DDR_HIGH_SIZE NO_DDR_LOW \
|
||||
BOOT_EL1 BOOT_EL2 \
|
||||
WOLFBOOT_DTS_BOOT_ADDRESS WOLFBOOT_DTS_UPDATE_ADDRESS \
|
||||
WOLFBOOT_SMALL_STACK DELTA_UPDATES DELTA_BLOCK_SIZE WOLFBOOT_IMG_HASH_ONESHOT \
|
||||
WOLFBOOT_HUGE_STACK FORCE_32BIT\
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ TESTS:=unit-parser unit-parser-large-header unit-fdt unit-extflash unit-string \
|
|||
unit-update-flash-hook \
|
||||
unit-update-flash-self-update \
|
||||
unit-nsc-update \
|
||||
unit-update-flash-enc unit-update-flash-enc-full unit-update-flash-enc-intswap unit-update-ram unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-nofixed-noramboot unit-update-ram-noramboot unit-update-ram-custom-trailer unit-custom-trailer-nopart unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
|
||||
unit-update-disk unit-update-disk-confirm unit-update-disk-golden unit-update-disk-fsp unit-update-disk-oob unit-update-disk-fit unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
|
||||
unit-update-flash-enc unit-update-flash-enc-full unit-update-flash-enc-intswap unit-update-ram unit-update-ram-fit unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-nofixed-noramboot unit-update-ram-noramboot unit-update-ram-custom-trailer unit-custom-trailer-nopart unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
|
||||
unit-update-disk unit-update-disk-confirm unit-update-disk-golden unit-update-disk-fsp unit-update-disk-oob unit-update-disk-fit unit-update-disk-fit-ramdisk unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
|
||||
unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-image-dts \
|
||||
unit-image-dts-sha384 unit-image-dts-sha3-384 unit-store-sbrk \
|
||||
unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \
|
||||
|
|
@ -154,6 +154,7 @@ TESTS+=unit-rp2350-flash-write
|
|||
TESTS+=unit-fwtpm-rsp-overrun
|
||||
TESTS+=unit-fwtpm-cmd-toctou
|
||||
TESTS+=unit-fdt-memrsv-wrap
|
||||
TESTS+=unit-fdt-addr32
|
||||
TESTS+=unit-ns16550
|
||||
TESTS+=unit-zynq-dma-range
|
||||
TESTS+=unit-pkcs11_store-stalecache
|
||||
|
|
@ -365,6 +366,13 @@ unit-update-flash-self-update:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNI
|
|||
-DRAM_CODE -DARCH_SIM -DUNIT_TEST_SELF_UPDATE_ONLY \
|
||||
-DARCH_FLASH_OFFSET=MOCK_ADDRESS_BOOT -DWOLFBOOT_VERSION=7 \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
unit-update-ram-fit:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
|
||||
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \
|
||||
-DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP \
|
||||
-DMMU -DWOLFBOOT_FDT -DWOLFBOOT_FIT_RAMDISK \
|
||||
-DWOLFBOOT_DTS_BOOT_ADDRESS=0xCF000000 \
|
||||
-DWOLFBOOT_DTS_UPDATE_ADDRESS=0xCF100000 \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
unit-update-ram:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
|
||||
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \
|
||||
-DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP \
|
||||
|
|
@ -463,9 +471,11 @@ unit-update-disk-oob:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED \
|
|||
# FIT (flattened uImage tree) exits of the encrypted disk loader. The panic hook
|
||||
# is what lets the test observe the key material at the instant wolfBoot_panic()
|
||||
# is entered, since on target that call never returns.
|
||||
unit-update-disk-fit:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_FDT \
|
||||
UNIT_DISK_FIT_CFLAGS:=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_FDT \
|
||||
-DWOLFBOOT_HOOK_PANIC -DWOLFBOOT_RAMBOOT_MAX_SIZE=0x40 \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
unit-update-disk-fit:CFLAGS+=$(UNIT_DISK_FIT_CFLAGS)
|
||||
unit-update-disk-fit-ramdisk:CFLAGS+=$(UNIT_DISK_FIT_CFLAGS)
|
||||
# Regression coverage for wolfBoot_check_flash_image_elf() (scattered-ELF
|
||||
# integrity check). WOLFBOOT_NO_SIGN keeps this to the hashing path only (no
|
||||
# signature verification is exercised by that function).
|
||||
|
|
@ -494,6 +504,13 @@ unit-fdt: ../../include/target.h unit-fdt.c ../../src/fdt.c
|
|||
gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \
|
||||
-Wl,--gc-sections
|
||||
|
||||
# Same suite with the pointer bound narrowed to 32 bits, so a two-cell
|
||||
# load/entry above 4 GB has to be rejected rather than truncated.
|
||||
unit-fdt-addr32:CFLAGS+=-DWOLFBOOT_FDT -DFDT_PTR_MAX=0xFFFFFFFFUL
|
||||
unit-fdt-addr32: ../../include/target.h unit-fdt.c ../../src/fdt.c
|
||||
gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \
|
||||
-Wl,--gc-sections
|
||||
|
||||
unit-extflash: ../../include/target.h unit-extflash.c
|
||||
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
@ -1002,6 +1019,7 @@ unit-gzip-root7: ../../include/target.h unit-gzip.c
|
|||
# failure paths) and once without (compile-time fail-closed path).
|
||||
unit-fit-gzip: ../../include/target.h unit-fit-gzip.c
|
||||
gcc -o $@ unit-fit-gzip.c $(CFLAGS) -DWOLFBOOT_FDT -DWOLFBOOT_GZIP \
|
||||
-DWOLFBOOT_FIT_RAMDISK \
|
||||
-DWOLFBOOT_NO_PRINTF \
|
||||
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
|
||||
|
||||
|
|
@ -1012,7 +1030,7 @@ unit-fit-nogzip: ../../include/target.h unit-fit-gzip.c
|
|||
|
||||
unit-fit-gzip-small: ../../include/target.h unit-fit-gzip.c
|
||||
gcc -o $@ unit-fit-gzip.c $(CFLAGS) -DWOLFBOOT_FDT -DWOLFBOOT_GZIP \
|
||||
-DWOLFBOOT_GZIP_SMALL -DWOLFBOOT_NO_PRINTF \
|
||||
-DWOLFBOOT_GZIP_SMALL -DWOLFBOOT_FIT_RAMDISK -DWOLFBOOT_NO_PRINTF \
|
||||
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
|
||||
|
||||
# FIT fpga-subimage discovery (fit_find_images / fit_get_compatible).
|
||||
|
|
@ -1114,6 +1132,11 @@ unit-update-flash-enc-intswap: ../../include/target.h unit-update-flash.c
|
|||
unit-update-ram: ../../include/target.h unit-update-ram.c
|
||||
gcc -o $@ unit-update-ram.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
# Loader-level coverage for the kernel-only-FIT device-tree fallback and the
|
||||
# deferred initrd fixup; the FDT/FIT API is stubbed inside the test.
|
||||
unit-update-ram-fit: ../../include/target.h unit-update-ram-fit.c
|
||||
gcc -o $@ unit-update-ram-fit.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-ram-uboot: ../../include/target.h unit-update-ram-uboot.c
|
||||
gcc -o $@ unit-update-ram-uboot.c ../../src/image.c ../../src/gpt.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
@ -1184,6 +1207,11 @@ unit-update-disk-fs-enc: ../../include/target.h unit-update-disk-fs.c
|
|||
unit-update-disk-fit: ../../include/target.h unit-update-disk-fit.c
|
||||
gcc -o $@ unit-update-disk-fit.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
# Same source with MMU and FIT_RAMDISK on: the device-tree fallback and the
|
||||
# deferred initrd fixup both live under #ifdef MMU in update_disk.c.
|
||||
unit-update-disk-fit-ramdisk: ../../include/target.h unit-update-disk-fit.c
|
||||
gcc -o $@ unit-update-disk-fit.c $(CFLAGS) -DWOLFBOOT_FIT_RAMDISK -DMMU $(LDFLAGS)
|
||||
|
||||
unit-pkcs11_store: ../../include/target.h unit-pkcs11_store.c
|
||||
gcc -o $@ $(WOLFCRYPT_SRC) unit-pkcs11_store.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@
|
|||
|
||||
#include "../../include/fdt.h"
|
||||
|
||||
/* Must match src/fdt.c; the unit-fdt-addr32 target narrows both to 32 bits
|
||||
* so the two-cell rejection runs on a 64-bit host. */
|
||||
#ifndef FDT_PTR_MAX
|
||||
#define FDT_PTR_MAX UINTPTR_MAX
|
||||
#endif
|
||||
|
||||
void wolfBoot_printf(const char *fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
|
|
@ -1306,6 +1312,53 @@ START_TEST(test_fdt_get_reg_two_address_two_size_cells)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fdt_getprop_address_two_cells)
|
||||
{
|
||||
/* A two-cell value is the only way a FIT can express an address above
|
||||
* 4 GB, so the 8-byte form has to decode whole. */
|
||||
static uint8_t buf[sizeof(chassis_dtb) + 256] __attribute__((aligned(8)));
|
||||
fdt_ctx ctx;
|
||||
uint8_t val[8];
|
||||
int off;
|
||||
|
||||
memcpy(buf, chassis_dtb, sizeof(chassis_dtb));
|
||||
ck_assert_int_eq(fdt_open(&ctx, buf, sizeof(buf)), 0);
|
||||
|
||||
off = fdt_get_alias(&ctx, "Chassis_Manager");
|
||||
ck_assert_int_ge(off, 0);
|
||||
|
||||
/* 0x00000400_08000000, big-endian, as mkimage emits it from
|
||||
* load = <0x00000400 0x08000000>; under #address-cells = <2> */
|
||||
val[0] = 0x00; val[1] = 0x00; val[2] = 0x04; val[3] = 0x00;
|
||||
val[4] = 0x08; val[5] = 0x00; val[6] = 0x00; val[7] = 0x00;
|
||||
ck_assert_int_eq(fdt_setprop(&ctx, off, "load", val, (int)sizeof(val)), 0);
|
||||
|
||||
#if FDT_PTR_MAX > 0xFFFFFFFFUL
|
||||
ck_assert_ptr_eq(fdt_getprop_address(&ctx, off, "load"),
|
||||
(void*)(uintptr_t)0x40008000000ULL);
|
||||
#else
|
||||
/* A 32-bit target cannot represent it; truncating would yield an
|
||||
* unrelated address, so it has to come back NULL. */
|
||||
ck_assert_ptr_null(fdt_getprop_address(&ctx, off, "load"));
|
||||
#endif
|
||||
|
||||
/* A two-cell value that does fit must still decode: #address-cells = <2>
|
||||
* is what mkimage emits on a 64-bit host even for a low address, so
|
||||
* rejecting these would break every such FIT on a 32-bit target. */
|
||||
val[0] = 0x00; val[1] = 0x00; val[2] = 0x00; val[3] = 0x00;
|
||||
val[4] = 0x08; val[5] = 0x00; val[6] = 0x00; val[7] = 0x00;
|
||||
ck_assert_int_eq(fdt_setprop(&ctx, off, "load", val, (int)sizeof(val)), 0);
|
||||
ck_assert_ptr_eq(fdt_getprop_address(&ctx, off, "load"),
|
||||
(void*)(uintptr_t)0x08000000UL);
|
||||
|
||||
/* A single cell still decodes. */
|
||||
val[0] = 0x00; val[1] = 0x20; val[2] = 0x00; val[3] = 0x00;
|
||||
ck_assert_int_eq(fdt_setprop(&ctx, off, "entry", val, 4), 0);
|
||||
ck_assert_ptr_eq(fdt_getprop_address(&ctx, off, "entry"),
|
||||
(void*)(uintptr_t)0x200000UL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fdt_get_reg_one_cell_and_index)
|
||||
{
|
||||
static uint8_t buf[sizeof(chassis_dtb) + 16] __attribute__((aligned(8)));
|
||||
|
|
@ -1517,6 +1570,7 @@ static Suite *fdt_suite(void)
|
|||
tcase_add_test(tc, test_fdt_get_alias_resolves_named_node);
|
||||
tcase_add_test(tc, test_fdt_get_alias_rejects_bad_input);
|
||||
tcase_add_test(tc, test_fdt_get_reg_two_address_two_size_cells);
|
||||
tcase_add_test(tc, test_fdt_getprop_address_two_cells);
|
||||
tcase_add_test(tc, test_fdt_get_reg_one_cell_and_index);
|
||||
tcase_add_test(tc, test_fdt_get_reg_rejects_partial_entry);
|
||||
tcase_add_test(tc, test_fdt_get_reg_uses_spec_default_cells);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@ void wolfBoot_printf(const char *fmt, ...)
|
|||
(void)fmt;
|
||||
}
|
||||
|
||||
/* Resolves to a variable here so one binary can exercise both branches of
|
||||
* fit_load_kernel() with the override pointing at a real buffer. */
|
||||
static uintptr_t kernel_override_addr = 0;
|
||||
#define WOLFBOOT_LOAD_KERNEL_ADDRESS kernel_override_addr
|
||||
static uintptr_t ramdisk_override_addr = 0;
|
||||
#define WOLFBOOT_LOAD_RAMDISK_ADDRESS ramdisk_override_addr
|
||||
|
||||
/* Pull in the production code under test. fdt.c's body is gated on
|
||||
* WOLFBOOT_FDT; the Makefile defines that for both build variants.
|
||||
* gzip.c is only needed for the WOLFBOOT_GZIP build. */
|
||||
|
|
@ -65,6 +72,17 @@ void wolfBoot_printf(const char *fmt, ...)
|
|||
static const char fit_plain_payload[] = "hello fit test payload\n";
|
||||
#define FIT_PLAIN_LEN 23
|
||||
|
||||
/* Smallest valid flat device tree, standing in for one the boot firmware
|
||||
* left rather than the FIT. */
|
||||
static const uint8_t empty_dtb[] = {
|
||||
0xd0,0x0d,0xfe,0xed, 0x00,0x00,0x00,0x48, 0x00,0x00,0x00,0x38,
|
||||
0x00,0x00,0x00,0x48, 0x00,0x00,0x00,0x28, 0x00,0x00,0x00,0x11,
|
||||
0x00,0x00,0x00,0x10, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x10, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,
|
||||
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x02, 0x00,0x00,0x00,0x09
|
||||
};
|
||||
|
||||
/* gzip-compressed kernel, load=0xC0001000 */
|
||||
static const uint8_t fit_with_gzip_kernel[] = {
|
||||
0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x38,
|
||||
|
|
@ -263,6 +281,122 @@ START_TEST(test_fit_ex_gzip_no_load_returns_null)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fit_kernel_no_override_gzip_no_load_fails)
|
||||
{
|
||||
/* No `load`/`entry` and no override: nowhere to decompress to, so the
|
||||
* load must fail rather than hand back compressed bytes. */
|
||||
int len = -1;
|
||||
void *ret;
|
||||
static uint8_t fit_scratch[sizeof(fit_gzip_no_load)] __attribute__((aligned(4)));
|
||||
memcpy(fit_scratch, fit_gzip_no_load, sizeof(fit_scratch));
|
||||
|
||||
kernel_override_addr = 0;
|
||||
ret = fit_load_kernel(fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &len);
|
||||
ck_assert_ptr_null(ret);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fit_kernel_override_loads_gzip_no_load)
|
||||
{
|
||||
/* Same FIT with the override set: it supplies the destination, so the
|
||||
* kernel inflates and the override comes back as the entry point. */
|
||||
static uint8_t dst[64 * 1024] __attribute__((aligned(16)));
|
||||
int len = -1;
|
||||
void *ret;
|
||||
static uint8_t fit_scratch[sizeof(fit_gzip_no_load)] __attribute__((aligned(4)));
|
||||
memcpy(fit_scratch, fit_gzip_no_load, sizeof(fit_scratch));
|
||||
|
||||
kernel_override_addr = (uintptr_t)dst;
|
||||
ret = fit_load_kernel(fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &len);
|
||||
kernel_override_addr = 0;
|
||||
|
||||
ck_assert_ptr_eq(ret, dst);
|
||||
ck_assert_int_eq(len, FIT_PLAIN_LEN);
|
||||
ck_assert_int_eq(memcmp(dst, fit_plain_payload, FIT_PLAIN_LEN), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fit_initrd_fixup_on_dtb_outside_the_fit)
|
||||
{
|
||||
/* A kernel-only FIT has no `fdt`, so the device tree comes from the
|
||||
* boot firmware - and the initrd fixup must land in THAT tree. */
|
||||
static uint8_t rd_dst[64 * 1024] __attribute__((aligned(16)));
|
||||
static uint8_t dtb[4096] __attribute__((aligned(8)));
|
||||
static uint8_t fit_scratch[sizeof(fit_with_none_comp)] __attribute__((aligned(4)));
|
||||
fdt_ctx dts;
|
||||
const uint8_t *start, *end;
|
||||
int off;
|
||||
|
||||
memcpy(fit_scratch, fit_with_none_comp, sizeof(fit_scratch));
|
||||
memset(dtb, 0, sizeof(dtb));
|
||||
memcpy(dtb, empty_dtb, sizeof(empty_dtb));
|
||||
|
||||
/* Full staging capacity, as the loaders do, so /chosen fits. */
|
||||
ck_assert_int_eq(fdt_open(&dts, dtb, (uint32_t)sizeof(dtb)), 0);
|
||||
|
||||
ramdisk_override_addr = (uintptr_t)rd_dst;
|
||||
ck_assert_int_eq(fit_load_ramdisk(
|
||||
fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &dts), 0);
|
||||
ramdisk_override_addr = 0;
|
||||
|
||||
ck_assert_int_eq(memcmp(rd_dst, fit_plain_payload, FIT_PLAIN_LEN), 0);
|
||||
|
||||
off = fdt_path_offset(&dts, "/chosen");
|
||||
ck_assert_int_ge(off, 0);
|
||||
start = (const uint8_t*)fdt_getprop(&dts, off, "linux,initrd-start", NULL);
|
||||
end = (const uint8_t*)fdt_getprop(&dts, off, "linux,initrd-end", NULL);
|
||||
ck_assert_ptr_nonnull(start);
|
||||
ck_assert_ptr_nonnull(end);
|
||||
ck_assert_uint_eq(fdt_rd64u(start), (uint64_t)(uintptr_t)rd_dst);
|
||||
ck_assert_uint_eq(fdt_rd64u(end),
|
||||
(uint64_t)(uintptr_t)rd_dst + FIT_PLAIN_LEN);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fit_initrd_without_a_dtb_reports_failure)
|
||||
{
|
||||
/* Staging the ramdisk but silently skipping the fixup would leave the
|
||||
* kernel with no initrd and nothing on the console to say so. */
|
||||
static uint8_t rd_dst[64 * 1024] __attribute__((aligned(16)));
|
||||
static uint8_t fit_scratch[sizeof(fit_with_none_comp)] __attribute__((aligned(4)));
|
||||
|
||||
memcpy(fit_scratch, fit_with_none_comp, sizeof(fit_scratch));
|
||||
|
||||
ramdisk_override_addr = (uintptr_t)rd_dst;
|
||||
ck_assert_int_eq(fit_load_ramdisk(
|
||||
fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", NULL), -1);
|
||||
ramdisk_override_addr = 0;
|
||||
|
||||
/* The ramdisk itself still landed; only the fixup was impossible. */
|
||||
ck_assert_int_eq(memcmp(rd_dst, fit_plain_payload, FIT_PLAIN_LEN), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fit_kernel_override_beats_fit_load)
|
||||
{
|
||||
/* The override wins over a FIT that does declare `load` (0xC0001000
|
||||
* here, which this process cannot touch). */
|
||||
static uint8_t dst[64 * 1024] __attribute__((aligned(16)));
|
||||
int len = -1;
|
||||
void *ret;
|
||||
static uint8_t fit_scratch[sizeof(fit_with_gzip_kernel)] __attribute__((aligned(4)));
|
||||
memcpy(fit_scratch, fit_with_gzip_kernel, sizeof(fit_scratch));
|
||||
|
||||
kernel_override_addr = (uintptr_t)dst;
|
||||
ret = fit_load_kernel(fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &len);
|
||||
kernel_override_addr = 0;
|
||||
|
||||
ck_assert_ptr_eq(ret, dst);
|
||||
ck_assert_int_eq(len, FIT_PLAIN_LEN);
|
||||
ck_assert_int_eq(memcmp(dst, fit_plain_payload, FIT_PLAIN_LEN), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
#else /* !WOLFBOOT_GZIP */
|
||||
|
||||
START_TEST(test_fit_to_gzip_disabled_returns_null)
|
||||
|
|
@ -356,6 +490,89 @@ START_TEST(test_fit_to_none_oversized_rejected)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* fit_load_kernel()'s override path with an uncompressed kernel: the only
|
||||
* shape a build without WOLFBOOT_GZIP can stage, and the one a FIT that
|
||||
* declares neither `load` nor `entry` most often carries. */
|
||||
START_TEST(test_fit_kernel_override_loads_uncompressed)
|
||||
{
|
||||
static uint8_t dst[64 * 1024] __attribute__((aligned(16)));
|
||||
static uint8_t fit_scratch[sizeof(fit_with_none_comp)] __attribute__((aligned(4)));
|
||||
int len = -1;
|
||||
void *ret;
|
||||
|
||||
memcpy(fit_scratch, fit_with_none_comp, sizeof(fit_scratch));
|
||||
memset(dst, 0, FIT_PLAIN_LEN + 1);
|
||||
|
||||
kernel_override_addr = (uintptr_t)dst;
|
||||
ret = fit_load_kernel(fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &len);
|
||||
kernel_override_addr = 0;
|
||||
|
||||
ck_assert_ptr_eq(ret, dst);
|
||||
ck_assert_int_eq(len, FIT_PLAIN_LEN);
|
||||
ck_assert_int_eq(memcmp(dst, fit_plain_payload, FIT_PLAIN_LEN), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* The raw copy stops below the staged FIT just as the inflate does: an
|
||||
* override under the FIT's own `data` would otherwise overwrite the blob
|
||||
* that the later `fdt` and `ramdisk` lookups still read from. */
|
||||
START_TEST(test_fit_kernel_override_into_the_fit_rejected)
|
||||
{
|
||||
static uint8_t fit_scratch[sizeof(fit_with_none_comp)] __attribute__((aligned(4)));
|
||||
uint8_t *payload = NULL;
|
||||
int len = -1;
|
||||
void *ret;
|
||||
unsigned i;
|
||||
|
||||
memcpy(fit_scratch, fit_with_none_comp, sizeof(fit_scratch));
|
||||
for (i = 0; i + FIT_PLAIN_LEN <= sizeof(fit_scratch); i++) {
|
||||
if (memcmp(fit_scratch + i, fit_plain_payload, FIT_PLAIN_LEN) == 0) {
|
||||
payload = fit_scratch + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ck_assert_ptr_nonnull(payload);
|
||||
|
||||
/* 8 bytes of headroom for a 23-byte payload. */
|
||||
kernel_override_addr = (uintptr_t)(payload - 8);
|
||||
ret = fit_load_kernel(fit_open(fit_scratch, (uint32_t)sizeof(fit_scratch)),
|
||||
"kernel-1", &len);
|
||||
kernel_override_addr = 0;
|
||||
|
||||
ck_assert_ptr_null(ret);
|
||||
ck_assert_int_eq(memcmp(fit_scratch, fit_with_none_comp,
|
||||
sizeof(fit_scratch)), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* The bound is the start of the blob, not its `data` pointer: a destination
|
||||
* below the FIT would otherwise be allowed to run over the header and
|
||||
* structure block while the `fdt` and `ramdisk` lookups still need them. */
|
||||
START_TEST(test_fit_kernel_override_under_the_fit_rejected)
|
||||
{
|
||||
/* The FIT is placed inside a larger arena so there is addressable
|
||||
* space below the blob to aim the override at. */
|
||||
static uint8_t arena[256 + sizeof(fit_with_none_comp)] __attribute__((aligned(4)));
|
||||
uint8_t *blob = arena + 200;
|
||||
int len = -1;
|
||||
void *ret;
|
||||
|
||||
memset(arena, 0, sizeof(arena));
|
||||
memcpy(blob, fit_with_none_comp, sizeof(fit_with_none_comp));
|
||||
|
||||
/* 8 bytes of headroom for a 23-byte payload. */
|
||||
kernel_override_addr = (uintptr_t)(blob - 8);
|
||||
ret = fit_load_kernel(fit_open(blob, (uint32_t)sizeof(fit_with_none_comp)),
|
||||
"kernel-1", &len);
|
||||
kernel_override_addr = 0;
|
||||
|
||||
ck_assert_ptr_null(ret);
|
||||
ck_assert_int_eq(memcmp(blob, fit_with_none_comp,
|
||||
sizeof(fit_with_none_comp)), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static Suite *fit_gzip_suite(void)
|
||||
|
|
@ -368,12 +585,20 @@ static Suite *fit_gzip_suite(void)
|
|||
tcase_add_test(tc, test_fit_to_gzip_corrupt_returns_null);
|
||||
tcase_add_test(tc, test_fit_to_none_compression_copies_plain);
|
||||
tcase_add_test(tc, test_fit_ex_gzip_no_load_returns_null);
|
||||
tcase_add_test(tc, test_fit_kernel_no_override_gzip_no_load_fails);
|
||||
tcase_add_test(tc, test_fit_kernel_override_loads_gzip_no_load);
|
||||
tcase_add_test(tc, test_fit_kernel_override_beats_fit_load);
|
||||
tcase_add_test(tc, test_fit_initrd_fixup_on_dtb_outside_the_fit);
|
||||
tcase_add_test(tc, test_fit_initrd_without_a_dtb_reports_failure);
|
||||
#else
|
||||
tcase_add_test(tc, test_fit_to_gzip_disabled_returns_null);
|
||||
#endif
|
||||
tcase_add_test(tc, test_fit_to_lzma_unknown_returns_null);
|
||||
tcase_add_test(tc, test_fit_to_none_unterminated_fails_closed);
|
||||
tcase_add_test(tc, test_fit_to_none_oversized_rejected);
|
||||
tcase_add_test(tc, test_fit_kernel_override_loads_uncompressed);
|
||||
tcase_add_test(tc, test_fit_kernel_override_into_the_fit_rejected);
|
||||
tcase_add_test(tc, test_fit_kernel_override_under_the_fit_rejected);
|
||||
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
|
|
|
|||
|
|
@ -78,9 +78,29 @@ static int mock_dts_size;
|
|||
static int mock_boot_part_prop;
|
||||
static int mock_chosen_val_ret;
|
||||
static int mock_do_boot_called;
|
||||
/* The device tree actually handed to the kernel. */
|
||||
static const uint32_t *mock_do_boot_dts;
|
||||
static int mock_fit_memcpy_ret;
|
||||
static int mock_fit_memcpy_called;
|
||||
static int mock_panic_hook_called;
|
||||
/* Which sub-images fit_find_images() reports. Defaults keep the existing
|
||||
* tests on the "FIT supplies its own fdt, no ramdisk" path. */
|
||||
static const char *mock_flat_dt = "fdt";
|
||||
static const char *mock_ramdisk;
|
||||
/* Ordering witnesses: hal_flash_protect() runs between the FIT block and
|
||||
* the deferred block, so a fixup that drifted back is visible here. */
|
||||
static int mock_flash_protect_calls;
|
||||
static int mock_ramdisk_calls;
|
||||
static int mock_ramdisk_ret;
|
||||
static int mock_ramdisk_saw_flash_protect;
|
||||
static fdt_ctx *mock_ramdisk_dts;
|
||||
/* Stands in for the blob a bootgen raw partition leaves at
|
||||
* WOLFBOOT_LOAD_DTS_ADDRESS. NULL means the HAL offers none. */
|
||||
static uint8_t configured_dts[TEST_DTS_STAGE_SIZE];
|
||||
static void *mock_configured_dts;
|
||||
/* A blob fdt_open() refuses, so a tree that passes the size check but will
|
||||
* not open for the initrd fixup can be reached. */
|
||||
static const void *mock_fdt_open_fail_blob;
|
||||
/* Snapshot of the key material taken from inside wolfBoot_panic() */
|
||||
static uint8_t panic_key_snapshot[ENCRYPT_KEY_SIZE];
|
||||
static uint8_t panic_nonce_snapshot[ENCRYPT_NONCE_SIZE];
|
||||
|
|
@ -121,11 +141,22 @@ static void reset_mocks(void)
|
|||
memset(fit_dts_image, 0xDD, sizeof(fit_dts_image));
|
||||
mock_dts_size = TEST_DTS_SIZE;
|
||||
mock_do_boot_called = 0;
|
||||
mock_do_boot_dts = (const uint32_t*)0x1; /* poison; must be overwritten */
|
||||
mock_fit_memcpy_ret = 0;
|
||||
mock_fit_memcpy_called = 0;
|
||||
mock_boot_part_prop = -1;
|
||||
mock_chosen_val_ret = 0;
|
||||
mock_panic_hook_called = 0;
|
||||
mock_flat_dt = "fdt";
|
||||
mock_ramdisk = NULL;
|
||||
mock_flash_protect_calls = 0;
|
||||
mock_ramdisk_calls = 0;
|
||||
mock_ramdisk_ret = 0;
|
||||
mock_ramdisk_saw_flash_protect = 0;
|
||||
mock_ramdisk_dts = NULL;
|
||||
memset(configured_dts, 0xCD, sizeof(configured_dts));
|
||||
mock_configured_dts = configured_dts;
|
||||
mock_fdt_open_fail_blob = NULL;
|
||||
memset(panic_key_snapshot, 0xFF, sizeof(panic_key_snapshot));
|
||||
memset(panic_nonce_snapshot, 0xFF, sizeof(panic_nonce_snapshot));
|
||||
wolfBoot_panicked = 0;
|
||||
|
|
@ -253,6 +284,9 @@ void fdt_set_dtb_authenticated(int authenticated)
|
|||
* we only need update_disk.c to take the FIT branch. */
|
||||
int fdt_open(fdt_ctx* ctx, void* blob, uint32_t capacity)
|
||||
{
|
||||
if (mock_fdt_open_fail_blob != NULL && blob == mock_fdt_open_fail_blob) {
|
||||
return -1;
|
||||
}
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->blob = (uint8_t*)blob;
|
||||
ctx->capacity = capacity;
|
||||
|
|
@ -272,9 +306,9 @@ const char* fit_find_images(fdt_ctx* ctx, const char** pkernel,
|
|||
if (pkernel != NULL)
|
||||
*pkernel = NULL;
|
||||
if (pflat_dt != NULL)
|
||||
*pflat_dt = "fdt";
|
||||
*pflat_dt = mock_flat_dt;
|
||||
if (pramdisk != NULL)
|
||||
*pramdisk = NULL;
|
||||
*pramdisk = mock_ramdisk;
|
||||
if (pfpga != NULL)
|
||||
*pfpga = NULL;
|
||||
return "conf";
|
||||
|
|
@ -299,6 +333,13 @@ void* fit_load_image(fdt_ctx* ctx, const char* image, int* lenp)
|
|||
return fit_dts_image;
|
||||
}
|
||||
|
||||
/* This suite's fit_find_images() reports no kernel node, so the call is
|
||||
* never reached; the stub only satisfies the link. */
|
||||
void* fit_load_kernel(fdt_ctx* ctx, const char* kernel_node, int* lenp)
|
||||
{
|
||||
return fit_load_image(ctx, kernel_node, lenp);
|
||||
}
|
||||
|
||||
int wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len)
|
||||
{
|
||||
mock_fit_memcpy_called++;
|
||||
|
|
@ -314,18 +355,43 @@ void hal_prepare_boot(void)
|
|||
|
||||
void do_boot(const uint32_t *address, const uint32_t *dts_address)
|
||||
{
|
||||
(void)dts_address;
|
||||
(void)address;
|
||||
mock_do_boot_dts = dts_address;
|
||||
mock_do_boot_called++;
|
||||
}
|
||||
|
||||
void* hal_get_dts_address(void)
|
||||
{
|
||||
return mock_configured_dts;
|
||||
}
|
||||
|
||||
void* hal_get_dts_update_address(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
(void)address;
|
||||
(void)len;
|
||||
mock_flash_protect_calls++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* Records when it ran relative to hal_flash_protect(), and which device
|
||||
* tree it was handed. */
|
||||
int fit_load_ramdisk(fdt_ctx* ctx, const char* ramdisk_node, fdt_ctx* dts)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)ramdisk_node;
|
||||
mock_ramdisk_calls++;
|
||||
mock_ramdisk_saw_flash_protect = mock_flash_protect_calls;
|
||||
mock_ramdisk_dts = dts;
|
||||
return mock_ramdisk_ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "update_disk.c"
|
||||
|
||||
/* Runs from inside wolfBoot_panic(), before it spins forever on target. */
|
||||
|
|
@ -436,6 +502,173 @@ START_TEST(test_update_disk_fit_dts_below_min_rejected)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* A kernel-only FIT must still get /chosen/linux,initrd-* written into
|
||||
* whichever device tree is selected, so the fixup runs after the fallback. */
|
||||
START_TEST(test_update_disk_fit_ramdisk_fixup_runs_after_dtb_selection)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL; /* kernel-only FIT: no fdt sub-image */
|
||||
mock_ramdisk = "ramdisk-1";
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
/* It ran at all ... */
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
/* ... and only once the device tree had been selected, rather than
|
||||
* back inside the FIT block. */
|
||||
ck_assert_int_gt(mock_ramdisk_saw_flash_protect, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* With a FIT that carries its own fdt the fixup still runs exactly once
|
||||
* and is handed a tree. */
|
||||
START_TEST(test_update_disk_fit_ramdisk_fixup_gets_the_fit_dtb)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_ramdisk = "ramdisk-1"; /* mock_flat_dt stays "fdt" */
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_int_gt(mock_ramdisk_saw_flash_protect, 0);
|
||||
ck_assert_ptr_nonnull(mock_ramdisk_dts);
|
||||
}
|
||||
END_TEST
|
||||
/* A ramdisk that cannot be staged, or that has no tree to be announced in,
|
||||
* is logged and the kernel boots anyway rather than the boot aborting. */
|
||||
START_TEST(test_update_disk_fit_ramdisk_failure_still_boots)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL;
|
||||
mock_ramdisk = "ramdisk-1";
|
||||
mock_ramdisk_ret = -1;
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
}
|
||||
END_TEST
|
||||
#endif /* WOLFBOOT_FIT_RAMDISK */
|
||||
|
||||
#ifdef MMU
|
||||
/* A configured DTB that cannot be relocated is dropped and the kernel is
|
||||
* entered with no tree, rather than the boot aborting. */
|
||||
START_TEST(test_update_disk_fit_configured_dtb_relocation_failure)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL; /* kernel-only FIT: reach the fallback */
|
||||
mock_fit_memcpy_ret = -1;
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_ptr_null(mock_do_boot_dts);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* hal_get_boot_dts() is a weak NULL on every target but CM4, so without
|
||||
* the configured-DTB fallback a kernel-only FIT gets a NULL tree. */
|
||||
START_TEST(test_update_disk_fit_kernel_only_uses_configured_dtb)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL; /* kernel-only FIT */
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
/* Relocated into the staging window, not used where the HAL put it:
|
||||
* the initrd fixup grows the tree in place. */
|
||||
ck_assert_ptr_eq(mock_do_boot_dts, (void*)dts_buffer);
|
||||
ck_assert_int_eq(memcmp(dts_buffer, configured_dts, TEST_DTS_SIZE), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* When the HAL hands back the staging window itself, as hal/versal.c does,
|
||||
* it is used directly with no self-copy. */
|
||||
START_TEST(test_update_disk_fit_configured_dtb_already_staged)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL;
|
||||
memset(dts_buffer, 0xAB, TEST_DTS_SIZE);
|
||||
mock_configured_dts = dts_buffer;
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_ptr_eq(mock_do_boot_dts, (void*)dts_buffer);
|
||||
ck_assert_uint_eq(dts_buffer[0], 0xAB);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* No DTB from anywhere: the HAL offers none and the FIT carried none. The
|
||||
* kernel is entered with a NULL tree instead of the boot aborting. */
|
||||
START_TEST(test_update_disk_fit_no_dtb_at_all)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL; /* kernel-only FIT */
|
||||
mock_configured_dts = NULL; /* and no firmware DTB either */
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
mock_ramdisk = "ramdisk-1";
|
||||
#endif
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_ptr_null(mock_do_boot_dts);
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* The fixup is still attempted, with nothing to write into. */
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_ptr_null(mock_ramdisk_dts);
|
||||
#endif
|
||||
}
|
||||
END_TEST
|
||||
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
/* A firmware DTB that passes the size check but will not open must not be
|
||||
* handed to fit_load_ramdisk() as if it were a tree. The kernel still gets
|
||||
* the tree itself, only without the initrd properties. */
|
||||
START_TEST(test_update_disk_fit_dtb_that_does_not_open)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL; /* kernel-only FIT: reach the fallback */
|
||||
mock_ramdisk = "ramdisk-1";
|
||||
mock_fdt_open_fail_blob = dts_buffer;
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_ptr_eq(mock_do_boot_dts, (void*)dts_buffer);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_ptr_null(mock_ramdisk_dts);
|
||||
}
|
||||
END_TEST
|
||||
#endif /* WOLFBOOT_FIT_RAMDISK */
|
||||
|
||||
/* An unparseable configured DTB is dropped, not forwarded. */
|
||||
START_TEST(test_update_disk_fit_bad_configured_dtb_rejected)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_flat_dt = NULL;
|
||||
mock_dts_size = 8; /* below WOLFBOOT_DTS_MIN_SIZE */
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_ptr_null(mock_do_boot_dts);
|
||||
}
|
||||
END_TEST
|
||||
#endif /* MMU */
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfBoot");
|
||||
|
|
@ -444,6 +677,21 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(tc, test_update_disk_fit_dts_copy_failure_zeroizes_key_material);
|
||||
tcase_add_test(tc, test_update_disk_fit_dts_oversized_rejected);
|
||||
tcase_add_test(tc, test_update_disk_fit_dts_below_min_rejected);
|
||||
#ifdef MMU
|
||||
tcase_add_test(tc, test_update_disk_fit_kernel_only_uses_configured_dtb);
|
||||
tcase_add_test(tc, test_update_disk_fit_configured_dtb_already_staged);
|
||||
tcase_add_test(tc, test_update_disk_fit_bad_configured_dtb_rejected);
|
||||
tcase_add_test(tc, test_update_disk_fit_configured_dtb_relocation_failure);
|
||||
tcase_add_test(tc, test_update_disk_fit_no_dtb_at_all);
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
tcase_add_test(tc, test_update_disk_fit_dtb_that_does_not_open);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFBOOT_FIT_RAMDISK
|
||||
tcase_add_test(tc, test_update_disk_fit_ramdisk_fixup_runs_after_dtb_selection);
|
||||
tcase_add_test(tc, test_update_disk_fit_ramdisk_fixup_gets_the_fit_dtb);
|
||||
tcase_add_test(tc, test_update_disk_fit_ramdisk_failure_still_boots);
|
||||
#endif
|
||||
tcase_add_test(tc, test_update_disk_fit_dts_copy_success_boots);
|
||||
tcase_add_test(tc, test_update_disk_fit_boot_part_fixup_failure_panics);
|
||||
suite_add_tcase(s, tc);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,467 @@
|
|||
/* unit-update-ram-fit.c
|
||||
*
|
||||
* loader-level tests for the FIT device-tree fallback and the deferred
|
||||
* initrd fixup in update_ram.c
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
#ifndef WOLFBOOT_HASH_SHA256
|
||||
#define WOLFBOOT_HASH_SHA256
|
||||
#endif
|
||||
#define IMAGE_HEADER_SIZE 256
|
||||
#define MOCK_ADDRESS_UPDATE 0xCC000000
|
||||
#define MOCK_ADDRESS_BOOT 0xCD000000
|
||||
#define MOCK_ADDRESS_SWAP 0xCE000000
|
||||
#include "target.h"
|
||||
static __thread unsigned char wolfboot_ram[2 * WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE];
|
||||
|
||||
#define WOLFBOOT_LOAD_ADDRESS (((uintptr_t)wolfboot_ram + IMAGE_HEADER_SIZE))
|
||||
|
||||
#define TEST_SIZE_SMALL 5300
|
||||
#define TEST_SIZE_LARGE 9800
|
||||
|
||||
#define NO_FORK 1 /* Set to 1 to disable fork mode (e.g. for gdb debugging) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "user_settings.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
#include "libwolfboot.c"
|
||||
/* FDT/FIT stubs, declared before update_ram.c is pulled in. fdt_open()
|
||||
* accepts anything, so wolfBoot_start() always takes the FIT branch. */
|
||||
static const char *mock_kernel = "kernel-1";
|
||||
static const char *mock_flat_dt; /* NULL = kernel-only FIT */
|
||||
static const char *mock_ramdisk = "ramdisk-1";
|
||||
static int mock_get_dts_calls;
|
||||
static int mock_ramdisk_calls;
|
||||
static int mock_ramdisk_ret;
|
||||
static int mock_ramdisk_saw_get_dts;
|
||||
static void *mock_ramdisk_dts;
|
||||
/* The device tree the boot firmware hands us, as a bootgen raw partition
|
||||
* would. Big enough for the staging copy update_ram.c makes. */
|
||||
#include "fdt.h"
|
||||
static uint8_t firmware_dtb[WOLFBOOT_DTS_MAX_SIZE];
|
||||
static uint8_t dts_stage[WOLFBOOT_DTS_MAX_SIZE];
|
||||
#define WOLFBOOT_LOAD_DTS_ADDRESS ((uintptr_t)dts_stage)
|
||||
/* What the HAL hands back, and a blob fdt_open() refuses, so the "present
|
||||
* but unusable" branch can be reached without failing the size check too. */
|
||||
static void *mock_hal_dts;
|
||||
static const void *mock_fdt_open_fail_blob;
|
||||
|
||||
void* hal_get_dts_address(void)
|
||||
{
|
||||
mock_get_dts_calls++;
|
||||
return mock_hal_dts;
|
||||
}
|
||||
|
||||
int hal_dts_fixup(void *dts_addr, uint32_t capacity)
|
||||
{
|
||||
(void)dts_addr;
|
||||
(void)capacity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_open(fdt_ctx* ctx, void* blob, uint32_t capacity)
|
||||
{
|
||||
if (mock_fdt_open_fail_blob != NULL && blob == mock_fdt_open_fail_blob) {
|
||||
return -1;
|
||||
}
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->blob = blob;
|
||||
ctx->capacity = capacity;
|
||||
ctx->totalsize = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t fdt_size(const fdt_ctx* ctx)
|
||||
{
|
||||
return (ctx != NULL) ? ctx->totalsize : 0;
|
||||
}
|
||||
|
||||
int fdt_peek_size(const void* hdr, uint32_t hdr_len, uint32_t* totalsize)
|
||||
{
|
||||
(void)hdr;
|
||||
(void)hdr_len;
|
||||
if (totalsize != NULL)
|
||||
*totalsize = 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fdt_set_dtb_authenticated(int authenticated)
|
||||
{
|
||||
(void)authenticated;
|
||||
}
|
||||
|
||||
const char* fit_find_images(fdt_ctx* ctx, const char** pkernel,
|
||||
const char** pflat_dt, const char** pramdisk, const char** pfpga)
|
||||
{
|
||||
(void)ctx;
|
||||
if (pkernel != NULL)
|
||||
*pkernel = mock_kernel;
|
||||
if (pflat_dt != NULL)
|
||||
*pflat_dt = mock_flat_dt;
|
||||
if (pramdisk != NULL)
|
||||
*pramdisk = mock_ramdisk;
|
||||
if (pfpga != NULL)
|
||||
*pfpga = NULL;
|
||||
return "conf-1";
|
||||
}
|
||||
|
||||
void* fit_load_image(fdt_ctx* ctx, const char* image, int* lenp)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)image;
|
||||
if (lenp != NULL)
|
||||
*lenp = 64;
|
||||
return firmware_dtb;
|
||||
}
|
||||
|
||||
void* fit_load_kernel(fdt_ctx* ctx, const char* kernel_node, int* lenp)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)kernel_node;
|
||||
if (lenp != NULL)
|
||||
*lenp = 64;
|
||||
/* Any in-range address; the test only cares about the DTB path. */
|
||||
return (void*)WOLFBOOT_LOAD_ADDRESS;
|
||||
}
|
||||
|
||||
int fit_load_ramdisk(fdt_ctx* ctx, const char* ramdisk_node, fdt_ctx* dts)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)ramdisk_node;
|
||||
mock_ramdisk_calls++;
|
||||
mock_ramdisk_saw_get_dts = mock_get_dts_calls;
|
||||
mock_ramdisk_dts = dts;
|
||||
return mock_ramdisk_ret;
|
||||
}
|
||||
|
||||
#include "update_ram.c"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <check.h>
|
||||
#include "unit-mock-flash.c"
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
const char *argv0;
|
||||
|
||||
Suite *wolfboot_suite(void);
|
||||
|
||||
int wolfBoot_staged_ok = 0;
|
||||
const uint32_t *wolfBoot_stage_address = (uint32_t *) 0xFFFFFFFF;
|
||||
|
||||
void do_boot(const uint32_t *address, const uint32_t *dts)
|
||||
{
|
||||
struct wolfBoot_image boot_image;
|
||||
(void)dts;
|
||||
/* Mock of do_boot */
|
||||
if (wolfBoot_panicked)
|
||||
return;
|
||||
wolfBoot_staged_ok++;
|
||||
wolfBoot_stage_address = address;
|
||||
ck_assert_uint_eq((uintptr_t)address, WOLFBOOT_LOAD_ADDRESS);
|
||||
memset(&boot_image, 0, sizeof(boot_image));
|
||||
printf("Called do_boot with address %p\n", address);
|
||||
ck_assert_uint_eq(0,wolfBoot_open_image_address(&boot_image, wolfboot_ram));
|
||||
boot_image.hdr = wolfboot_ram;
|
||||
boot_image.fw_base = (void *)(uintptr_t)WOLFBOOT_LOAD_ADDRESS;
|
||||
boot_image.part = 0;
|
||||
boot_image.not_ext = 1;
|
||||
ck_assert_uint_eq(0,wolfBoot_verify_integrity(&boot_image));
|
||||
|
||||
}
|
||||
|
||||
static int mock_flash_protect_called = 0;
|
||||
static haladdr_t mock_flash_protect_addr = 0;
|
||||
static int mock_flash_protect_len = 0;
|
||||
|
||||
static void reset_mock_stats(void)
|
||||
{
|
||||
wolfBoot_panicked = 0;
|
||||
wolfBoot_staged_ok = 0;
|
||||
mock_flash_protect_called = 0;
|
||||
mock_flash_protect_addr = 0;
|
||||
mock_flash_protect_len = 0;
|
||||
}
|
||||
|
||||
int hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
mock_flash_protect_called++;
|
||||
mock_flash_protect_addr = address;
|
||||
mock_flash_protect_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t get_version_ramloaded(void)
|
||||
{
|
||||
return wolfBoot_get_blob_version(wolfboot_ram);
|
||||
}
|
||||
|
||||
static void assert_part_state(uint8_t part, uint8_t expected)
|
||||
{
|
||||
uint8_t st = 0xBB;
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(part, &st), 0);
|
||||
ck_assert_uint_eq(st, expected);
|
||||
}
|
||||
|
||||
|
||||
static void prepare_flash(void)
|
||||
{
|
||||
int ret;
|
||||
ret = mmap_file("/tmp/wolfboot-unit-ext-file.bin", (void *)(uintptr_t)MOCK_ADDRESS_UPDATE,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert(ret >= 0);
|
||||
ret = mmap_file("/tmp/wolfboot-unit-int-file.bin", (void *)(uintptr_t)MOCK_ADDRESS_BOOT,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert(ret >= 0);
|
||||
ext_flash_unlock();
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS, WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_lock();
|
||||
}
|
||||
|
||||
static void cleanup_flash(void)
|
||||
{
|
||||
munmap((void *)WOLFBOOT_PARTITION_BOOT_ADDRESS, WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
munmap((void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
|
||||
#define DIGEST_TLV_OFF_IN_HDR 28
|
||||
static int add_payload(uint8_t part, uint32_t version, uint32_t size)
|
||||
{
|
||||
uint32_t word;
|
||||
uint16_t word16;
|
||||
int i;
|
||||
uint8_t *base = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
int ret;
|
||||
wc_Sha256 sha;
|
||||
uint8_t digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
||||
if (part == PART_UPDATE)
|
||||
base = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
srandom(part); /* Ensure reproducible "random" image */
|
||||
|
||||
|
||||
ext_flash_unlock();
|
||||
ext_flash_write((uintptr_t)base, "WOLF", 4);
|
||||
printf("Written magic: \"WOLF\"\n");
|
||||
|
||||
ext_flash_write((uintptr_t)base + 4, (void *)&size, 4);
|
||||
printf("Written size: %u\n", size);
|
||||
|
||||
/* Headers */
|
||||
word = 4 << 16 | HDR_VERSION;
|
||||
ext_flash_write((uintptr_t)base + 8, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + 12, (void *)&version, 4);
|
||||
printf("Written version: %u\n", version);
|
||||
|
||||
word = 2 << 16 | HDR_IMG_TYPE;
|
||||
ext_flash_write((uintptr_t)base + 16, (void *)&word, 4);
|
||||
word16 = HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP;
|
||||
ext_flash_write((uintptr_t)base + 20, (void *)&word16, 2);
|
||||
printf("Written img_type: %04X\n", word16);
|
||||
|
||||
/* Add 28B header to sha calculation */
|
||||
ret = wc_Sha256Update(&sha, base, DIGEST_TLV_OFF_IN_HDR);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* Payload */
|
||||
size += IMAGE_HEADER_SIZE;
|
||||
for (i = IMAGE_HEADER_SIZE; i < size; i+=4) {
|
||||
uint32_t word = (random() << 16) | random();
|
||||
ext_flash_write((uintptr_t)base + i, (void *)&word, 4);
|
||||
}
|
||||
for (i = IMAGE_HEADER_SIZE; i < size; i+= WOLFBOOT_SHA_BLOCK_SIZE) {
|
||||
int len = WOLFBOOT_SHA_BLOCK_SIZE;
|
||||
if ((size - i) < len)
|
||||
len = size - i;
|
||||
ret = wc_Sha256Update(&sha, base + i, len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Calculate final digest */
|
||||
ret = wc_Sha256Final(&sha, digest);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
wc_Sha256Free(&sha);
|
||||
|
||||
word = SHA256_DIGEST_SIZE << 16 | HDR_SHA256;
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR + 4, digest,
|
||||
SHA256_DIGEST_SIZE);
|
||||
printf("SHA digest written\n");
|
||||
for (i = 0; i < 32; i++) {
|
||||
printf("%02x ", digest[i]);
|
||||
}
|
||||
printf("\n");
|
||||
ext_flash_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_fit_mocks(void)
|
||||
{
|
||||
reset_mock_stats();
|
||||
mock_kernel = "kernel-1";
|
||||
mock_flat_dt = NULL;
|
||||
mock_ramdisk = "ramdisk-1";
|
||||
mock_get_dts_calls = 0;
|
||||
mock_ramdisk_calls = 0;
|
||||
mock_ramdisk_ret = 0;
|
||||
mock_ramdisk_saw_get_dts = 0;
|
||||
mock_ramdisk_dts = NULL;
|
||||
mock_hal_dts = firmware_dtb;
|
||||
mock_fdt_open_fail_blob = NULL;
|
||||
memset(firmware_dtb, 0, sizeof(firmware_dtb));
|
||||
memset(dts_stage, 0, sizeof(dts_stage));
|
||||
}
|
||||
|
||||
/* A kernel-only FIT falls back to the device tree the boot firmware left,
|
||||
* and the initrd fixup has to be applied to THAT tree. */
|
||||
START_TEST (test_ram_fit_kernel_only_uses_firmware_dtb_for_initrd)
|
||||
{
|
||||
reset_fit_mocks();
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
/* The firmware DTB was consulted ... */
|
||||
ck_assert_int_gt(mock_get_dts_calls, 0);
|
||||
/* ... the fixup ran ... */
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
/* ... and it ran after that fallback, not before it. */
|
||||
ck_assert_int_gt(mock_ramdisk_saw_get_dts, 0);
|
||||
/* ... against a real tree rather than NULL. */
|
||||
ck_assert_ptr_nonnull(mock_ramdisk_dts);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* When the FIT does carry its own fdt, that one wins and the firmware
|
||||
* fallback is not consulted - but the fixup still runs exactly once. */
|
||||
START_TEST (test_ram_fit_own_dtb_skips_firmware_fallback)
|
||||
{
|
||||
reset_fit_mocks();
|
||||
mock_flat_dt = "fdt-1";
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(mock_get_dts_calls, 0);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_ptr_nonnull(mock_ramdisk_dts);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A ramdisk that cannot be staged, or that has no tree to be announced in,
|
||||
* is logged and the kernel boots anyway rather than the boot aborting. */
|
||||
START_TEST (test_ram_fit_ramdisk_failure_still_boots)
|
||||
{
|
||||
reset_fit_mocks();
|
||||
mock_ramdisk_ret = -1;
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A firmware DTB that passes the size check but will not open for the fixup
|
||||
* must not be handed to fit_load_ramdisk() as if it were a tree. */
|
||||
START_TEST (test_ram_fit_firmware_dtb_that_does_not_open)
|
||||
{
|
||||
reset_fit_mocks();
|
||||
/* The size check runs on the HAL address; only the staged copy the
|
||||
* fixup opens is refused. */
|
||||
mock_fdt_open_fail_blob = dts_stage;
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_ptr_null(mock_ramdisk_dts);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A HAL that already returns the staging address is used in place: nothing
|
||||
* is copied over it, and the fixup still gets that same tree. */
|
||||
START_TEST (test_ram_fit_hal_returns_the_staging_address)
|
||||
{
|
||||
reset_fit_mocks();
|
||||
mock_hal_dts = dts_stage;
|
||||
memset(firmware_dtb, 0x5A, sizeof(firmware_dtb));
|
||||
memset(dts_stage, 0xA5, sizeof(dts_stage));
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
/* Not overwritten from the HAL buffer. */
|
||||
ck_assert_uint_eq(dts_stage[0], 0xA5);
|
||||
ck_assert_int_eq(mock_ramdisk_calls, 1);
|
||||
ck_assert_ptr_nonnull(mock_ramdisk_dts);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfBoot");
|
||||
TCase *tc = tcase_create("update-ram-fit");
|
||||
tcase_set_timeout(tc, 20);
|
||||
tcase_add_test(tc, test_ram_fit_kernel_only_uses_firmware_dtb_for_initrd);
|
||||
tcase_add_test(tc, test_ram_fit_own_dtb_skips_firmware_fallback);
|
||||
tcase_add_test(tc, test_ram_fit_ramdisk_failure_still_boots);
|
||||
tcase_add_test(tc, test_ram_fit_firmware_dtb_that_does_not_open);
|
||||
tcase_add_test(tc, test_ram_fit_hal_returns_the_staging_address);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
SRunner *sr = srunner_create(wolfboot_suite());
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return fails ? 1 : 0;
|
||||
}
|
||||
Loading…
Reference in New Issue