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
150 lines
9.1 KiB
Markdown
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.)
|