Skip to content

src/update_ram.c: fix RAMBOOT skipping payload relocation when uImage ih_load differs from WOLFBOOT_LOAD_ADDRESS#827

Open
94xhn wants to merge 1 commit into
wolfSSL:masterfrom
94xhn:fix-ramboot-uboot-legacy-ih-load-relocation
Open

src/update_ram.c: fix RAMBOOT skipping payload relocation when uImage ih_load differs from WOLFBOOT_LOAD_ADDRESS#827
94xhn wants to merge 1 commit into
wolfSSL:masterfrom
94xhn:fix-ramboot-uboot-legacy-ih-load-relocation

Conversation

@94xhn

@94xhn 94xhn commented Jul 18, 2026

Copy link
Copy Markdown

Problem

When WOLFBOOT_USE_RAMBOOT is active (EXT_FLASH+NO_XIP, or EXT_ENCRYPTED+MMU), wolfBoot_ramboot() copies the entire signed image to a fixed RAM staging address derived from WOLFBOOT_LOAD_ADDRESS.

If the image is also a WOLFBOOT_UBOOT_LEGACY (uImage) payload whose header field ih_load is non-zero, wolfBoot_start() only rewrites the jump target (load_address = ih_load) — it never copies the payload bytes there. The generic "copy image to RAM" memcpy a few lines below is compiled out entirely under #ifndef WOLFBOOT_USE_RAMBOOT, because it assumes RAMBOOT already placed the data where it needs to be. That assumption only holds when ih_load happens to coincide with the RAMBOOT staging address (WOLFBOOT_LOAD_ADDRESS + the 64-byte uImage header). When it doesn't, do_boot() jumps to ih_load, which was never written.

The non-RAMBOOT path does not have this problem, since its memcpy runs after load_address has already been corrected to ih_load.

Reachability

arch.mk unconditionally enables WOLFBOOT_UBOOT_LEGACY for TARGET=zynq (ZynqMP), TARGET=zynq7000, and TARGET=versal. The shipped config/examples/zynqmp.config, zynq7000.config, and versal_vmk180.config all set EXT_FLASH=1 + NO_XIP=1, which is exactly the WOLFBOOT_USE_RAMBOOT trigger condition, and WOLFBOOT_NO_RAMBOOT is never set anywhere in the repo. So all three of these example targets compile in both features simultaneously. The officially-tested Linux flow for these targets currently boots a raw zImage rather than a mkimage-wrapped uImage, so this isn't hit by the documented/tested path today — but the ih_load handling is unconditionally compiled in as an opportunistic feature, and any user who mkimage-wraps a kernel with a real, non-zero ih_load for one of these targets (standard PetaLinux/U-Boot practice) would hit it.

Fix

When ih_load != 0 under WOLFBOOT_USE_RAMBOOT, relocate the payload from its current RAM location (os_image.fw_base, where RAMBOOT already staged it) to ih_load via memmove if the two addresses differ — mirroring what the non-RAMBOOT arm already does with its memcpy. The ih_load == 0 (Linux/PPC) path is untouched.

Testing

Built a standalone C harness that replays the exact control flow of wolfBoot_ramboot() (header + body copy to a fixed RAM staging address) and the WOLFBOOT_UBOOT_LEGACY block's ih_load handling against a simulated flat RAM buffer, using the literal post-fix code from this diff:

  • ih_load coincidentally equal to the RAMBOOT staging address: payload present (no relocation needed, none performed) — pass.
  • ih_load a distinct, higher address (realistic board load address): without this fix, payload is missing at the jump target (bug reproduced); with this fix, memmove relocates it correctly — pass.
  • ih_load a distinct, lower address (exercises memmove's backward-overlap safety): same result — pass.
  • ih_load == 0 (Linux/PPC convention): unaffected by this diff, behavior identical before/after — pass.

No CI/hardware available locally to exercise a full Zynq/Versal build; this was validated via the isolated control-flow harness described above rather than an end-to-end board boot.

…OT_LOAD_ADDRESS

When WOLFBOOT_USE_RAMBOOT is active (EXT_FLASH+NO_XIP, or EXT_ENCRYPTED+MMU),
wolfBoot_ramboot() copies the whole signed image to a fixed RAM staging
address derived from WOLFBOOT_LOAD_ADDRESS. If the image is also a
WOLFBOOT_UBOOT_LEGACY (uImage) payload with a non-zero ih_load field, the
code only rewrites the jump target (load_address = ih_load) but never
copies the payload bytes there, because the generic "copy image to RAM"
memcpy is compiled out entirely under #ifndef WOLFBOOT_USE_RAMBOOT (it
assumes RAMBOOT already placed the data where it needs to be, which is
only true when ih_load happens to coincide with the RAMBOOT staging
address). do_boot() then jumps to ih_load, which was never written.

This is reachable structurally: the zynqmp, zynq7000 and versal_vmk180
example configs all set EXT_FLASH+NO_XIP (enabling RAMBOOT) while arch.mk
unconditionally enables WOLFBOOT_UBOOT_LEGACY for those targets, and
WOLFBOOT_NO_RAMBOOT is never set anywhere in the repo. Any user who
mkimage-wraps a Linux/RTOS kernel with a real, non-zero ih_load for one of
these targets (standard PetaLinux/U-Boot practice) would hit this.

Fix: when ih_load != 0 under WOLFBOOT_USE_RAMBOOT, relocate the payload
from its current RAM location (os_image.fw_base, where RAMBOOT already
staged it) to ih_load via memmove if the two addresses differ, mirroring
what the non-RAMBOOT arm already does with its memcpy. The ih_load == 0
(Linux/PPC) path is untouched.
Copilot AI review requested due to automatic review settings July 18, 2026 14:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants