Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/evidence/build-pmos-era.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
PAF becomes saf/device (history kept), STATE.md dissolves
into saf/state.md with the dated era archived, the substrate
SAF moves up from souveraine, and every agreement points at
saf/INDEX.md and nowhere else. one map, nothing to remember
2026-08-18 09:47:30 -04:00

150 lines
9.1 KiB
Markdown

# PAF · Build & packaging (pmbootstrap, the proper path)
How the blueline kernel + device package are actually built and shipped. This
is the canonical build layer — earlier sessions wasted hours on host `make`
and on undocumented pmbootstrap behaviours. Build with **pmbootstrap**; it is
what every other sdm845-mainline contributor uses and what produces installable
APKs. Host `make ARCH=arm64 LLVM=1` is not the path (it needs llvm-readelf/nm/
objcopy on the host and does not produce a package).
## The two packages — do not conflate them
- **`linux-postmarketos-qcom-sdm845`** (`device/community/`) — the KERNEL
package. Kernel image (`vmlinuz`/Image.gz), all modules, the blueline DTB.
Userspace never goes here.
- **`device-google-blueline`** (`device/testing/`) — the DEVICE package.
Device userspace: firmware deps, hexagonrpcd/q6voiced confs, the systemd
preset, and now the **modem PDC self-provisioner** (`qcril-autoselect.py` +
`.service`, enabled via the preset). This is where per-device userspace
belongs — modelled on how `q6voiced.conf` is already installed.
## Unified 7.1.1 kernel build (2026-06-22)
Base is the sdm845-mainline tree at tag `85f1df2a` (= 7.1.1), checked out at
`references/linux-7.1.1-sdm845-CANONICAL`. All accumulated fixes are now **commits in that
tree**, not scattered `source=` patches:
- SLPI keep-proxy-power-on (the `sar.cc:27` crash-loop fix — see slpi.md)
- SLPI enabled in the blueline DTS
- fastrpc SDSP hyp-assign tolerance
Built via source override:
```
cd ~/pmos/pmaports
pmbootstrap build --src=$REPO/references/linux-7.1.1-sdm845-CANONICAL \
linux-postmarketos-qcom-sdm845
```
APK lands in `~/.local/var/pmbootstrap/packages/edge/aarch64/`.
## pmbootstrap build gotchas (each cost real time — RECORD)
0. **The in-tree `arch/arm64/configs/sdm845.config` is merged LAST and silently
overrides our pmaports config.** `prepare()` runs
`merge_config.sh -m <pmaports config-*.aarch64> arch/$_carch/configs/sdm845.config`
— and `merge_config -m` is last-wins. So any symbol set in the **in-tree**
fragment beats the same symbol in the pmaports config, even though the
pmaports config "looks" authoritative. **This silently broke wifi
(2026-06-29):** pmaports set `CONFIG_QCOM_PD_MAPPER=m`, but the upstream
sdm845-mainline in-tree fragment carried `=n` (their "use userspace daemon,
poco-f1 mic race" default), so every build shipped pd-mapper OFF → no wlan PD
`ath10k_snoc` probe defers → no `wlan0`. The `=n` rode in via the vanilla
import commit `310c17cc6`, not a deliberate choice. **Fix:** set the symbol in
the in-tree fragment (`references/linux-7.1.1-sdm845-CANONICAL/arch/arm64/configs/sdm845.config`),
not just pmaports. **Audit rule:** when a built kernel disagrees with the
pmaports config, `grep` the in-tree fragment first — it wins. Verify on-device
with `zcat /proc/config.gz | grep <SYMBOL>`.
1. **`olddefconfig` is mandatory on a version bump.** 7.1.1 added Kconfig
symbols not in the rc1 fragment config; abuild's build runs non-interactive,
so a bare `make` hits the `oldconfig` EOF prompt and dies at `prepare0`.
The APKBUILD `build()` now runs `make ARCH=$_carch LLVM=1 olddefconfig`
first, then a serial `make ... -j1 prepare`, then the parallel build.
2. **The `*.bc` gitignore drops a tracked kernel source file.** pmbootstrap's
source-override rsync is gitignore-aware but honors `*.bc` (line 16) WITHOUT
the negation `!kernel/time/timeconst.bc` (line 180). So `timeconst.bc` — a
real bc(1) source needed by `prepare0` for `include/generated/timeconst.h`
is silently excluded → `No rule to make target 'kernel/time/timeconst.bc'`.
Same class of breakage hits the negated dotfiles (`.clang-format`, etc.).
**Fix:** the kernel APKBUILD `prepare()` restores these from the source mount
(`/mnt/pmbootstrap/source-override/`) after `default_prepare`.
3. **The persistent build copy poisons incremental builds.** pmbootstrap rsyncs
the override into `/tmp/pmbootstrap-local-source-copy` (inside the native
chroot) WITHOUT `--delete`. A prior interrupted build leaves stale host-tool
binaries (`scripts/basic/fixdep`, `scripts/mod/mk_elfconfig`) + `.cmd` files;
the next build's `make` then trusts the stale stamps, never recompiles them,
and fails `mk_elfconfig: not found` at `prepare0`. If you see host-tool
"not found" with no preceding `HOSTCC` line, the copy is dirty.
**Recovery:** let pmbootstrap recreate it cleanly (delete the copy dir;
a fresh rsync from the clean host tree rebuilds host tools from scratch).
Note: `git clean -dfx` on the HOST tree does NOT touch this chroot copy.
## Kernel-swap / boot.img (from modem.md, kept here for the build flow)
The modem stack is fully modular (`qcom_q6v5_mss`, `qrtr*`, `qcom_glink_smem`,
`mhi*` all `=m`), so a kernel swap needs the matching `lib/modules/<kver>/` on
the rootfs or the modem silently no-loads (a false 52). UFS + USB-gadget-NCM are
`=y`, so a module-mismatched boot still reaches rootfs + USB.
Boot image: `cat vmlinuz <blueline>.dtb` + the standing initramfs, `mkbootimg`
header v0, base 0x0, kernel_off 0x8000, ramdisk_off 0x1000000, tags 0x100, real
cmdline `quiet splash … pmos_boot_uuid=2CE8-836F pmos_root_uuid=26733d78…`.
Rootfs is found by `pmos_root_uuid` (the ext4 FS UUID), so the slot LETTER only
decides which block device we write into — the cmdline is slot-agnostic.
SLOT MAP (verified live via `fastboot getvar current-slot` 2026-06-26):
**Android = slot B** (`current-slot: b`, `slot-successful:b: yes`). **pmOS = slot A.**
Flash ONLY `boot_a` (`fastboot flash boot_a …; fastboot --set-active=a`) and write
the rootfs to `system_a`. NEVER touch `boot_b` / `system_b` (that is Android now),
EFS(modemst1/2), userdata (single SHARED partition, ~119 GB), or TZ/XBL/ABL.
(Earlier revs of this doc had the slots reversed — they were wrong; the device is
authoritative.)
### FLASH GOTCHAS (each cost a boot this session — RECORD)
1. **`system_a` rejects bootloader-`fastboot flash` → must use TWRP cat (or fastbootd).**
`fastboot flash system_a` from the bootloader fails `Partition should be flashed
in fastbootd`. This fastboot/bootloader gates system writes to userspace. We DON'T
have a working `reboot fastbootd` (binary says "unknown reboot target"), so the
path is: `fastboot boot ~/Downloads/twrp-3.7.1_12-0-blueline.img`, wait for adb
`recovery`, then `adb shell -T "cat > /dev/block/by-name/system_a; sync" < raw.img`
(`system_a``/dev/block/sda5`, a real 2.95 GB physical partition, NOT dynamic).
2. **pmbootstrap's `google-blueline.img` is ANDROID-SPARSE, not raw — un-sparse before cat.**
THE bug that gave a kernel-7.1.0rc1 **debug-shell boot** (initramfs can't find root).
deviceinfo has `deviceinfo_flash_sparse="true"`, so the final image starts with the
sparse magic `3a ff 26 ed`, not a partition table. `cat`'ing it raw onto `system_a`
writes a sparse container, so there's no GPT/ext4 on the partition → initramfs
`findfs UUID=` fails → debug shell. **Fix:** `simg2img google-blueline.img raw.img`
first, THEN cat `raw.img`. Verify: a correct raw image starts with `0000`, reads as
GPT @ **4096-byte sectors** (`fdisk -b 4096 -l`: p1 ≈236M EFI/boot, p2 ext4 root at
byte 255852544), and has the ext4 magic `53ef` at `255852544+1080`. This matches the
known-good `slotb-backup` byte-for-byte. (UFS is 4096-sector — `fdisk` default 512
shows NO partitions on the raw image; that's a red herring, not corruption.)
3. **Quarantine stale `_p<timestamp>` kernel apks before `install`, or apk grabs the wrong one.**
A `--src` build auto-versions as `7.1.1_p20260625…`, which apk sorts ABOVE the tagged
`7.1.1-r2` (the `_p` post-suffix outranks the bare tag). `pmbootstrap install` then
silently pulls the OLD kernel. Move the `_p` apks out of
`~/.local/var/pmbootstrap/packages/edge/aarch64/`, rebuild the index (`apk index
*.apk` + `abuild-sign` in the native chroot — the `pmbootstrap index` wrapper failed
on its own quoting), then `pmbootstrap zap` (the rootfs chroot has the `_p` kernel
installed and apk won't downgrade) and re-install.
## The libqmi fork — packaged as the temp/libqmi override
`qcril-autoselect.py` needs the **openimsd libqmi fork** (PDC Set Feature msg
0x2E) via GObject-introspection; stock libqmi lacks both the message and the
typelib. It is packaged as **`temp/libqmi`** (pkgname=`libqmi`, source
`gitlab.postmarketos.org/modem/openimsd/libqmi` commit `b683efb`, built with
`-Dintrospection=true`, provides `libqmi` + `qmi-utils`). Because the aport's
pkgname is `libqmi`, it shadows Alpine's stock libqmi, and `device-google-blueline`
pulls it in via its `libqmi` / `qmi-utils` deps — a fresh `pmbootstrap install`
lays down kernel + provisioner + fork as one unit. The built apk carries
`usr/lib/girepository-1.0/Qmi-1.0.typelib` (verified).
Watch-item: the override is pinned at `1.39.0-r0`, equal to current Alpine edge.
If Alpine bumps libqmi above this, apk could prefer stock at install — bump the
override's pkgver/pkgrel to stay ahead. (`references/openimsd-libqmi` +
`tools/build-openimsd-libqmi.sh` remain as the on-device overlay fallback.)