Appendix · reference

Build environment

ssh honor, mise tasks, the patch pipeline, and the cross-build.sh trap.

This is the operational reference for the build host and the patch-build-deploy loop. For the narrative — why these constraints exist and what we tried before settling on them — see essay 3, “Building from source on honor”.

Build host

honor is the only machine we cross-build on.

A clean buildkernel is about 5 minutes. An incremental rebuild after touching one driver file is around 18 seconds.

Why stable/15 + clang specifically

The patch pipeline

mise run patch does, in order:

  1. honeyguide/patch.shgit reset --hard on freebsd-src, then apply the Honeyguide overlays and patches on top of stock stable/15.
  2. cp -r ~/drm-subtree/* freebsd-src/sys/dev/drm/ — drop the full out-of-tree DRM panfrost stack on top.
  3. cp -r ../src/* freebsd-src/ — apply our overlays. These override anything Honeyguide or drm-subtree shipped, file-for-file.
  4. For every *.patch under ../patches/, patch -p0 it onto the matching path in freebsd-src/.

The git reset --hard is destructive. Never edit freebsd-src/ directly on honor — anything you change there gets wiped on the next mise run patch. Always edit in this repo, push, pull on honor, then run patch.

mise tasks

mise run patch                          # sync overlays + apply patches
mise run build-kernel                   # full PINEPHONE_PRO buildkernel (clang)
mise run kernel:compare:phone           # prove phone kernel matches honor artifact
mise run kernel:deploy:phone            # install honor kernel, save kernel.prev, verify hash
mise run kernel:refresh:phone           # build on honor, install to phone, verify hash
KERNEL_DEPLOY_REBOOT=1 mise run kernel:deploy:phone
                                       # install, verify, then reboot
mise run build-module bcm_hostwake      # single module
mise run module:refresh:phone -- bwfm_sdio
                                       # build on honor, deploy, verify hash, reload
mise run module:compare:phone -- bwfm_sdio
                                       # prove phone module matches honor artifact
mise run debug:wifi:setup:phone         # install per-minute dmesg snapshots
mise run debug:wifi:phone -- before-scan bwfm_sdio
                                       # snapshot hashes + phone WiFi state + recent logs
mise run debug:wifi:transfer -- phone-to-host crash-repro
                                       # transfer over WiFi while polling bwfm_sdio into serial
mise run bench:phone -- --name fresh-kernel
                                       # run component receipts into logs/phone-bench/
mise run bench:phone:analyze
                                       # summarize the latest logs/phone-bench/ bundle
mise run bench:phone -- --with-actuators
                                       # opt in to physical vibrator + flash LED pulse checks
mise run coppice:preflight             # bounded Coppice create/exec/cleanup receipt
mise run coppice:prepare-snapshot      # cache FreeBSD stable/15 + drm-subtree in a forkable snapshot
COPPICE_SNAPSHOT_ID=<id> mise run coppice:build-smoke
                                       # fork a prepared Coppice snapshot; cached FreeBSD/drm trees are reused
                                       # add COPPICE_FULL_KERNEL=1 for buildkernel
COPPICE_FULL_KERNEL=1 COPPICE_KERNEL_NO_MODULES=1 mise run coppice:build-smoke
                                       # constrained bhyve compile check; starts detached and polls /work/logs/coppice-build.log
COPPICE_KERNCONF=PINETAB2 COPPICE_BUILD_DTB=1 mise run coppice:build-smoke
                                       # Coppice PineTab2 DTB/config smoke without using honor
COPPICE_URLS=http://192.168.122.182:3000 mise run coppice:build-smoke
                                       # current bhyve builder path; node2 has coppbhyve0 + pf NAT
mise run serial:capture -- wifi-scan    # record live UART to logs/serial/
mise run build-dtb                      # rebuild base DTB (rare; we use overlays)
mise run build                          # patch + build-dtb + build-kernel
mise run build-dtb:pinetab2             # build rk3566-pinetab2-v2.0-freebsd.dtb
mise run build-kernel:pinetab2          # build PINETAB2 kernel
mise run build:pinetab2                 # patch + PineTab2 DTB + PINETAB2 kernel
mise run stage:pinetab2                 # pull PINETAB2 kernel + DTB into artifacts/pinetab2-firstboot/
mise run install:pinetab2:sd -- <bundle> <mountpoint>
                                       # install staged PineTab2 bundle onto a mounted SD-card root
mise run bench:pinetab2:firstboot -- start --name first-power
                                       # start detached PineTab2 UART receipt capture before power-on
mise run bench:pinetab2:firstboot -- collect --ssh pinetab2
                                       # add post-boot SSH evidence once a network path is reachable
mise run bench:pinetab2:firstboot -- analyze
                                       # summarize latest PineTab2 first-boot receipt bundle
mise run create-image                   # clean expandable SD image
mise run create-image-preloaded         # image with X11 + omfreebdy + packages
mise run install <drive>                # copy kernel + DTB to mounted SD card
mise run flash                          # dd image to phone in UMS mode
mise run boot                           # tap U-Boot menu over serial to boot
mise run image                          # full pipeline: sync + build + create-image-preloaded
mise run sync                           # fast-forward honor's git checkout after push

Task definitions live in mise.toml at the repo root.

bench:phone is the first stop after booting a new PinePhone Pro kernel. Its default path is non-destructive: combined sensor receipt, read-only SPI NOR boot-region check, SGM3140 status, and modem enumeration / AT status probes. Physical outputs stay opt-in behind --with-actuators, which adds the vibrator rumble verifier and bounded SGM3140 pulse test. Each run writes summary.txt, per-step logs, and analysis.txt; bench:phone:analyze can re-read the latest bundle or an explicit logs/phone-bench/<timestamp>-<name>/ directory.

bench:pinetab2:firstboot is the first stop when the tablet arrives. It starts a detached UART capture before power-on, writes a checklist and next commands into logs/pinetab2-firstboot/<timestamp>-<name>/, and can later add an SSH receipt plus analysis.txt once FreeBSD is reachable over a USB network adapter or another temporary network path.

The deploy step

Kernels build on honor; the phone runs them; the laptop sits in the middle. We pipe through it rather than letting honor push directly to the phone, both because honor doesn’t have routing to the phone’s USB network and because we want one canonical workflow regardless of where honor lives.

╞═ deploy a freshly built kernel ═╡
mise run kernel:refresh:phone           # build, deploy, verify; no reboot
mise run kernel:compare:phone

# Only when somebody is around to recover the phone:
KERNEL_DEPLOY_REBOOT=1 mise run kernel:deploy:phone

ssh pinephone resolves to the phone’s USB-Ethernet IP (10.0.0.2 in our setup; see essay 4). The phone reboots, the kernel comes up, and serial console is captured to a local log file (always — see the serial capture rule). The deploy task writes through a temporary file, saves the previous kernel as /boot/kernel/kernel.prev, and fails if the phone hash does not match the kernel artifact on honor.

For module work, the phone path is now deliberately single-command and hash-verified:

╞═ deploy one module repeatably ═╡
mise run module:refresh:phone -- bwfm_sdio
mise run module:compare:phone -- bwfm_sdio

The git workflow rule

All code changes happen locally in this repo. The flow is:

  1. Edit src/, patches/, sys/, etc. on the laptop
  2. git commit && git push origin master
  3. ssh honor 'cd ~/pine64-freebsd && git pull'
  4. mise run patch && mise run build-kernel
  5. Pipe + reboot as above

Never rsync or scp directly to honor’s freebsd-src. The git reset --hard in patch.sh will wipe it. Everything has to flow through git so the source of truth stays in one place and Honeyguide’s upstream-merge flow stays clean.

Build smoke test

If a kernel built on honor hangs at boot but a kernel from a known-good day boots fine, the suspect is almost always a stale object directory or a buildworld/kernel mismatch. The recovery is rm -rf ~/pine64-freebsd/honeyguide/obj.clang && mise run build — it costs ten minutes but isolates the variable.