Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a4427919e | |||
| 155b427478 | |||
| 9575e5517d |
7 changed files with 177 additions and 26 deletions
|
|
@ -50,8 +50,8 @@ jobs:
|
|||
# the FP6 patches to the next upstream version bump. Requires the
|
||||
# PACKAGE_TOKEN repo secret (catbot account, package:write scope);
|
||||
# skips quietly until it exists. 409 = same version already published.
|
||||
# imsd is skipped: build.sh 3b took it FROM the registry (re-signed
|
||||
# for the chroot), so it is not ours to publish.
|
||||
# imsd and fingerprintd are skipped: build.sh 3b took them FROM the
|
||||
# registry (re-signed for the chroot), so they are not ours to publish.
|
||||
- name: Publish packages to the apk registry
|
||||
env:
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
|
|
@ -65,7 +65,7 @@ jobs:
|
|||
for f in /home/build/.local/var/pmbootstrap/packages/*/aarch64/*.apk; do
|
||||
[ -e "$f" ] || continue
|
||||
case "$(basename "$f")" in
|
||||
imsd-*) echo "registry-sourced, not republished: $(basename "$f")"; continue ;;
|
||||
imsd-*|fingerprintd-*) echo "registry-sourced, not republished: $(basename "$f")"; continue ;;
|
||||
esac
|
||||
found=1
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' \
|
||||
|
|
|
|||
26
README.md
26
README.md
|
|
@ -14,7 +14,9 @@ Maintained by [Jorijn van der Graaf](https://catcrafts.net/about)
|
|||
|
||||
Everything on the `combined-stable` branch: display, touch, wifi, cellular
|
||||
data, NFC (reader), speaker audio, microphone, IMU, magnetometer, barometer,
|
||||
ambient light/proximity plus VoLTE calls (both directions) through imsd.
|
||||
ambient light/proximity, fingerprint unlock through
|
||||
[fingerprintd](https://forgejo.catcrafts.net/Catcrafts/fingerprintd), plus
|
||||
VoLTE calls (both directions) through imsd.
|
||||
|
||||
## Flashing
|
||||
|
||||
|
|
@ -50,3 +52,25 @@ Find it in a stock-firmware capture or your carrier's IMS documentation, then
|
|||
file to exist). See the
|
||||
[imsd README](https://forgejo.catcrafts.net/Catcrafts/imsd) for the full
|
||||
variable reference and carrier assumptions.
|
||||
|
||||
## Fingerprint
|
||||
|
||||
Enrol under **Settings → Users** (Plasma's own fingerprint page; the `fprintd`
|
||||
command-line tools are not installed, fingerprintd replaces that package).
|
||||
**Hold** the finger on the sensor for each of the ~20 presses rather than
|
||||
tapping it; a held press is what the matcher was measured on.
|
||||
|
||||
Two things to know:
|
||||
|
||||
- The lock screen listens for a finger for **60 seconds after it appears**.
|
||||
A press after that reaches nothing and looks like a dead sensor. Lock and
|
||||
unlock again to re-arm it.
|
||||
- The matcher is the phone's own proprietary trustlet, reassembled from the
|
||||
stock modem partition on first boot and never shipped by us. Templates are
|
||||
stored on the Android `persist` partition, sealed to the same hardware
|
||||
anti-rollback counter stock Android uses. Whether fingerprints enrolled under
|
||||
stock Android survive a return to it after using this has **not** been
|
||||
tested.
|
||||
|
||||
A finger can also run something in your session on a match
|
||||
(`~/.config/fingerprintd/fingers.conf`, see the fingerprintd README).
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"
|
||||
pkgname=fp6-vendor-blobs
|
||||
pkgver=1
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="On-device extraction of vendor blobs from the stock Android partitions"
|
||||
url="https://forgejo.catcrafts.net/Catcrafts/fp6-img"
|
||||
arch="noarch"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
# processed in sorted order; '#' comments and blank lines ignored:
|
||||
#
|
||||
# file <partition[,partition...]> <path-in-partition> <dest> <sha256>
|
||||
# mbn <partition[,partition...]> <dir-in-partition> <name> <dest> <sha256>
|
||||
# rebind <bus> <device>
|
||||
#
|
||||
# file: mount the first available listed partition READ-ONLY (ext4 also
|
||||
|
|
@ -20,6 +21,16 @@
|
|||
# unverified blob is never installed and a missing one never silently
|
||||
# skipped. Dests that already exist are left alone (no hashing: a
|
||||
# deliberately replaced file stays).
|
||||
# mbn: the same, for a Qualcomm trustlet, which is not shipped as one file.
|
||||
# QTEE images live in the modem partition's image/ as an ELF header+hashes
|
||||
# file (<name>.mdt) plus one payload per program header (<name>.b00, .b01,
|
||||
# ...), and the loader wants them written back at each segment's p_offset.
|
||||
# Reassembly is therefore not a concatenation: segments are page aligned
|
||||
# but not contiguous, gaps stay zero, and two segments may share an offset
|
||||
# (focal64 has two such pairs), so they are written in index order and the
|
||||
# later one wins. Same guarantees as file: the sha256 is of the reassembled
|
||||
# image, a mismatch tries the next partition, and an unverified image is
|
||||
# never installed.
|
||||
# rebind: if this fragment's run extracted at least one file, unbind and
|
||||
# re-probe <device> on <bus> so the consuming driver picks the file up
|
||||
# in the same boot. Unconditional on purpose: a still-bound consumer may
|
||||
|
|
@ -103,6 +114,90 @@ mount_part() {
|
|||
MNT_PART=$1
|
||||
}
|
||||
|
||||
# Little-endian scalars out of an ELF header. aarch64 is little endian and so
|
||||
# is the image, so od's host order is the right one.
|
||||
u64() { od -An -tu8 -j "$2" -N 8 "$1" | tr -d ' '; }
|
||||
u16() { od -An -tu2 -j "$2" -N 2 "$1" | tr -d ' '; }
|
||||
|
||||
# Reassemble <dir>/<name>.mdt + .b0N into a flat image at <out>. Mirrors
|
||||
# utilities/ta-analysis/reassemble.py in the fp6 bring-up repo, which is where
|
||||
# the format was worked out and where the known-good hash comes from.
|
||||
# POSIX sh has no locals, so these names are deliberately distinct from
|
||||
# extract_mbn's: reassemble() taking rdir= would rewrite its CALLER's copy to
|
||||
# the mount path, and the next partition in the retry loop would then be
|
||||
# searched at $MNT/$MNT/...
|
||||
reassemble() { # <dir> <name> <out>
|
||||
mdir=$1 mname=$2 mout=$3
|
||||
mdt="$mdir/$mname.mdt"
|
||||
[ -f "$mdt" ] || return 1
|
||||
phoff=$(u64 "$mdt" 32) phentsize=$(u16 "$mdt" 54) phnum=$(u16 "$mdt" 56)
|
||||
[ -n "$phoff" ] && [ -n "$phentsize" ] && [ -n "$phnum" ] || return 1
|
||||
[ "$phnum" -gt 0 ] 2>/dev/null || return 1
|
||||
|
||||
# The image is as long as the furthest segment reaches; everything no
|
||||
# segment covers stays zero.
|
||||
total=0 i=0
|
||||
while [ "$i" -lt "$phnum" ]; do
|
||||
o=$((phoff + i * phentsize))
|
||||
pfsz=$(u64 "$mdt" $((o + 32)))
|
||||
if [ "$pfsz" -gt 0 ]; then
|
||||
poff=$(u64 "$mdt" $((o + 8)))
|
||||
[ $((poff + pfsz)) -gt "$total" ] && total=$((poff + pfsz))
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$total" -gt 0 ] || return 1
|
||||
: > "$mout" || return 1
|
||||
truncate -s "$total" "$mout" || return 1
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt "$phnum" ]; do
|
||||
o=$((phoff + i * phentsize))
|
||||
pfsz=$(u64 "$mdt" $((o + 32)))
|
||||
if [ "$pfsz" -gt 0 ]; then
|
||||
poff=$(u64 "$mdt" $((o + 8)))
|
||||
seg=$(printf '%s/%s.b%02d' "$mdir" "$mname" "$i")
|
||||
[ -f "$seg" ] || { log "$mname: segment $i missing"; return 1; }
|
||||
# dd seeks in whole blocks, which is only correct because
|
||||
# every p_offset in these images is page aligned. Refuse
|
||||
# rather than silently misplace a segment if that changes.
|
||||
[ $((poff % 4096)) -eq 0 ] || {
|
||||
log "$mname: segment $i offset $poff is not page aligned"
|
||||
return 1
|
||||
}
|
||||
dd if="$seg" of="$mout" bs=4096 seek=$((poff / 4096)) \
|
||||
conv=notrunc 2>/dev/null || return 1
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
extract_mbn() { # <partition,...> <dir-in-partition> <name> <dest> <sha256>
|
||||
parts=$1 rdir=$2 rname=$3 dest=$4 want=$5
|
||||
for part in $(echo "$parts" | tr ',' ' '); do
|
||||
mount_part "$part" || { log "$part: not mountable, trying next"; continue; }
|
||||
[ -f "$MNT/$rdir/$rname.mdt" ] || { log "$part: no $rdir/$rname.mdt, trying next"; continue; }
|
||||
tmp="$dest.fp6-extract.$$"
|
||||
mkdir -p "${dest%/*}" || fail "cannot create ${dest%/*}"
|
||||
if ! reassemble "$MNT/$rdir" "$rname" "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
log "$part: reassembling $rname failed, trying next"
|
||||
continue
|
||||
fi
|
||||
got=$(sha256sum "$tmp" | awk '{print $1}')
|
||||
if [ "$got" != "$want" ]; then
|
||||
rm -f "$tmp"
|
||||
log "$part:$rdir/$rname sha256 $got != expected, trying next"
|
||||
continue
|
||||
fi
|
||||
chmod 644 "$tmp" && mv "$tmp" "$dest" || { rm -f "$tmp"; fail "installing $dest failed"; }
|
||||
log "reassembled $part:$rdir/$rname.{mdt,b0N} -> $dest"
|
||||
return 0
|
||||
done
|
||||
fail "no listed partition ($parts) yields $rname with sha256 $want - $dest NOT installed"
|
||||
}
|
||||
|
||||
extract() { # <partition,...> <path-in-partition> <dest> <sha256>
|
||||
parts=$1 src=$2 dest=$3 want=$4
|
||||
for part in $(echo "$parts" | tr ',' ' '); do
|
||||
|
|
@ -155,8 +250,11 @@ fi
|
|||
missing=
|
||||
for f in "$MANIFEST_DIR"/*.manifest; do
|
||||
[ -e "$f" ] || continue
|
||||
while read -r kind _ _ dest _; do
|
||||
[ "$kind" = file ] && [ ! -e "$dest" ] && missing=1
|
||||
while read -r kind a b c d e; do
|
||||
case "$kind" in
|
||||
file) [ -e "$c" ] || missing=1 ;;
|
||||
mbn) [ -e "$d" ] || missing=1 ;;
|
||||
esac
|
||||
done < "$f"
|
||||
done
|
||||
[ -z "$missing" ] && exit 0
|
||||
|
|
@ -165,7 +263,7 @@ for f in "$MANIFEST_DIR"/*.manifest; do
|
|||
[ -e "$f" ] || continue
|
||||
extracted=
|
||||
rebinds=
|
||||
while read -r kind a b c d; do
|
||||
while read -r kind a b c d e; do
|
||||
case "$kind" in
|
||||
''|'#'*) ;;
|
||||
file)
|
||||
|
|
@ -174,6 +272,12 @@ for f in "$MANIFEST_DIR"/*.manifest; do
|
|||
extract "$a" "$b" "$c" "$d" </dev/null
|
||||
extracted=1
|
||||
;;
|
||||
mbn)
|
||||
[ -n "$e" ] || fail "$f: malformed mbn line"
|
||||
[ -e "$d" ] && continue
|
||||
extract_mbn "$a" "$b" "$c" "$d" "$e" </dev/null
|
||||
extracted=1
|
||||
;;
|
||||
rebind)
|
||||
[ -n "$b" ] || fail "$f: malformed rebind line"
|
||||
rebinds="$rebinds $a/$b"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ _flavor="postmarketos-qcom-milos"
|
|||
pkgname=linux-$_flavor
|
||||
pkgver=7.2.0
|
||||
# always sorts above the upstream aport (r0..r99)
|
||||
pkgrel=100
|
||||
pkgrel=101
|
||||
pkgdesc="Milos mainline kernel + Catcrafts FP6 bring-up carries (combined-stable)"
|
||||
arch="aarch64"
|
||||
_carch="arm64"
|
||||
|
|
|
|||
|
|
@ -7224,7 +7224,7 @@ CONFIG_TEE_DMABUF_HEAPS=y
|
|||
CONFIG_OPTEE=y
|
||||
# CONFIG_OPTEE_INSECURE_LOAD_IMAGE is not set
|
||||
CONFIG_OPTEE_STATIC_PROTMEM_POOL=y
|
||||
# CONFIG_QCOMTEE is not set
|
||||
CONFIG_QCOMTEE=m
|
||||
# CONFIG_MUX_CORE is not set
|
||||
CONFIG_PM_OPP=y
|
||||
# CONFIG_SIOX is not set
|
||||
|
|
|
|||
53
build.sh
53
build.sh
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/sh -eu
|
||||
# fp6-img pipeline: build a flashable postmarketOS image for the Fairphone 6
|
||||
# with the Catcrafts kernel (milos-linux combined-stable) and imsd (VoLTE)
|
||||
# with the Catcrafts kernel (milos-linux combined-stable), imsd (VoLTE) and
|
||||
# fingerprintd (fingerprint unlock)
|
||||
# installed from the Catcrafts apk registry.
|
||||
#
|
||||
# Runs in CI inside an Alpine container on the privileged "pmos" runner
|
||||
|
|
@ -30,6 +31,19 @@ IMSD_SHA256="
|
|||
f1c317d7ff9448c05df068d683d31da08e4bfc074704d96cf52cb8acbdee6304 imsd-0.3.1-r0.apk
|
||||
a78ef31fc2943ac02e120c46df26353d515ac9840d959cf5193b2afe1c665fa6 imsd-systemd-0.3.1-r0.apk
|
||||
"
|
||||
# fingerprintd (fingerprint unlock) comes from the same registry the same way:
|
||||
# its repo's package CI is the only producer, and the same pinning rule holds.
|
||||
# Three apks: the daemon, its systemd units, and the session agent (inert
|
||||
# until a user writes ~/.config/fingerprintd/fingers.conf). Needs the kernel
|
||||
# aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs >= 1-r2, both
|
||||
# built in this run. 0.2.3: 0.2.2 (enrol, unlock, agent, actions) + the
|
||||
# versioned blobs dependency + a post-upgrade daemon restart.
|
||||
FPD_VERSION=0.2.3-r0
|
||||
FPD_SHA256="
|
||||
3e28f0c1a9a844592ab6878b2dfc0d8f91674549e44bdc1652e7d7d029de1765 fingerprintd-0.2.3-r0.apk
|
||||
0cc46eba5c6c77d5bb54cd9f0e2902f7644720f9c98153062ffa33e19ca36889 fingerprintd-systemd-0.2.3-r0.apk
|
||||
6dfdbc6f971ba4b8f811f828e5868869c7d71fea6c7045e2bffd51bf2736c040 fingerprintd-agent-0.2.3-r0.apk
|
||||
"
|
||||
PMAPORTS_REPO=https://gitlab.postmarketos.org/postmarketOS/pmaports.git
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
|
@ -215,7 +229,7 @@ aports = $WORK/pmaports
|
|||
device = fairphone-fp6
|
||||
ui = plasma-mobile
|
||||
systemd = always
|
||||
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel
|
||||
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fingerprintd,fingerprintd-systemd,fingerprintd-agent,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel
|
||||
EOF
|
||||
|
||||
# All four source tarballs are generated locally above, so every checksum
|
||||
|
|
@ -244,9 +258,9 @@ retry "build modemmanager" pmbootstrap $NOCROSS build --arch aarch64 modemmanage
|
|||
# patched -r2 exists for the publish step even if the install set resolves
|
||||
# it before the overlay is considered.
|
||||
retry "build libcamera" pmbootstrap $NOCROSS build --arch aarch64 libcamera
|
||||
# --- 3b. imsd: the published apk, not a local build --------------------------
|
||||
# The imsd repo's package CI is the only producer of the imsd apk; the image
|
||||
# installs the exact registry package users later get via 'apk upgrade'.
|
||||
# --- 3b. imsd + fingerprintd: the published apks, not local builds -----------
|
||||
# Each repo's package CI is the only producer of its apk; the image installs
|
||||
# the exact registry package users later get via 'apk upgrade'.
|
||||
# pmbootstrap has no knob for a third-party repository, and after the main
|
||||
# 'apk add' it re-adds every package found in its local packages dir BY FILE
|
||||
# PATH — which makes apk verify the package's own signature, and registry
|
||||
|
|
@ -256,13 +270,21 @@ retry "build libcamera" pmbootstrap $NOCROSS build --arch aarch64 libcamera
|
|||
# stay byte-identical, so the identity checksum equals the registry's), drop
|
||||
# into the local packages dir, re-index. The abuild key exists because the
|
||||
# builds above initialized the buildroot.
|
||||
IMSD_DL="$WORK/imsd-apk"
|
||||
rm -rf "$IMSD_DL"
|
||||
mkdir -p "$IMSD_DL"
|
||||
for _f in "imsd-$IMSD_VERSION.apk" "imsd-systemd-$IMSD_VERSION.apk"; do
|
||||
retry "fetch $_f" curl -fsSL -o "$IMSD_DL/$_f" "$IMSD_REGISTRY/aarch64/$_f"
|
||||
REG_DL="$WORK/registry-apks"
|
||||
rm -rf "$REG_DL"
|
||||
mkdir -p "$REG_DL"
|
||||
for _f in "imsd-$IMSD_VERSION.apk" "imsd-systemd-$IMSD_VERSION.apk" \
|
||||
"fingerprintd-$FPD_VERSION.apk" "fingerprintd-systemd-$FPD_VERSION.apk" \
|
||||
"fingerprintd-agent-$FPD_VERSION.apk"; do
|
||||
# every fetched file must have a pin: 'grep .' below drops empty lines,
|
||||
# so an empty pin list would otherwise pass the check with nothing checked
|
||||
printf '%s\n' "$IMSD_SHA256" "$FPD_SHA256" | grep -q " $_f\$" || {
|
||||
echo "no sha256 pin for $_f - add it to IMSD_SHA256/FPD_SHA256" >&2
|
||||
exit 1
|
||||
}
|
||||
retry "fetch $_f" curl -fsSL -o "$REG_DL/$_f" "$IMSD_REGISTRY/aarch64/$_f"
|
||||
done
|
||||
(cd "$IMSD_DL" && printf '%s\n' "$IMSD_SHA256" | grep . | sha256sum -c -)
|
||||
(cd "$REG_DL" && printf '%s\n' "$IMSD_SHA256" "$FPD_SHA256" | grep . | sha256sum -c -)
|
||||
ABUILD_KEY=$(echo "$WORKDIR"/config_abuild/*.rsa)
|
||||
if [ ! -f "$ABUILD_KEY" ]; then
|
||||
echo "expected exactly one abuild key in $WORKDIR/config_abuild" >&2
|
||||
|
|
@ -271,9 +293,9 @@ fi
|
|||
# abuild-keygen ran inside the chroot as pmbootstrap's user (uid 12345), so
|
||||
# the key is 0600 to that uid and unreadable here (run 49 died on exactly
|
||||
# this); sign from a private copy taken via sudo, then drop it.
|
||||
KEYCOPY="$IMSD_DL/abuild-key.rsa"
|
||||
KEYCOPY="$REG_DL/abuild-key.rsa"
|
||||
sudo install -m 0600 -o "$(id -un)" "$ABUILD_KEY" "$KEYCOPY"
|
||||
for _f in "$IMSD_DL"/*.apk; do
|
||||
for _f in "$REG_DL"/*.apk; do
|
||||
python3 ./apk-resign.py "$_f" "$KEYCOPY" "$(basename "$ABUILD_KEY").pub"
|
||||
done
|
||||
rm -f "$KEYCOPY"
|
||||
|
|
@ -286,11 +308,11 @@ if [ ! -d "$PKGDIR" ]; then
|
|||
echo "$PKGDIR missing - the package builds above should have created it" >&2
|
||||
exit 1
|
||||
fi
|
||||
for _f in "$IMSD_DL"/*.apk; do
|
||||
for _f in "$REG_DL"/*.apk; do
|
||||
sudo install -m 0644 -o "$(stat -c %u "$PKGDIR")" -g "$(stat -c %g "$PKGDIR")" \
|
||||
"$_f" "$PKGDIR/$(basename "$_f")"
|
||||
done
|
||||
rm -f "$IMSD_DL"/*.apk
|
||||
rm -f "$REG_DL"/*.apk
|
||||
pmbootstrap index
|
||||
|
||||
# --- 4. build the image -------------------------------------------------------
|
||||
|
|
@ -323,6 +345,7 @@ cp README.md install.sh "$STAGE/fp6-img/"
|
|||
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "default login: user / 147147 (same as official postmarketOS images)"
|
||||
echo "imsd: $IMSD_REGISTRY imsd-$IMSD_VERSION (registry package, sha256-pinned)"
|
||||
echo "fingerprintd: $IMSD_REGISTRY fingerprintd-$FPD_VERSION (registry package, sha256-pinned)"
|
||||
} > "$STAGE/fp6-img/build-info.txt"
|
||||
# sums of the extracted contents
|
||||
(cd "$STAGE/fp6-img" && sha256sum -- * > sha256sums.txt)
|
||||
|
|
|
|||
Loading…
Reference in a new issue