Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0c7e82b
surface/arm64: add bootc install config (systemd-boot, DPS, Qualcomm …
corning-croak-cable May 9, 2026
34872dc
surface/arm64: add kargs.d (clk_ignore_unused, pd_ignore_unused)
corning-croak-cable May 9, 2026
af2c37f
surface/arm64: add modprobe config (tpm_crb forced, tpm_tis blacklist…
corning-croak-cable May 9, 2026
cff43a3
surface/arm64: add sleep config (s2idle/freeze only - no S3 on Snapdr…
corning-croak-cable May 9, 2026
46dca21
surface/arm64: add qrtr-ns service for Qualcomm IPC routing
corning-croak-cable May 9, 2026
9201855
surface/arm64: add pd-mapper service for Qualcomm protection domain m…
corning-croak-cable May 9, 2026
4956a49
surface/arm64: add firstboot Secure Boot enrollment service (NX Mode …
corning-croak-cable May 9, 2026
221d802
surface/arm64: add Secure Boot enrollment script (NX Mode workaround,…
corning-croak-cable May 9, 2026
26f4ca1
surface/arm64: add systemd preset enabling Qualcomm and enrollment se…
corning-croak-cable May 9, 2026
d92fcff
surface/arm64: add Fedora bootc Containerfile (upstream kernel, Qualc…
corning-croak-cable May 9, 2026
f27935a
surface/arm64: add README
corning-croak-cable May 9, 2026
3a9a544
fix(surface-arm64): pin fedora-bootc to digest, add containers.bootc=…
foil-copy-overrate May 11, 2026
4b3847b
fix(surface-arm64): oneshot sb-enroll service with ConditionPathExist…
foil-copy-overrate May 11, 2026
8aee394
ci: add surface-arm64 build validation workflow (2026/)
foil-copy-overrate May 11, 2026
e6e7ee6
feat(surface-arm64-audio): add snd_soc_sc8280xp + Q6ASM modprobe options
foil-copy-overrate May 11, 2026
b429908
feat(surface-arm64-audio): add WirePlumber Q6DSP routing policy
foil-copy-overrate May 11, 2026
f2c53a9
docs(surface-arm64-audio): document x1e80100 UCM profile status and w…
foil-copy-overrate May 11, 2026
084f4b6
feat(surface-arm64-audio): add audio packages (alsa-ucm-conf, pipewir…
foil-copy-overrate May 11, 2026
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
35 changes: 35 additions & 0 deletions contrib/surface/arm64/Containerfile.fedora
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Surface Laptop 7 (Snapdragon X Elite, aarch64) bootc image - Fedora base
# Includes Qualcomm LPASS/Q6DSP audio support.
#
# 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

FROM quay.io/fedora/fedora-bootc:42@sha256:2066df85e559d50753c4faf3dcb692610c3113f0815b8f3e8f0b87358f69dd9c

LABEL containers.bootc=1

RUN dnf install -y \
kernel-core \
kernel-modules \
kernel-modules-extra \
qrtr-tools \
pd-mapper \
linux-firmware \
tpm2-tools tpm2-tss \
sbctl efivar efitools \
alsa-utils \
alsa-ucm-conf \
pipewire \
wireplumber \
pipewire-pulseaudio && \
dnf clean all

COPY usr/ /usr/

RUN chmod +x /usr/lib/surface/sb-enroll.sh
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",
]
16 changes: 16 additions & 0 deletions contrib/surface/arm64/usr/lib/modprobe.d/surface-arm64-audio.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Audio module options for Snapdragon X Elite (Surface Laptop 7)
# Supplements surface-arm64.conf (TPM/QRTR options live there).

# snd_soc_sc8280xp: Qualcomm Low Power Audio Sub-System driver family.
# Used on x1e80100; upstream since kernel 6.11. Force-load as the
# ACPI/DT probe may not fire before userspace tries to use audio.
options snd_soc_sc8280xp auto_soc=1

# snd_soc_qcom_q6asm: Q6 Audio Subsystem Manager - needed for DSP firmware loading.
# Depends on qrtr (already loaded via pd-mapper service chain).
options snd_soc_qcom_q6asm debug=0

# hdmi-codec: Surface Laptop 7 uses DisplayPort audio via USB4.
# Autoload when DP device detected.
install snd_soc_hdmi_codec /sbin/modprobe --ignore-install snd_soc_hdmi_codec && \
/sbin/modprobe snd_soc_sc8280xp_common
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
15 changes: 15 additions & 0 deletions contrib/surface/arm64/usr/share/alsa/ucm2/UCM-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ALSA UCM profile hint for x1e80100 (Snapdragon X Elite)
# alsa-ucm-conf from Fedora may not yet include x1e80100 profiles.
# This file documents the expected UCM layout for Surface Laptop 7.
#
# UCM card name: x1e80100 (matches /proc/asound/cards)
# Profile set: HiFi (primary), VoiceCall (call routing)
#
# Until upstream alsa-ucm-conf ships x1e80100 profiles:
# 1. Check alsa-ucm-conf-git from COPR @alsa/alsa-utils
# 2. Or copy UCM from linux-surface firmware repo:
# https://github.com/linux-surface/surface-ath11k-firmware
# (contains some UCM profiles for Qualcomm Surface devices)
#
# Tracker: https://github.com/alsa-project/alsa-ucm-conf/issues
# Once upstream ships, remove this file and rely on alsa-ucm-conf package.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# WirePlumber policy for Qualcomm Q6DSP audio on Snapdragon X Elite
# Place at /usr/share/wireplumber/main.lua.d/91-surface-arm64-qcom.lua
# or /etc/wireplumber/main.lua.d/91-surface-arm64-qcom.lua
--
-- Surface Laptop 7 / Snapdragon X Elite: prefer Q6DSP path for internal audio.
-- Fallback to UCM routing if Q6DSP firmware not yet available.

rule = {
matches = {
{
{ "node.name", "matches", "alsa_output.platform*sc8280xp*" },
},
},
apply_properties = {
["audio.rate"] = 48000,
["audio.format"] = "S16LE",
-- Q6DSP prefers 48kHz; resampling to other rates goes through LPASS mixer
["resample.quality"] = 4,
["priority.session"] = 1100,
},
}