U-Boot persistent environment + Linux-controlled boot state ("EFI variables") #7

Open
opened 2026-08-20 15:01:19 +00:00 by kit · 3 comments
Owner

Goal: control boot behavior from inside Linux — choose the next boot target (normal / fastboot / recovery), change the default bootmenu entry and timeout, and persist U-Boot settings — the moral equivalent of EFI variables. Bonus tier: talk to sboot as well.

1. Persistent U-Boot env on a partition

  • Today the env is compiled into U-Boot (gts6l.env); nothing persists.
  • Switch to CONFIG_ENV_IS_IN_BLK (UFS/SCSI) pointing at a dedicated partition — candidates from the existing GPT: a spare like dqmdbg/dpo/pad, or shrink/repurpose deliberately (do NOT touch efs/sec_efs/modemst/persist — modem calibration lives there). Redundant env (CONFIG_SYS_REDUNDAND_ENVIRONMENT) for power-cut safety.
  • Compiled-in env becomes the fallback/defaults; env save writes the partition.

2. Linux side: libubootenv

  • fw_printenv/fw_setenv with /etc/fw_env.config pointing at the same partition/offset — instant "EFI variables" UX from Linux. Package it in gts6l-alarm with the config.

3. Boot-state protocol

  • U-Boot bootcmd consults a bootmode variable before the normal flow: once_fastboot, once_recovery, once_sd, etc. — one-shot semantics (U-Boot clears it after acting) so a bad target can't wedge the boot loop.
  • Linux helper: gts6l-reboot fastboot = fw_setenv bootmode once_fastboot && reboot. Optional systemd integration later.
  • Default entry + bootdelay/menu timeout become plain env vars — settable from Linux.

4. sboot communication (stretch)

  • Downstream reaches download/recovery via Qualcomm/Samsung restart-reason magics (IMEM cookie + PMIC PON reason — see samsung-src sec_debug/restart handlers for the exact addresses/values). Mainline has the plumbing (syscon-reboot-mode, qcom,pon reboot modes) — wiring the right magic values would make reboot download work from Linux and possibly let sboot skip straight to where we want.
  • Also worth checking what sboot itself reads at boot (param partition flags) — Samsung traditionally keeps boot flags there.

Interaction with existing infra: the CRC-verified sdboot flow and bootmenu_16 mirror stay; they just start reading their knobs (default target, timeout, image path) from the persistent env. Also relates to #2 (vol-up navigation) — together they make U-Boot fully drivable both physically and remotely.

Goal: control boot behavior from inside Linux — choose the next boot target (normal / fastboot / recovery), change the default bootmenu entry and timeout, and persist U-Boot settings — the moral equivalent of EFI variables. Bonus tier: talk to sboot as well. **1. Persistent U-Boot env on a partition** - Today the env is compiled into U-Boot (`gts6l.env`); nothing persists. - Switch to `CONFIG_ENV_IS_IN_BLK` (UFS/SCSI) pointing at a dedicated partition — candidates from the existing GPT: a spare like `dqmdbg`/`dpo`/`pad`, or shrink/repurpose deliberately (do NOT touch efs/sec_efs/modemst/persist — modem calibration lives there). Redundant env (`CONFIG_SYS_REDUNDAND_ENVIRONMENT`) for power-cut safety. - Compiled-in env becomes the fallback/defaults; `env save` writes the partition. **2. Linux side: libubootenv** - `fw_printenv`/`fw_setenv` with `/etc/fw_env.config` pointing at the same partition/offset — instant \"EFI variables\" UX from Linux. Package it in gts6l-alarm with the config. **3. Boot-state protocol** - U-Boot `bootcmd` consults a `bootmode` variable before the normal flow: `once_fastboot`, `once_recovery`, `once_sd`, etc. — *one-shot* semantics (U-Boot clears it after acting) so a bad target can't wedge the boot loop. - Linux helper: `gts6l-reboot fastboot` = `fw_setenv bootmode once_fastboot && reboot`. Optional systemd integration later. - Default entry + `bootdelay`/menu timeout become plain env vars — settable from Linux. **4. sboot communication (stretch)** - Downstream reaches download/recovery via Qualcomm/Samsung restart-reason magics (IMEM cookie + PMIC PON reason — see samsung-src `sec_debug`/restart handlers for the exact addresses/values). Mainline has the plumbing (`syscon-reboot-mode`, `qcom,pon` reboot modes) — wiring the right magic values would make `reboot download` work from Linux and possibly let sboot skip straight to where we want. - Also worth checking what sboot itself reads at boot (`param` partition flags) — Samsung traditionally keeps boot flags there. Interaction with existing infra: the CRC-verified sdboot flow and bootmenu_16 mirror stay; they just start reading their knobs (default target, timeout, image path) from the persistent env. Also relates to #2 (vol-up navigation) — together they make U-Boot fully drivable both physically and remotely.
Author
Owner

2026-08-21 recon: the planned CONFIG_ENV_IS_IN_BLK route doesn't exist in our U-Boot

Storage survey done on-device; the issue's part 1 needs revising before any of it gets built.

Blocker for the UFS-partition plan

Our tree is U-Boot 2025.04-rc5, and env/Kconfig offers only:
EEPROM, FAT, EXT4, FLASH, MMC, NAND, NVRAM, ONENAND, REMOTE, SPI_FLASH, UBI.

There is no ENV_IS_IN_BLK and no ENV_IS_IN_SCSI — so "point the env at a UFS partition" means writing an env backend for SCSI/blk, not setting a Kconfig symbol. That's a real (upstreamable) mini-project, not a config change.

Partition survey (UFS), for whenever that route is taken

Empty (all-zero) 512 KiB candidates, both on stock-purpose partitions:

  • keystore → sda11, 512 K, all zero
  • uefivarstore → sdd28, 512 K, all zero (semantically perfect, but it's XBL's own variable store — repurposing it is the riskiest option here)

Not empty, i.e. in use — leave alone: dqmdbg (sda15, 16 M), steady (sda14, 4 M), bota (sda12, 40 M), logfs (sdd19, 8 M), debug (sda8, 10 M), omr (sda29, 20 M). (Confirmed the untouchables are untouched: no proposal goes near efs/sec_efs/modemst*/persist.)

SD layout

mmcblk0p1 = 1 GiB vfat GTS6LBOOT, mmcblk0p2 = 58.5 GiB ext4 gts6l-root, 0 B unpartitioned. So a raw ENV_IS_IN_MMC offset has nowhere clean to live — it would have to squat inside p1's sectors the way gtslog squats at 64 MiB. Fine for an expendable log, a landmine for persistent state (FAT can hand those sectors to a file).

Recommendation: ENV_IS_IN_FAT on mmc 0:1, file uboot.env

I'd lean here for the first cut:

  • U-Boot already loads boot-gts6l.img from that exact partition in sdboot — proven path, no new storage stack at env-load time.
  • Zero risk to any stock UFS partition, and nothing to repartition.
  • Recoverable from any host by putting the card in a reader — important given the env can brick a boot flow.
  • A corrupt/missing env falls back to the compiled-in gts6l.env, which is precisely the fallback semantics part 1 wants.
  • Linux side: libubootenv's fw_env.config accepts a plain file path, so fw_setenv works against the mounted file (needs verifying that libubootenv is packaged for ALARM aarch64 — not yet checked, the tablet has no NAT route up this session).

Trade-off to accept, stated plainly: the env then lives on the SD card, so no card = default env. Given the whole boot chain already depends on the SD, I think that's the right price for not touching stock partitions. If you'd rather have it independent of the SD, that's option (d) — write the SCSI env backend and put it in keystore; more work, more upside, and it's the version worth upstreaming.

Nothing has been built or flashed for this yet — flagging the decision first since it changes what gets implemented.

## 2026-08-21 recon: the planned `CONFIG_ENV_IS_IN_BLK` route doesn't exist in our U-Boot Storage survey done on-device; the issue's part 1 needs revising before any of it gets built. ### Blocker for the UFS-partition plan Our tree is **U-Boot 2025.04-rc5**, and `env/Kconfig` offers only: `EEPROM, FAT, EXT4, FLASH, MMC, NAND, NVRAM, ONENAND, REMOTE, SPI_FLASH, UBI`. There is **no `ENV_IS_IN_BLK` and no `ENV_IS_IN_SCSI`** — so "point the env at a UFS partition" means *writing an env backend for SCSI/blk*, not setting a Kconfig symbol. That's a real (upstreamable) mini-project, not a config change. ### Partition survey (UFS), for whenever that route is taken Empty (all-zero) 512 KiB candidates, both on stock-purpose partitions: - `keystore` → sda11, 512 K, **all zero** - `uefivarstore` → sdd28, 512 K, **all zero** (semantically perfect, but it's XBL's own variable store — repurposing it is the riskiest option here) Not empty, i.e. in use — leave alone: `dqmdbg` (sda15, 16 M), `steady` (sda14, 4 M), `bota` (sda12, 40 M), `logfs` (sdd19, 8 M), `debug` (sda8, 10 M), `omr` (sda29, 20 M). (Confirmed the untouchables are untouched: no proposal goes near `efs`/`sec_efs`/`modemst*`/`persist`.) ### SD layout `mmcblk0p1` = 1 GiB vfat `GTS6LBOOT`, `mmcblk0p2` = 58.5 GiB ext4 `gts6l-root`, **0 B unpartitioned**. So a raw `ENV_IS_IN_MMC` offset has nowhere clean to live — it would have to squat inside p1's sectors the way `gtslog` squats at 64 MiB. Fine for an expendable log, a landmine for persistent state (FAT can hand those sectors to a file). ### Recommendation: `ENV_IS_IN_FAT` on `mmc 0:1`, file `uboot.env` I'd lean here for the first cut: - U-Boot already loads `boot-gts6l.img` from that exact partition in `sdboot` — proven path, no new storage stack at env-load time. - Zero risk to any stock UFS partition, and nothing to repartition. - Recoverable from any host by putting the card in a reader — important given the env can brick a boot flow. - A corrupt/missing env falls back to the compiled-in `gts6l.env`, which is precisely the fallback semantics part 1 wants. - Linux side: libubootenv's `fw_env.config` accepts a plain file path, so `fw_setenv` works against the mounted file (needs verifying that libubootenv is packaged for ALARM aarch64 — not yet checked, the tablet has no NAT route up this session). Trade-off to accept, stated plainly: **the env then lives on the SD card**, so no card = default env. Given the whole boot chain already depends on the SD, I think that's the right price for not touching stock partitions. If you'd rather have it independent of the SD, that's option (d) — write the SCSI env backend and put it in `keystore`; more work, more upside, and it's the version worth upstreaming. Nothing has been built or flashed for this yet — flagging the decision first since it changes what gets implemented.
Author
Owner

2026-08-21: parts 1 and 2 working — environment lives on UFS, editable from Linux

Part 1 — persistent env on a UFS partition: DONE

The issue's premise needed correcting first: U-Boot 2025.04-rc5 has no ENV_IS_IN_BLK and no ENV_IS_IN_SCSI. env/Kconfig offers only EEPROM/FAT/EXT4/FLASH/MMC/NAND/NVRAM/ONENAND/REMOTE/SPI_FLASH/UBI. ENV_IS_IN_MMC cannot reach a SCSI device and the filesystem backends need a filesystem to already exist, so this needed a new backend rather than a Kconfig line.

Written as env/scsi.c — U-Boot commit a2742d0640e on kit/u-boot-gts6l, branch gts6l/uboot:

  • Stores the environment as raw blocks at the start of a GPT partition looked up by name, so no offset is baked into the build.
  • A missing, undersized or unreadable partition is not fatal — it falls back to the built-in environment, so a bad save can't brick the boot.
  • Gotcha worth recording: ENV_IS_IN_SCSI must also be added to the ENV_IS_DEFAULT expression in env/Kconfig, or a board selecting only this backend is still treated as having no persistent env and the env save subcommand is silently compiled out (cmd/nvedit.c gates it on !ENV_IS_DEFAULT). That cost a build/flash cycle to find.

Configured for keystore (sda11, 512 KiB, verified all-zero — Android keymaster storage, untouched by a Linux userspace). No partition table change, so the sboot/PIT risk that rules out repartitioning does not apply here. Nothing goes near efs/sec_efs/modemst*/persist.

Verified on hardware:

  • env saveSaving Environment to SCSI... done
  • Decoded straight off the partition: CRC32 stored 0x52f3c3df == computed — a structurally valid environment, 103 variables.
  • Load confirmed by Kit: a bootmenu_default written to UFS is honoured on the next boot (built-in default is 16, so the persisted value being used proves the read path).

Part 2 — Linux-side control: WORKING (via our own tool, not libubootenv yet)

tools/gts6l-env.pydump / get / set / unset against the raw partition. The format is just CRC32 + NUL-separated key=value, so libubootenv isn't required to get the "EFI variables" UX. It refuses to write if the existing env has a bad CRC, and verifies the readback after writing.

Demonstrated end-to-end: flipped bootmenu_default 17 → 16 from Linux, read it back CRC-valid off the device.

Two practical notes:

  • The tablet has no python3, so the tool currently runs host-side against a pulled copy (dd out, edit, dd back). For on-device use it needs either python packaged, a C rewrite, or the real libubootenv + /etc/fw_env.config — which is the remaining packaging work for this part.
  • Writes must be block-aligned: dd ... bs=4096 oflag=direct (a bs=1024 direct write is rejected with Invalid argument), and ENV_SIZE is exactly 0x1f000 = 126976 bytes = 31 × 4096.

Still open

  • Part 2 packaging: libubootenv + /etc/fw_env.config on the rootfs, in gts6l-alarm.
  • Part 3: the bootmode one-shot protocol (once_fastboot / once_recovery) and a gts6l-reboot helper. This is the high-value remainder — it removes most of the physical button-holding.
  • Part 4: sboot restart-reason magics (stretch).
## 2026-08-21: parts 1 and 2 working — environment lives on UFS, editable from Linux ### Part 1 — persistent env on a UFS partition: DONE The issue's premise needed correcting first: **U-Boot 2025.04-rc5 has no `ENV_IS_IN_BLK` and no `ENV_IS_IN_SCSI`.** `env/Kconfig` offers only EEPROM/FAT/EXT4/FLASH/MMC/NAND/NVRAM/ONENAND/REMOTE/SPI_FLASH/UBI. `ENV_IS_IN_MMC` cannot reach a SCSI device and the filesystem backends need a filesystem to already exist, so this needed a new backend rather than a Kconfig line. Written as `env/scsi.c` — U-Boot commit **`a2742d0640e`** on `kit/u-boot-gts6l`, branch `gts6l/uboot`: - Stores the environment as raw blocks at the start of a GPT partition looked up **by name**, so no offset is baked into the build. - A missing, undersized or unreadable partition is **not fatal** — it falls back to the built-in environment, so a bad save can't brick the boot. - Gotcha worth recording: `ENV_IS_IN_SCSI` must also be added to the `ENV_IS_DEFAULT` expression in `env/Kconfig`, or a board selecting only this backend is still treated as having no persistent env and **the `env save` subcommand is silently compiled out** (`cmd/nvedit.c` gates it on `!ENV_IS_DEFAULT`). That cost a build/flash cycle to find. Configured for **`keystore`** (sda11, 512 KiB, verified all-zero — Android keymaster storage, untouched by a Linux userspace). **No partition table change**, so the sboot/PIT risk that rules out repartitioning does not apply here. Nothing goes near `efs`/`sec_efs`/`modemst*`/`persist`. Verified on hardware: - `env save` → `Saving Environment to SCSI... done` - Decoded straight off the partition: **CRC32 stored `0x52f3c3df` == computed** — a structurally valid environment, 103 variables. - **Load confirmed by Kit**: a `bootmenu_default` written to UFS is honoured on the next boot (built-in default is 16, so the persisted value being used proves the read path). ### Part 2 — Linux-side control: WORKING (via our own tool, not libubootenv yet) `tools/gts6l-env.py` — `dump` / `get` / `set` / `unset` against the raw partition. The format is just CRC32 + NUL-separated `key=value`, so libubootenv isn't required to get the "EFI variables" UX. It refuses to write if the existing env has a bad CRC, and verifies the readback after writing. Demonstrated end-to-end: flipped `bootmenu_default` 17 → 16 **from Linux**, read it back CRC-valid off the device. Two practical notes: - The tablet has **no python3**, so the tool currently runs host-side against a pulled copy (`dd` out, edit, `dd` back). For on-device use it needs either python packaged, a C rewrite, or the real `libubootenv` + `/etc/fw_env.config` — which is the remaining packaging work for this part. - Writes must be block-aligned: `dd ... bs=4096 oflag=direct` (a `bs=1024` direct write is rejected with `Invalid argument`), and `ENV_SIZE` is exactly `0x1f000` = 126976 bytes = 31 × 4096. ### Still open - **Part 2 packaging**: libubootenv + `/etc/fw_env.config` on the rootfs, in gts6l-alarm. - **Part 3**: the `bootmode` one-shot protocol (`once_fastboot` / `once_recovery`) and a `gts6l-reboot` helper. This is the high-value remainder — it removes most of the physical button-holding. - **Part 4**: sboot restart-reason magics (stretch).
Author
Owner

2026-08-21: part 3 complete, part 4 (recovery/download) WORKING via PMIC PON

Part 4 — reboot to recovery/download from Linux: DONE (recovery verified on hardware)

The restart-reason values come from ABL's BootChecker (PMIC PON register): recovery = 0x01, bootloader = 0x02, download = 0x15. Mainline already has the whole mechanism — qcom-pon.c writes PON_SOFT_RB_SPARE, and pm8150.dtsi has pon@800 (qcom,pm8998-pon). So this was a DT-only change, no driver.

Kernel commit e681e6386e09 adds to &pon:

mode-recovery = <0x01>;
mode-bootloader = <0x02>;
mode-download = <0x15>;

Verified on the device: reboot(RESTART2, "recovery") from Linux → the device landed in TWRP, routed by sboot reading the PON register before U-Boot loads — no env one-shot, no button combo. recovery=0x01/bootloader=0x02 also match the generic Qualcomm values (pmk8350.dtsi), so they're not even Samsung-specific.

download=0x15 is from the same BootChecker table and high-confidence, but not yet fire-tested (it sends the device to Odin, which needs a person to exit) — will confirm when convenient.

The full picture, and how targets are split

gts6l-reboot <target> now routes by mechanism:

  • recovery, download → PMIC PON (reboot-mode helper → reboot(RESTART2, ...)), handled by sboot before U-Boot. reboot bootloader/0x02 would hit ABL's own fastboot, not ours, so it's not used.
  • fastboot, console, menu, sd → the U-Boot env bootmode one-shot (parts already done), handled by U-Boot.

So between the two mechanisms, every useful boot target is now selectable from a running Linux with a single command. The button combo and the panel-menu are no longer required for anything.

Status

  • Part 1 — persistent env on UFS (env/scsi.c, keystore)
  • Part 2 — Linux-side control (gts6l-env)
  • Part 3 — bootmode one-shot protocol + gts6l-reboot (fastboot/console/menu/sd)
  • Part 4 — recovery/download via PMIC PON (recovery verified; download high-confidence, untested)
  • Remaining polish: package gts6l-env/gts6l-reboot/reboot-mode + libubootenv into gts6l-alarm; fire-test download.

This issue is essentially done — the "EFI variables" goal is met and then some.

## 2026-08-21: part 3 complete, part 4 (recovery/download) WORKING via PMIC PON ### Part 4 — reboot to recovery/download from Linux: DONE (recovery verified on hardware) The restart-reason values come from ABL's `BootChecker` (PMIC PON register): `recovery = 0x01`, `bootloader = 0x02`, `download = 0x15`. Mainline already has the whole mechanism — `qcom-pon.c` writes `PON_SOFT_RB_SPARE`, and `pm8150.dtsi` has `pon@800` (`qcom,pm8998-pon`). So this was a **DT-only** change, no driver. Kernel commit **`e681e6386e09`** adds to `&pon`: ``` mode-recovery = <0x01>; mode-bootloader = <0x02>; mode-download = <0x15>; ``` **Verified on the device:** `reboot(RESTART2, "recovery")` from Linux → the device landed in **TWRP**, routed by sboot reading the PON register *before* U-Boot loads — no env one-shot, no button combo. `recovery=0x01`/`bootloader=0x02` also match the generic Qualcomm values (pmk8350.dtsi), so they're not even Samsung-specific. `download=0x15` is from the same BootChecker table and high-confidence, but **not yet fire-tested** (it sends the device to Odin, which needs a person to exit) — will confirm when convenient. ### The full picture, and how targets are split `gts6l-reboot <target>` now routes by mechanism: - **recovery, download** → PMIC PON (`reboot-mode` helper → `reboot(RESTART2, ...)`), handled by **sboot** before U-Boot. `reboot bootloader`/`0x02` would hit ABL's own fastboot, not ours, so it's not used. - **fastboot, console, menu, sd** → the U-Boot env `bootmode` one-shot (parts already done), handled by **U-Boot**. So between the two mechanisms, every useful boot target is now selectable from a running Linux with a single command. The button combo and the panel-menu are no longer required for anything. ### Status - [x] Part 1 — persistent env on UFS (`env/scsi.c`, keystore) - [x] Part 2 — Linux-side control (`gts6l-env`) - [x] Part 3 — `bootmode` one-shot protocol + `gts6l-reboot` (fastboot/console/menu/sd) - [x] Part 4 — recovery/download via PMIC PON (recovery verified; download high-confidence, untested) - [ ] Remaining polish: package `gts6l-env`/`gts6l-reboot`/`reboot-mode` + libubootenv into gts6l-alarm; fire-test `download`. This issue is essentially done — the "EFI variables" goal is met and then some.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kit/linux-gts6l#7
No description provided.