Compare commits

..
Author SHA1 Message Date
fa28b20df7 Merge branch 'main' of https://forgejo.catcrafts.net/Catcrafts/fp6-img
All checks were successful
image / image (push) Successful in 1h28m5s
2026-09-11 13:26:56 +02:00
e21a2dd0ec build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Some checks failed
image / image (push) Has been cancelled
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.

registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.

Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
e57149f496 fp6-vendor-blobs 1-r3: extract from the active slot, and stop pinning a hash on a signed image
Two field units got no fingerprint sensor from fingerprintd 0.2.3's
manifest: its focal64 line pinned the sha256 of one Android build's
trustlet (16.82.0, the dev phone's), and Fairphone re-signs that trustlet
every release, so the pin matches exactly one of the six builds seen. A
user on 16.100.0 had to edit the manifest by hand; another ended up with
a file QTEE refuses.

The extractor now tries the active slot's partitions first
(androidboot.slot_suffix from the kernel command line): for a signed
image only the running TZ's own slot is guaranteed to load. An mbn line
may give '-' instead of a hash, which means structural verification
only: ELF64 header, every segment present at the size its program header
declares, page-aligned offsets, a sane total. The loader in TZ verifies
the signature and the per-segment hashes itself and refuses a damaged or
foreign image (one flipped byte -> ERROR_ELF_SIGNATURE_ERROR, measured),
so the whole-image hash added fragility and no protection. A real sha256
is still honoured, and the sha256 of what was installed is logged either
way.

--refresh re-derives mbn dests even when a file exists, replacing it
only with an image that verifies; consumers call it from
post-install/post-upgrade so a fresh 'apk add' needs no reboot and a
hand-placed or wrongly pinned trustlet is replaced on the next upgrade.

Verified on the dev phone (busybox): malformed inputs are refused with a
reason (missing, truncated or oversize segment; non-ELF, ELF32 or short
mdt; a garbage offset), both slots reassemble to the known-good hash, a
foreign file survives a plain run and is replaced by --refresh, a failed
refresh keeps the old file, pins still work, and the real post-upgrade
path re-derived the installed trustlet with the daemon restarting on it.
Record: fp6 repo journal/blobs/ and journal/fingerprint/, 2026-09-11.
2026-09-11 13:10:00 +02:00
ed18cce7d0 fp6-vendor-blobs 1-r3: extract from the active slot, and stop pinning a hash on a signed image
Two field units got no fingerprint sensor from fingerprintd 0.2.3's
manifest: its focal64 line pinned the sha256 of one Android build's
trustlet (16.82.0, the dev phone's), and Fairphone re-signs that trustlet
every release, so the pin matches exactly one of the six builds seen. A
user on 16.100.0 had to edit the manifest by hand; another ended up with
a file QTEE refuses.

The extractor now tries the active slot's partitions first
(androidboot.slot_suffix from the kernel command line): for a signed
image only the running TZ's own slot is guaranteed to load. An mbn line
may give '-' instead of a hash, which means structural verification
only: ELF64 header, every segment present at the size its program header
declares, page-aligned offsets, a sane total. The loader in TZ verifies
the signature and the per-segment hashes itself and refuses a damaged or
foreign image (one flipped byte -> ERROR_ELF_SIGNATURE_ERROR, measured),
so the whole-image hash added fragility and no protection. A real sha256
is still honoured, and the sha256 of what was installed is logged either
way.

--refresh re-derives mbn dests even when a file exists, replacing it
only with an image that verifies; consumers call it from
post-install/post-upgrade so a fresh 'apk add' needs no reboot and a
hand-placed or wrongly pinned trustlet is replaced on the next upgrade.

Verified on the dev phone (busybox): malformed inputs are refused with a
reason (missing, truncated or oversize segment; non-ELF, ELF32 or short
mdt; a garbage offset), both slots reassemble to the known-good hash, a
foreign file survives a plain run and is replaced by --refresh, a failed
refresh keeps the old file, pins still work, and the real post-upgrade
path re-derived the installed trustlet with the daemon restarting on it.
Record: fp6 repo journal/blobs/ and journal/fingerprint/, 2026-09-11.
2026-09-11 13:03:22 +02:00
34c687d160 libcamera, libqmi, modemmanager: build for the cores the phone has
Same string as the kernel commit, for the three compiled aports this image
builds itself: -march=armv8.6-a+fp16fml+aes+sha3+sm4 -mtune=cortex-a720
appended to CFLAGS (and CXXFLAGS for libcamera). abuild builds these with
Alpine's GCC; the string is gcc's own -march=native expansion on the phone
re-based on armv8.6-a, and it assembles on GCC 15 with binutils 2.45.1 and
on GCC 16 (whose -mcpu=cortex-a720 expansion binutils 2.45 rejects). On the
phone itself gcc -flto, g++ and clang all compile, link and run it.

The base stops at 8.6 on purpose: this phone exposes neither SVE nor MTE
(userspace autovectorised for SVE2 would SIGILL), and not WFxT either.

pkgrel bumps so apk upgrade delivers the rebuilt binaries.
2026-09-08 20:24:15 +02:00
27f923a817 kernel: build for the cores the phone has, pkgrel 102
The FP6 is 4x Cortex-A520 + 4x Cortex-A720 and this image boots nothing
else, so pass the compiler KCFLAGS="-march=armv8.6-a+fp16fml+aes+sha3+sm4
-mtune=cortex-a720". The stock arm64 build gives the compiler no arch flag
at all (so one image can boot any Armv8 board).

The string is what `gcc -march=native` derives on the phone, re-based on
armv8.6-a so clang can express the same set (clang has no flagm2/rcpc2/
frintts tokens; it reaches them through the architecture level). 8.6 is the
highest level whose mandatory set the phone exposes: 8.7 would assume WFxT
and 9.x SVE2, and neither is in its hwcaps or ID registers. clang emits
identical code for this and for -mcpu=cortex-a720+nosve+nomemtag; the
difference is only which system features (SPE, ETE, TRBE, FPAC) it knows
about, none of which generate code.

Verified on eef717f978f1 with the aport config: builds with zero warnings,
same module set as r100 (417), compat vDSO untouched, boots the dev phone
with a clean dmesg and a GREEN selftest. Record:
fp6 journal/base/captures/2026-09-05-mcpu-a720-verification.md.
2026-09-08 20:24:15 +02:00
60a29c2590 imsd 0.3.3-r0: landline callers ring
All checks were successful
image / image (push) Successful in 1h34m23s
Pin the registry's imsd 0.3.3-r0. The media leg plays AMR narrowband and
G.711 as well as AMR-WB, so calls from landlines and other narrowband
gateways ring instead of being refused with 488; a refused INVITE no longer
flashes the dialer; the offer lists AMR-NB after AMR-WB; CODECS=<list>
overrides the codec set for bench work. 0.3.3 over 0.3.2: the media leg
resolves the audio user once and thread-safely — a getpwnam race between
the mic and playout threads could start both PipeWire helpers in
/run/user/0 and answer a call into static.

Verified on the dev phone 2026-09-08 with the registry apks: 0.3.2 exposed
the race on an incoming call (found by this very check), 0.3.3 carries the
fix; the code path had sixteen bench calls that evening (PCMA and AMR-NB in
both directions, default AMR-WB unchanged).
2026-09-08 20:24:07 +02:00
11eb0f1aa8 Update README.md
All checks were successful
image / image (push) Successful in 1h55m43s
2026-09-06 14:00:13 +00:00
7bf226e73b fp6-vendor-blobs: the checksum the extractor change forgot, and a check that finds the next one in a second
All checks were successful
image / image (push) Successful in 1h53m7s
CI run 52 failed at minute 57 with `fp6-vendor-blobs-extract: FAILED` from
abuild's checksum verification: 9575e55 changed the extractor and left its
sha512sums entry alone. Nothing was installed or published; latest is the
run-51 image.

Fix the sum, and stop paying an hour to learn it. check-aports.sh sources
every APKBUILD under aports/ and compares the committed sha512sums of its
local source files (scripts, units, configs, patches, including ones in a
subdirectory) against the files themselves. build.sh runs it before
pmbootstrap touches anything, so this class of mistake now fails in the first
seconds of a run and prints the line to paste. Aports whose sums build.sh
regenerates with pmbootstrap checksum are read from build.sh and skipped, so
the two lists cannot drift.

Verified: the checker reports exactly the run-52 mismatch on the tree as
pushed and nothing on the tree as fixed; a scratch copy with one corrupted
sum is caught; the fixed aport builds under abuild in an alpine:edge
container (the only complaint was the throwaway signing key at the index
step, which the CI's pmbootstrap flow does not have).
2026-09-05 22:26:33 +02:00
2a4427919e Install fingerprintd from the registry, so the image unlocks with a finger
Some checks failed
image / image (push) Failing after 57m23s
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.

Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.

The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.

Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
155b427478 kernel: build qcomtee, which is one line and not the coupled change we recorded
The fingerprint stack needs /dev/tee0, and the pmOS config has never built the
driver -- so every session so far has loaded an out-of-tree module. That has
been recorded since 2 September as the release blocker, on the reading that
drivers/tee/qcomtee/Kconfig's `select QCOM_TZMEM_MODE_SHMBRIDGE` would flip
tzmem out of Generic mode and drag a boot+selftest round with it.

It does not. QCOM_TZMEM_MODE_SHMBRIDGE is a member of a choice block, and
Kconfig cannot select a choice member -- the statement is inert, and silently
so. Setting CONFIG_QCOMTEE=m and running olddefconfig against the tree
produces exactly one line of difference and leaves CONFIG_QCOM_TZMEM_MODE_
GENERIC=y, which is the mode every fingerprint measurement was taken on.

The driver is already in the base tree; the only reason it was out-of-tree
here was that nothing enabled it. Verified on the dev phone: the module built
from this config carries ZERO module parameters against the research build's
95, matching vermagic, and the phone boots on it with modules-load.d bringing
it up, /dev/tee0 present, fingerprintd ready and the enrolled template
loading. So the shipped module is upstream code on the tested tzmem mode.

Not pushed. Whether to ship QCOMTEE with tzmem in Generic mode is a separate
question with a real answer -- journal/tee warns about that mode -- and it is
now sequenceable on its own instead of forced by a select that does nothing.
2026-09-05 04:25:51 +02:00
9575e5517d fp6-vendor-blobs: reassemble a Qualcomm trustlet, not just copy a file
The fingerprint matcher is a proprietary OEM-signed trustlet, and it is not
shipped as one file: QTEE images live in the modem partition's image/ as an
ELF header+hashes file plus one payload per program header, and the loader
wants each payload written back at its segment's p_offset. So the existing
file directive cannot reach it.

An mbn directive does, with the same guarantees file has: the sha256 is of
the reassembled image, a mismatch tries the next partition, and an unverified
image is never installed. Reassembly is not a concatenation -- segments are
page aligned but not contiguous, gaps stay zero, and two pairs of focal64's
nine segments share an offset, so they are written in index order and the
later one wins, exactly as the bring-up repo's reassemble.py does.

Verified on the dev phone against the hash QTEE has actually accepted since
August: 3600472 bytes, sha256 1930c490..., reassembled from the phone's own
modem_a. The retry path was verified too, with a deliberately wrong first
partition -- which is how the variable clobber got caught: POSIX sh has no
locals, and reassemble() taking rdir= rewrote its caller's copy to the mount
path, so the second partition would have been searched at $MNT/$MNT/...

The fast path needed teaching as well: mbn's dest is the fifth field, and a
first boot would otherwise have exited early and extracted nothing.

Not pushed. The consumer fragment lives in the fingerprintd package.
2026-09-05 03:56:13 +02:00
57517217e4 imsd: place the re-signed apks with the packages dir's owner
All checks were successful
image / image (push) Successful in 1h45m31s
Run 50 got past the key copy and the re-sign, then died on the move into
pmbootstrap's packages dir: abuild created that directory from inside the
chroot, so it belongs to the chroot user (uid 12345) and the build user
cannot write there. Install the files via sudo with the directory's own
owner and mode 0644, as abuild would have left them, and fail with a clear
message if the directory is missing instead of creating a wrongly-owned one.
2026-09-02 19:35:20 +02:00
877fe92a4b imsd: sign from a readable copy of the abuild key
Some checks failed
image / image (push) Failing after 2h3m26s
Run 49 died in section 3b: pmbootstrap's abuild-keygen runs inside the
chroot as its own user (uid 12345), so the key in config_abuild/ is 0600 to
that uid and the build user cannot read it; openssl dgst -sign exited 1 and
apk-resign.py swallowed its stderr. Take a private copy via sudo for the
duration of the re-sign, and make the script name an unreadable key and let
openssl's stderr through instead of hiding it.
2026-09-02 17:21:30 +02:00
e7a1825b50 imsd: install the published apk instead of building it
Some checks failed
image / image (push) Failing after 48m0s
The imsd repo's package CI is now the only producer of the imsd apk. The
image installs the exact registry package users get via 'apk upgrade'
(pinned version + sha256 of the registry files), so the two can no longer
diverge and the payload-parity rule between two packagings is gone.

pmbootstrap has no knob for a third-party repository, and after the main
'apk add' it re-adds every package in its local packages dir by file path,
which makes apk verify the package's own signature. Registry packages are
signed with per-run keys nobody keeps (phones trust the registry-signed
index), so apk-resign.py replaces the signature stream with one from this
run's abuild key; control and data streams stay byte-identical and the
identity checksum equals the registry's. Verified on the host with apk
3.0.8: originals UNTRUSTED, re-signed OK, checksums equal.

The publish step skips imsd-*: those files came from the registry.
2026-09-02 16:11:50 +02:00
6d32d10a8a imsd 0.3.1: aport ownership moves to the imsd repo
All checks were successful
image / image (push) Successful in 1h48m1s
The imsd repo now owns its apk packaging (packaging/aport/, imsd commit
17e0f6b); build.sh copies the aport out of the same pinned checkout it
archives, so daemon and packaging can never skew. The local
aports/modem/imsd copy is gone.

0.3.1 ships the ims-pdn-up hardening (mmcli errors logged verbatim per
attempt, connect attempts gated on network registration, IMS_IP_TYPE
configurable) — the field-debugging fix for undecodable bearer failures
(journal/users/tu11ebukk), tested on the dev phone. build-info.txt now
derives the imsd version from the aport instead of hardcoding it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:26:33 +02:00
1cb5099add aports: extract vendor blobs before udev coldplug so first-boot audio fully works
All checks were successful
image / image (push) Successful in 1h39m0s
The preset fix (6ecc1dc) makes the unit run, but multi-user is too late: the
sound card probes at ~21 s, hard-fails (-2, no retry) on the missing ACF, and
the extractor's amp rebind at ~60 s cannot re-form a card whose machine
driver already failed. Re-probing the machine driver does re-form it, but a
late-formed card has a dead capture path - the wcd9378 TX sequencer refuses
PS0 (act_ps 0x3) until the next boot. So rebinding after the fact can never
fully win; extraction has to happen before the drivers probe.

- fp6-vendor-blobs.service: DefaultDependencies=no, run after remount-fs and
  before systemd-udev-trigger, so blobs exist before module coldplug.
  multi-user stays a second Wants as a post-udev retry if the early run
  failed (by-partlabel/make-dynpart-mappings fallbacks need udev).
- aw88261-acf.manifest: add 'rebind platform sound' so the fallback path at
  least recovers playback the same boot (capture then heals on reboot).
- fp6-vendor-blobs.post-upgrade (pkgrel 1): systemctl preset + immediate
  extract, so installs of the 2026-08-24 image (unit shipped disabled, no
  version bump to deliver the preset) finally heal via plain apk upgrade.
- soc-fairphone-fp6-audio pkgrel 1: carries the manifest change.

Verified on the dev phone with the exact staged bytes (sha256-compared):
simulated fresh install (ACF removed, reboot) extracts at 13.3 s, coldplug
probes the amps at 15.1 s, card init at 20.9 s loads the ACF cleanly, and a
1 kHz speaker-to-mic loopback passes on that same first boot with zero TX
sequencer warnings; next boot takes the all-dests-present fast path in 25 ms.
Evidence: fp6 repo journal/blobs/, 2026-08-29.

Assisted-by: Claude:claude-fable-5
2026-08-29 21:53:02 +02:00
7757449452 debian fix
All checks were successful
image / image (push) Successful in 1h35m22s
2026-08-29 00:40:38 +02:00
d1a6739405 readme change
Some checks failed
image / image (push) Has been cancelled
2026-08-28 00:59:45 +02:00
Jorijn van der Graaf
6ecc1dc87b fp6-vendor-blobs: ship a systemd preset so the unit stays enabled
The package installs a multi-user.target.wants symlink, but that alone does
not survive image build: pmbootstrap runs systemctl preset-all, which removes
manually-installed .wants links for any unit whose preset does not enable it.
The 2026-08-24 image therefore shipped the service as
'disabled; preset: disabled' and it never ran, so aw88261_acf.bin was never
extracted from the stock vendor partition and a fresh install came up with no
sound card at all.

Reproduced and verified on the dev phone: with no preset file installed,
systemctl preset leaves the unit disabled; with it, the unit comes back enabled
and stays enabled across a full preset-all.

Investigation and evidence: fp6 repo journal/blobs/, 2026-08-28.

Assisted-by: Claude:claude-fable-5
2026-08-28 00:57:56 +02:00
2 changed files with 2 additions and 13 deletions

View file

@ -8,7 +8,7 @@ _flavor="postmarketos-qcom-milos"
pkgname=linux-$_flavor pkgname=linux-$_flavor
pkgver=7.2.0 pkgver=7.2.0
# always sorts above the upstream aport (r0..r99) # always sorts above the upstream aport (r0..r99)
pkgrel=103 pkgrel=102
pkgdesc="Milos mainline kernel + Catcrafts FP6 bring-up carries (combined-stable)" pkgdesc="Milos mainline kernel + Catcrafts FP6 bring-up carries (combined-stable)"
arch="aarch64" arch="aarch64"
_carch="arm64" _carch="arm64"

View file

@ -1789,7 +1789,6 @@ CONFIG_ARM_SCMI_TRANSPORT_OPTEE=y
# EFI (Extensible Firmware Interface) Support # EFI (Extensible Firmware Interface) Support
# #
CONFIG_EFI_ESRT=y CONFIG_EFI_ESRT=y
# CONFIG_EFI_VARS_PSTORE is not set
CONFIG_EFI_PARAMS_FROM_FDT=y CONFIG_EFI_PARAMS_FROM_FDT=y
CONFIG_EFI_RUNTIME_WRAPPERS=y CONFIG_EFI_RUNTIME_WRAPPERS=y
CONFIG_EFI_GENERIC_STUB=y CONFIG_EFI_GENERIC_STUB=y
@ -7450,14 +7449,7 @@ CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
# CONFIG_QNX4FS_FS is not set # CONFIG_QNX4FS_FS is not set
# CONFIG_QNX6FS_FS is not set # CONFIG_QNX6FS_FS is not set
# CONFIG_ROMFS_FS is not set # CONFIG_ROMFS_FS is not set
CONFIG_PSTORE=y # CONFIG_PSTORE is not set
CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
CONFIG_PSTORE_COMPRESS=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
# CONFIG_PSTORE_FTRACE is not set
CONFIG_PSTORE_RAM=y
# CONFIG_PSTORE_BLK is not set
# CONFIG_UFS_FS is not set # CONFIG_UFS_FS is not set
CONFIG_EROFS_FS=m CONFIG_EROFS_FS=m
# CONFIG_EROFS_FS_DEBUG is not set # CONFIG_EROFS_FS_DEBUG is not set
@ -7971,9 +7963,6 @@ CONFIG_XZ_DEC_BCJ=y
CONFIG_DECOMPRESS_GZIP=y CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_ZSTD=y CONFIG_DECOMPRESS_ZSTD=y
CONFIG_GENERIC_ALLOCATOR=y CONFIG_GENERIC_ALLOCATOR=y
CONFIG_REED_SOLOMON=y
CONFIG_REED_SOLOMON_ENC8=y
CONFIG_REED_SOLOMON_DEC8=y
CONFIG_INTERVAL_TREE=y CONFIG_INTERVAL_TREE=y
CONFIG_XARRAY_MULTI=y CONFIG_XARRAY_MULTI=y
CONFIG_ASSOCIATIVE_ARRAY=y CONFIG_ASSOCIATIVE_ARRAY=y