FIT: gzip-compressed kernel + ramdisk (initramfs) support#763
FIT: gzip-compressed kernel + ramdisk (initramfs) support#763dgarske wants to merge 4 commits intowolfSSL:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds native gzip/DEFLATE decompression to wolfBoot’s FIT image loading path, plus optional FIT ramdisk extraction with DTB /chosen initrd fixups, along with build/config toggles, documentation updates, and unit tests.
Changes:
- Introduces an in-tree gzip inflater (
src/gzip.c,include/gzip.h) with host-side unit tests. - Extends FIT parsing/loading to support
compression = "gzip", per-subimagehash-1verification, and optional ramdisk discovery + DTB initrd fixups. - Wires new
GZIP/RAMDISKbuild switches and load-address configuration through the build system and example configs/docs.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/unit-tests/unit-gzip.c | Adds host unit tests for the gzip inflater (round-trip + negative cases). |
| tools/unit-tests/Makefile | Adds unit-gzip target to the unit test suite list and build rules. |
| tools/fdt-parser/fdt-parser.c | Updates FIT parsing tool to display an optional ramdisk image. |
| tools/config.mk | Plumbs WOLFBOOT_LOAD_RAMDISK_ADDRESS, GZIP, and RAMDISK through config variables. |
| src/update_ram.c | Loads FIT ramdisk (optional) and applies DTB initrd fixups in RAM-boot path. |
| src/update_disk.c | Loads FIT ramdisk (optional) and applies DTB initrd fixups in disk-boot path. |
| src/gzip.c | Implements RFC1951/RFC1952 decompression behind WOLFBOOT_GZIP. |
| src/fdt.c | Extends FIT loader for gzip decompression, hash verification, ramdisk discovery, and initrd fixups. |
| options.mk | Adds build switches GZIP and RAMDISK and wires in gzip object/CFLAGS. |
| include/target.h.in | Adds WOLFBOOT_LOAD_RAMDISK_ADDRESS template define. |
| include/gzip.h | Declares gzip API and constants/error codes for wolfBoot gunzip. |
| include/fdt.h | Updates FIT APIs and adds fdt_fixup_initrd() declaration. |
| hal/zynq.c | Increases DTB totalsize headroom for new /chosen properties. |
| hal/versal.c | Increases DTB totalsize headroom for new /chosen properties. |
| docs/Targets.md | Documents gzip FIT flow + ramdisk/initramfs flow for multiple targets. |
| config/examples/zynqmp_sdcard.config | Enables GZIP=1 by default and documents optional RAMDISK settings. |
| config/examples/zynqmp.config | Enables GZIP=1 by default. |
| config/examples/versal_vmk180_sdcard.config | Enables GZIP=1 by default. |
| config/examples/versal_vmk180.config | Enables GZIP=1 by default. |
| config/examples/polarfire_mpfs250_qspi.config | Enables GZIP=1 by default. |
| config/examples/polarfire_mpfs250.config | Enables GZIP=1 by default. |
| Makefile | Ensures WOLFBOOT_LOAD_RAMDISK_ADDRESS is substituted into include/target.h. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
New src/gzip.c implements DEFLATE (RFC 1951) plus the gzip wrapper (RFC 1952) from the RFC text only. Single-pass inflate, no allocations: the output buffer doubles as the LZ77 sliding window, so back-references read from out[out_pos - distance]. Canonical Huffman decode using counts[] / symbols[] tables, ~10x smaller code than fast lookup tables which matters in the bootloader. CRC32 + ISIZE verified against the gzip trailer. Gated by WOLFBOOT_GZIP. include/gzip.h carries the public entry point plus the RFC-canonical constants (magic bytes, CM=DEFLATE, fixed Huffman boundaries, EOB symbol, dynamic block field widths, run-length repeat metadata, CRC32 init/final-XOR, header/trailer sizes, alphabet sizes) so future maintainers can cross-reference the RFC sections by name instead of chasing literal numbers. Tests in tools/unit-tests/unit-gzip.c round-trip 6 corpora through host gzip(1) and back through wolfBoot_gunzip (empty, short text, all-zeros, structured text, pseudo-random, ~2 MB kernel-sized). 9 negative cases cover bad magic, bad CM, reserved FLG bits, truncated header, truncated DEFLATE body, CRC32 mismatch, ISIZE mismatch, output overflow, and NULL parameters. All 15 pass under libcheck.
Wires the new wolfBoot_gunzip inflater into the FIT image-loading path
and adds initramfs (ramdisk) extraction with DTB /chosen fixup so a
single signed FIT can carry kernel, DTB, and rootfs.
GZIP path
---------
* fit_load_image_ex(out_max) added; fit_load_image kept as a wrapper.
* When a subimage carries compression="gzip", inflate straight to the
FIT-declared load address, then verify the FIT hash-1 subnode
(sha256 / sha384 if available) for defense in depth on top of the
outer wolfBoot signature. The compression property is now read
unconditionally so a build without WOLFBOOT_GZIP can warn and fail
closed instead of silently memcpy-ing compressed bytes as if they
were raw.
* fit_verify_hash propagates wc_InitSha256 / wc_Sha256Update /
wc_Sha256Final return codes (and the SHA-384 equivalents) - any
non-zero return is treated as a verification failure so a misbehaving
backend cannot silently degrade to a no-op.
* GZIP=1 is the new default in the FIT-using example configs (zynqmp,
zynqmp_sdcard, polarfire_mpfs250, polarfire_mpfs250_qspi,
versal_vmk180, versal_vmk180_sdcard); set GZIP=0 to opt out.
Ramdisk path
------------
* fit_find_images() gains a ramdisk out-arg and fdt_fixup_initrd()
writes /chosen/linux,initrd-{start,end} as 64-bit big-endian cells.
* update_disk.c and update_ram.c load the FIT ramdisk node (under
WOLFBOOT_FIT_RAMDISK) and patch the loaded DTB. Compressed (gzip)
ramdisks reuse the same fit_load_image_ex() decompress path.
* RAMDISK=1 build switch defines WOLFBOOT_FIT_RAMDISK;
WOLFBOOT_LOAD_RAMDISK_ADDRESS is plumbed through tools/config.mk ->
Makefile sed -> include/target.h.in. Defaults to 0; when 0 the
ramdisk stays at whatever fit_load_image returned.
* hal/zynq.c and hal/versal.c bump fdt_totalsize headroom from 512 to
768 bytes to fit the new linux,initrd-{start,end} entries.
* config/examples/zynqmp_sdcard.config gains a commented-out opt-in
block (RAMDISK=1, WOLFBOOT_LOAD_RAMDISK_ADDRESS=0x40000000, alt
LINUX_BOOTARGS) so a single config file covers both rootfs-on-disk
and FIT-bundled-initramfs flows.
Builds against the existing master configs are byte-identical when
GZIP=0 and RAMDISK is unset.
…arFire
PolarFire MPFS250, Versal VMK180, and ZynqMP "Booting PetaLinux"
walkthroughs now describe both options for handing PetaLinux off through
the FIT image:
* Option A (default GZIP=1): set compression="gzip" in the .its,
point data at Image.gz / linux.bin.gz, and let mkimage build the
FIT directly. wolfBoot decompresses straight to the kernel load
address at boot and verifies hash-1.
* Option B (GZIP=0): keep the existing host-side gzip -cdvk /
gunzip step and compression="none" in the .its.
ZynqMP also gains a "FIT ramdisk (initramfs)" subsection covering
RAMDISK=1, WOLFBOOT_LOAD_RAMDISK_ADDRESS, the commented-out opt-in
block in zynqmp_sdcard.config, gzip ramdisk support, and a sample ITS
layout with kernel + DTB + ramdisk subimages.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #763
Scan targets checked: wolfboot-bugs, wolfboot-src
Findings: 3
3 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
Summary
Adds an in-tree DEFLATE/gzip inflater and wires it into the FIT image path so
wolfBoot can boot compressed Linux kernels and ramdisks directly out of a FIT
container, with per-subimage hash verification on top of the outer wolfBoot
signature. Also adds initramfs (ramdisk) extraction and DTB
/chosenfixup sothe kernel can find the loaded ramdisk.
Default-on (
GZIP=1) for the FIT-using example targets: ZynqMP (eMMC + SD),Versal VMK180 (eMMC + SD), PolarFire MPFS250 (SD + QSPI). Builds on the
existing master configs are byte-identical to before this branch.
What's in here
gzip inflater (
src/gzip.c,include/gzip.h)WOLFBOOT_GZIP.function,
/* … */comments, every callee return code checked.FIT integration (
src/fdt.c,include/fdt.h)fit_load_image_ex(out_max);fit_load_image()kept as a wrapper soexisting callers don't change.
WOLFBOOT_GZIPis on and a subimage carriescompression = "gzip",the inflater writes straight to the FIT-declared load address.
fit_verify_hashchecks thehash-1subnode against the loaded bytes. SHA-256 / SHA-384 return codes from
wc_InitSha*,wc_Sha*Update,wc_Sha*Finalare now propagated — amisbehaving crypto backend can't silently degrade to a no-op.
Ramdisk / initramfs support
fit_find_images()gains a ramdisk out-arg.fdt_fixup_initrd()writes/chosen/linux,initrd-startandlinux,initrd-endas 64-bit big-endian cells.src/update_disk.candsrc/update_ram.cload the FIT ramdisk node(under
WOLFBOOT_FIT_RAMDISK) and patch the DTB.fit_load_image_ex()decompress path.RAMDISK=1build switch +WOLFBOOT_LOAD_RAMDISK_ADDRESSplumbedthrough
tools/config.mk→Makefilesed →include/target.h.in.WOLFBOOT_LOAD_RAMDISK_ADDRESS=0(the default) keeps the ramdiskwherever
fit_load_imagereturned it (FIT load addr or in-FIT pointer).hal/zynq.candhal/versal.cfdt_totalsizeheadroom bumped512 → 768 bytes to fit the new
linux,initrd-{start,end}entries.Example configs
config/examples/zynqmp_sdcard_ramdisk.config(load address
0x40000000).GZIP=1flipped on by default in:zynqmp.config,zynqmp_sdcard.config,polarfire_mpfs250.config,polarfire_mpfs250_qspi.config,versal_vmk180.config,versal_vmk180_sdcard.config. PassGZIP=0to opt out and keep thepre-branch host-side gzip workflow.
Unit tests (
tools/unit-tests/unit-gzip.c)pseudo-random, ~2 MB kernel-sized) — host
gzip(1)→wolfBoot_gunzip.header, truncated DEFLATE body, CRC32 mismatch, ISIZE mismatch, output
overflow, NULL parameters.
Docs (
docs/Targets.md)GZIP=1(default,compression="gzip"+Image.gz) andGZIP=0(plainImage) flows.plus the
RAMDISK=1/WOLFBOOT_LOAD_RAMDISK_ADDRESSsetup and asample ITS layout for kernel + DTB + initramfs.
compression="gzip", mkimage builds the FIT directly) and Option B(host-side
gzip -cdvk,compression="none").Notes for reviewers
depth is provided by the existing wolfBoot signature on the outer FIT
plus the per-subimage
hash-1verify. CRC32 in the gzip trailer is adata-integrity check, not a security boundary.
fit_load_imagekeeps its oldprototype; the new behavior is reached via
fit_load_image_ex.WOLFBOOT_LOAD_RAMDISK_ADDRESS=0is the safe default — when unset theramdisk stays wherever the FIT/loader put it.