From 4bb4f53be1d726b89ac36e13c766beefeed08f22 Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 24 Aug 2026 20:04:35 +0800 Subject: [PATCH 01/16] Guard three x86-only assumptions so Omarchy can install on aarch64 Each of these is an unconditional assumption that only holds on x86_64. None is x86-specific in spirit, and each one blocks or breaks an ARM64 install. 1. etc/mkinitcpio.conf.d/thunderbolt_module.conf MODULES+=(thunderbolt) is unconditional, but Thunderbolt is x86-oriented hardware and the module is not built for every architecture -- Arch Linux ARM's aarch64 kernel has no `thunderbolt`. mkinitcpio treats an unresolvable MODULES entry as a hard error, so *every* initramfs build fails: ==> ERROR: module not found: `thunderbolt' That means no UKI, no boot entry, and an install that completes and then cannot boot. 2. install/post-install/pacman.sh This overwrites /etc/pacman.conf unconditionally. That config points [core]/[extra]/[multilib] at Omarchy's mirror of Arch, which is x86_64-only, and [omarchy] at pkgs.omarchy.org/stable/$arch, which 404s for aarch64. ([multilib] is 32-bit x86 libraries and exists on no ARM mirror at all.) Applying it on ARM leaves the installed system unable to update anything. 3. install/user/mise-work.sh Node publishes its builds as linux-x64 / linux-arm64, which does not match uname -m. The bundled-tarball lookup hardcodes linux-x64, so on ARM it finds nothing and the install aborts with "no bundled Node tarball". The sed that parses the version back out of the filename needs the same treatment. All three were found by installing Omarchy on aarch64 (Arch Linux ARM) and are fixed here the same way: derive from uname -m rather than assuming. --- etc/mkinitcpio.conf.d/thunderbolt_module.conf | 8 +++++++- install/post-install/pacman.sh | 10 ++++++++-- install/user/mise-work.sh | 9 +++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/etc/mkinitcpio.conf.d/thunderbolt_module.conf b/etc/mkinitcpio.conf.d/thunderbolt_module.conf index 9518f8b1201..2f145bb5534 100644 --- a/etc/mkinitcpio.conf.d/thunderbolt_module.conf +++ b/etc/mkinitcpio.conf.d/thunderbolt_module.conf @@ -1 +1,7 @@ -MODULES+=(thunderbolt) +# Thunderbolt is x86-oriented hardware and the module is not built for every +# architecture -- ALARM's aarch64 kernel has no `thunderbolt`. mkinitcpio treats +# an unresolvable MODULES entry as an error, which fails every initramfs build +# on such a system, so gate it rather than forcing it unconditionally. +if [[ $(uname -m) == x86_64 ]]; then + MODULES+=(thunderbolt) +fi diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index da84975b3ee..1efcd2aae1b 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,7 +1,13 @@ # Configure pacman after package installation completes. Offline target package # installs use the live ISO's offline pacman.conf until this final restore. -cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf -cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist +# Omarchy's pacman.conf points [core]/[extra]/[multilib] at Omarchy's mirror of +# Arch (x86_64 only) and [omarchy] at pkgs.omarchy.org/$arch, which 404s for +# aarch64. Applying it on ARM leaves the installed system unable to update at +# all, so keep the working configuration there. +if [[ $(uname -m) == x86_64 ]]; then + cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist +fi # Wait for CUPS to own the file, the way omarchy-settings does, so pacman does # not turn the override into a .pacnew during ISO package installation. diff --git a/install/user/mise-work.sh b/install/user/mise-work.sh index f77d5561e0e..17ea1e25df9 100644 --- a/install/user/mise-work.sh +++ b/install/user/mise-work.sh @@ -19,7 +19,12 @@ case ${OMARCHY_SETUP_CONTEXT:-runtime} in esac if [[ -n $NODE_PACKAGE_DIR ]]; then - NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-linux-x64.tar.gz" -type f 2>/dev/null | head -n1) + # Node names its builds x64/arm64, not by uname + case "$(uname -m)" in + aarch64) _NODE_ARCH=arm64 ;; + *) _NODE_ARCH=x64 ;; + esac + NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-linux-${_NODE_ARCH}.tar.gz" -type f 2>/dev/null | head -n1) if [[ -z $NODE_TARBALL ]]; then if [[ ${OMARCHY_SETUP_CONTEXT:-} == "provision-owner" ]]; then # A factory snapshot predating the bundled tarball may not have it staged. @@ -31,7 +36,7 @@ if [[ -n $NODE_PACKAGE_DIR ]]; then exit 1 fi else - NODE_VERSION=$(basename "$NODE_TARBALL" | sed 's/node-v\(.*\)-linux-x64.tar.gz/\1/') + NODE_VERSION=$(basename "$NODE_TARBALL" | sed "s/node-v\\(.*\\)-linux-${_NODE_ARCH}.tar.gz/\\1/") NODE_INSTALL_DIR="$HOME/.local/share/mise/installs/node/$NODE_VERSION" mkdir -p "$NODE_INSTALL_DIR" From dadd58cc2b3f558f471687ca35439b9cf371d2f8 Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 26 Aug 2026 16:25:42 +0800 Subject: [PATCH 02/16] Derive an aarch64 pacman.conf instead of skipping the restore The previous guard skipped the pacman.conf restore on aarch64 on the grounds that Omarchy's config points at x86-only repositories. But at that point the target still carries the live ISO's pacman.conf, which knows only the offline mirror, and that directory does not exist on the installed system. Skipping therefore left every aarch64 install unable to run pacman at all, which is the outcome the guard claimed to prevent. Derive the config from Omarchy's template instead: drop [multilib], keep [omarchy] (its $arch placeholder resolves correctly), add Arch Linux ARM's [alarm] and [aur] repositories, and leave the mirrorlist the distribution installed rather than replacing it with Omarchy's x86_64 mirror of Arch. The x86_64 path is unchanged. --- install/post-install/pacman.sh | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index 1efcd2aae1b..463f8181545 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,13 +1,31 @@ # Configure pacman after package installation completes. Offline target package # installs use the live ISO's offline pacman.conf until this final restore. -# Omarchy's pacman.conf points [core]/[extra]/[multilib] at Omarchy's mirror of -# Arch (x86_64 only) and [omarchy] at pkgs.omarchy.org/$arch, which 404s for -# aarch64. Applying it on ARM leaves the installed system unable to update at -# all, so keep the working configuration there. -if [[ $(uname -m) == x86_64 ]]; then - cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist -fi +case "$(uname -m)" in + aarch64) + # Omarchy's pacman.conf and mirrorlist describe an x86_64 Arch system: + # [multilib] is 32-bit x86 libraries and no ARM mirror carries it, and the + # mirrorlist points at Omarchy's mirror of Arch, which is x86_64 only. + # Arch Linux ARM also lays its tree out as $arch/$repo rather than Arch's + # $repo/os/$arch, so the mirrorlist cannot be reused with a different host. + # + # Derive the config from Omarchy's instead: drop [multilib], keep [omarchy] + # (its $arch placeholder resolves correctly), add Arch Linux ARM's own + # [alarm] and [aur] repositories, and leave the mirrorlist the distribution + # installed. Skipping this step is not an option: the live ISO's pacman.conf + # only knows the offline mirror, which does not exist on the installed system. + awk ' + /^\[multilib\]$/ { skip = 1; next } + /^\[/ { skip = 0 } + skip { next } + { print } + ' "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" >/etc/pacman.conf + printf '\n[alarm]\nInclude = /etc/pacman.d/mirrorlist\n\n[aur]\nInclude = /etc/pacman.d/mirrorlist\n' >>/etc/pacman.conf + ;; + *) + cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist + ;; +esac # Wait for CUPS to own the file, the way omarchy-settings does, so pacman does # not turn the override into a .pacnew during ISO package installation. From 482ddefacab62130276dc7b97126d8dc860b5e4c Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Tue, 25 Aug 2026 22:21:53 -0400 Subject: [PATCH 03/16] Carry the Windows-on-ARM device trees in every UKI on Qualcomm SoCs Snapdragon laptops boot with the device tree for their exact model and the firmware provides none. The live ISO already boots them with a systemd-stub UKI that carries every candidate tree as .dtbauto sections and picks one by SMBIOS hardware id; the installed system built its UKIs with no tree at all, so the first reboot after the install had nothing to boot. Add the omarchy-hw-qualcomm-soc probe (root device-tree compatible starts with qcom,) and a hardware leaf that lists the trees in /etc/kernel/uki.conf, which mkinitcpio hands to ukify for every UKI limine-mkinitcpio-hook builds. The list is a snapshot of /boot/dtbs/qcom taken when the leaf runs; the file header says so, and the enumeration retires when ukify accepts globs. --- bin/omarchy-hw-qualcomm-soc | 10 +++++++ install/hardware/all.sh | 3 ++ install/hardware/qualcomm/dtb-uki.sh | 42 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100755 bin/omarchy-hw-qualcomm-soc create mode 100644 install/hardware/qualcomm/dtb-uki.sh diff --git a/bin/omarchy-hw-qualcomm-soc b/bin/omarchy-hw-qualcomm-soc new file mode 100755 index 00000000000..a32865e5612 --- /dev/null +++ b/bin/omarchy-hw-qualcomm-soc @@ -0,0 +1,10 @@ +#!/bin/bash + +# omarchy:summary=Detect whether the computer runs on a Qualcomm SoC booted with its device tree (Snapdragon X and friends). + +# The Adreno GPU, the Wi-Fi and the rest of a Qualcomm SoC are platform devices, +# so neither lspci nor DMI can tell a Snapdragon laptop apart. The device tree +# can: every Qualcomm board's root node lists the SoC as a `qcom,` +# compatible, and it is there whenever the machine was booted with a device +# tree, which is how Omarchy boots Windows-on-ARM laptops. +tr '\0' '\n' /dev/null | grep -q '^qcom,' diff --git a/install/hardware/all.sh b/install/hardware/all.sh index 7b806ce8ea4..3c55e3604af 100644 --- a/install/hardware/all.sh +++ b/install/hardware/all.sh @@ -40,6 +40,9 @@ run_logged "$OMARCHY_INSTALL/hardware/apple/fix-suspend-nvme.sh" run_logged "$OMARCHY_INSTALL/hardware/apple/fix-t2.sh" run_logged "$OMARCHY_INSTALL/hardware/apple/fix-brcmfmac-supplicant.sh" +run_logged "$OMARCHY_INSTALL/hardware/qualcomm/dtb-uki.sh" +run_logged "$OMARCHY_INSTALL/hardware/qualcomm/kernel-params.sh" + run_logged "$OMARCHY_INSTALL/hardware/lenovo/fix-yoga-pro7-bass-speakers.sh" run_logged "$OMARCHY_INSTALL/hardware/fix-bcm43xx.sh" diff --git a/install/hardware/qualcomm/dtb-uki.sh b/install/hardware/qualcomm/dtb-uki.sh new file mode 100644 index 00000000000..94d30f176d3 --- /dev/null +++ b/install/hardware/qualcomm/dtb-uki.sh @@ -0,0 +1,42 @@ +# Snapdragon laptops boot with the device tree that describes the exact model, +# and their Windows-on-ARM firmware provides none. A systemd-stub UKI can carry +# every candidate as a .dtbauto section and pick the right one at boot from the +# machine's SMBIOS hardware ids (the .hwids section ukify builds from systemd's +# tables). mkinitcpio hands ukify /etc/kernel/uki.conf when it exists, so listing +# the device trees there makes every UKI limine-mkinitcpio-hook builds pick its +# own tree, on this machine and after every kernel update. +# +# Limitation: DeviceTreeAuto= takes literal paths, not globs, so this is a +# snapshot of /boot/dtbs/qcom from when the leaf ran. A kernel update that adds +# a new board's tree needs `omarchy apply hardware` to refresh the list. Retire +# the enumeration when ukify accepts globs in DeviceTreeAuto=. +# +# Same prefixes as the Omarchy ISO's live UKI: Snapdragon X Elite/Plus (x1*), +# X2 (hamoa*, glymur*) and 8cx Gen 3 (sc8280xp*). The -el2 variants exist for +# running the kernel at EL2, which this firmware does not. + +if omarchy-hw-qualcomm-soc; then + omarchy-pkg-add systemd-ukify + + dtbs=() + for dtb in /boot/dtbs/qcom/x1*.dtb /boot/dtbs/qcom/hamoa*.dtb \ + /boot/dtbs/qcom/glymur*.dtb /boot/dtbs/qcom/sc8280xp*.dtb; do + if [[ -f $dtb && $dtb != *-el2.dtb ]]; then + dtbs+=("$dtb") + fi + done + + if ((${#dtbs[@]} == 0)); then + echo "No Windows-on-ARM device trees under /boot/dtbs/qcom; the UKI gets no .dtbauto sections" >&2 + else + mkdir -p /etc/kernel + { + echo "# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/dtb-uki.sh)." + echo "# Device trees the systemd-stub picks from at boot by SMBIOS hardware id." + echo "# Rerun 'omarchy apply hardware' after a kernel update adds new boards." + echo "[UKI]" + echo "DeviceTreeAuto=${dtbs[*]}" + } >/etc/kernel/uki.conf + echo "Listed ${#dtbs[@]} device trees in /etc/kernel/uki.conf" + fi +fi From 2704962b2eddc7e5371403630676b4a0afcaac02 Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Tue, 25 Aug 2026 22:21:54 -0400 Subject: [PATCH 04/16] Kernel parameters Snapdragon laptops need under Windows-on-ARM firmware The same set Fedora's Snapdragon images and the live ISO carry, as a limine-entry-tool drop-in so limine-update folds it into every entry, with each parameter's reason and retirement condition in the file. --- install/hardware/qualcomm/kernel-params.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 install/hardware/qualcomm/kernel-params.sh diff --git a/install/hardware/qualcomm/kernel-params.sh b/install/hardware/qualcomm/kernel-params.sh new file mode 100644 index 00000000000..c9dad320ea5 --- /dev/null +++ b/install/hardware/qualcomm/kernel-params.sh @@ -0,0 +1,20 @@ +# Kernel parameters Snapdragon laptops need under their Windows-on-ARM firmware: +# the set Fedora's Snapdragon images and archiso carry, measured on the HP +# EliteBook Ultra G1q (X1E-78-100). Retire each one when the kernel no longer +# needs it; Fedora's images are the tell. + +DROP_IN="/etc/limine-entry-tool.d/qualcomm-snapdragon.conf" + +if omarchy-hw-qualcomm-soc; then + mkdir -p /etc/limine-entry-tool.d + cat >"$DROP_IN" <<'CONF' +# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/kernel-params.sh) +# clk_ignore_unused pd_ignore_unused -- display and I/O drivers load from the +# initramfs after the kernel would otherwise switch their clocks and power +# domains off; the screen goes dark at 15 s without them. +# arm64.nopauth -- pointer authentication faults under this firmware. +# systemd.tpm2_wait=0 -- the firmware advertises a TPM2 Linux never gets; +# without this, a 90 s wait for /dev/tpmrm0 on every boot. +KERNEL_CMDLINE[default]+=" clk_ignore_unused pd_ignore_unused arm64.nopauth systemd.tpm2_wait=0" +CONF +fi From 7ed5c9b1d56088645c3baf7468182bfe6f486f3c Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Tue, 25 Aug 2026 22:21:55 -0400 Subject: [PATCH 05/16] Install the Adreno Vulkan driver on Qualcomm SoCs Adreno GPUs are platform devices, so the lspci vendor scan in vulkan.sh never sees them; ask the SoC probe instead. --- install/hardware/vulkan.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/install/hardware/vulkan.sh b/install/hardware/vulkan.sh index a2447d46ffa..e13de205b8c 100644 --- a/install/hardware/vulkan.sh +++ b/install/hardware/vulkan.sh @@ -15,6 +15,11 @@ for vendor in "${!VULKAN_DRIVERS[@]}"; do fi done +# Adreno GPUs are platform devices, so lspci never sees them. +if omarchy-hw-qualcomm-soc; then + PACKAGES+=(vulkan-freedreno) +fi + if (( ${#PACKAGES[@]} > 0 )); then omarchy-pkg-add "${PACKAGES[@]}" fi From 913531175890acb8fd4618220b61c8a33e6d177e Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Wed, 26 Aug 2026 11:51:09 -0400 Subject: [PATCH 06/16] Qualcomm firmware package, and blacklist the audio DSP until its blobs exist Arch Linux ARM keeps the redistributable Qualcomm blobs in linux-firmware-qcom; the installed system had none of them. The vendor-signed DSP firmware comes later from the owner's Windows partition; until it is there the DSP driver's failing probe resets the USB-C mux, which drops a USB-C root disk and hangs the boot (seen on the HP EliteBook Ultra G1q). --- install/hardware/all.sh | 1 + install/hardware/qualcomm/firmware.sh | 29 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 install/hardware/qualcomm/firmware.sh diff --git a/install/hardware/all.sh b/install/hardware/all.sh index 3c55e3604af..19b26b7eb78 100644 --- a/install/hardware/all.sh +++ b/install/hardware/all.sh @@ -42,6 +42,7 @@ run_logged "$OMARCHY_INSTALL/hardware/apple/fix-brcmfmac-supplicant.sh" run_logged "$OMARCHY_INSTALL/hardware/qualcomm/dtb-uki.sh" run_logged "$OMARCHY_INSTALL/hardware/qualcomm/kernel-params.sh" +run_logged "$OMARCHY_INSTALL/hardware/qualcomm/firmware.sh" run_logged "$OMARCHY_INSTALL/hardware/lenovo/fix-yoga-pro7-bass-speakers.sh" diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh new file mode 100644 index 00000000000..9a85b9e0637 --- /dev/null +++ b/install/hardware/qualcomm/firmware.sh @@ -0,0 +1,29 @@ +# Firmware for the Qualcomm SoC. Arch Linux ARM splits the redistributable +# Qualcomm blobs out of linux-firmware into linux-firmware-qcom (Adreno GPU +# microcode, Wi-Fi/Bluetooth board data); without it the GPU and Wi-Fi never +# come up. The device-specific blobs signed by the laptop vendor (GPU zap +# shader, audio/compute DSP, WLAN/BT) are not redistributable and are +# extracted from the owner's Windows partition instead (qcom-firmware-extract). +# +# Until those signed blobs are present the audio DSP driver cannot load its +# firmware; its probe resets the USB-C mux while it fails, which drops a root +# disk attached over USB-C and hangs the boot. Keep it blacklisted while the +# DSP firmware is absent; the file is removed when the blobs are installed. + +if omarchy-hw-qualcomm-soc; then + omarchy-pkg-add linux-firmware-qcom + + mkdir -p /etc/modprobe.d + if compgen -G '/usr/lib/firmware/updates/qcom/*/adsp*' >/dev/null || + compgen -G '/usr/lib/firmware/qcom/*/*/adsp*' >/dev/null; then + rm -f /etc/modprobe.d/qualcomm-adsp-nofw.conf + else + cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' +# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) +# No signed audio-DSP firmware is installed yet; the driver's failing probe +# resets the USB-C mux and can take a USB-C root disk down with it. Removed +# by the same leaf once the firmware is present. +blacklist qcom_q6v5_pas +CONF + fi +fi From f666cc91677416bef7c440f30efe5c2f12af1f60 Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Wed, 26 Aug 2026 15:11:23 -0400 Subject: [PATCH 07/16] Copy the vendor-signed Qualcomm firmware from Windows during install install/hardware/qualcomm/firmware.sh installs qcom-firmware-extract next to linux-firmware-qcom and runs it in the target: the files the device tree names (GPU zap shader, audio and compute DSP images) are taken from the stage the ISO saved before the disk was written, or from a Windows partition still on disk, into /usr/lib/firmware/updates. The audio-DSP blacklist now follows what is actually missing (qcom-firmware-extract --list-missing) instead of a path glob, so it clears itself once the firmware is present. install/omarchy-other.packages lists qcom-firmware-extract (arch=any) so the offline mirror carries it. --- install/hardware/qualcomm/firmware.sh | 38 ++++++++++++++++----------- install/omarchy-other.packages | 3 +++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index 9a85b9e0637..b196a768fc8 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -1,23 +1,29 @@ -# Firmware for the Qualcomm SoC. Arch Linux ARM splits the redistributable -# Qualcomm blobs out of linux-firmware into linux-firmware-qcom (Adreno GPU -# microcode, Wi-Fi/Bluetooth board data); without it the GPU and Wi-Fi never -# come up. The device-specific blobs signed by the laptop vendor (GPU zap -# shader, audio/compute DSP, WLAN/BT) are not redistributable and are -# extracted from the owner's Windows partition instead (qcom-firmware-extract). +# Firmware for the Qualcomm SoC. # -# Until those signed blobs are present the audio DSP driver cannot load its -# firmware; its probe resets the USB-C mux while it fails, which drops a root -# disk attached over USB-C and hangs the boot. Keep it blacklisted while the -# DSP firmware is absent; the file is removed when the blobs are installed. +# Arch Linux ARM splits the redistributable Qualcomm blobs out of +# linux-firmware into linux-firmware-qcom (Adreno GPU microcode, Wi-Fi and +# Bluetooth board data); without it the GPU and Wi-Fi never come up. +# +# The blobs signed by the laptop vendor (GPU zap shader, audio and compute DSP +# images) are not redistributable. qcom-firmware-extract copies the ones this +# machine's device tree names from the owner's own Windows installation: the +# ISO staged them before the disk was written, and a free-space install still +# has the Windows partition on disk. The installer builds the boot image once +# after every hardware leaf has run, so nothing is rebuilt here. +# +# Until the audio DSP firmware is present its driver cannot load; the failing +# probe resets the USB-C mux, which drops a root disk attached over USB-C and +# hangs the boot. Keep the driver blacklisted while that firmware is absent; +# the file is removed once the firmware is installed (re-run +# `sudo qcom-firmware-extract` after providing it). if omarchy-hw-qualcomm-soc; then - omarchy-pkg-add linux-firmware-qcom + omarchy-pkg-add linux-firmware-qcom qcom-firmware-extract + + qcom-firmware-extract --install --no-rebuild mkdir -p /etc/modprobe.d - if compgen -G '/usr/lib/firmware/updates/qcom/*/adsp*' >/dev/null || - compgen -G '/usr/lib/firmware/qcom/*/*/adsp*' >/dev/null; then - rm -f /etc/modprobe.d/qualcomm-adsp-nofw.conf - else + if qcom-firmware-extract --list-missing | grep -q 'adsp'; then cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' # Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) # No signed audio-DSP firmware is installed yet; the driver's failing probe @@ -25,5 +31,7 @@ if omarchy-hw-qualcomm-soc; then # by the same leaf once the firmware is present. blacklist qcom_q6v5_pas CONF + else + rm -f /etc/modprobe.d/qualcomm-adsp-nofw.conf fi fi diff --git a/install/omarchy-other.packages b/install/omarchy-other.packages index e5d56d566fe..1a7be9fec7b 100644 --- a/install/omarchy-other.packages +++ b/install/omarchy-other.packages @@ -75,3 +75,6 @@ t2fanrd # Framework 16 qmk-hid + +# Qualcomm Snapdragon support packages +qcom-firmware-extract From 0b803ef63700512c4cb83a72eac883279cc55518 Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Wed, 26 Aug 2026 16:24:51 -0400 Subject: [PATCH 08/16] Keep the Qualcomm DSPs off when the root disk is on USB With real DSP firmware the Type-C port controller takes over the ports once the ADSP is up and resets them, which drops a root disk behind a USB-C port (G1q, external NVMe: cdsp up at 10.8 s, I/O errors and a read-only root at 22 s). Installs whose root disk reports TRAN=usb keep qcom_q6v5_pas blacklisted (no audio or battery reporting) until the kernel stops resetting connected ports; internal-disk installs are unchanged. --- install/hardware/qualcomm/firmware.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index b196a768fc8..317f5677164 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -23,7 +23,23 @@ if omarchy-hw-qualcomm-soc; then qcom-firmware-extract --install --no-rebuild mkdir -p /etc/modprobe.d - if qcom-firmware-extract --list-missing | grep -q 'adsp'; then + if lsblk -sno TRAN "$(findmnt -no SOURCE /)" 2>/dev/null | grep -q '^usb$'; then + # Root disk on USB. When the DSPs boot with real firmware the Type-C port + # controller (pmic_glink/UCSI) takes over the ports and resets them, which + # drops a root disk behind a USB-C port (observed on the HP EliteBook Ultra + # G1q, JimmayVV/omarchy-iso#27). Keep the DSPs off on such installs: no + # audio or battery reporting, but the system boots. RETIRE when the kernel + # stops resetting an already-connected port (#27). + cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' +# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) +# The root disk is attached over USB. Booting the DSPs hands the USB-C ports +# to the port controller, which resets them and drops the root disk. Keep the +# DSP driver off on this install (no audio or battery reporting) until the +# kernel stops resetting connected ports; delete this file after moving the +# system to an internal disk. +blacklist qcom_q6v5_pas +CONF + elif qcom-firmware-extract --list-missing | grep -q 'adsp'; then cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' # Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) # No signed audio-DSP firmware is installed yet; the driver's failing probe From 9ee74e816c411244c287d4759d5ef063d30501a6 Mon Sep 17 00:00:00 2001 From: Jimmy Van Veen Date: Wed, 26 Aug 2026 19:35:12 -0400 Subject: [PATCH 09/16] Give an aarch64 install Arch Linux ARM's package sources The final pacman restore is skipped entirely on ARM, which leaves the installed system pointed at the live ISO's offline config -- a file:// repo under a bind mount that only exists during the install. The first thing a new aarch64 desktop does is fail: $ sudo pacman -Sy evtest failed retrieving file 'offline.db' from disk: Could not open file /var/cache/omarchy/mirror/offline/offline.db No pacman -S, no omarchy-update. The skip was right that Omarchy's channel configs cannot be applied on ARM -- [core]/[extra] come from Omarchy's mirror of Arch, which builds x86_64 only, [multilib] is 32-bit x86, and the Omarchy package repository serves no aarch64 tree -- but the configuration it keeps instead is the offline one, so it trades a broken config for no config. Restore Arch Linux ARM's repositories there instead: [core] [extra] [alarm] [aur], its stock set, through a mirrorlist of its own because ALARM serves $arch/$repo where Arch serves $repo/os/$arch. [options] is kept byte-identical to the x86_64 channel configs. [omarchy] is left out until that tree exists; including it would 404 on every sync, and Omarchy's own packages simply hold at the versions the ISO installed. Three further things the restore has to do that the x86_64 path does not: - Take archlinuxarm-keyring while the offline mirror is still the active source. Arch's `base` pulls in archlinux-keyring and nothing pulls in this one, and the repositories being written are unreachable during an offline install. - Write the mirrorlist unconditionally. The target's mirrorlist carries Omarchy's and Arch's x86_64 mirrors ahead of ALARM's, so a sync logs 404s from mirror.omarchy.org, mirror.rackspace.com and geo.mirror.pkgbuild.com before it finds anything. - Populate the keyring. The install leaves ALARM's build key untrusted on the target, so the first signed install fails with "Arch Linux ARM Build System is unknown trust". x86_64 keeps running the same two lines it ran before, and every other architecture keeps the behaviour it had before the skip was introduced. --- default/pacman/mirrorlist-aarch64 | 6 ++++ default/pacman/pacman-aarch64.conf | 53 ++++++++++++++++++++++++++++++ install/post-install/pacman.sh | 51 ++++++++++++++-------------- 3 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 default/pacman/mirrorlist-aarch64 create mode 100644 default/pacman/pacman-aarch64.conf diff --git a/default/pacman/mirrorlist-aarch64 b/default/pacman/mirrorlist-aarch64 new file mode 100644 index 00000000000..3aebd0e77a3 --- /dev/null +++ b/default/pacman/mirrorlist-aarch64 @@ -0,0 +1,6 @@ +# Arch Linux ARM's geo-IP balanced mirror, as shipped by its own +# pacman-mirrorlist. Note the layout: ALARM serves $arch/$repo, where Arch (and +# so Omarchy's mirror of it) serves $repo/os/$arch, which is why the x86_64 +# mirrorlists cannot simply be repointed. HTTP is what ALARM publishes; package +# and database signatures are what integrity rests on either way. +Server = http://mirror.archlinuxarm.org/$arch/$repo diff --git a/default/pacman/pacman-aarch64.conf b/default/pacman/pacman-aarch64.conf new file mode 100644 index 00000000000..fd3ef87edff --- /dev/null +++ b/default/pacman/pacman-aarch64.conf @@ -0,0 +1,53 @@ +# See the pacman.conf(5) manpage for option and repository directives +# +# Package sources for an aarch64 install (see install/post-install/pacman.sh). +# Omarchy's pacman-.conf files describe an x86_64 machine: +# [core]/[extra]/[multilib] come from Omarchy's mirror of Arch, which builds +# x86_64 only, and [multilib] is 32-bit x86 libraries that exist on no ARM +# mirror at all. Arch Linux ARM supplies the equivalents here. +# +# [options] is kept byte-identical to the x86_64 channel configs so the two +# architectures behave the same; only the repository list differs. +# +# One file, not one per channel, because Arch Linux ARM has no channels and the +# only channel-varying section -- [omarchy] -- is absent below. Retire that +# when the Omarchy package repository serves aarch64: this then splits into +# pacman-{stable,rc,edge}-aarch64.conf exactly as the x86_64 files do. + +[options] +Color +ILoveCandy +VerbosePkgLists +HoldPkg = pacman glibc +Architecture = auto +CheckSpace +ParallelDownloads = 5 +DownloadUser = alpm + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional + +# pacman searches repositories in the order defined here. +# +# [core] [extra] [alarm] [aur] is Arch Linux ARM's stock set. [alarm] carries +# its ARM-specific packages; [aur] carries its builds of AUR-origin packages, +# which on x86_64 Omarchy ships from [omarchy] instead. Deviating from the +# distribution default needs a reason, and there isn't one yet. +[core] +Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +[alarm] +Include = /etc/pacman.d/mirrorlist + +[aur] +Include = /etc/pacman.d/mirrorlist + +# [omarchy] is deliberately absent: the Omarchy package repository serves no +# aarch64 tree, so including it makes every pacman -Sy fail on a 404. Omarchy's +# own packages stay at the versions the ISO installed until that tree exists; +# everything else updates normally. Restore the section when it does. diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index 463f8181545..6b909e5f1d6 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,31 +1,30 @@ # Configure pacman after package installation completes. Offline target package # installs use the live ISO's offline pacman.conf until this final restore. -case "$(uname -m)" in - aarch64) - # Omarchy's pacman.conf and mirrorlist describe an x86_64 Arch system: - # [multilib] is 32-bit x86 libraries and no ARM mirror carries it, and the - # mirrorlist points at Omarchy's mirror of Arch, which is x86_64 only. - # Arch Linux ARM also lays its tree out as $arch/$repo rather than Arch's - # $repo/os/$arch, so the mirrorlist cannot be reused with a different host. - # - # Derive the config from Omarchy's instead: drop [multilib], keep [omarchy] - # (its $arch placeholder resolves correctly), add Arch Linux ARM's own - # [alarm] and [aur] repositories, and leave the mirrorlist the distribution - # installed. Skipping this step is not an option: the live ISO's pacman.conf - # only knows the offline mirror, which does not exist on the installed system. - awk ' - /^\[multilib\]$/ { skip = 1; next } - /^\[/ { skip = 0 } - skip { next } - { print } - ' "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" >/etc/pacman.conf - printf '\n[alarm]\nInclude = /etc/pacman.d/mirrorlist\n\n[aur]\nInclude = /etc/pacman.d/mirrorlist\n' >>/etc/pacman.conf - ;; - *) - cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist - ;; -esac +# Omarchy's channel configs describe an x86_64 machine, so aarch64 is restored +# to Arch Linux ARM's repositories instead; leaving the ISO's offline config in +# place there would end the install with no package sources at all. +if [[ $(uname -m) == aarch64 ]]; then + # Still on the offline pacman.conf at this point, which is the only reachable + # source during an offline install -- so take the keyring package now, before + # the repositories below replace it. Arch's `base` pulls in archlinux-keyring + # and nothing pulls in this one; the ISO carries it for exactly this step. + omarchy-pkg-add archlinuxarm-keyring + + cp -f "$OMARCHY_PATH/default/pacman/pacman-aarch64.conf" /etc/pacman.conf + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-aarch64" /etc/pacman.d/mirrorlist + + # Arch Linux ARM signs its repositories with its own key, and the install + # leaves that key untrusted on the target: the first signed sync fails with + # "Arch Linux ARM Build System is unknown trust". + # --init is a no-op on an initialised keyring, and --populate without an + # argument locally signs every keyring installed -- archlinux for the `any` + # packages Arch builds, archlinuxarm for the rest. + pacman-key --init + pacman-key --populate +else + cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist +fi # Wait for CUPS to own the file, the way omarchy-settings does, so pacman does # not turn the override into a .pacnew during ISO package installation. From 2e6ebc3c0fd9c789cadfd502a30ba8621b5d3e40 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Fri, 28 Aug 2026 02:43:41 +0200 Subject: [PATCH 10/16] Address Snapdragon setup review feedback --- install/hardware/qualcomm/dtb-uki.sh | 35 ++++++++++--- install/hardware/qualcomm/firmware.sh | 13 +++-- test/shell.d/snapdragon-hardware-test.sh | 67 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 test/shell.d/snapdragon-hardware-test.sh diff --git a/install/hardware/qualcomm/dtb-uki.sh b/install/hardware/qualcomm/dtb-uki.sh index 94d30f176d3..ea83d7cb1f3 100644 --- a/install/hardware/qualcomm/dtb-uki.sh +++ b/install/hardware/qualcomm/dtb-uki.sh @@ -18,25 +18,48 @@ if omarchy-hw-qualcomm-soc; then omarchy-pkg-add systemd-ukify + dtb_dir=${OMARCHY_QUALCOMM_DTB_DIR:-/boot/dtbs/qcom} + uki_config=${OMARCHY_QUALCOMM_UKI_CONFIG:-/etc/kernel/uki.conf} dtbs=() - for dtb in /boot/dtbs/qcom/x1*.dtb /boot/dtbs/qcom/hamoa*.dtb \ - /boot/dtbs/qcom/glymur*.dtb /boot/dtbs/qcom/sc8280xp*.dtb; do + for dtb in "$dtb_dir"/x1*.dtb "$dtb_dir"/hamoa*.dtb \ + "$dtb_dir"/glymur*.dtb "$dtb_dir"/sc8280xp*.dtb; do if [[ -f $dtb && $dtb != *-el2.dtb ]]; then dtbs+=("$dtb") fi done if ((${#dtbs[@]} == 0)); then - echo "No Windows-on-ARM device trees under /boot/dtbs/qcom; the UKI gets no .dtbauto sections" >&2 + echo "No Windows-on-ARM device trees under $dtb_dir; the UKI gets no .dtbauto sections" >&2 else - mkdir -p /etc/kernel + managed_begin="# BEGIN OMARCHY QUALCOMM DEVICE TREES" + managed_end="# END OMARCHY QUALCOMM DEVICE TREES" + + mkdir -p "$(dirname "$uki_config")" + uki_tmp=$(mktemp "${uki_config}.XXXXXX") + + if [[ -f $uki_config ]]; then + cp -p "$uki_config" "$uki_tmp" + awk -v begin="$managed_begin" -v end="$managed_end" ' + $0 == begin { managed = 1; next } + managed && $0 == end { managed = 0; next } + !managed { print } + ' "$uki_config" >"$uki_tmp" + else + chmod 0644 "$uki_tmp" + fi + { + echo + echo "$managed_begin" echo "# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/dtb-uki.sh)." echo "# Device trees the systemd-stub picks from at boot by SMBIOS hardware id." echo "# Rerun 'omarchy apply hardware' after a kernel update adds new boards." echo "[UKI]" echo "DeviceTreeAuto=${dtbs[*]}" - } >/etc/kernel/uki.conf - echo "Listed ${#dtbs[@]} device trees in /etc/kernel/uki.conf" + echo "$managed_end" + } >>"$uki_tmp" + + mv -f "$uki_tmp" "$uki_config" + echo "Listed ${#dtbs[@]} device trees in $uki_config" fi fi diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index 317f5677164..dbde1eefb80 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -22,15 +22,18 @@ if omarchy-hw-qualcomm-soc; then qcom-firmware-extract --install --no-rebuild - mkdir -p /etc/modprobe.d - if lsblk -sno TRAN "$(findmnt -no SOURCE /)" 2>/dev/null | grep -q '^usb$'; then + modprobe_dir=${OMARCHY_QUALCOMM_MODPROBE_DIR:-/etc/modprobe.d} + root_source=$(findmnt -no SOURCE --nofsroot / 2>/dev/null || true) + + mkdir -p "$modprobe_dir" + if [[ -n $root_source ]] && lsblk -sno TRAN "$root_source" 2>/dev/null | grep -q '^usb$'; then # Root disk on USB. When the DSPs boot with real firmware the Type-C port # controller (pmic_glink/UCSI) takes over the ports and resets them, which # drops a root disk behind a USB-C port (observed on the HP EliteBook Ultra # G1q, JimmayVV/omarchy-iso#27). Keep the DSPs off on such installs: no # audio or battery reporting, but the system boots. RETIRE when the kernel # stops resetting an already-connected port (#27). - cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' + cat >"$modprobe_dir/qualcomm-adsp-nofw.conf" <<'CONF' # Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) # The root disk is attached over USB. Booting the DSPs hands the USB-C ports # to the port controller, which resets them and drops the root disk. Keep the @@ -40,7 +43,7 @@ if omarchy-hw-qualcomm-soc; then blacklist qcom_q6v5_pas CONF elif qcom-firmware-extract --list-missing | grep -q 'adsp'; then - cat >/etc/modprobe.d/qualcomm-adsp-nofw.conf <<'CONF' + cat >"$modprobe_dir/qualcomm-adsp-nofw.conf" <<'CONF' # Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) # No signed audio-DSP firmware is installed yet; the driver's failing probe # resets the USB-C mux and can take a USB-C root disk down with it. Removed @@ -48,6 +51,6 @@ CONF blacklist qcom_q6v5_pas CONF else - rm -f /etc/modprobe.d/qualcomm-adsp-nofw.conf + rm -f "$modprobe_dir/qualcomm-adsp-nofw.conf" fi fi diff --git a/test/shell.d/snapdragon-hardware-test.sh b/test/shell.d/snapdragon-hardware-test.sh new file mode 100644 index 00000000000..f227825fd26 --- /dev/null +++ b/test/shell.d/snapdragon-hardware-test.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +firmware_setup="$ROOT/install/hardware/qualcomm/firmware.sh" +dtb_setup="$ROOT/install/hardware/qualcomm/dtb-uki.sh" +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT + +bash -n "$firmware_setup" "$dtb_setup" || fail "Snapdragon hardware scripts have valid syntax" + +( + omarchy-hw-qualcomm-soc() { return 0; } + omarchy-pkg-add() { :; } + qcom-firmware-extract() { :; } + findmnt() { + [[ $* == "-no SOURCE --nofsroot /" ]] || fail "Snapdragon firmware setup strips the Btrfs subvolume suffix" + printf '/dev/mapper/root\n' + } + lsblk() { + [[ ${!#} == "/dev/mapper/root" ]] || fail "Snapdragon firmware setup passes a resolvable root device to lsblk" + printf 'usb\n' + } + + OMARCHY_QUALCOMM_MODPROBE_DIR="$scratch/modprobe.d" + source "$firmware_setup" +) + +[[ -f $scratch/modprobe.d/qualcomm-adsp-nofw.conf ]] || + fail "Snapdragon firmware setup protects a USB-backed root disk" + +mkdir -p "$scratch/dtbs" +: >"$scratch/dtbs/x1e80100-test.dtb" +: >"$scratch/dtbs/x1e80100-test-el2.dtb" +cat >"$scratch/uki.conf" <<'CONF' +[UKI] +SecureBootPrivateKey=/secure/db.key +PCRPrivateKey=/secure/pcr.key +CONF + +run_dtb_setup() ( + omarchy-hw-qualcomm-soc() { return 0; } + omarchy-pkg-add() { :; } + + OMARCHY_QUALCOMM_DTB_DIR="$scratch/dtbs" + OMARCHY_QUALCOMM_UKI_CONFIG="$scratch/uki.conf" + source "$dtb_setup" +) + +run_dtb_setup +run_dtb_setup + +grep -Fq 'SecureBootPrivateKey=/secure/db.key' "$scratch/uki.conf" || + fail "Snapdragon DTB setup preserves Secure Boot settings" +grep -Fq 'PCRPrivateKey=/secure/pcr.key' "$scratch/uki.conf" || + fail "Snapdragon DTB setup preserves PCR settings" +[[ $(grep -Fc '# BEGIN OMARCHY QUALCOMM DEVICE TREES' "$scratch/uki.conf") == 1 ]] || + fail "Snapdragon DTB setup keeps one managed UKI block" +grep -Fq "DeviceTreeAuto=$scratch/dtbs/x1e80100-test.dtb" "$scratch/uki.conf" || + fail "Snapdragon DTB setup lists the matching device tree" +if grep -Fq 'x1e80100-test-el2.dtb' "$scratch/uki.conf"; then + fail "Snapdragon DTB setup excludes EL2-only device trees" +fi + +pass "Snapdragon setup preserves UKI settings and detects USB root disks" From 2ca93eeaddb0ba79b6e350b40730a3d7178949b6 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Fri, 28 Aug 2026 12:52:46 +0200 Subject: [PATCH 11/16] Trim Snapdragon implementation comments --- bin/omarchy-hw-qualcomm-soc | 7 +--- default/pacman/mirrorlist-aarch64 | 6 +-- default/pacman/pacman-aarch64.conf | 25 +---------- etc/mkinitcpio.conf.d/thunderbolt_module.conf | 5 +-- install/hardware/qualcomm/dtb-uki.sh | 22 ++-------- install/hardware/qualcomm/firmware.sh | 41 ++++--------------- install/hardware/qualcomm/kernel-params.sh | 15 ++----- install/post-install/pacman.sh | 18 ++------ 8 files changed, 22 insertions(+), 117 deletions(-) diff --git a/bin/omarchy-hw-qualcomm-soc b/bin/omarchy-hw-qualcomm-soc index a32865e5612..c85c73b1e5e 100755 --- a/bin/omarchy-hw-qualcomm-soc +++ b/bin/omarchy-hw-qualcomm-soc @@ -1,10 +1,5 @@ #!/bin/bash -# omarchy:summary=Detect whether the computer runs on a Qualcomm SoC booted with its device tree (Snapdragon X and friends). +# omarchy:summary=Detect a Qualcomm SoC from the boot device tree. -# The Adreno GPU, the Wi-Fi and the rest of a Qualcomm SoC are platform devices, -# so neither lspci nor DMI can tell a Snapdragon laptop apart. The device tree -# can: every Qualcomm board's root node lists the SoC as a `qcom,` -# compatible, and it is there whenever the machine was booted with a device -# tree, which is how Omarchy boots Windows-on-ARM laptops. tr '\0' '\n' /dev/null | grep -q '^qcom,' diff --git a/default/pacman/mirrorlist-aarch64 b/default/pacman/mirrorlist-aarch64 index 3aebd0e77a3..c7185ed8f65 100644 --- a/default/pacman/mirrorlist-aarch64 +++ b/default/pacman/mirrorlist-aarch64 @@ -1,6 +1,2 @@ -# Arch Linux ARM's geo-IP balanced mirror, as shipped by its own -# pacman-mirrorlist. Note the layout: ALARM serves $arch/$repo, where Arch (and -# so Omarchy's mirror of it) serves $repo/os/$arch, which is why the x86_64 -# mirrorlists cannot simply be repointed. HTTP is what ALARM publishes; package -# and database signatures are what integrity rests on either way. +# Arch Linux ARM publishes this endpoint with a $arch/$repo layout. Server = http://mirror.archlinuxarm.org/$arch/$repo diff --git a/default/pacman/pacman-aarch64.conf b/default/pacman/pacman-aarch64.conf index fd3ef87edff..b11e10a47c7 100644 --- a/default/pacman/pacman-aarch64.conf +++ b/default/pacman/pacman-aarch64.conf @@ -1,18 +1,5 @@ # See the pacman.conf(5) manpage for option and repository directives -# -# Package sources for an aarch64 install (see install/post-install/pacman.sh). -# Omarchy's pacman-.conf files describe an x86_64 machine: -# [core]/[extra]/[multilib] come from Omarchy's mirror of Arch, which builds -# x86_64 only, and [multilib] is 32-bit x86 libraries that exist on no ARM -# mirror at all. Arch Linux ARM supplies the equivalents here. -# -# [options] is kept byte-identical to the x86_64 channel configs so the two -# architectures behave the same; only the repository list differs. -# -# One file, not one per channel, because Arch Linux ARM has no channels and the -# only channel-varying section -- [omarchy] -- is absent below. Retire that -# when the Omarchy package repository serves aarch64: this then splits into -# pacman-{stable,rc,edge}-aarch64.conf exactly as the x86_64 files do. +# Arch Linux ARM provides the aarch64 repositories used below. [options] Color @@ -30,11 +17,6 @@ SigLevel = Required DatabaseOptional LocalFileSigLevel = Optional # pacman searches repositories in the order defined here. -# -# [core] [extra] [alarm] [aur] is Arch Linux ARM's stock set. [alarm] carries -# its ARM-specific packages; [aur] carries its builds of AUR-origin packages, -# which on x86_64 Omarchy ships from [omarchy] instead. Deviating from the -# distribution default needs a reason, and there isn't one yet. [core] Include = /etc/pacman.d/mirrorlist @@ -46,8 +28,3 @@ Include = /etc/pacman.d/mirrorlist [aur] Include = /etc/pacman.d/mirrorlist - -# [omarchy] is deliberately absent: the Omarchy package repository serves no -# aarch64 tree, so including it makes every pacman -Sy fail on a 404. Omarchy's -# own packages stay at the versions the ISO installed until that tree exists; -# everything else updates normally. Restore the section when it does. diff --git a/etc/mkinitcpio.conf.d/thunderbolt_module.conf b/etc/mkinitcpio.conf.d/thunderbolt_module.conf index 2f145bb5534..eb0565bef59 100644 --- a/etc/mkinitcpio.conf.d/thunderbolt_module.conf +++ b/etc/mkinitcpio.conf.d/thunderbolt_module.conf @@ -1,7 +1,4 @@ -# Thunderbolt is x86-oriented hardware and the module is not built for every -# architecture -- ALARM's aarch64 kernel has no `thunderbolt`. mkinitcpio treats -# an unresolvable MODULES entry as an error, which fails every initramfs build -# on such a system, so gate it rather than forcing it unconditionally. +# linux-aarch64 does not provide the thunderbolt module. if [[ $(uname -m) == x86_64 ]]; then MODULES+=(thunderbolt) fi diff --git a/install/hardware/qualcomm/dtb-uki.sh b/install/hardware/qualcomm/dtb-uki.sh index ea83d7cb1f3..07bd8eb2005 100644 --- a/install/hardware/qualcomm/dtb-uki.sh +++ b/install/hardware/qualcomm/dtb-uki.sh @@ -1,19 +1,5 @@ -# Snapdragon laptops boot with the device tree that describes the exact model, -# and their Windows-on-ARM firmware provides none. A systemd-stub UKI can carry -# every candidate as a .dtbauto section and pick the right one at boot from the -# machine's SMBIOS hardware ids (the .hwids section ukify builds from systemd's -# tables). mkinitcpio hands ukify /etc/kernel/uki.conf when it exists, so listing -# the device trees there makes every UKI limine-mkinitcpio-hook builds pick its -# own tree, on this machine and after every kernel update. -# -# Limitation: DeviceTreeAuto= takes literal paths, not globs, so this is a -# snapshot of /boot/dtbs/qcom from when the leaf ran. A kernel update that adds -# a new board's tree needs `omarchy apply hardware` to refresh the list. Retire -# the enumeration when ukify accepts globs in DeviceTreeAuto=. -# -# Same prefixes as the Omarchy ISO's live UKI: Snapdragon X Elite/Plus (x1*), -# X2 (hamoa*, glymur*) and 8cx Gen 3 (sc8280xp*). The -el2 variants exist for -# running the kernel at EL2, which this firmware does not. +# Embed Qualcomm device trees so systemd-stub can select one from SMBIOS data. +# DeviceTreeAuto requires literal paths, so hardware setup refreshes the list. if omarchy-hw-qualcomm-soc; then omarchy-pkg-add systemd-ukify @@ -51,8 +37,8 @@ if omarchy-hw-qualcomm-soc; then { echo echo "$managed_begin" - echo "# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/dtb-uki.sh)." - echo "# Device trees the systemd-stub picks from at boot by SMBIOS hardware id." + echo "# Generated by Omarchy for Qualcomm Snapdragon devices." + echo "# systemd-stub selects a device tree from SMBIOS data." echo "# Rerun 'omarchy apply hardware' after a kernel update adds new boards." echo "[UKI]" echo "DeviceTreeAuto=${dtbs[*]}" diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index dbde1eefb80..a25a0a5792d 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -1,21 +1,5 @@ -# Firmware for the Qualcomm SoC. -# -# Arch Linux ARM splits the redistributable Qualcomm blobs out of -# linux-firmware into linux-firmware-qcom (Adreno GPU microcode, Wi-Fi and -# Bluetooth board data); without it the GPU and Wi-Fi never come up. -# -# The blobs signed by the laptop vendor (GPU zap shader, audio and compute DSP -# images) are not redistributable. qcom-firmware-extract copies the ones this -# machine's device tree names from the owner's own Windows installation: the -# ISO staged them before the disk was written, and a free-space install still -# has the Windows partition on disk. The installer builds the boot image once -# after every hardware leaf has run, so nothing is rebuilt here. -# -# Until the audio DSP firmware is present its driver cannot load; the failing -# probe resets the USB-C mux, which drops a root disk attached over USB-C and -# hangs the boot. Keep the driver blacklisted while that firmware is absent; -# the file is removed once the firmware is installed (re-run -# `sudo qcom-firmware-extract` after providing it). +# Install packaged Qualcomm firmware and vendor-signed blobs extracted from Windows. +# Keep the DSP driver disabled when starting it could disconnect the root disk. if omarchy-hw-qualcomm-soc; then omarchy-pkg-add linux-firmware-qcom qcom-firmware-extract @@ -27,27 +11,16 @@ if omarchy-hw-qualcomm-soc; then mkdir -p "$modprobe_dir" if [[ -n $root_source ]] && lsblk -sno TRAN "$root_source" 2>/dev/null | grep -q '^usb$'; then - # Root disk on USB. When the DSPs boot with real firmware the Type-C port - # controller (pmic_glink/UCSI) takes over the ports and resets them, which - # drops a root disk behind a USB-C port (observed on the HP EliteBook Ultra - # G1q, JimmayVV/omarchy-iso#27). Keep the DSPs off on such installs: no - # audio or battery reporting, but the system boots. RETIRE when the kernel - # stops resetting an already-connected port (#27). + # Starting the DSPs resets USB-C, so keep them off when root is on USB. cat >"$modprobe_dir/qualcomm-adsp-nofw.conf" <<'CONF' -# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) -# The root disk is attached over USB. Booting the DSPs hands the USB-C ports -# to the port controller, which resets them and drops the root disk. Keep the -# DSP driver off on this install (no audio or battery reporting) until the -# kernel stops resetting connected ports; delete this file after moving the -# system to an internal disk. +# Generated by Omarchy for Qualcomm Snapdragon devices. +# Keep the DSP driver off while the root disk is attached over USB. blacklist qcom_q6v5_pas CONF elif qcom-firmware-extract --list-missing | grep -q 'adsp'; then cat >"$modprobe_dir/qualcomm-adsp-nofw.conf" <<'CONF' -# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/firmware.sh) -# No signed audio-DSP firmware is installed yet; the driver's failing probe -# resets the USB-C mux and can take a USB-C root disk down with it. Removed -# by the same leaf once the firmware is present. +# Generated by Omarchy for Qualcomm Snapdragon devices. +# Keep the DSP driver off until its signed firmware is installed. blacklist qcom_q6v5_pas CONF else diff --git a/install/hardware/qualcomm/kernel-params.sh b/install/hardware/qualcomm/kernel-params.sh index c9dad320ea5..4536395f557 100644 --- a/install/hardware/qualcomm/kernel-params.sh +++ b/install/hardware/qualcomm/kernel-params.sh @@ -1,20 +1,13 @@ -# Kernel parameters Snapdragon laptops need under their Windows-on-ARM firmware: -# the set Fedora's Snapdragon images and archiso carry, measured on the HP -# EliteBook Ultra G1q (X1E-78-100). Retire each one when the kernel no longer -# needs it; Fedora's images are the tell. +# Add firmware workarounds required by current Snapdragon laptops. DROP_IN="/etc/limine-entry-tool.d/qualcomm-snapdragon.conf" if omarchy-hw-qualcomm-soc; then mkdir -p /etc/limine-entry-tool.d cat >"$DROP_IN" <<'CONF' -# Generated by Omarchy for Qualcomm Snapdragon devices (install/hardware/qualcomm/kernel-params.sh) -# clk_ignore_unused pd_ignore_unused -- display and I/O drivers load from the -# initramfs after the kernel would otherwise switch their clocks and power -# domains off; the screen goes dark at 15 s without them. -# arm64.nopauth -- pointer authentication faults under this firmware. -# systemd.tpm2_wait=0 -- the firmware advertises a TPM2 Linux never gets; -# without this, a 90 s wait for /dev/tpmrm0 on every boot. +# Generated by Omarchy for Qualcomm Snapdragon devices. +# Keep display and I/O resources enabled until their drivers load. +# Disable pointer authentication and waiting for an unavailable TPM. KERNEL_CMDLINE[default]+=" clk_ignore_unused pd_ignore_unused arm64.nopauth systemd.tpm2_wait=0" CONF fi diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index 6b909e5f1d6..427c5003ea8 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,24 +1,12 @@ -# Configure pacman after package installation completes. Offline target package -# installs use the live ISO's offline pacman.conf until this final restore. -# Omarchy's channel configs describe an x86_64 machine, so aarch64 is restored -# to Arch Linux ARM's repositories instead; leaving the ISO's offline config in -# place there would end the install with no package sources at all. +# Replace the installer's offline pacman configuration with online repositories. if [[ $(uname -m) == aarch64 ]]; then - # Still on the offline pacman.conf at this point, which is the only reachable - # source during an offline install -- so take the keyring package now, before - # the repositories below replace it. Arch's `base` pulls in archlinux-keyring - # and nothing pulls in this one; the ISO carries it for exactly this step. + # Install the keyring before replacing the offline package source. omarchy-pkg-add archlinuxarm-keyring cp -f "$OMARCHY_PATH/default/pacman/pacman-aarch64.conf" /etc/pacman.conf cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-aarch64" /etc/pacman.d/mirrorlist - # Arch Linux ARM signs its repositories with its own key, and the install - # leaves that key untrusted on the target: the first signed sync fails with - # "Arch Linux ARM Build System is unknown trust". - # --init is a no-op on an initialised keyring, and --populate without an - # argument locally signs every keyring installed -- archlinux for the `any` - # packages Arch builds, archlinuxarm for the rest. + # Trust every installed keyring before the first signed sync. pacman-key --init pacman-key --populate else From db105809e4938061a1a0b92a5613002f87a69215 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Fri, 4 Sep 2026 17:01:51 +0200 Subject: [PATCH 12/16] Keep missing Qualcomm firmware from aborting setup --- install/hardware/qualcomm/firmware.sh | 2 +- test/shell.d/snapdragon-hardware-test.sh | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index a25a0a5792d..f9a860d16c5 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -4,7 +4,7 @@ if omarchy-hw-qualcomm-soc; then omarchy-pkg-add linux-firmware-qcom qcom-firmware-extract - qcom-firmware-extract --install --no-rebuild + qcom-firmware-extract --install --no-rebuild || true modprobe_dir=${OMARCHY_QUALCOMM_MODPROBE_DIR:-/etc/modprobe.d} root_source=$(findmnt -no SOURCE --nofsroot / 2>/dev/null || true) diff --git a/test/shell.d/snapdragon-hardware-test.sh b/test/shell.d/snapdragon-hardware-test.sh index f227825fd26..52366e0b585 100644 --- a/test/shell.d/snapdragon-hardware-test.sh +++ b/test/shell.d/snapdragon-hardware-test.sh @@ -14,7 +14,13 @@ bash -n "$firmware_setup" "$dtb_setup" || fail "Snapdragon hardware scripts have ( omarchy-hw-qualcomm-soc() { return 0; } omarchy-pkg-add() { :; } - qcom-firmware-extract() { :; } + qcom-firmware-extract() { + if [[ $1 == "--install" ]]; then + return 1 + else + return 0 + fi + } findmnt() { [[ $* == "-no SOURCE --nofsroot /" ]] || fail "Snapdragon firmware setup strips the Btrfs subvolume suffix" printf '/dev/mapper/root\n' @@ -64,4 +70,4 @@ if grep -Fq 'x1e80100-test-el2.dtb' "$scratch/uki.conf"; then fail "Snapdragon DTB setup excludes EL2-only device trees" fi -pass "Snapdragon setup preserves UKI settings and detects USB root disks" +pass "Snapdragon setup tolerates missing firmware and preserves UKI settings" From 88ce320e30beba022f927be83ccd37dcb527edde Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Sun, 6 Sep 2026 00:55:11 +0200 Subject: [PATCH 13/16] Preserve safe Snapdragon setup on failed firmware inspection --- bin/omarchy-hw-qualcomm-soc | 3 ++- install/hardware/qualcomm/dtb-uki.sh | 1 - install/hardware/qualcomm/firmware.sh | 4 ++-- test/shell.d/snapdragon-hardware-test.sh | 24 ++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/bin/omarchy-hw-qualcomm-soc b/bin/omarchy-hw-qualcomm-soc index c85c73b1e5e..7bbe6c8b24b 100755 --- a/bin/omarchy-hw-qualcomm-soc +++ b/bin/omarchy-hw-qualcomm-soc @@ -2,4 +2,5 @@ # omarchy:summary=Detect a Qualcomm SoC from the boot device tree. -tr '\0' '\n' /dev/null | grep -q '^qcom,' +[[ -r /sys/firmware/devicetree/base/compatible ]] || exit 1 +tr '\0' '\n' /dev/null diff --git a/install/hardware/qualcomm/dtb-uki.sh b/install/hardware/qualcomm/dtb-uki.sh index 07bd8eb2005..a7728f8260b 100644 --- a/install/hardware/qualcomm/dtb-uki.sh +++ b/install/hardware/qualcomm/dtb-uki.sh @@ -35,7 +35,6 @@ if omarchy-hw-qualcomm-soc; then fi { - echo echo "$managed_begin" echo "# Generated by Omarchy for Qualcomm Snapdragon devices." echo "# systemd-stub selects a device tree from SMBIOS data." diff --git a/install/hardware/qualcomm/firmware.sh b/install/hardware/qualcomm/firmware.sh index f9a860d16c5..9c2b6cb6249 100644 --- a/install/hardware/qualcomm/firmware.sh +++ b/install/hardware/qualcomm/firmware.sh @@ -17,10 +17,10 @@ if omarchy-hw-qualcomm-soc; then # Keep the DSP driver off while the root disk is attached over USB. blacklist qcom_q6v5_pas CONF - elif qcom-firmware-extract --list-missing | grep -q 'adsp'; then + elif ! missing_firmware=$(qcom-firmware-extract --list-missing) || grep -q 'adsp' <<<"$missing_firmware"; then cat >"$modprobe_dir/qualcomm-adsp-nofw.conf" <<'CONF' # Generated by Omarchy for Qualcomm Snapdragon devices. -# Keep the DSP driver off until its signed firmware is installed. +# Keep the DSP driver off until its signed firmware can be verified. blacklist qcom_q6v5_pas CONF else diff --git a/test/shell.d/snapdragon-hardware-test.sh b/test/shell.d/snapdragon-hardware-test.sh index 52366e0b585..1f1da582553 100644 --- a/test/shell.d/snapdragon-hardware-test.sh +++ b/test/shell.d/snapdragon-hardware-test.sh @@ -37,6 +37,27 @@ bash -n "$firmware_setup" "$dtb_setup" || fail "Snapdragon hardware scripts have [[ -f $scratch/modprobe.d/qualcomm-adsp-nofw.conf ]] || fail "Snapdragon firmware setup protects a USB-backed root disk" +run_internal_firmware_setup() ( + omarchy-hw-qualcomm-soc() { return 0; } + omarchy-pkg-add() { :; } + findmnt() { printf '/dev/mapper/root\n'; } + lsblk() { printf 'nvme\n'; } + inspection_status=$1 + qcom-firmware-extract() { + [[ $1 == "--list-missing" ]] || return 0 + return "$inspection_status" + } + OMARCHY_QUALCOMM_MODPROBE_DIR="$scratch/modprobe.d" + source "$firmware_setup" +) + +run_internal_firmware_setup 1 +[[ -f $scratch/modprobe.d/qualcomm-adsp-nofw.conf ]] || + fail "Snapdragon setup keeps DSPs disabled when firmware inspection fails" +run_internal_firmware_setup 0 +[[ ! -f $scratch/modprobe.d/qualcomm-adsp-nofw.conf ]] || + fail "Snapdragon setup enables DSPs after firmware is verified on an internal root" + mkdir -p "$scratch/dtbs" : >"$scratch/dtbs/x1e80100-test.dtb" : >"$scratch/dtbs/x1e80100-test-el2.dtb" @@ -56,7 +77,10 @@ run_dtb_setup() ( ) run_dtb_setup +first_uki_config=$(<"$scratch/uki.conf") run_dtb_setup +[[ $(<"$scratch/uki.conf") == "$first_uki_config" ]] || + fail "Snapdragon DTB setup is idempotent" grep -Fq 'SecureBootPrivateKey=/secure/db.key' "$scratch/uki.conf" || fail "Snapdragon DTB setup preserves Secure Boot settings" From c1379b8693041120724cda424a85c9ad1bac2cec Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Sun, 6 Sep 2026 01:20:40 +0200 Subject: [PATCH 14/16] Retain the published Omarchy repository on ARM edge installs --- install/post-install/pacman.sh | 16 +++++++--- test/shell.d/pacman-aarch64-test.sh | 45 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/shell.d/pacman-aarch64-test.sh diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index 427c5003ea8..5c5cf2a10e3 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,17 +1,25 @@ # Replace the installer's offline pacman configuration with online repositories. +pacman_config=${OMARCHY_PACMAN_CONFIG:-/etc/pacman.conf} +mirrorlist=${OMARCHY_MIRRORLIST:-/etc/pacman.d/mirrorlist} + if [[ $(uname -m) == aarch64 ]]; then # Install the keyring before replacing the offline package source. omarchy-pkg-add archlinuxarm-keyring - cp -f "$OMARCHY_PATH/default/pacman/pacman-aarch64.conf" /etc/pacman.conf - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-aarch64" /etc/pacman.d/mirrorlist + cp -f "$OMARCHY_PATH/default/pacman/pacman-aarch64.conf" "$pacman_config" + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-aarch64" "$mirrorlist" + + # Only edge publishes an aarch64 Omarchy repository. + if [[ ${OMARCHY_MIRROR:-stable} == "edge" ]]; then + printf '\n[omarchy]\nServer = https://pkgs.omarchy.org/edge/$arch\n' >>"$pacman_config" + fi # Trust every installed keyring before the first signed sync. pacman-key --init pacman-key --populate else - cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" /etc/pacman.conf - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" /etc/pacman.d/mirrorlist + cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" "$pacman_config" + cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" "$mirrorlist" fi # Wait for CUPS to own the file, the way omarchy-settings does, so pacman does diff --git a/test/shell.d/pacman-aarch64-test.sh b/test/shell.d/pacman-aarch64-test.sh new file mode 100644 index 00000000000..7e11e6caac2 --- /dev/null +++ b/test/shell.d/pacman-aarch64-test.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT +mkdir -p "$scratch/source/default" "$scratch/install/hardware" +cp -r "$ROOT/default/pacman" "$scratch/source/default/" +printf ':\n' >"$scratch/install/hardware/pacman.sh" + +run_setup() ( + architecture=$1 + export OMARCHY_MIRROR=$2 + export OMARCHY_PATH="$scratch/source" OMARCHY_INSTALL="$scratch/install" + export OMARCHY_PACMAN_CONFIG="$scratch/pacman.conf" OMARCHY_MIRRORLIST="$scratch/mirrorlist" + uname() { printf '%s\n' "$architecture"; } + omarchy-pkg-add() { printf 'add %s\n' "$*" >>"$scratch/keys"; } + pacman-key() { printf 'key %s\n' "$*" >>"$scratch/keys"; } + source "$ROOT/install/post-install/pacman.sh" +) + +run_setup aarch64 edge +grep -Fxq 'Server = https://pkgs.omarchy.org/edge/$arch' "$scratch/pacman.conf" || + fail "ARM edge installations retain the published Omarchy repository" +grep -Fxq '[alarm]' "$scratch/pacman.conf" || fail "ARM installations retain ALARM repositories" +if grep -Fxq '[multilib]' "$scratch/pacman.conf"; then + fail "ARM installations do not inherit x86 multilib" +fi +grep -Fxq 'add archlinuxarm-keyring' "$scratch/keys" || fail "ALARM keyring is installed" +grep -Fxq 'key --populate' "$scratch/keys" || fail "installed keyrings are trusted" + +for mirror in stable rc; do + run_setup aarch64 "$mirror" + if grep -Fxq '[omarchy]' "$scratch/pacman.conf"; then + fail "ARM $mirror does not select an unpublished repository or switch to edge" + fi +done + +run_setup x86_64 stable +cmp "$ROOT/default/pacman/pacman-stable.conf" "$scratch/pacman.conf" || + fail "x86 repository configuration remains unchanged" + +pass "ARM edge retains Omarchy packages without switching other channels" From c10a306fae365dea628f5c92cac90b3d009aa8bd Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Sun, 6 Sep 2026 03:22:52 +0200 Subject: [PATCH 15/16] Syntax-check each Snapdragon setup script --- test/shell.d/snapdragon-hardware-test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/shell.d/snapdragon-hardware-test.sh b/test/shell.d/snapdragon-hardware-test.sh index 1f1da582553..7b294448a89 100644 --- a/test/shell.d/snapdragon-hardware-test.sh +++ b/test/shell.d/snapdragon-hardware-test.sh @@ -9,7 +9,9 @@ dtb_setup="$ROOT/install/hardware/qualcomm/dtb-uki.sh" scratch=$(mktemp -d) trap 'rm -rf "$scratch"' EXIT -bash -n "$firmware_setup" "$dtb_setup" || fail "Snapdragon hardware scripts have valid syntax" +for script in "$firmware_setup" "$dtb_setup"; do + bash -n "$script" || fail "Snapdragon hardware scripts have valid syntax" +done ( omarchy-hw-qualcomm-soc() { return 0; } From acf19b8ce3c39b4d29706fb77fc23887a1d18e93 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Sun, 6 Sep 2026 21:15:24 +0200 Subject: [PATCH 16/16] Preserve ARM repositories during pacman refresh --- bin/omarchy-refresh-pacman | 23 ++++++-- install/helpers/pacman.sh | 25 ++++++++ install/post-install/pacman.sh | 16 ++--- test/shell.d/pacman-aarch64-test.sh | 91 ++++++++++++++++++++++++++++- 4 files changed, 136 insertions(+), 19 deletions(-) create mode 100644 install/helpers/pacman.sh diff --git a/bin/omarchy-refresh-pacman b/bin/omarchy-refresh-pacman index 299d6c20d20..1f5921c7c6b 100755 --- a/bin/omarchy-refresh-pacman +++ b/bin/omarchy-refresh-pacman @@ -1,23 +1,34 @@ #!/bin/bash # omarchy:summary=Overwrite the package configuration for /etc/pacman with the Omarchy default of using its dedicated mirrors and repositories, then update all packages. +# omarchy:args=[stable|rc|edge] # omarchy:requires-sudo=true -sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak -sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak +set -e channel="${1:-stable}" +pacman_config=${OMARCHY_PACMAN_CONFIG:-/etc/pacman.conf} +mirrorlist=${OMARCHY_MIRRORLIST:-/etc/pacman.d/mirrorlist} -if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then - echo "Error: Invalid channel '$channel'. Must be one of: stable, rc, edge" +# Do not replace a working ARM repository setup with an unpublished channel. +if [[ $(uname -m) == "aarch64" && ( $channel == "stable" || $channel == "rc" ) ]]; then + echo "Omarchy $channel packages are not published for aarch64 yet. No repository configuration changed." >&2 + echo "Use 'omarchy refresh pacman edge' only if you intend to use edge packages and upgrade the system." >&2 exit 1 fi +staged=$(mktemp -d) +trap 'rm -rf -- "$staged"' EXIT +source "$OMARCHY_PATH/install/helpers/pacman.sh" +pacman_write_repository_config "$channel" "$staged/pacman.conf" "$staged/mirrorlist" + echo "Setting channel to $channel" echo -sudo cp -f "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" /etc/pacman.conf -sudo cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" /etc/pacman.d/mirrorlist +sudo cp -f "$pacman_config" "$pacman_config.bak" +sudo cp -f "$mirrorlist" "$mirrorlist.bak" +sudo cp -f "$staged/pacman.conf" "$pacman_config" +sudo cp -f "$staged/mirrorlist" "$mirrorlist" # Allow user customization of /etc/pacman.conf before the upgrade runs omarchy-hook pre-refresh-pacman diff --git a/install/helpers/pacman.sh b/install/helpers/pacman.sh new file mode 100644 index 00000000000..7198695a311 --- /dev/null +++ b/install/helpers/pacman.sh @@ -0,0 +1,25 @@ +# Write repository files for both offline-install finalization and refreshes. +# Callers choose the destination so refreshes can stage files before using sudo. +pacman_write_repository_config() { + local channel=$1 config=$2 mirrorlist=$3 + local defaults="$OMARCHY_PATH/default/pacman" + + if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then + echo "Error: Invalid channel '$channel'. Must be one of: stable, rc, edge" >&2 + return 1 + fi + + if [[ $(uname -m) == "aarch64" ]]; then + cp -f "$defaults/pacman-aarch64.conf" "$config" || return 1 + cp -f "$defaults/mirrorlist-aarch64" "$mirrorlist" || return 1 + + # Only edge publishes an aarch64 Omarchy repository. Offline stable/RC + # installs retain ALARM without silently opting into an experimental channel. + if [[ $channel == "edge" ]]; then + printf "\n[omarchy]\nServer = https://pkgs.omarchy.org/edge/\$arch\n" >>"$config" || return 1 + fi + else + cp -f "$defaults/pacman-$channel.conf" "$config" || return 1 + cp -f "$defaults/mirrorlist-$channel" "$mirrorlist" || return 1 + fi +} diff --git a/install/post-install/pacman.sh b/install/post-install/pacman.sh index 5c5cf2a10e3..97a8a1d012d 100644 --- a/install/post-install/pacman.sh +++ b/install/post-install/pacman.sh @@ -1,25 +1,19 @@ # Replace the installer's offline pacman configuration with online repositories. pacman_config=${OMARCHY_PACMAN_CONFIG:-/etc/pacman.conf} mirrorlist=${OMARCHY_MIRRORLIST:-/etc/pacman.d/mirrorlist} +source "$OMARCHY_PATH/install/helpers/pacman.sh" -if [[ $(uname -m) == aarch64 ]]; then +if [[ $(uname -m) == "aarch64" ]]; then # Install the keyring before replacing the offline package source. omarchy-pkg-add archlinuxarm-keyring +fi - cp -f "$OMARCHY_PATH/default/pacman/pacman-aarch64.conf" "$pacman_config" - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-aarch64" "$mirrorlist" - - # Only edge publishes an aarch64 Omarchy repository. - if [[ ${OMARCHY_MIRROR:-stable} == "edge" ]]; then - printf '\n[omarchy]\nServer = https://pkgs.omarchy.org/edge/$arch\n' >>"$pacman_config" - fi +pacman_write_repository_config "${OMARCHY_MIRROR:-stable}" "$pacman_config" "$mirrorlist" || return 1 +if [[ $(uname -m) == "aarch64" ]]; then # Trust every installed keyring before the first signed sync. pacman-key --init pacman-key --populate -else - cp -f "$OMARCHY_PATH/default/pacman/pacman-${OMARCHY_MIRROR:-stable}.conf" "$pacman_config" - cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-${OMARCHY_MIRROR:-stable}" "$mirrorlist" fi # Wait for CUPS to own the file, the way omarchy-settings does, so pacman does diff --git a/test/shell.d/pacman-aarch64-test.sh b/test/shell.d/pacman-aarch64-test.sh index 7e11e6caac2..c878051ad28 100644 --- a/test/shell.d/pacman-aarch64-test.sh +++ b/test/shell.d/pacman-aarch64-test.sh @@ -1,13 +1,17 @@ #!/bin/bash +# Each invocation has an isolated environment; exported stubs run in the child bash. +# shellcheck disable=SC2030,SC2031,SC2329 + set -euo pipefail source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" scratch=$(mktemp -d) trap 'rm -rf "$scratch"' EXIT -mkdir -p "$scratch/source/default" "$scratch/install/hardware" +mkdir -p "$scratch/source/default" "$scratch/source/install/helpers" "$scratch/install/hardware" cp -r "$ROOT/default/pacman" "$scratch/source/default/" +cp "$ROOT/install/helpers/pacman.sh" "$scratch/source/install/helpers/" printf ':\n' >"$scratch/install/hardware/pacman.sh" run_setup() ( @@ -22,7 +26,7 @@ run_setup() ( ) run_setup aarch64 edge -grep -Fxq 'Server = https://pkgs.omarchy.org/edge/$arch' "$scratch/pacman.conf" || +grep -Fxq "Server = https://pkgs.omarchy.org/edge/\$arch" "$scratch/pacman.conf" || fail "ARM edge installations retain the published Omarchy repository" grep -Fxq '[alarm]' "$scratch/pacman.conf" || fail "ARM installations retain ALARM repositories" if grep -Fxq '[multilib]' "$scratch/pacman.conf"; then @@ -43,3 +47,86 @@ cmp "$ROOT/default/pacman/pacman-stable.conf" "$scratch/pacman.conf" || fail "x86 repository configuration remains unchanged" pass "ARM edge retains Omarchy packages without switching other channels" + +run_refresh() ( + export OMARCHY_TEST_ARCH=$1 + shift + export OMARCHY_PATH="$scratch/source" + export OMARCHY_PACMAN_CONFIG="$scratch/pacman.conf" OMARCHY_MIRRORLIST="$scratch/mirrorlist" + export OMARCHY_TEST_LOG="$scratch/refresh.log" + : >"$OMARCHY_TEST_LOG" + + uname() { printf '%s\n' "$OMARCHY_TEST_ARCH"; } + sudo() { + printf '%s\n' "$*" >>"$OMARCHY_TEST_LOG" + if [[ $1 == "cp" ]]; then + [[ ${OMARCHY_TEST_COPY_FAIL:-0} != "1" ]] || return 1 + command "$@" + elif [[ $* == "env OMARCHY_UPDATE_PACMAN=1 pacman -Syyuu --noconfirm" ]]; then + # Deliberately never invoke pacman from this test. + grep -Fxq 'hook pre-refresh-pacman' "$OMARCHY_TEST_LOG" || return 1 + else + return 1 + fi + } + omarchy-hook() { printf 'hook %s\n' "$*" >>"$OMARCHY_TEST_LOG"; } + export -f uname sudo omarchy-hook + bash "$ROOT/bin/omarchy-refresh-pacman" "$@" +) + +run_setup aarch64 edge +cp "$scratch/pacman.conf" "$scratch/expected-arm.conf" +cp "$scratch/mirrorlist" "$scratch/expected-arm-mirrorlist" +printf 'original config\n' >"$scratch/pacman.conf" +printf 'original mirrors\n' >"$scratch/mirrorlist" +run_refresh aarch64 edge +cmp "$scratch/expected-arm.conf" "$scratch/pacman.conf" || fail "ARM refresh matches installer repositories" +cmp "$scratch/expected-arm-mirrorlist" "$scratch/mirrorlist" || fail "ARM refresh preserves ALARM mirror layout" +grep -Fxq 'original config' "$scratch/pacman.conf.bak" || fail "refresh backs up the previous configuration" +grep -Fxq 'original mirrors' "$scratch/mirrorlist.bak" || fail "refresh backs up the previous mirrors" +grep -Fxq 'env OMARCHY_UPDATE_PACMAN=1 pacman -Syyuu --noconfirm' "$scratch/refresh.log" || + fail "refresh retains the guarded upgrade after its customization hook" +run_refresh aarch64 edge +[[ $(grep -Fxc '[omarchy]' "$scratch/pacman.conf") == "1" ]] || fail "repeated refresh does not duplicate repositories" +pass "ARM refresh shares installer selection, backs up files and remains idempotent" + +for channel in stable rc invalid ''; do + args=() + [[ -z $channel ]] || args+=("$channel") + if run_refresh aarch64 "${args[@]}" >"$scratch/error.out" 2>&1; then + fail "ARM refresh rejects unpublished or invalid channels" + fi + [[ ! -s $scratch/refresh.log ]] || fail "rejected refresh does not invoke sudo, hooks or upgrades" + cmp "$scratch/expected-arm.conf" "$scratch/pacman.conf" || fail "rejected refresh keeps the current repository" + cmp "$scratch/expected-arm-mirrorlist" "$scratch/mirrorlist" || fail "rejected refresh keeps the current mirrors" + if [[ $channel != "invalid" ]]; then + grep -Fq 'No repository configuration changed' "$scratch/error.out" || fail "unpublished channel has an actionable error" + fi +done +pass "unpublished ARM channels never remove a working repository or silently select edge" + +for channel in stable rc edge; do + run_refresh x86_64 "$channel" + cmp "$ROOT/default/pacman/pacman-$channel.conf" "$scratch/pacman.conf" || fail "x86 $channel configuration is unchanged" + cmp "$ROOT/default/pacman/mirrorlist-$channel" "$scratch/mirrorlist" || fail "x86 $channel mirrors are unchanged" +done +pass "all x86 channel templates are preserved" + +cp "$scratch/pacman.conf" "$scratch/before-error.conf" +cp "$scratch/mirrorlist" "$scratch/before-error-mirrorlist" +mv "$scratch/source/default/pacman/mirrorlist-aarch64" "$scratch/source/default/pacman/mirrorlist-aarch64.saved" +if run_refresh aarch64 edge >"$scratch/error.out" 2>&1; then + fail "refresh stops when a template cannot be staged" +fi +[[ ! -s $scratch/refresh.log ]] || fail "missing templates never reach privileged writes or an upgrade" +cmp "$scratch/before-error.conf" "$scratch/pacman.conf" || fail "staging failure preserves configuration" +cmp "$scratch/before-error-mirrorlist" "$scratch/mirrorlist" || fail "staging failure preserves mirrors" +mv "$scratch/source/default/pacman/mirrorlist-aarch64.saved" "$scratch/source/default/pacman/mirrorlist-aarch64" + +if OMARCHY_TEST_COPY_FAIL=1 run_refresh aarch64 edge >"$scratch/error.out" 2>&1; then + fail "refresh stops when a privileged copy fails" +fi +if grep -Eq 'hook|pacman -S' "$scratch/refresh.log"; then + fail "failed privileged copies never reach hooks or upgrades" +fi +pass "failed staging or privileged copies stop before a system upgrade"