Upstreaming Legacy Systems: A Practical Guide to PX30 & Yocto Integration
Note
TL;DR
Practical guide to booting a mainline Linux kernel with a Yocto-built root filesystem on a legacy PX30 device without overwriting the stock bootloader — only the rootfs partition is flashed, so the device stays revertible to its original firmware.
Covers the non-destructive upstreaming workflow: decompiling the vendor
.dtbto map the hardware, converting Yoctoext4images into Android sparse images for Fastboot, and packaging the kernel and DTB into an uncompressed FIT image to avoid legacy U-Boot relocation crashes.Includes a reusable U-Boot boot macro (
yoctobootcmd) that relocates the staged payload out of the Fastboot RAM buffer and boots it withbootm.
Transitioning a legacy embedded system to an upstream Linux kernel and a modern Yocto Project environment is a balancing act: the vendor bootloader stays in place, while the kernel, the devicetree, and the root filesystem are replaced underneath it. This guide combines the strategic principles of non-destructive upstreaming with the concrete procedures required to flash a custom root filesystem and boot a mainline kernel on a PX30 device through U-Boot and Fastboot, while retaining the ability to restore the stock firmware.
What is the strategy for upstreaming a legacy embedded system?
When bridging the gap between legacy vendor code and mainline Linux, preserving a known-good working state is critical.
Retain stock fallbacks: Never overwrite the stock bootloader or partition tables until the new system is fully validated. Only flash the rootfs partition to keep the device revertible to its original firmware.
Bridge the devicetree gap: Decompile the original
.dtbfrom the running legacy system to accurately map the hardware, and iteratively enable peripherals — starting with the UART console.Isolate Yocto layers: Build the project using reproducible environments, keeping custom BSP layers separate from core Yocto layers. For example:
kas-container build kas-px30.yml
Build commands should be generalized for your specific hardware target.
How do you flash a Yocto rootfs over Fastboot on legacy U-Boot?
Legacy U-Boot environments impose constraints that must be navigated
carefully during deployment. Yocto outputs raw ext4 images that must be
converted to Android sparse images before flashing: this strips
unallocated storage blocks so the transfer fits within the Fastboot size
limit.
How do you convert a Yocto ext4 image to a sparse image?
On your host workstation, convert the raw .ext4 image using
img2simg:
img2simg build/tmp/deploy/images/px30/core-image-px30.rootfs.ext4 rootfs_sparse.img
How do you flash the rootfs partition over Fastboot?
Access the U-Boot serial console on the device and start the Fastboot service:
fastboot usb 0
On your host workstation, flash the sparse rootfs image to the target partition:
fastboot flash rootfs rootfs_sparse.img
How do you package a mainline kernel as a FIT image?
You do not need an upstream bootloader to boot an upstream kernel, provided
the kernel is packaged correctly for the legacy environment. The kernel
image and the devicetree blob (.dtb) must be bundled together into a
Flattened Image Tree (FIT) image.
Bypass compression limits: Older bootloaders frequently crash during
kernel decompression. For this specific U-Boot build, FIT images with
gzip-compressed kernels trigger relocation crashes. An uncompressed raw
kernel (Image) with compression = "none" must be used.
How do you write the FIT image source (px30.its)?
Ensure your px30.its file points to your uncompressed Yocto kernel build
artifacts:
/dts-v1/;
/ {
description = "PX30 Buildroot/Yocto FIT Image";
#address-cells = <1>;
images {
kernel-1 {
description = "Linux Kernel Raw";
data = /incbin/("build/tmp/deploy/images/px30/Image");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x09000000>;
entry = <0x09000000>;
hash-1 {
algo = "sha1";
};
};
fdt-1 {
description = "PX30 DTB";
data = /incbin/("build/tmp/deploy/images/px30/px30.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
load = <0x08300000>;
hash-1 {
algo = "sha1";
};
};
};
configurations {
default = "config-1";
config-1 {
description = "Default Boot Configuration";
kernel = "kernel-1";
fdt = "fdt-1";
};
};
};
How do you compile the FIT image?
Generate the .itb binary on your host workstation:
mkimage -f px30.its px30_boot.itb
How do you stage and boot the kernel from RAM in U-Boot?
U-Boot receives Fastboot transfers into a limited RAM buffer at address
0x00800800. To prevent memory corruption and overlap errors, staged
files must be copied out of this buffer to a high memory address
(0x10000000) before execution.
How do you stage the FIT image into target RAM?
With fastboot usb 0 active in U-Boot, send the FIT image from your host
PC:
fastboot stage px30_boot.itb
How do you boot the staged kernel?
After staging, interrupt Fastboot in the U-Boot console (Ctrl+C), relocate
the staged payload in RAM, set the kernel arguments, and launch bootm:
cp.b 0x00800800 0x10000000 0x03D00000
setenv bootargs "console=ttyS2,115200n8 earlycon=uart8250,mmio32,0xff160000 root=/dev/mmcblk1p2 rootfstype=ext4 rw rootwait"
bootm 0x10000000:kernel-1 - 0x10000000:fdt-1
How do you automate the boot process with a U-Boot macro?
To streamline future boots, save the relocation and boot sequence as a
custom U-Boot environment variable (yoctobootcmd).
Warning
Serial overrun. Do not copy and paste the entire command string at once over the serial terminal: high baud rate input without flow control causes dropped characters. Type or paste the variables in smaller individual fragments.
setenv yoctobootcmd 'cp.b 0x00800800 0x10000000 0x03D00000; setenv bootargs "console=ttyS2,115200n8 earlycon=uart8250,mmio32,0xff160000 root=/dev/mmcblk1p2 rootfstype=ext4 rw rootwait"; bootm 0x10000000:kernel-1 - 0x10000000:fdt-1'
saveenv
What is the routine for subsequent deployments?
Interrupt the bootloader during power-on to reach the U-Boot prompt (
=>).Start the Fastboot server:
fastboot usb 0.Upload the FIT image from the host PC:
fastboot stage px30_boot.itb.Interrupt Fastboot (Ctrl+C) and run the macro command:
run yoctobootcmd.
Tip
Bringing a legacy product onto a mainline kernel and a modern Yocto environment? Amarula Solutions handles U-Boot board bring-up, kernel upstreaming, and long-term BSP maintenance for Rockchip, i.MX, STM32MP, and Allwinner platforms. Contact our embedded team