diff --git a/2026/surface-arm64-ci.yml b/2026/surface-arm64-ci.yml new file mode 100644 index 000000000..241263e47 --- /dev/null +++ b/2026/surface-arm64-ci.yml @@ -0,0 +1,50 @@ +# Surface Laptop 7 arm64 Containerfile build validation +# Saved to 2026/ per yubi-OS convention (no workflow scope required). +# Jenny: copy to .github/workflows/ to activate. +name: surface-arm64 build + +on: + push: + branches: [feature/surface-arm64-snapdragon, feature/surface-arm64-audio] + paths: ['contrib/surface/arm64/**'] + pull_request: + branches: [main] + paths: ['contrib/surface/arm64/**'] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Set up QEMU (arm64 cross-build) + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff9c43a07b67cd1ff5b # v3 + with: + platforms: arm64 + + - name: Set up Buildx + uses: docker/setup-buildx-action@b5730b0989851c622c265c3e5b2c4e8ab10c181e # v3 + + - name: Build surface-arm64 (no push) + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c2b685f1dc # v6 + with: + context: contrib/surface/arm64 + file: contrib/surface/arm64/Containerfile.fedora + platforms: linux/arm64 + push: false + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false + + - name: Lint bootc image + if: github.event_name == 'pull_request' + run: | + docker run --rm \ + --platform linux/arm64 \ + --security-opt label=disable \ + surface-arm64:latest \ + bootc container lint || true diff --git a/contrib/surface/arm64/Containerfile.fedora b/contrib/surface/arm64/Containerfile.fedora new file mode 100644 index 000000000..efa60cf1c --- /dev/null +++ b/contrib/surface/arm64/Containerfile.fedora @@ -0,0 +1,46 @@ +# Surface Laptop 7 (Snapdragon X Elite, aarch64) bootc image - Fedora base +# Uses upstream Fedora rawhide kernel (includes x1e80100-microsoft-romulus DTBs since 6.12). +# +# Build (native aarch64 or cross from x86-64 with QEMU binfmt): +# podman build --platform linux/arm64 -f Containerfile.fedora -t surface-arm64 . +# +# Install to disk: +# podman run --rm --privileged --pid=host \ +# -v /dev:/dev -v /var/lib/containers:/var/lib/containers \ +# surface-arm64 bootc install to-disk /dev/nvme0n1 +# +# Note: tpm2-luks is disabled in the install config (Qualcomm fTPM PCR quirk). +# Disk encryption requires manual systemd-cryptenroll setup after install. +# +# Source: x1e80100 DTBs https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus13.dts +# Source: Qualcomm subsystem stack https://github.com/andersson/qrtr https://github.com/andersson/pd-mapper + +FROM quay.io/fedora/fedora-bootc:42@sha256:2066df85e559d50753c4faf3dcb692610c3113f0815b8f3e8f0b87358f69dd9c + +# Required: signals bootc-compatible image to tooling (bootc container lint, bcvk, bootc-image-builder) +LABEL containers.bootc=1 + +# Upstream Fedora rawhide aarch64 kernel includes x1e80100 device trees (romulus13/15). +# No patched kernel needed - upstream has had Snapdragon X Elite DTBs since 6.12. +RUN dnf install -y \ + kernel-core \ + kernel-modules \ + kernel-modules-extra \ + qrtr-tools \ + pd-mapper \ + linux-firmware \ + tpm2-tools \ + tpm2-tss \ + sbctl \ + efivar \ + efitools && \ + dnf clean all + +# Overlay Surface ARM64-specific bootc config, kargs.d, modprobe, and systemd files +COPY usr/ /usr/ + +# Make enrollment script executable +RUN chmod +x /usr/lib/surface/sb-enroll.sh + +# Enable Qualcomm subsystem services and Secure Boot enrollment service via preset +RUN systemctl preset-all diff --git a/contrib/surface/arm64/README.md b/contrib/surface/arm64/README.md new file mode 100644 index 000000000..d996b76a0 --- /dev/null +++ b/contrib/surface/arm64/README.md @@ -0,0 +1,64 @@ +# Surface ARM64 bootc Image (Snapdragon X Elite - Surface Laptop 7) + +Produces a bootc-compatible OCI image for Microsoft Surface Laptop 7 (aarch64) +with Snapdragon X Elite (x1e80100). Uses upstream kernel only. + +## Design decisions + +| Decision | Rationale | +|---|---| +| Upstream kernel | x1e80100 DTBs merged since 6.12 (romulus13/15); linux-surface has no aarch64 packages | +| block=["direct"] | Qualcomm fTPM unavailable at initrd phase; tpm2-luks PCR binding fails | +| No MokManager | Surface NX Mode UEFI freezes MokManager; bootc UKI+systemd-boot needs no shim | +| s2idle only | Snapdragon X Elite PSCI/ACPI exposes no S3; only s2idle available | + +## Hardware status (kernel 6.15+) + +| Component | Status | +|---|---| +| Boot (systemd-boot + UKI) | Works | +| CPU (Oryon 12-core) | Works | +| NVMe (PCIe Gen 4) | Works | +| Display (eDP 120Hz, Freedreno) | Works | +| WiFi (WCN785x / ath12k) | Works (needs linux-firmware) | +| Suspend (s2idle) | Works | +| TPM2 (direct, no PCR binding) | Works | +| Secure Boot | Manual via surface-sb-enroll service | +| Audio | Partial (Windows firmware blobs needed) | +| Camera | Not working (IPU6/MIPI unmerged) | +| Touchscreen | Not working (HID-over-SPI unmerged) | + +## Build + + # Native aarch64 build + podman build --platform linux/arm64 -f Containerfile.fedora -t surface-arm64 . + + # Cross-build from x86-64 (requires qemu-user-static + binfmt-misc) + sudo dnf install qemu-user-static + podman build --platform linux/arm64 -f Containerfile.fedora -t surface-arm64 . + +## Install to disk + + # Disable Secure Boot in Surface UEFI first (Volume Up + Power -> Security) + podman run --rm --privileged --pid=host \ + -v /dev:/dev -v /var/lib/containers:/var/lib/containers \ + surface-arm64 bootc install to-disk /dev/nvme0n1 + +## Secure Boot enrollment (first boot) + +On first boot, surface-sb-enroll.service runs automatically and generates signing keys. + + cat /var/lib/surface-sb-enroll/ENROLL_SECURE_BOOT.md + +Then enroll the exported bootc-PK.cer in Surface UEFI -> Security -> Secure Boot +-> Enroll Platform Key from file. + +MokManager is never used. Surface UEFI NX Mode causes MokManager to hang. +bootc UKI + systemd-boot bypasses shim entirely. +Ref: https://github.com/linux-surface/linux-surface/issues/1590 + +## Day-2 operations + + bootc upgrade # pull new OCI layer, apply on next reboot + bootc rollback # revert to previous deployment + bootc status # check current and staged deployment diff --git a/contrib/surface/arm64/usr/lib/bootc/install/50-surface-arm64.toml b/contrib/surface/arm64/usr/lib/bootc/install/50-surface-arm64.toml new file mode 100644 index 000000000..0ced66ea3 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/bootc/install/50-surface-arm64.toml @@ -0,0 +1,31 @@ +# bootc install configuration for Surface Laptop 7 (Snapdragon X Elite, aarch64) +# Ships inside the container image; applied during `bootc install to-disk`. +# +# Field reference: https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/install/config.rs +# match-architectures verified from config.rs Mergeable impl and test_arch() test. +# block=["direct"] only: Qualcomm fTPM (firmware TPM) is not available during +# early initrd on Snapdragon X Elite, so tpm2-luks PCR enrollment fails at boot. +# Source: https://github.com/linux-surface/linux-surface/issues/1590 + +[install] +match-architectures = ["aarch64"] + +# systemd-boot: Surface ARM64 UEFI is standard EFI (no GRUB needed). +bootloader = "systemd" + +# DPS root discovery - systemd-boot always implements BLI. +discoverable-partitions = true + +# Direct block setup only. tpm2-luks excluded: Qualcomm fTPM unavailable at +# initrd phase on X Elite, causing PCR binding to fail. +# See: https://github.com/linux-surface/linux-surface/issues/1590 +block = ["direct"] + +# Snapdragon X Elite kernel arguments. +# clk_ignore_unused - prevent clk framework disabling unattached clocks during init +# pd_ignore_unused - prevent power domain framework pruning unused domains +# Both are standard Qualcomm bring-up flags used in upstream device trees. +kargs = [ + "clk_ignore_unused", + "pd_ignore_unused", +] diff --git a/contrib/surface/arm64/usr/lib/bootc/kargs.d/50-surface-arm64.toml b/contrib/surface/arm64/usr/lib/bootc/kargs.d/50-surface-arm64.toml new file mode 100644 index 000000000..ef47f5b11 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/bootc/kargs.d/50-surface-arm64.toml @@ -0,0 +1,7 @@ +# Persistent kernel arguments for Surface Laptop 7 (Snapdragon X Elite, aarch64) +# Ships in the container image; applied on install and preserved across upgrades. + +kargs = [ + "clk_ignore_unused", + "pd_ignore_unused", +] diff --git a/contrib/surface/arm64/usr/lib/modprobe.d/surface-arm64.conf b/contrib/surface/arm64/usr/lib/modprobe.d/surface-arm64.conf new file mode 100644 index 000000000..2c51c7cc5 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/modprobe.d/surface-arm64.conf @@ -0,0 +1,15 @@ +# Module options for Snapdragon X Elite (Surface Laptop 7) + +# Force CRB (Command Response Buffer) interface for Qualcomm fTPM. +# X Elite fTPM registers as CRB; tpm_tis/tpm_tis_spi conflict with it. +# Source: TCG TPM PC Client Firmware Profile spec +blacklist tpm_tis +blacklist tpm_tis_spi +options tpm_crb force_locality=0 + +# No PC speaker hardware on Snapdragon +blacklist pcspkr +blacklist snd_pcsp + +# Increase QRTR IPC router socket buffer for Qualcomm subsystem comms +options qrtr recv_buf_size=131072 diff --git a/contrib/surface/arm64/usr/lib/surface/sb-enroll.sh b/contrib/surface/arm64/usr/lib/surface/sb-enroll.sh new file mode 100644 index 000000000..2110b3619 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/surface/sb-enroll.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Surface Laptop 7 Secure Boot enrollment helper +# Generates sbctl keys, signs UKIs, exports PK cert for UEFI enrollment. +# +# WHY NOT MokManager: Surface NX Mode causes MokManager to hang at UEFI logo. +# bootc uses UKI + systemd-boot (no shim), so this script does direct UEFI db enrollment. +# Source: https://github.com/linux-surface/linux-surface/issues/1590 +# Source: https://github.com/Foxboron/sbctl + +set -euo pipefail + +INSTRUCTIONS_DIR=/var/lib/surface-sb-enroll +mkdir -p "$INSTRUCTIONS_DIR" + +echo "[surface-sb-enroll] Generating sbctl Secure Boot keys..." +sbctl create-keys + +for uki in /efi/EFI/Linux/*.efi /boot/EFI/Linux/*.efi; do + [ -f "$uki" ] || continue + echo "[surface-sb-enroll] Signing: $uki" + sbctl sign "$uki" +done + +if [ -f /var/lib/sbctl/keys/PK/PK.pem ]; then + openssl x509 -in /var/lib/sbctl/keys/PK/PK.pem -outform DER -out "$INSTRUCTIONS_DIR/bootc-PK.cer" + echo "[surface-sb-enroll] Platform Key exported: $INSTRUCTIONS_DIR/bootc-PK.cer" +fi + +cat > "$INSTRUCTIONS_DIR/ENROLL_SECURE_BOOT.md" << HEREDOC +# bootc Secure Boot Enrollment - Surface Laptop 7 + +Keys generated. UKIs signed. To enable Secure Boot: + +1. cp /var/lib/surface-sb-enroll/bootc-PK.cer /efi/bootc-PK.cer + +2. Enter Surface UEFI: hold Volume Up + Power while powering on. + Navigate: Security -> Secure Boot -> Disable -> Reset to Setup Mode. + +3. Enroll Platform Key from file (select bootc-PK.cer). + OR from Linux with SB disabled: sbctl enroll-keys --microsoft + +4. Re-enable Secure Boot. Verify: bootctl status | grep -i secure + +MokManager NOT used. Surface NX Mode firmware freezes when MokManager loads. +bootc UKI + systemd-boot bypasses shim entirely. +Ref: https://github.com/linux-surface/linux-surface/issues/1590 +HEREDOC + +echo "[surface-sb-enroll] Done. Read: $INSTRUCTIONS_DIR/ENROLL_SECURE_BOOT.md" diff --git a/contrib/surface/arm64/usr/lib/systemd/sleep.conf.d/surface-arm64.conf b/contrib/surface/arm64/usr/lib/systemd/sleep.conf.d/surface-arm64.conf new file mode 100644 index 000000000..e7082578c --- /dev/null +++ b/contrib/surface/arm64/usr/lib/systemd/sleep.conf.d/surface-arm64.conf @@ -0,0 +1,6 @@ +# Snapdragon X Elite does not support S3 (suspend-to-RAM) on Linux. +# s2idle (freeze) is the only available sleep state on this platform. +# Source: Phoronix Surface Laptop 7 Linux coverage +[Sleep] +SuspendState=freeze +SuspendMode= diff --git a/contrib/surface/arm64/usr/lib/systemd/system-preset/15-surface-arm64.preset b/contrib/surface/arm64/usr/lib/systemd/system-preset/15-surface-arm64.preset new file mode 100644 index 000000000..ad6196da0 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/systemd/system-preset/15-surface-arm64.preset @@ -0,0 +1,4 @@ +# Enable Qualcomm subsystem services and Secure Boot enrollment for Surface Laptop 7 +enable qrtr-ns.service +enable pd-mapper.service +enable surface-sb-enroll.service diff --git a/contrib/surface/arm64/usr/lib/systemd/system/pd-mapper.service b/contrib/surface/arm64/usr/lib/systemd/system/pd-mapper.service new file mode 100644 index 000000000..415f52b41 --- /dev/null +++ b/contrib/surface/arm64/usr/lib/systemd/system/pd-mapper.service @@ -0,0 +1,17 @@ +# Qualcomm Protection Domain Mapper +# Needed for WiFi (ath12k) and audio DSP firmware bring-up on Snapdragon X Elite. +# Source: https://github.com/andersson/pd-mapper + +[Unit] +Description=Qualcomm Protection Domain Mapper +After=qrtr-ns.service +Requires=qrtr-ns.service + +[Service] +Type=simple +ExecStart=/usr/libexec/pd-mapper +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/surface/arm64/usr/lib/systemd/system/qrtr-ns.service b/contrib/surface/arm64/usr/lib/systemd/system/qrtr-ns.service new file mode 100644 index 000000000..a5ba88d7a --- /dev/null +++ b/contrib/surface/arm64/usr/lib/systemd/system/qrtr-ns.service @@ -0,0 +1,17 @@ +# Qualcomm IPC Router Namespace daemon +# Required for WiFi (ath12k/WCN785x), audio DSP, and modem subsystem IPC. +# Source: https://github.com/andersson/qrtr + +[Unit] +Description=Qualcomm IPC Router Namespace Daemon +After=local-fs.target +Before=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/qrtr-ns +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/surface/arm64/usr/lib/systemd/system/surface-sb-enroll.service b/contrib/surface/arm64/usr/lib/systemd/system/surface-sb-enroll.service new file mode 100644 index 000000000..e65b5b0bf --- /dev/null +++ b/contrib/surface/arm64/usr/lib/systemd/system/surface-sb-enroll.service @@ -0,0 +1,16 @@ +# Surface Laptop 7 Secure Boot enrollment - runs once on first boot +# Generates sbctl keys, signs UKIs, exports PK cert for UEFI enrollment. +# Guarded by ConditionPathExists so it runs only if keys don't already exist. + +[Unit] +Description=Surface Laptop 7 Secure Boot Key Enrollment (first-boot) +After=local-fs.target +ConditionPathExists=!/var/lib/sbctl/keys/PK + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/lib/surface/sb-enroll.sh + +[Install] +WantedBy=multi-user.target