Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions 2026/surface-arm64-ci.yml
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions contrib/surface/arm64/Containerfile.fedora
Original file line number Diff line number Diff line change
@@ -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
64 changes: 64 additions & 0 deletions contrib/surface/arm64/README.md
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions contrib/surface/arm64/usr/lib/bootc/install/50-surface-arm64.toml
Original file line number Diff line number Diff line change
@@ -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",
]
Original file line number Diff line number Diff line change
@@ -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",
]
15 changes: 15 additions & 0 deletions contrib/surface/arm64/usr/lib/modprobe.d/surface-arm64.conf
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions contrib/surface/arm64/usr/lib/surface/sb-enroll.sh
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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=
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions contrib/surface/arm64/usr/lib/systemd/system/pd-mapper.service
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions contrib/surface/arm64/usr/lib/systemd/system/qrtr-ns.service
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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