lib: zstd: decompress every frame, not just the first - #39
Conversation
Split 32-bit size_and_off and size_and_nimage fields of the v2 image format header into their own 16-bit size, offset and num_images fields. Set num_images based on number of images passed by the datafile parameter and size based on the offset to the hash field to fix using a single init data file and no boot data file for the v2 image format. Signed-off-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Alexey Charkov <alchark@flipper.net>
The v2 image format can embed up to 4 data files compared to the two init and boot data files using the older image format. Add support for displaying more of the image header information that exists in the v2 image format, e.g. image load address and flag. Example for v2 image format: > tools/mkimage -l rk3576_idblock_v1.09.107.img Rockchip Boot Image (v2) Image 1: 4096 @ 0x1000 - Load address: 0x3ffc0000 Image 2: 77824 @ 0x2000 - Load address: 0x3ff81000 Image 3: 262144 @ 0x15000 Example for older image format: > tools/mkimage -l u-boot-rockchip.bin Rockchip RK32 (SD/MMC) Boot Image Init Data: 20480 @ 0x800 Boot Data: 112640 @ 0x5800 Signed-off-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Alexey Charkov <alchark@flipper.net>
The v2 image format embeds boot0 and boot1 parameters, the vendor tool boot_merger may write these parameters based on the rkboot miniall.ini files. E.g. a RK3576 boot image may contain a boot1 parameter that signals BootROM or vendor blobs to use 1 GHz instead of the regular 24 MHz rate for the high precision timer. Add support for printing boot0 and boot1 parameters, e.g.: > tools/mkimage -l rk3576_idblock_v1.09.107.img Rockchip Boot Image (v2) Boot1 2: 0x100 Image 1: 4096 @ 0x1000 - Load address: 0x3ffc0000 Image 2: 77824 @ 0x2000 - Load address: 0x3ff81000 Image 3: 262144 @ 0x15000 Signed-off-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Alexey Charkov <alchark@flipper.net>
The vendor boot_merger tool support a ALIGN parameter that is used to define offset alignment of the embedded images. Vendor use this for RK3576 to change offset alignment from the common 2 KiB to 4 KiB, presumably it may have something to do with UFS. Testing with eMMC has shown that using a 512-byte alignment also work. Add support for overriding offset alignment in case this is needed for e.g. RK3576 in the future. Signed-off-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Alexey Charkov <alchark@flipper.net>
The v2 image format can support up to 4 embedded images that can be
loaded by the BootROM using the back-to-bootrom method.
Currently two input files can be passed in using the datafile parameter,
separated by a colon (":").
Extend the datafile parameter parsing to support up to 4 input files
separated by a colon (":") for use with the v2 image format.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
The v2 image format supports defining a load address and flag for each embedded image. Add initial support for writing the image load address and flag to the v2 image format header. This may later be used for RK3576 to embed a minimal initial image that if required to fix booting from SD-card due to a BootROM issue. Signed-off-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Alexey Charkov <alchark@flipper.net>
The BootROM on RK3576 has an issue loading boot images from an SD-card. This issue can be worked around by injecting an initial boot image before TPL that: writel(0x3ffff800, 0x3ff803b0) Prepend an image containing binary code that does this and return to BootROM to load next image, TPL. Signed-off-by: Jonas Karlman <jonas@kwiboo.se> [switch from pre-built binary to from-source build via existing Makefiles] Signed-off-by: Alexey Charkov <alchark@flipper.net>
NanoPi M5 uses the M1 pin configuration for its serial flash controller. Set the pin mode to M1 explicitly to enable SPL loading from SPI flash. Signed-off-by: Alexey Charkov <alchark@gmail.com>
RK3576 EVB1 is the evaluation board for the RK3576 SoC, which also serves as the reference design for board vendors. Enable building images for it using upstream DTS and a minimal defconfig. Signed-off-by: Alexey Charkov <alchark@gmail.com>
Introduce CONFIG_USB_FUNCTION_FASTBOOT_EP_BUFFER_SIZE to allow customization of the endpoint buffer size used for fastboot transfers. The EP_BUFFER_SIZE must always be an integral multiple of the maxpacket size (64, 512, or 1024 bytes depending on USB speed), as controllers like DWC3 expect bulk OUT requests to be divisible by maxpacket size. On DWC3 controllers operating in SuperSpeed mode, increasing the endpoint buffer size from the default value significantly improves download throughput - from approximately 50 MB/s to over 170 MB/s. This is particularly beneficial for flashing large images during development or production. The configurable buffer size allows board maintainers to tune the trade-off between memory usage and transfer performance based on their specific requirements and available resources. Change-Id: 0c3402e0-3855-4d3f-b6c1-293793eebc4a Signed-off-by: Anton Burticica <mouse@ya.ru>
This change will go via the upstream DTS tree, so just put it in here temporarily to get Ethernet working on Omni3576 boards. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Remove the supply regulators from the eMMC controller node on Luckfox
Core3576, as they cause the eMMC to endlessly re-tune phase at startup,
and are most likely wrong:
1. The vendor DTS doesn't define those regulators
2. The VCCQ regulator referenced here is actually used for the SD card,
and it is unlikely that both can share the same regulator with an
1.8-3.3V range, whereas eMMC only expects 1.8V
3. Rockchip reference schematic, which this board broadly follows,
drives the VCCQ supply of eMMC flash from VCC_1V8_S0 rather than
VCCIO_SD_S0, where VCC_1V8_S0 is a fixed 1.8V load switch with VIN tied
to VCC_1V8_S3 and EN tied to VCCA_1V8_S0 (a.k.a. PMIC PLDO1, not PLDO5)
There is no published schematic for the Core3576 SoM unfortunately.
Cc: stable@vger.kernel.org
Fixes: d7ad90d22abe ("arm64: dts: rockchip: Add Luckfox Omni3576 Board support")
Signed-off-by: Alexey Charkov <alchark@flipper.net>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
gpt_fill_header() computes last_usable_lba and first_usable_lba from
hardcoded block counts that silently assumes 512-byte sectors and the
default number of partition entries.
The partition entry array holds GPT_ENTRY_NUMBERS entries of 128 bytes
each, so the number of blocks it needs depends on both the entry count
and the block size. write_gpt_table() derives that count correctly and
writes the backup array at last_usable_lba + 1, so whenever the two
disagree the backup array no longer immediately precedes the backup GPT
header. Furthermore, on a device with 4096-byte native sectors, such as
UFS flash, and the default 128 entries, the array is 4 blocks rather than
32. Current code reserves 34 blocks at each end of the disk for any block
size and wastes about 114 KiB at each end.
Add a new helper gpt_pte_blocks() and use it in all four places which
currently calculate the number of blocks each in its own way, so the
layout written by gpt_fill_header() and the extent written by
write_gpt_table() cannot drift apart again. With this, first_usable_lba
on 4096-byte sectors is 6, last_usable_lba is lba - 6, and the backup
array occupies lba - 5 .. lba - 2, immediately preceding the backup
header, as the UEFI specification describes.
Note that this changes the 512-byte layout too for boards that do not use
the default entry count. CONFIG_EFI_PARTITION_ENTRIES_NUMBERS is
"default 56 if ARCH_SUNXI" and is set to 64 by a number of Rockchip
defconfigs:
entries array blocks first_usable_lba last_usable_lba
56 14 34 -> 16 lba - 34 -> lba - 16
64 16 34 -> 18 lba - 34 -> lba - 18
128 32 34 -> 34 lba - 34 -> lba - 34
Existing partition tables stay readable either way, since is_gpt_valid()
locates the entry array from the on-disk partition_entry_lba. Only newly
written tables change. Partitions given without an explicit start= will
now be placed lower on those boards; on sunxi first_usable_lba lands on
the 8 KiB SPL offset, so such boards should keep specifying start=
explicitly.
While here, report both LBAs when the requested layout does not fit, since
the existing "Partitions layout exceeds disk size" debug message gives no
clue as to by how much, making it less helpful in debugging.
Co-developed-by: Anton Burticica <mouse@ya.ru>
Signed-off-by: Anton Burticica <mouse@ya.ru>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
Several GPT header fields are read without converting from little-endian, which is wrong on big-endian hosts. gpt_fill_pte() takes my_lba and partition_entry_lba raw when working out the region a partition must not overlap, so on a big-endian host both bounds are byte-swapped garbage and the overlap check does not do anything useful. gpt_verify_partitions() compares the loop counter against num_partition_entries raw, so its "More partitions than allowed!" guard never triggers. It also swaps gpt_part_size, which is already in host order, having been computed from two le64_to_cpu() results a few lines above. Drop the conversion rather than adding one. All of this is a no-op on little-endian targets. Signed-off-by: Alexey Charkov <alchark@flipper.net>
part_test_mac(), part_print_mac() and part_get_info_mac() each declare a single-block buffer sized after the descriptor struct: ALLOC_CACHE_ALIGN_BUFFER(mac_driver_desc_t, ddesc, 1); ALLOC_CACHE_ALIGN_BUFFER(mac_partition_t, mpart, 1); Both structs are 512 bytes, but every blk_dread() below them asks for one block, which transfers desc->blksz bytes. On a device with 4096-byte logical blocks that writes 4096 bytes into a 512-byte on-stack buffer and corrupts the stack. part_test_mac() runs on every block device during partition probing, so on sandbox with CONFIG_MAC_PARTITION=y this crashes on any access at all to a device with large blocks, for instance: host bind 0 disk.img 4096 part list host 0 Pad the buffers out to the block size with ALLOC_CACHE_ALIGN_BUFFER_PAD(), which is what part_efi.c already does for its own block buffers. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Add a test that runs gpt_fill_header() and gpt_fill_pte() over a range of
block sizes and checks that the partition entry array is sized and placed
consistently, in particular that the backup array ends exactly where the
backup GPT header begins.
Neither function performs any block I/O, so the test builds a synthetic
struct blk_desc rather than needing a block device with a configurable
block size. The expected array size is taken from the GPT header fields
rather than from GPT_ENTRY_NUMBERS, so the test still holds for builds
with a non-default CONFIG_EFI_PARTITION_ENTRIES_NUMBERS, such as
ARCH_SUNXI with 56 entries.
Without the preceding fixes this fails on the first block size other than
512:
test/dm/part.c:313, dm_test_part_gpt_blksz():
entry_lba + pte_blks == first_lba: Expected 0x12 (18), got 0x22 (34)
Signed-off-by: Alexey Charkov <alchark@flipper.net>
Unlike every other test in this file, test_gpt_write_part_type() never binds the disk image it operates on. It has worked since it was added only because some test that ran before it left host 0 bound to the image this fixture provides. That breaks as soon as a test which binds something else is added above it, and it means the test silently depends on collection order rather than on its own fixture. Bind the image like the other tests do. Fixes: 7a598e6 ("test/py: tests: gpt: add test_gpt_write_part_type") Signed-off-by: Alexey Charkov <alchark@flipper.net>
Bind a blank image with a 4096-byte logical block size, let U-Boot write a GPT to it, and then check the result both through 'part list' and by unpacking the primary and backup headers straight out of the image. The interesting assertion is that the backup partition entry array ends immediately before the backup header in the last block, which is what regresses when the array size is computed with a hardcoded block count. No sgdisk is needed since U-Boot creates the table itself, so unlike the other tests here this one has no requiredtool marker. The number of entries comes from the build config rather than being hardcoded to 128, so the expected LBAs are recomputed for builds that set CONFIG_EFI_PARTITION_ENTRIES_NUMBERS differently. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Update FASTBOOT_COMMAND_LEN and FASTBOOT_RESPONSE_LEN to match the buffer sizes defined in the Android fastboot client: https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/constants.h #define FB_COMMAND_SZ 4096 #define FB_RESPONSE_SZ 256 The original 64-byte command limit dates back to the early fastboot protocol specification. Modern Android fastboot clients support commands up to 4096 bytes, enabling: - Longer partition names in flash/erase commands - Extended getvar queries with complex arguments - OEM commands with substantial payloads Change-Id: 1aaaa615-0811-4f81-949c-1ae4b9935fb0 Signed-off-by: Anton Burticica <mouse@ya.ru>
… consoles The bitmap console driver does not implement ops->measure, so vidconsole_measure() fell back to returning a single-line bounding box (y1 = y_charsize) with x1 = x_charsize * strlen(text), regardless of any pixel-width limit passed by the caller. This caused Expo scene objects using CENTRE or RIGHT alignment to compute incorrect x offsets for long strings. For example, the ~68- character help prompt on a 220 px wide scene produced: x1 = 6 * 68 = 408 px, xofs = (220 - 408) / 2 = -94 px pushing the text ~94 pixels off the left edge of the display. Add a word-wrapping fallback that is taken when a pixel-width limit is provided and the caller requests line records (lines != NULL). The fallback iterates character-by-character, breaks at the last space before an overflow, and populates the vidconsole_mline array and the overall bbox.y1 in the same way a truetype driver would, so that callers that lay out text using the returned metrics (e.g. scene_render_txt) produce correctly wrapped, on-screen output.
Add a simple video uclass driver for the Flipper One's 256x144 8-bit grayscale SPI display. The display accepts a full frame in the BPP8 format (8-bit grayscale) in a single SPI write transaction, padded to a line stride of 258 bytes (256 pixels + 2 footer bytes per row). No other configuration is required as long as SPI mode 3 (CPOL=1, CPHA=1) is used and the SPI clock is up to 24 MHz. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Flipper One has an onboard MCU which exposes its front panel buttons over the I2C interconnect bus. Add a small keyboard driver to use these buttons for menu navigation. Signed-off-by: Alexey Charkov <alchark@flipper.net>
This will ultimately need to come from upstream, but for now we need to get it in here so that we can build a working image for the device. Drop DP nodes for now, as they are not yet included in rk3576.dtsi in its U-boot version. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Flipper One is a portable multi-tool device with a built-in display and various interfaces, built around the Rockchip RK3576 SoC. Add a new defconfig file for building U-Boot for this device. Signed-off-by: Alexey Charkov <alchark@flipper.net>
… PHY dwc3_glue_probe() currently fetches the "usb3-phy" handle and calls generic_phy_init() on it as the very first thing, before the glue's own clocks and resets are touched. Only afterwards does it call dwc3_glue_clk_init() and dwc3_glue_reset_init(), and later still generic_phy_power_on(). For most platforms this happens to work because the controller has been held in reset since power-on, so whatever state it presents on its PIPE interface is benign while the PHY runs its bring-up sequence. It breaks, however, when an earlier stage has already taken the controller out of reset. A typical case is the Rockchip BootROM in USB download (Maskrom) mode on RK3576: it leaves USB3OTG0 active so that it can talk to the host over USB 2.0, which means the DWC3 wrapper is clocked and out of reset by the time U-Boot probes it. The wrapper then keeps driving phy_reset / phy_powerdown / phy_rate / etc. into the USBDP combo PHY's PMA while rk3588_udphy_init() tries to bring up the LCPLL, which never reaches AFC/LOCK_DONE and times out with: rockchip_udphy phy@2b010000: cmn ana lcpll lock timeout rockchip_udphy phy@2b010000: failed to init usbdp combophy Linux's dwc3-of-simple deasserts the glue resets and enables the glue clocks in its own probe, and only then calls of_platform_populate(), so the dwc3 child (and therefore phy_init()) cannot run until the wrapper is in a known state. Mirror that ordering here by moving the clk/reset init ahead of generic_phy_init(). This is a prerequisite for any per-compatible "pulse the reset before deasserting it" fix in dwc3_glue_reset_init() to actually take effect on platforms where the controller may be left running by an earlier boot stage. With the old ordering, such a pulse would happen after the PHY had already failed its LCPLL poll and would have no effect. No functional change is expected on platforms where the controller is already in reset at probe time. Signed-off-by: Alexey Charkov <alchark@flipper.net>
dwc3_glue_reset_init() currently only calls reset_deassert_bulk() on the resets it has just acquired (with a pre-existing assert+udelay exception for "qcom,dwc3"). If the controller has already been taken out of reset by an earlier boot stage, deasserting an already-deasserted reset is a no-op, and the glue probe leaves whatever state that stage left behind in place. This becomes a problem when the controller has been actively used before U-Boot runs. For example, on Rockchip RK3576 the BootROM keeps USB3OTG0 powered up and clocked while it talks to the host in USB download (Maskrom) mode. With the controller still alive on its PIPE interface, the subsequent USBDP combo PHY bring-up cannot reliably get its LCPLL to lock and the PHY .init callback fails with: rockchip_udphy phy@2b010000: cmn ana lcpll lock timeout Drop the qcom-only special case and always assert the reset, wait briefly, then deassert it. A bulk-assert followed by a small delay and a bulk-deassert is harmless on platforms where the controller was already in reset (the reset just gets re-pulsed before the rest of the glue runs), and forces the controller into a known state on platforms where it was not. This relies on the preceding reordering of dwc3_glue_probe() so that the glue resets are toggled before generic_phy_init() runs; otherwise the PHY would already have observed the stale controller state and failed before this code is reached. Closes: https://lore.kernel.org/u-boot/CAKTNdwGo434ShEsP=e=uUAbJzVxfQPPfNPeTxOOqwbuoCyzRjw@mail.gmail.com/ Signed-off-by: Alexey Charkov <alchark@flipper.net>
Add the missing parameter and enum value descriptions reported by scripts/kernel-doc when building with W=1: - get_relfile(): document @type - get_pxelinux_path(): document @pxefile_addr_r - enum lex_state: document L_NORMAL, L_KEYWORD, L_SLITERAL - get_token(): document @t and @State No functional change. Signed-off-by: Alexey Charkov <alchark@flipper.net>
The Boot Loader Specification [1] type flipperdevices#2 entry files use the keyword "options" for the kernel command line, which corresponds to extlinux's "append". Recognising it here lets the existing pxelinux parser ingest BLS entries unchanged, paving the way for a BLS bootmeth that reuses the parser instead of duplicating it. No effect on existing extlinux/pxelinux files: "options" is not a valid keyword in those formats, so it cannot collide with prior usage. [1] https://uapi-group.org/specifications/specs/boot_loader_specification/ Signed-off-by: Alexey Charkov <alchark@flipper.net>
Split the body of parse_label() into a standalone parse_label_keys() helper that walks key/value lines and populates a pre-existing struct pxe_label. parse_label() becomes a thin wrapper that creates the label, reads its name, attaches it to the menu, and delegates. This is a pure refactor: the new helper contains the original loop verbatim, with the local variable declarations moved to its scope. No call sites or behaviour change. A subsequent change will export this helper so callers parsing formats that lack a 'label' header (notably Boot Loader Specification type flipperdevices#2 entries) can populate a label directly from a file body without duplicating the parser. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Selecting the next integer multiplier m is coupled to setting a negative fractional coefficient k. The current code checks for negative k in two separate places, which is error-prone. Let rockchip_rk3588_pll_k_get update m directly, to make it the single source of truth for the final value of the integer multiplier m, which also reduces the number of scattered conditional branches in the code. Signed-off-by: Alexey Charkov <alchark@flipper.net>
The TRM defines the fractional PLL adjustment coefficient as a signed two's complement number, 16 bits wide, so store it as such to avoid confusion. Signed-off-by: Alexey Charkov <alchark@flipper.net>
…rk3588_pll_get_rate Current code calculates the fractional component in 32 bits before assigning it to a 64-bit holding variable, causing overflow for real-world values of k, given that OSC_HZ is 24000000U. It also does bitwise manual massaging of an unsigned representation of what is actually a two's complement signed value, which is confusing and makes the code harder to read. Read k into a properly signed type and promote operands to avoid overflow, which also enables the use of div_s64() to express the math more clearly. Fixes: b851c00 ("clk: rockchip: pll: Add pll_rk3588 type for rk3588") Signed-off-by: Alexey Charkov <alchark@flipper.net>
The USB boot path in the RK3576 boot ROM is very slow to jump to the 2nd stage uploaded via the Maskrom 0x472 command. This is due to a very slow unoptimized CRC16 routine checking the 0x472 payload byte-by-byte and fetching ~100 instructions per byte from uncached ROM. Enable the I-cache in EL3 before jumping to the 2nd stage, which provides a ~16x speedup (~2 MB/s) for the 0x472 path. This is most relevant for booting meaningful (megabytes-sized) payloads via USB, as the slow CRC routine is never called from other boot paths. The change is harmless for normal boot from persistent storage though, so can be enabled unconditionally. Signed-off-by: Alexey Charkov <alchark@flipper.net>
spl_load_legacy_lzma() lives in common/spl/spl_legacy.c, which is only built when CONFIG_$(PHASE_)LEGACY_IMAGE_FORMAT is set, but the call site in _spl_load() is guarded by CONFIG_SPL_LZMA alone. Enabling LZMA decompression without the legacy image format therefore fails to link: ld: common/spl/spl.o: in function `_spl_load': include/spl_load.h:73: undefined reference to `spl_load_legacy_lzma' CONFIG_SPL_LZMA is also needed to support LZMA-compressed payloads with a FIT-only SPL, so add the missing condition to avoid including the legacy image related function call when legacy image support is not built. Signed-off-by: Alexey Charkov <alchark@flipper.net>
load_simple_fit() is expected to fill in the image_info structure it receives upon successful return, but the path which skips a zero-sized image returns success without touching it. The result is that spl_fit_record_loadable() then publishes whatever else the descriptor happened to hold in /fit-images under the skipped image's name: the size and entry point of the previous loadable, or - for the first one, since image_info is declared without an initialiser - uninitialised stack. This is reachable whenever a FIT carries an image node with no content, which binman produces for an optional blob that was not supplied, such as an OP-TEE which the build did not provide. Ensure that the image_info structure is filled in with a size and entry point before returning, same way as other successful paths do (but skipping the actual load). Fixes: 6d99f86 ("spl: fit: Skip attempting to load 0 length image") Signed-off-by: Alexey Charkov <alchark@flipper.net>
WriteDocs() and write_bintool_docs() strip four characters from the start of every docstring line but the first, to undo the indentation the source file gives them. Since Python 3.13 the compiler already removes the common indentation from docstrings [1], so this removes four characters of actual text from every line of every entry and bintool description: $ binman entry-docs | head ... that an image node whose only content is an optional entry which was is an example showing ATF, TEE and a device tree all combined:: Use inspect.cleandoc() instead, which produces the same result on both older and newer interpreters. The existing tests only checked that some output was produced, so they missed this entirely; make them also confirm that a known line of a known description survives intact. Link: python/cpython#81283 [1] Signed-off-by: Alexey Charkov <alchark@flipper.net>
Board implementations of spl_start_uboot() are not required to be idempotent - the documented examples sample a GPIO or read a character from the SPL console - so calling it more than once can yield different answers. Add spl_falcon_boot(), which calls spl_start_uboot() at most once and caches the result. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Modern SoCs such as Rockchip RK3576 need TFA to be running to provide firmware services to the OS. Enable the TFA boot flow to allow using Linux as BL33 (including its calling convention) to facilitate Falcon mode boot on such SoCs. Signed-off-by: Alexey Charkov <alchark@flipper.net>
In Falcon mode, the SPL loads a FIT image containing a Linux kernel instead of U-boot proper, and it may need to fall back to loading U-boot if Linux is unavailable. Add support for images containing a Linux kernel, optionally on a different UFS LUN and/or offset vs. U-boot proper to enable fallback. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Add an simple named Linux kernel blob type to binman, activated by the "LINUX_KERNEL" make variable. No processing is done on the passed blob. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Add a simple named Linux ramdisk blob type to binman, activated by the "LINUX_INITRD" make variable. No processing is done on the blob. Signed-off-by: Alexey Charkov <alchark@flipper.net>
When a generated FIT is used to boot Linux directly, bypassing U-Boot proper (Falcon mode), there is no runtime code to discover the kernel command line and (optional) initrd location and include them in a FDT. To facilitate easier preparation of a ready-to-boot FIT, add support for pre-patching the FDTs in a FIT with a preconfigured /chosen node including the bootargs and initrd location. Signed-off-by: Alexey Charkov <alchark@flipper.net>
… and /chosen Add a test for a FIT image with a fake Linux kernel and initrd, and a valid device tree into which a /chosen node is added (containing a bootargs property, as is relevant for Falcon mode boot). Signed-off-by: Alexey Charkov <alchark@flipper.net>
Common code calls into a platform specific function to determine whether to attempt OS boot in Falcon mode. There is currently no platform logic to drive that decision on Rockchip, so attempt Falcon mode boot whenever it is enabled in the configuration. This can be overridden by board code if needed with appropriate logic, such as checking for a button state to skip Falcon mode boot when pressed. Signed-off-by: Alexey Charkov <alchark@flipper.net>
Modern Rockchip SoCs such as RK3576 require TF-A to be running to provide
firmware services to the OS. To enable booting Linux in Falcon mode on
such SoCs, add a binman template for generation of FIT images containing
appropriately split TF-A and TEE binaries (as is currently done by binman
for U-boot proper images), externally provided Linux kernel and optionally
an initrd.
Any FDTs provided to the generator will be pre-patched with a /chosen
node containing an initrd load address and size (if an initrd is provided)
and a bootargs property containing the kernel command line (if provided),
so that the kernel can be booted directly without external preprocessing.
A config fragment rockchip-falcon.config can be used to enable Falcon
mode boot and the building of TF-A+Falcon with binman, e.g.:
make nanopi-m5-rk3576_defconfig \
rockchip-falcon.config
This will produce a FIT image u-boot-rockchip-falcon.itb containing TF-A,
TEE, Linux kernel and optionally an initrd, which can be flashed to UFS
and booted in Falcon mode.
This can also be combined with the existing rockchip-ramboot.config
fragment to obtain images suitable for booting from RAM, e.g.:
make nanopi-m5-rk3576_defconfig \
rockchip-falcon.config \
rockchip-ramboot.config
The resulting images can be uploaded directly to RAM via Maskrom with no
storage or USB loader required, e.g.:
rockusb download-sram u-boot-rockchip-usb471.bin
rockusb download-ddr u-boot-rockchip-usb472-falcon.bin
Signed-off-by: Alexey Charkov <alchark@flipper.net>
Falcon Mode documentation only covers SPL entering the kernel itself, which is not what happens on ARM64 SoCs needing ARM Trusted Firmware resident to provide firmware services: SPL loads a FIT holding BL31 as its firmware image and the kernel as a loadable, and BL31 enters the kernel as BL33. Describe that flow, what the kernel image may be given that neither booti_setup() nor bootz_setup() runs, how it differs from the classic flow, and how to configure and build it on Rockchip. Signed-off-by: Alexey Charkov <alchark@flipper.net>
The kernel compresses an inline extent as a whole block: run_delalloc_inline() calls btrfs_compress_bio(inode, 0, blocksize, ...), so the data is zero-filled past EOF and the resulting zstd frame declares a content size of one block. The extent item records the unaligned file size though - __cow_file_range_inline() passes i_size down to insert_inline_extent(), which stores it as ram_bytes. btrfs_read_extent_inline() sizes its decompression buffer from ram_bytes, so for a 1900-byte file the destination is 1900 bytes while the frame decodes to 4096. Since commit 918adf8 ("btrfs: Use U-Boot API for decompression") btrfs decompresses through the common U-Boot helper, which uses the one-shot zstd_decompress_dctx(). That API requires the destination to cover the whole frame and fails with dstSize_tooSmall, error 70, otherwise. The streaming ZSTD_decompressStream() path it replaced stopped once the output buffer was full, so it never hit this. The kernel side does not notice because fs/btrfs/zstd.c streams into its own buffer and copies out at most destlen. Allocate a full block for the decompression buffer and copy only ram_bytes back to the caller. An inline extent never spans more than one block, which bounds the allocation. This shows up on RK3399 and ODROID-N2 as "zstd_decompress: failed to decompress: 70" (armbian/build#9651, #10208), where it breaks fdt apply on zstd-compressed overlays. Images built with mkfs.btrfs --rootdir --compress zstd do not reproduce it, since btrfs-progs writes a frame whose content size already equals ram_bytes. Only files written at runtime through the kernel trip it. Fixes: 918adf8 ("btrfs: Use U-Boot API for decompression") Signed-off-by: Cole Munz <Munzzyy1@proton.me> Reviewed-by: Qu Wenruo <wqu@suse.com>
btrfs_readdir() zeroes the dirent and fills in only the name and the
type, so dent->size stays 0 and every file is listed as zero bytes:
=> ls host 0 /
0 f_192k.bin
0 small_3k.bin
Reads themselves are fine, since btrfs_read() takes the size from
btrfs_size(), which does its own inode item lookup. It affects EFI
too: dir_read() in lib/efi_loader/efi_file.c copies dent->size into
both file_size and physical_size, so an EFI application enumerating a
directory on btrfs sees every file as empty, which is the generic-code
path Alexey's readdir series moves btrfs onto.
The custom listing that fs_ls_generic() replaced looked the inode item
up and printed the real size, and every other filesystem in the tree
fills dent->size in its own readdir: ext4fs.c:327, exfat io.c:805,
erofs fs.c:186, squashfs sqfs.c:1095 and fat.c:1555.
btrfs_next_dir_entry() already has the dir item mapped, so read the
key it points at while we are there and hand it back to the caller,
and use that to reach the inode item. A subvolume entry points at a
root item instead and has no size of its own, so leave that one at 0.
=> ls host 0 /
196608 f_192k.bin
3000 small_3k.bin
Fixes: 31cf3f1 ("fs: btrfs: use fs_ls_generic() and drop custom implementation")
Signed-off-by: Cole Munz <Munzzyy1@proton.me>
The U-Boot copy of btrfs_search_slot() returns on error with the nodes it has descended through still attached to the path. The kernel one releases the path on any error unless p->skip_release_on_error is set, and callers written against that convention treat a failed search as owning nothing. btrfs_size() is one: it returns straight away on a search error and never reaches its btrfs_release_path() call, so the attached extent buffer references leak. Route both error exits through a release of the path. The error returns of read_node_slot() carry no extra reference, so the path is the only thing to clean up. Suggested-by: Qu Wenruo <quwenruo.btrfs@gmx.com> Signed-off-by: Cole Munz <Munzzyy1@proton.me>
btrfs_readdir() and btrfs_size() both open code the same search for an inode item to read its size field. Move it into one helper. Signed-off-by: Cole Munz <Munzzyy1@proton.me>
zstd_decompress() probes the input with zstd_find_frame_compressed_size() and hands that single frame to zstd_decompress_dctx(). A zstd payload is allowed to be several frames concatenated, and when it is, everything after the first frame is silently dropped: the caller gets a short buffer and no error. On btrfs this shows up as truncated reads of zstd-compressed files. Both read paths funnel into this helper - decompress_zstd() for regular extents and btrfs_read_extent_inline() via btrfs_decompress() for inline ones - so a device-tree overlay stored on a compressed subvolume comes back short and "fdt apply" fails. Storing the file uncompressed is currently the only reliable workaround. Loop over the input instead, decoding frames until the output buffer is full or the input stops beginning with a frame header. Stopping on a non-frame header preserves the trailing-junk tolerance the size probe was added for, so a single frame followed by padding still decodes exactly as before, and input that never had a frame is still rejected. Add a regression test covering a two-frame payload. Without this change it decodes 166 of 350 bytes. Signed-off-by: lachytonner <lachytonner32@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes zstd_decompress() in U-Boot’s bundled zstd helper so it can decode inputs containing multiple concatenated zstd frames (a valid zstd encoding pattern), and adds a unit test to cover the multi-frame case.
Changes:
- Update
lib/zstd/zstd.c:zstd_decompress()to iterate over consecutive frames, tolerating a non-frame “junk” tail after the last frame. - Add a new
test/lib/compression.cfixture containing two concatenated zstd frames. - Add
compression_test_zstd_multiframeto ensure concatenated-frame payloads decompress fully.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/lib/compression.c | Adds a multi-frame zstd fixture and a new unit test validating full decompression across concatenated frames. |
| lib/zstd/zstd.c | Extends zstd_decompress() to process multiple concatenated frames instead of only the first. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * ever decodes one. Keep decoding until the output fills up or the | ||
| * input stops looking like a frame; a non-frame tail is the junk the | ||
| * size probe below has always been here to tolerate. | ||
| */ | ||
| len = zstd_find_frame_compressed_size(abuf_data(in), abuf_size(in)); | ||
| if (zstd_is_error(len)) { | ||
| log_err("%s: failed to detect compressed size: %d\n", __func__, | ||
| zstd_get_error_code(len)); | ||
| ret = -EINVAL; | ||
| goto do_free; | ||
| while (out_left && zstd_frame_starts_at(in_pos, in_left)) { |
|
Hi @Lachytonner, thanks a lot for your contribution! Can you please clarify if Btrfs ever produces concatenated multi-frame Zstd compressed blobs? It doesn't follow from your commit description, but seemingly implied given that you link it to the Btrfs related issue. If it does, it would be great to highlight the specific code path / circumstances where it gets produced. Please also have a look at https://git.u-boot-project.org/u-boot/u-boot/-/blob/main/doc/develop/process.rst?ref_type=heads and related docs (e.g. full name is required in the Signed-off-by trailer by upstream standards, by which you legally provide the Developer Certificate of Origin) |
1f5af61 to
761c654
Compare
Summary
Fixes the remaining half of #35:
zstd_decompress()only ever decodes the first zstd frame of its input.lib/zstd/zstd.cprobes the input withzstd_find_frame_compressed_size()and passes that single frame tozstd_decompress_dctx(). When the payload is several concatenated frames — which is legal zstd, and whatpzstdand appended compressed writes produce — everything after the first frame is silently dropped. The caller gets a short buffer and no error, which is exactly the truncation described in the issue.Both btrfs read paths funnel through this helper (
decompress_zstd()for regular extents,btrfs_read_extent_inline()→btrfs_decompress()for inline ones), so a DT overlay on a compressed subvolume reads back short andfdt applyfails.This is separate from b5b70ee, which fixed the inline-extent destination buffer (
dstSize_tooSmall, error 70). That was a real bug, but it does not touch the single-frame limit — @alchark's "there might be more to it" was right.Change
Loop over the input, decoding frames until the output buffer is full or the input stops beginning with a frame header.
Stopping on a non-frame header is what preserves existing behaviour: the original size probe was added to tolerate junk after the frame, and that still works, since junk does not carry a frame magic. Input that never contained a frame is still rejected with
-EINVAL.Verification
The existing
test/lib/compression.chad no multi-frame coverage, so I addedcompression_test_zstd_multiframe— the same fixture text as two concatenated frames.I could not complete a sandbox build on my machine (Fedora 44 ships OpenSSL 3.5, which no longer has
openssl/engine.h, so the host tools fail to compile beforeut lib compressioncan run — unrelated to this change). Instead I linked U-Boot's bundled zstd objects, built with the sandbox kbuild flags, against a harness calling the realzstd_decompress():-EINVAL✅-EINVAL✅350 bytes is the full fixture; 166 is the first frame alone. Output content is byte-compared, not just the length. The three non-multi-frame cases are unchanged, so trailing-junk tolerance and error handling do not regress.
scripts/checkpatch.pl: 0 errors, 0 warnings. The single CHECK about a blank line after}beforeLIB_TEST(...)matches how every other test in that file is written.If someone with the hardware can confirm a zstd-compressed overlay now applies on RK3576 without
compression=none, that would close out the issue's original symptom — I don't have a device to test the end-to-end path on.Relates to #35