|
Linux 5.10 + UBIFS RootFS on SPI NOR (W25Q256) + Dual-Kernel Strategy This article documents a complete OTA firmware update solution on the NUC980 Chili board (with Winbond W25Q256, 32MB, SPI NOR Flash) using: - Buildroot 2024 / Linux kernel 5.10
- UBIFS as root filesystem
- Dual-kernel layout (kernel1/kernel2)
- Custom U-Boot OTA command
- fwupdate userspace tool
The design provides: - UBIFS rootfs on SPI NOR
- Safe kernel update via A/B strategy
- Automatic copy from kernel2 → kernel1 after validation
1. System Architecture Overview Flash layout (32MB W25Q256): [td]
Update flow: - System runs kernel1.
- OTA writes new kernel to kernel2.
- On next boot, U-Boot validates kernel2.
- If valid → copy to kernel1.
- Reset and boot updated system.
2. Buildroot Setup Strongly recommended: clone a fresh repository to avoid residual files occupied on kernel image. $ git clone https://github.com/OpenNuvoton/buildroot_2024
$ cd buildroot_2024
$ make nuvoton_nuc980_chili_defconfig 3. Configure UBIFS as RootFS $ make menuconfig Disable RAM filesystem Filesystem images --->
[ ] initial RAM filesystem linked into linux kernel
[ ] cpio the root filesystem (for use as an initial RAM filesystem) Enable UBIFS Filesystem images --->
ubi image containing an ubifs root filesystem
UBIFS parameters (for W25Q256 SPI NOR) [td]
Parameter | | | | | | | | | | Maximum logical eraseblock count | | | |
These values must match the SPI NOR geometry. 4. Linux Kernel Configuration $ make linux-menuconfig Remove initramfs General setup --->
[ ] Boot config support
[ ] Initial RAM filesystem and RAM disk support Enable MTD / UBI / SPI NOR Device Drivers --->
Memory Technology Device (MTD) support --->
Partition parsers --->
<*> Command line partition table parsing
Enable UBI - Unsorted block images --->
UBI Fastmap
SPI NOR device support --->
[ ] Use small 4096 B erase sectors
Important: disable 4K sectors (W25Q256 uses 64K erase blocks). 5. Device Tree Partition Layout Edit the qspi0 session of ./output/build/linux-custom/arch/arm/boot/dts/nuc980-chili.dts qspi0: spi@b0060000 { status = "okay"; flash: flash@0 { compatible = "jedec,spi-nor"; #address-cells = <1>; #size-cells = <1>; reg = <0>; spi-max-frequency = <30000000>; partition@0 { label = "WHOLE"; reg = <0x00000000 0x02000000>; }; partition@1 { label = "uboot"; reg = <0x00000000 0x000A0000>; }; partition@2 { label = "kernel1"; reg = <0x000A0000 0x00400000>; }; partition@3 { label = "kernel2"; reg = <0x004A0000 0x00400000>; }; partition@4 { label = "rootfs"; reg = <0x008A0000 0x01760000>; }; };};6. Prevent Incorrect mdev Mounting Since 5 partitions exist, modify ./board/nuvoton/nuc9x0/rootfs-chili/etc/mdev.conf Change last line: mtdblock([5-9]+) ... This prevents unwanted yaffs/jffs2 auto-mount attempts. 7. Add OTA Command to U-Boot Extract U-Boot source: $ make uboot-patch Add following files which are in the attached ZIP to ./output/build/uboot-master/common/ directory. - ota_update.c
- check_crc.c / .h
- crc_checksum.c / .h
nuc980_uboot_ota_update_files.zip
Edit ./output/build/uboot-master/common/Makefile Add following three lines to the Makefile obj-y += ota_update.oobj-y += check_crc.oobj-y += crc_checksum.oThis adds a custom ota_update command that: - Validates kernel2
- Performs CRC check
- Returns success/failure
8. U-Boot Environment Configuration Create env.txt (The file is also in attached ZIP file) baudrate=115200bootdelay=1stderr=serialstdin=serialstdout=serialimage1_flash_offset=0xA0000image2_flash_offset=0x4A0000image_size=0x400000image1_ram_offset=0x7fc0image2_ram_offset=0x407fc0dtb_flash_offset=0x90000dtb_ram_offset=0x1800000dtb_size=0x10000setspi=sf probe 0 30000000loadkernel1=sf read ${image1_ram_offset} ${image1_flash_offset} ${image_size}loadkernel2=sf read ${image2_ram_offset} ${image2_flash_offset} ${image_size}eraseflash=sf erase ${image1_flash_offset} ${image_size}copykernel=sf write ${image2_ram_offset} ${image1_flash_offset} ${image_size}loaddtb=sf read ${dtb_ram_offset} ${dtb_flash_offset} ${dtb_size}bootcmd=run setspi;run loaddtb;run loadkernel1;run loadkernel2;if ota_update ${image1_ram_offset} ${image2_ram_offset} ${image_size} ${dtb_ram_offset};then run eraseflash;run copykernel;reset;fi;bootargs=noinitrd ubi.mtd=rootfs root=ubi0:rootfs rootfstype=ubifs rw console=ttyS0 rdinit=/sbin/init mem=64MBoot logic: - Always load both kernels
- If kernel2 valid and differ from kernel1 → copy to kernel1
- Reset system
- Otherwise continue booting kernel1
9. Build Everything $ make 10. Build fwupdate Tool Setup toolchain: $ cd
$ source ~/buildroot_2024/output/host/environment_setup Clone and build: $ git clone https://github.com/OpenNuvoton/NUC980_Linux_Applications
$ cd NUC980_Linux_Applications/demos/fwupdate
$ make Copy into rootfs: $ cp fwupdate ~/buildroot_2024/output/target/usr/bin
$ cd ~/buildroot_2024
$ make 11. Flash Images From ./output/images directory, use NuWriter to program following files to SPI Flash. [td]
Ensure full erase of rootfs partition before flashing. 12. OTA Update Procedure For future updates: - Use NuWriter to pack new kernel image (targeting kernel2 offset).
- Transfer pack file to device.
- Run fwupdate to overwrite kernel2 region.
- Reboot.
- U-Boot validates and copies kernel2 → kernel1.
- System reboots into updated firmware.
Final Notes This design provides: - UBIFS rootfs on SPI NOR
- Dual-kernel safety
- CRC-verified upgrade
- No NAND required
- No initramfs dependency
- Deterministic boot behavior
|