Flash layout

The image uses all 16 MiB of NOR flash with no gaps between regions. The first 8 KiB is the mandatory ROM FlashEncryption reservation; every remaining byte belongs to one of the slots below. Range ends are exclusive.

Raw start

Raw end

Capacity

Contents

0x000000

0x002000

8 KiB

Reserved for ROM FlashEncryption metadata

0x002000

0x00E000

48 KiB

spl_app.bin

0x00E000

0x05E000

320 KiB

u-boot.itb

0x05E000

0x06E000

64 KiB

esp32s31_generic.dtb

0x06E000

0x1EE000

1536 KiB

radio.bin: prelinked radio XIP payload

0x1EE000

0x400000

2120 KiB

Persistent JFFS2 filesystem

0x400000

0xA00000

6144 KiB

xipImage

0xA00000

0x1000000

6144 KiB

rootfs.sqfs

Persist fills the space between radio and Linux. There is no separate HIL scratch partition. The seven child MTD partitions are read-only except persist. CONFIG_MTD_PARTITIONED_MASTER=y intentionally also exposes the writable 40000000.flash master device covering all 16 MiB. Master access bypasses the child partitions’ read-only flags and includes the reserved prefix and boot slots; callers must account for those ranges when performing raw operations. Persist/rootfs are found by label, not by fixed MTD device numbers.

The same whole-flash master is continuously readable from offset 0 through 0xFFFFFF. Linux supplies its normal /dev/mtdN character node and a read-only /dev/mtdNro alias; identify N from the sysfs name 40000000.flash rather than assuming its number. Reading the read-only alias covers the complete 16 MiB, including the reserved prefix and every partition, without granting write access through that alias. With CONFIG_MTD_BLOCK=y, /dev/mtdblockN also exposes the complete master as a block device. The writable master is intentionally retained by the full-board configuration.

Raw offsets and XIP addresses

The boot mapping covers raw flash [0x000000, 0x1000000) linearly at physical [0x40000000, 0x41000000). Therefore the FIT is at 0x4000E000, OpenSBI’s XIP payload starts at 0x4000E400, the Linux DTB is at 0x4005E000, and Linux starts at 0x40400000.

The Linux start is aligned to a 4 MiB Sv32 megapage. This matters for kernels larger than 4 MiB: an unaligned XIP start can reach the page-table BUG_ON described in issue #1. Do not move Linux merely to close a gap; persist already fills the space up to the required boundary.

The radio slot starts at physical 0x4006E000 and virtual 0xBE06E000, inside the aligned 4 MiB leaf mapping from virtual 0xBE000000 to physical 0x40000000. See Memory map and Boot process.

Change the layout

The offsets and filenames are defined in configs/esp32s31-layout.cfg. The merge script checks that each file fits its region, including the 48 KiB SPL limit. Coordinate changes with the bootloader, OpenSBI, kernel XIP and radio mappings, device-tree partitions, image tooling, and flashing commands.

Run python3 tools/checks/layout.py from the parent repository to check the shared flash and SRAM contracts. To read a slot capacity in bytes, use, for example, python3 tools/checks/layout.py --size PERSIST.

The radio XIP image is linked against the kernel and must be updated with its matching kernel/module. The Linux radio module lives in rootfs.sqfs.

Preserve data

Slot-wise make flash-existing-all (flash-all is an alias) leaves persist untouched only when the board already uses this layout. Writing the contiguous s31_full_flash.bin overwrites persist, even without a separate chip erase.

Whenever changing the installed layout, back up needed files and settings to another device, verify the backup, perform a clean installation, then restore the needed files/settings. See Flash and first boot.