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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,9 @@ power-on.
`0x01000000`, which is uncached and coherent for all harts. Cacheable L2-scratchpad memory
must not be used for cross-hart signalling (stores can be lost on dirty-line eviction).

**Device-tree fixups** applied to the loaded dtb (`hal/mpfs250.c`): bootargs/root device,
**Device-tree fixups** applied to the loaded dtb (`hal/mpfs250.c`): bootargs/root device
(the DTB's own `/chosen/bootargs` wins unless `LINUX_BOOTARGS`/`LINUX_BOOTARGS_ROOT` is set;
see "Linux kernel command line (bootargs)" in `docs/compile.md`),
MAC addresses from the device serial number, and all five MSS watchdog nodes are disabled.

**Watchdog policy:** the MSS watchdogs always count and reset the chip on timeout (they cannot
Expand Down Expand Up @@ -4453,7 +4455,10 @@ images {
that wolfBoot decompresses straight to the kernel load address at boot.
See the [Versal "Booting Linux via FIT image"](#versal-gen-1-vmk180)
section for a full walkthrough - the flow is identical apart from the
load addresses and the `bl31`/`fsbl` versus `bl31`/`plm` boot chain. Set
load addresses and the `bl31`/`fsbl` versus `bl31`/`plm` boot chain,
including the bootargs handling (the FIT DTB's own `/chosen/bootargs`
win unless `LINUX_BOOTARGS`/`LINUX_BOOTARGS_ROOT` is set; see
"Linux kernel command line (bootargs)" in `docs/compile.md`). Set
`GZIP=0` in
`.config` if you want to keep using an uncompressed `Image` plus
`compression = "none"`.
Expand Down Expand Up @@ -5134,21 +5139,17 @@ 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.

**DTB Fixup for Root Filesystem**
**Kernel Command Line (bootargs)**

wolfBoot automatically modifies the device tree to set the kernel command line (`bootargs`). The default configuration mounts the root filesystem from SD card partition 2:

```
earlycon root=/dev/mmcblk0p2 rootwait
```

To customize the root device, add to your config:
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.

```makefile
# Mount root from SD card partition 4
# Replace the image's bootargs, mounting root from SD card partition 4
CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\"
```

On Versal the PS UART console is `ttyAMA0` (PL011); when supplying your own bootargs prefer an explicit `earlycon=pl011,mmio32,0xFF000000,115200n8 console=ttyAMA0,115200`.

**Automated Testing**

```sh
Expand Down
12 changes: 12 additions & 0 deletions docs/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,18 @@ downgrades, compile with `ALLOW_DOWNGRADE=1`.
Warning: this option will disable version checking before the updates, thus exposing the system to potential
forced downgrade attacks.

### Linux kernel command line (bootargs)

On the FIT Linux-boot targets whose DTB comes from the signed FIT (Versal, ZynqMP, PolarFire SoC), a DTB that already carries a non-empty `/chosen/bootargs` keeps it by default - the image boots with the arguments its kernel was validated with, and wolfBoot logs `FDT: using DTB bootargs: ...`. Three macros control this:

- `LINUX_BOOTARGS` - the full command line wolfBoot injects. Setting it (via `CFLAGS_EXTRA+=-DLINUX_BOOTARGS='"..."'`) replaces the DTB's value; the replaced value is logged.
- `LINUX_BOOTARGS_ROOT` - shorthand that only swaps the `root=` device in the HAL's default command line; setting it also selects replace semantics.
- `LINUX_BOOTARGS_OVERRIDE` - explicit polarity control: `1` forces replacement, `0` demotes `LINUX_BOOTARGS` to a fallback used only when the DTB carries no bootargs. Unset, it defaults to `1` when either macro above is configured and `0` otherwise.

Upgrade note: before this behavior existed, these targets always overwrote `/chosen/bootargs`. A build that sets neither macro therefore changes on upgrade from "HAL default always wins" to "the authenticated DTB's own bootargs win". If a deployment relied on the HAL default to correct a `root=` baked into its DTB, set `LINUX_BOOTARGS_ROOT` (or `LINUX_BOOTARGS`) to restore the previous behavior.

Keeping a DTB's bootargs requires the DTB to be authenticated: a FIT DTB (covered by the outer image signature) or a raw DTB with a verified `HDR_DEVICE_TREE_DIGEST`. An unauthenticated raw DTB always has its bootargs replaced regardless of these macros, so a writable DTB partition cannot inject `root=`, `init=` or console policy into a signed kernel's command line; wolfBoot logs `FDT: DTB not authenticated, forcing bootargs` when that happens. The Raspberry Pi CM4 firmware-DTB path likewise always injects wolfBoot's command line, because that DTB is firmware-provided and unverified.

### Require an authenticated device tree (raw-DTB targets)

On non-FIT MMU targets that load a raw device tree from flash, wolfBoot authenticates the DTB against the `HDR_DEVICE_TREE_DIGEST` TLV bound to the signed kernel (`sign --dts <board.dtb>`, see `docs/Signing.md`). A DTB carrying the digest is always verified; a raw DTB with no digest only warns and boots by default. Compile with `WOLFBOOT_REQUIRE_SIGNED_DTB=1` to make a missing digest a hard failure (fail-closed) once every raw-DTB payload is signed with `--dts`.
Expand Down
17 changes: 7 additions & 10 deletions hal/mpfs250.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ int mpfs_read_serial_number(uint8_t *serial)
}

/* Linux kernel command line arguments */
/* Must stay below the fdt.h include: the LINUX_BOOTARGS_OVERRIDE default
* there keys off build-supplied macros only, not this fallback. */
#ifndef LINUX_BOOTARGS
#ifndef LINUX_BOOTARGS_ROOT
/* wolfBoot SD layout (tools/scripts/program-sdcard.sh): p1=boot FIT,
Expand Down Expand Up @@ -637,16 +639,11 @@ static int mpfs_dts_fixup_inplace(void* dts_addr, uint32_t capacity)
return ret;
}

/* Find /chosen node */
off = fdt_subnode_offset(&ctx, 0, "chosen");
if (off < 0) {
/* Create /chosen node if it doesn't exist */
off = fdt_add_subnode(&ctx, 0, "chosen");
}

if (off >= 0) {
/* Set bootargs property */
fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS);
/* Later fixups (watchdog disable, serial number) must run even if the
* bootargs fixup fails, so log and continue rather than return. */
ret = fdt_fixup_bootargs(&ctx, LINUX_BOOTARGS, LINUX_BOOTARGS_OVERRIDE);
Comment thread
dgarske marked this conversation as resolved.
if (ret < 0) {
wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret);
}

#if defined(MPFS_DDR_INIT) && defined(WOLFBOOT_MMODE_SMODE_BOOT)
Expand Down
21 changes: 4 additions & 17 deletions hal/versal.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
*/

/* Linux kernel command line arguments */
/* Must stay below the fdt.h include: the LINUX_BOOTARGS_OVERRIDE default
* there keys off build-supplied macros only, not this fallback. */
#ifndef LINUX_BOOTARGS
#ifndef LINUX_BOOTARGS_ROOT
/* Default Versal SD layout: rootfs on partition 2. Configurations that use
Expand Down Expand Up @@ -1290,7 +1292,7 @@ void* hal_get_dts_update_address(void)
int hal_dts_fixup(void* dts_addr, uint32_t capacity)
{
fdt_ctx ctx;
int off, ret;
int ret;

/* Validate the blob against the window it actually occupies. */
ret = fdt_open(&ctx, dts_addr, capacity);
Expand All @@ -1310,22 +1312,7 @@ int hal_dts_fixup(void* dts_addr, uint32_t capacity)
return ret;
}

/* Find /chosen node; create it only if genuinely missing. Any other
* negative return (malformed FDT, etc.) is surfaced directly rather
* than masked by a follow-on fdt_add_subnode() failure. */
off = fdt_subnode_offset(&ctx, 0, "chosen");
if (off == -FDT_ERR_NOTFOUND) {
off = fdt_add_subnode(&ctx, 0, "chosen");
}

if (off < 0) {
wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off);
return off;
}

/* Set bootargs property - overrides the PetaLinux default root= with
* the wolfBoot partition layout. */
ret = fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS);
ret = fdt_fixup_bootargs(&ctx, LINUX_BOOTARGS, LINUX_BOOTARGS_OVERRIDE);
if (ret < 0) {
wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret);
return ret;
Expand Down
28 changes: 12 additions & 16 deletions hal/zynq.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@
* Note: console=ttyPS0 is ZynqMP-specific (PS UART0). Versal's default
* (hal/versal.c) omits the console= token because Versal relies on
* earlycon alone plus a DT-declared stdout-path. */
/* fdt.h defines the LINUX_BOOTARGS_OVERRIDE default from the build macros, so
* it must be processed before the fallback LINUX_BOOTARGS below. Included
* explicitly (the guard makes it a no-op) rather than relying on image.h
* pulling it in transitively. */
#if defined(MMU) || defined(WOLFBOOT_FDT)
#include "fdt.h"
#endif

/* Must stay below the fdt.h include: the LINUX_BOOTARGS_OVERRIDE default
* there keys off build-supplied macros only, not this fallback. */
#ifndef LINUX_BOOTARGS
#ifndef LINUX_BOOTARGS_ROOT
#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4"
Expand Down Expand Up @@ -2632,7 +2642,7 @@ void* hal_get_dts_address(void)
int hal_dts_fixup(void* dts_addr, uint32_t capacity)
{
fdt_ctx ctx;
int off, ret;
int ret;

/* Validate the blob against the window it actually occupies. Every
* bound the parser applies below comes from `capacity`, not from the
Expand All @@ -2657,21 +2667,7 @@ int hal_dts_fixup(void* dts_addr, uint32_t capacity)
return ret;
}

/* Find /chosen node; create it only if genuinely missing. Any other
* negative return (malformed FDT, etc.) is surfaced directly rather
* than masked by a follow-on fdt_add_subnode() failure. */
off = fdt_subnode_offset(&ctx, 0, "chosen");
if (off == -FDT_ERR_NOTFOUND) {
off = fdt_add_subnode(&ctx, 0, "chosen");
}
if (off < 0) {
wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off);
return off;
}

/* Set bootargs property - overrides PetaLinux default root= with
* the wolfBoot partition layout. */
ret = fdt_fixup_str(&ctx, off, "chosen", "bootargs", LINUX_BOOTARGS);
ret = fdt_fixup_bootargs(&ctx, LINUX_BOOTARGS, LINUX_BOOTARGS_OVERRIDE);
if (ret < 0) {
wolfBoot_printf("FDT: Failed to set bootargs (%d)\n", ret);
return ret;
Expand Down
21 changes: 21 additions & 0 deletions include/fdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,27 @@ int fdt_del_node(fdt_ctx* ctx, int nodeoffset);
int fdt_add_mem_rsv(fdt_ctx* ctx, uint64_t address, uint64_t size);

/* Logging wrappers around fdt_setprop() used by the HAL dts fixups. */
/* LINUX_BOOTARGS_OVERRIDE: nonzero replaces the DTB's own bootargs, zero
* keeps them when present (LINUX_BOOTARGS then only fills a missing value).
* Defaults to replace when the build supplies LINUX_BOOTARGS or
* LINUX_BOOTARGS_ROOT, and to keep when only the HAL fallback exists. */
#ifndef LINUX_BOOTARGS_OVERRIDE
#if defined(LINUX_BOOTARGS) || defined(LINUX_BOOTARGS_ROOT)
#define LINUX_BOOTARGS_OVERRIDE 1
#else
#define LINUX_BOOTARGS_OVERRIDE 0
Comment thread
dgarske marked this conversation as resolved.
#endif
#endif

/* Set /chosen bootargs; force 0 keeps an existing non-empty DTB value.
* Evaluated here (before any HAL fallback LINUX_BOOTARGS definition) so
* the default reflects only build-supplied macros. */
/* Report whether the DTB about to be fixed up is authenticated (FIT DTB or a
* raw DTB with a verified digest). Unauthenticated DTBs always have their
* bootargs replaced regardless of force. */
void fdt_set_dtb_authenticated(int authenticated);
int fdt_fixup_bootargs(fdt_ctx* ctx, const char* args, int force);

int fdt_fixup_str(fdt_ctx* ctx, int off, const char* node, const char* name,
const char* str);
int fdt_fixup_val(fdt_ctx* ctx, int off, const char* node, const char* name,
Expand Down
58 changes: 58 additions & 0 deletions src/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,64 @@ int fdt_fixup_val64(fdt_ctx* ctx, int off, const char* node, const char* name,
return fdt_setprop(ctx, off, name, &be, (int)sizeof(be));
}

/* Set /chosen bootargs. With force nonzero the DTB's existing value is
* replaced by `args` (and logged); with force 0 an existing non-empty
* value wins, so an image keeps the arguments its kernel was validated
* with unless the build explicitly overrides them. */
/* DTB provenance for the bootargs policy below. Keeping a DTB's own bootargs
* is only safe when its contents are authenticated: a FIT DTB (covered by the
* outer image signature) or a raw DTB with a verified HDR_DEVICE_TREE_DIGEST.
* Defaults to unauthenticated, so a target that never reports provenance keeps
* the historical always-replace behavior. */
static int fdt_dtb_authenticated = 0;

void fdt_set_dtb_authenticated(int authenticated)
{
fdt_dtb_authenticated = (authenticated != 0);
}

int fdt_fixup_bootargs(fdt_ctx* ctx, const char* args, int force)
{
const char* old_args;
int off, old_len = 0;

if (!fdt_ctx_ok(ctx) || args == NULL) {
return -FDT_ERR_BADARG;
}
off = fdt_subnode_offset(ctx, 0, "chosen");
if (off == -FDT_ERR_NOTFOUND) {
off = fdt_add_subnode(ctx, 0, "chosen");
}
if (off < 0) {
wolfBoot_printf("FDT: Failed to find/create chosen node (%d)\n", off);
return off;
}
/* An unauthenticated DTB never gets to supply the kernel command line:
* its bootargs are attacker-influenceable, so honoring them would let a
* DTB-partition write change root=/init=/console= without touching the
* signed kernel. */
if (!force && !fdt_dtb_authenticated) {
wolfBoot_printf("FDT: DTB not authenticated, forcing bootargs\n");
force = 1;
}
/* Treat the existing value as present only when it is a non-empty string
* terminated at the end of the property: property bytes are opaque, and
* %s printing or keeping a value that is unterminated (or is a multi-part
* string list rather than one command line) would be wrong either way.
* Malformed or empty bootargs are simply replaced. */
old_args = (const char*)fdt_getprop(ctx, off, "bootargs", &old_len);
if (old_args != NULL && old_len > 1 && old_args[0] != '\0' &&
old_args[old_len - 1] == '\0' &&
memchr(old_args, '\0', (size_t)(old_len - 1)) == NULL) {
if (!force) {
wolfBoot_printf("FDT: using DTB bootargs: %s\n", old_args);
return 0;
}
wolfBoot_printf("FDT: replacing DTB bootargs: %s\n", old_args);
}
Comment thread
dgarske marked this conversation as resolved.
return fdt_fixup_str(ctx, off, "chosen", "bootargs", args);
}

int fdt_fixup_initrd(fdt_ctx* ctx, uint64_t start, uint64_t size)
{
int off, ret;
Expand Down
3 changes: 3 additions & 0 deletions src/update_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,9 @@ void RAMFUNCTION wolfBoot_start(void)
dts_size = (uint32_t)parsed;
wolfBoot_printf("Loading DTS: %p -> %p (%d bytes)\n",
dts_ptr, dts_addr, dts_size);
/* The FIT is signature-verified as a whole, so its DTB (and
* the bootargs inside it) are authenticated. */
fdt_set_dtb_authenticated(1);
if (wolfBoot_fit_memcpy(dts_addr, dts_ptr, dts_size) != 0) {
wolfBoot_printf("FIT: failed to load DTS\r\n");
#ifdef DISK_ENCRYPT
Expand Down
4 changes: 4 additions & 0 deletions src/update_ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,9 @@ void RAMFUNCTION wolfBoot_start(void)
dts_size = (uint32_t)parsed;
wolfBoot_printf("Loading DTS: %p -> %p (%d bytes)\n",
dts_ptr, dts_addr, dts_size);
/* The FIT is signature-verified as a whole, so its DTB (and
* the bootargs inside it) are authenticated. */
fdt_set_dtb_authenticated(1);
memcpy(dts_addr, dts_ptr, dts_size);
}
}
Expand Down Expand Up @@ -763,6 +766,7 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_panic();
}
wolfBoot_printf("DTB digest verified\n");
fdt_set_dtb_authenticated(1);
}
else if (dts_digest_present < 0) {
wolfBoot_printf("Malformed DTB digest TLV - rejecting\n");
Expand Down
Loading
Loading