Boot-args record
Changing the next boot's arguments from iOS, without reflashing boot.
The boot arguments normally live in the payload inside boot. LK write-protects boot
while iOS runs, so iOS cannot change them there. The record is a place outside boot that iOS can write and the shim can
read before XNU starts.
Where it is
The first 4 KB block of the last MiB of the root partition (block nblocks - 256, 4 KB blocks), outside the HFS+
volume. The kernel checks the volume’s header before every write and refuses (ENOSPC) when the volume reaches the
record’s block.
Layout
672 bytes, little endian (shim/bootrec.h, the same struct in the kernel’s mtk_ufs.c):
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | magic VIVABOOT |
| 8 | u32 | version, 1 |
| 12 | u32 | flags, bit 0 = enabled |
| 16 | u64 | base_hash: FNV-1a 64 of the payload command line the record was written against |
| 24 | u64 | seq, up by one on every write |
| 32 | u64 | line_hash: FNV-1a 64 of cmdline |
| 40 | 24 | reserved |
| 64 | 608 | cmdline, NUL terminated, printable ASCII (0x20-0x7e), at most 607 characters |
When the shim uses it
mapple_bootfile in shim/bootrec.c, before the kernel is copied anywhere:
- It is off when the payload’s line has
mp_bootrec=0. That is how a safe boot ignores it. - It looks for the root partition by name in the GPT of the UFS LUN:
mp_ufslun(default 2) andmp_ufsroot. On spaced the record is read only when the payload namesmp_ufsroot=:userdatais Android’s there. - It needs the host as LK leaves it (enabled, link up, device present, request list ready, nothing outstanding). It points the transfer list at its own memory, issues polled READ(10) commands, and gives LK’s list back. There is no write in the file.
- The record applies only if the magic and version match, the enabled bit is set,
line_hashmatches the line, andbase_hashmatches the hash of the payload’s current line. Reflashingbootwith another line drops the record by itself. - Anything else (no partition, timeout, no record, a record for another payload) gives the payload’s line, as if the record did not exist, and the console says why.
Writing it
From iOS, as root:
kern.mapple_nextbootargsreads the next boot’s line (the record’s when it applies, else the payload’s) and, written by root, stores a new one in the record. An empty string, or the payload’s own line, disables the record. The payload is never written. Writes needmp_ufsrw=1(EROFSotherwise); the block is flushed, read back and compared.kern.mapple_nextbootargs_srcsaysbootrecorpayload;kern.mapple_nextbootargs_origis the payload’s line as first seen.kern.mapple_bootrecis the raw 4 KB block: anyone may read it, root writes it with only the volume check.mapple-link.py bootrec [hex|show]on the Mac edits it over the USB link.
Source: shim/bootrec.h, shim/bootrec.c of mapple_loader at 2aa5786; bsd/dev/mtk_ufs.c and bsd/dev/mapple_sysctl.c of
the kernel at 76b737c; tools/mapple-link.py of mapple_build at f7431bb.
Applies to Release 1, MediaTek · Updated 15 November 2026