Skip to content

Boot and install Omarchy on Snapdragon X ARM64 systems - #129

Open
birkskyum wants to merge 24 commits into
omacom:quattrofrom
birkskyum:upstream/snapdragon
Open

Boot and install Omarchy on Snapdragon X ARM64 systems#129
birkskyum wants to merge 24 commits into
omacom:quattrofrom
birkskyum:upstream/snapdragon

Conversation

@birkskyum

@birkskyum birkskyum commented Aug 27, 2026

Copy link
Copy Markdown

Summary

  • carries the host-architecture aarch64 ISO work from Build the ISO for the host architecture, adding aarch64 support #121
  • builds the aarch64 Omarchy package repository on a native GitHub ARM runner
  • builds the ISO from an Arch Linux ARM container and that local repository
  • creates a live UKI containing Windows-on-ARM device trees and lets firmware select the matching one
  • adds the kernel parameters and Qualcomm firmware needed by Snapdragon X laptops
  • stages vendor firmware from Windows before the selected disk is written

Provenance

The Snapdragon commits are authored by Jimmy Van Veen and come from JimmayVV/omarchy-iso@snapdragon. The prerequisite aarch64 build commits are authored by Sean and come from #121. Follow-up commits from this fork address issues found by native CI and physical installation while preserving the original commit authors.

Jimmy has confirmed the provenance and this upstream split in his PR comment.

Dependencies

The branch carries #121 so it can be built and tested end to end. It should be rebased to remove those commits after #121 merges.

Physical validation

Validated on a Lenovo Yoga Slim 7 14Q8X9 (83ED) with Snapdragon X Elite using the ISO from native ARM run 33119220270:

  • the firmware-selected live UKI boots the installer at the panel's native resolution
  • full-disk installation creates a 951.9 GB LUKS2 partition with an Argon2id passphrase keyslot
  • the installed root is Btrfs on /dev/mapper/root, with the expected @, @home, @log, @pkg and read-only @factory subvolumes
  • Limine boots the installed aarch64 UKI with cryptdevice=PARTUUID=...:root root=/dev/mapper/root
  • the installed initramfs contains i2c-hid-of, i2c-hid, hid-multitouch and the Qualcomm DRM driver
  • the built-in keyboard accepts the LUKS passphrase and the system reaches an active graphical Omarchy login
  • the installed system reports running, with no failed systemd units

That physical run exposed one ISO closure bug after pacstrap: Omarchy's ARM post-install hook installs archlinuxarm-keyring, but the package was absent from the offline mirror. Commit f263419 adds it to the ARM profile and adds a post-prune build assertion so this cannot regress silently. The corrected native ARM build passed package construction, ISO construction, ISO inspection and artifact upload in run 33128799299.

Known limitation

The LUKS prompt is functional but invisible on this Yoga. The built-in keyboard works and blind passphrase entry boots successfully, but the Snapdragon DRM framebuffer appears only after the encrypted root is unlocked. Forcing msm into the early module list was tested physically and did not make the prompt visible, so that unproven workaround is not included here.

The encrypted-install functionality and corrected native ARM build are proven. This PR is ready for review.

oceanapplications and others added 18 commits August 24, 2026 21:44
builder/build-iso.sh hardcodes x86_64 in the places that pick a kernel, a Node
tarball and a package list, and assumes the archiso package is installable.
Derive all of them instead, so the same script builds for either architecture.

Architecture selection:

  - one case statement sets ISO_ARCH / ISO_NODE_ARCH / ISO_KERNEL from uname -m;
    everything downstream uses those rather than a literal
  - packages.$ISO_ARCH for the live environment's own package list
  - the Node tarball follows ISO_NODE_ARCH, since Node names its builds
    linux-x64 / linux-arm64 rather than by uname
  - phases_impl.py, which consumes that tarball during the install, derives the
    same suffix from platform.machine(). Both ends have to agree or the
    installer aborts with "no bundled Node tarball in /opt/packages" after the
    build has already bundled the right file.
  - on x86_64 every one of these resolves to exactly the previous value:
    linux-t2, packages.x86_64, linux-x64.tar.gz

Host portability, not architecture-specific:

  - prefer the archiso package, and fall back to the vendored submodule when the
    repository has no archiso (Arch Linux ARM does not). Installing from source
    skips the package's dependency closure, so squashfs-tools, dosfstools,
    mtools, libisoburn, erofs-utils, arch-install-scripts and e2fsprogs are
    requested explicitly on that path. Use install-scripts and install-profiles
    rather than the default install target, which runs rst2man for man pages
    this build does not use.

  - match both .pkg.tar.zst and .pkg.tar.xz when indexing the offline mirror.
    Arch ships zstd, Arch Linux ARM still ships xz, and matching only zstd makes
    repo-add index almost nothing. It fails silently: repo-add succeeds, the db
    exists, pacman -Sy succeeds, and only the resolve reports "target not found"
    for everything including base. Both extensions are enumerated rather than
    globbed so .sig files are not swept in.

aarch64 package selection, gated on uname -m:

  - OMARCHY_ARCH_DROP plus a filter, since pacman -Syw aborts the whole
    transaction on the first missing target. Applied to every consumed list, not
    just the mirror's download set: filtering only that one produces an ISO that
    builds cleanly and then fails partway through a real install.
  - quickshell-git and mise substituted for their packaged equivalents.

Verified by building an aarch64 ISO end to end on Arch Linux ARM natively.
mkarchiso reads packages.$arch from the profile directory. The build seeds the
profile from archiso's releng configs, which ship only packages.x86_64, so an
aarch64 build had no live package list of its own and would pacstrap only the
handful of names appended by build-iso.sh.

Derived from releng's packages.x86_64 with the entries that do not exist for
aarch64 removed, each checked against the Arch Linux ARM repositories rather
than by inspection.
customize_airootfs.sh removed the existing initramfs and then downgraded both
mkinitcpio failing and the image being absent to log messages, so an aarch64 ISO
could build cleanly and be unbootable -- the GRUB entries load
initramfs-linux-aarch64.img by name.

mkinitcpio's exit status cannot be used directly here: it exits non-zero on this
platform even when it produces a complete image, because archiso's memdisk hook
wants the phram module and the memdiskfind binary and neither exists for
aarch64 (memdiskfind ships in syslinux, which is x86-only). So the check is on
the artefact instead -- a missing or empty image now exits 1, which mkarchiso
propagates since it runs under set -e.

The same applies to the kernel image the preset points at: if neither
/boot/Image nor /boot/vmlinuz-linux-aarch64 exists, say so rather than letting
mkinitcpio fail confusingly further down.
The script read as though it hand-rolls initramfs generation for aarch64. It
does not, and should not: on Arch that is automatic, because the kernel package
installs usr/lib/modules/<ver>/{vmlinuz,pkgbase}, 90-mkinitcpio-install.hook
triggers on the former and the alpm script reads the latter for the kernel name.
Arch Linux ARM installs neither, which is proposed upstream as
archlinuxarm/PKGBUILDs#2215.

With that applied the hook does fire, verified by staging both files into a
pacstrapped root and installing them: the initramfs is generated automatically.
Two naming gaps remain, and closing those is this script's actual job:

  - ALARM's preset writes /boot/initramfs-linux.img while the GRUB entries load
    initramfs-linux-aarch64.img
  - nothing creates /boot/vmlinuz-*, which is the glob mkarchiso copies the
    kernel from

Also records why the deprecated customize_airootfs.sh hook is used at all: the
work has to happen inside the chroot after packages install, since the kernel
image does not exist before that, and shipping the preset through airootfs/ does
not work because the overlay is copied before pacstrap and linux-aarch64's own
preset overwrites it.

Comments only; no behaviour change.
archiso hardcodes a GRUB module list taken from an x86 bug report. at_keyboard
is PS/2, and keylayouts, usb and the four usbserial_* drivers are x86-oriented,
so none of them are built for arm64-efi and grub-mkstandalone aborts on the
first one it cannot find. Without this an aarch64 ISO cannot be built at all.

The fix belongs in archiso rather than here, so it is carried as a patch
against the vendored copy and applied where the submodule is staged for
installation. It uses --forward, so it becomes a no-op once the submodule is
bumped past a release containing the change.

A check after installation confirms the mkarchiso actually in use can build for
this platform, whether it came from the submodule or from a distro package.
Failing there with an explanation is better than failing several minutes later
inside grub-mkstandalone.

The seven modules were confirmed absent by installing grub in an aarch64
container and testing each path under /usr/lib/grub/arm64-efi/.
build-iso.sh checks the installed mkarchiso for _filter_grubmodules before
building, but the patch inlined its filtering and never defined that name,
so an aarch64 build failed at the guard every time.

Move the filtering into a _filter_grubmodules helper that build-iso.sh can
find. Its diagnostic goes to stderr, since stdout carries the module list.
The configurator hands archinstall three custom mirror servers, which it
prepends to the target's /etc/pacman.d/mirrorlist. All three are Arch x86_64
mirrors laid out as $repo/os/$arch. Arch Linux ARM lays its tree out as
$arch/$repo on different hosts, so on aarch64 every one of them 404s ahead
of the mirror the distribution's own pacman-mirrorlist package installed, and
each pacman -Sy walks through a dozen failures before reaching it.

Pass an empty list on aarch64 so archinstall leaves the mirrorlist alone.
The x86_64 rendering is byte-identical to before.
repo-add was handed both *.pkg.tar.zst and *.pkg.tar.xz as inline globs. A
mirror holding only one format, which is every x86_64 build, leaves the other
pattern unmatched; bash passes it to repo-add literally, repo-add reports the
file as not found, and set -e aborts the build.

Collect the existing files first, as build-omarchy-packages.sh already does,
and fail with a clear message if there are none.
…chy] repo

bin/omarchy-iso-make ran archlinux/archlinux:latest, whose manifest is
amd64-only; on an aarch64 host use menci/archlinuxarm:base-devel instead.
x86_64 keeps the same image as before.

Add --local-repo <dir>: mounts a built [omarchy] repository at /omarchy-repo
and build-iso.sh rewrites the repo's Server line to file:///omarchy-repo.
pkgs.omarchy.org serves no aarch64 tree, so this is how the aarch64 ISO
gets omarchy's own packages (built by omarchy-pkgs bin/repo build --arch
aarch64).

Carry builder/patches/archiso-grub-modules-arm64.patch: archiso v87
hardcodes seven GRUB modules that have no arm64-efi build, so
grub-mkstandalone aborts. build-iso.sh applies builder/patches/archiso-*.patch
to the vendored submodule copy on aarch64 only; the patch header names its
retirement condition.

Populate the archlinuxarm keyring after pacman-key --init when the
container ships it, so ALARM's signed databases verify.
Twin of nightly-build.yml: builds the aarch64 [omarchy] repo through
omarchy-pkgs' reusable workflow, then the ISO on ubuntu-24.04-arm with
--local-repo, and uploads the ISO as an artifact. The x86_64 workflow is
untouched.
Windows-on-ARM laptops boot with ACPI tables Linux cannot drive the SoC
from, and their firmware hands over no device tree. Wrap the live kernel
and initramfs into a systemd-stub UKI that carries every Windows-on-ARM
device tree linux-aarch64 ships as .dtbauto sections plus systemd's
SMBIOS-ID database as .hwids; the stub picks the board's tree at boot.
No board is named anywhere (JimmayVV#12).

- configs/aarch64/live-uki.sh builds it at the end of customize_airootfs.sh,
  the only point where the kernel exists and /boot is not yet emptied.
- builder/patches/archiso-copy-boot-efi.patch copies /boot/*.efi into the
  ISO next to the kernel; mkarchiso only knows vmlinuz-* and initramfs-*.
- configs/grub/grub.cfg gains an arm64-only default entry that chainloads
  it with the archiso arguments as EFI load options (GRUB's `linux`
  rejects a stub-wrapped image on arm64); the plain entry stays as the
  fallback. The x86_64 menu is unchanged.
- systemd-ukify joins configs/packages.aarch64.
…gon X

Measured on the HP EliteBook Ultra G1q (JimmayVV#10): with the
plain archiso command line the panel goes dark at 15 s (clocks and power
domains switched off under drivers still loading), systemd waits 90 s for a
TPM the firmware only advertises, and the ADSP driver drops the USB boot
stick as it resets the Type-C mux. Fedora's Snapdragon guide carries the same
set; each parameter's reason and retirement condition is next to it.

linux-firmware-qcom joins packages.aarch64: Arch Linux ARM splits the GPU
microcode and Qualcomm board data out of linux-firmware.
Arch Linux ARM's kernel ships no pkgbase, so limine-mkinitcpio-hook builds no
UKI for it; the shim from omarchy-pkgs writes the file from a pacman hook.
The orchestrator installs it with the early bootstrap set on aarch64 only,
so the x86_64 install is unchanged. Both packages ride in packages.aarch64
because that list also feeds the offline mirror, and the omarchy repo's own
package lists cannot name aarch64-only packages: the x86_64 build downloads
every entry and aborts on a missing one.
qcom-firmware-extract (omarchy-pkgs) joins the aarch64 live root and the
offline mirror. The configurator runs it right after disk selection and
tells the user what was saved; the orchestrator's prepare_live runs it again
(idempotent) for deferred installs, and _prepare_target_setup copies the
stage into <target>/var/lib/omarchy/firmware-stage for omarchy's
install/hardware/qualcomm/firmware.sh. Both call sites are gated on the tool
being present, so the x86_64 ISO is unchanged.
Copilot AI lite review requested due to automatic review settings August 27, 2026 21:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds end-to-end support for building and booting the Omarchy installer ISO on aarch64 (targeting Snapdragon X/Windows-on-ARM laptops), including ARM-specific boot flow (UKI + DTBs), package/mirror handling for Arch Linux ARM, and native ARM CI.

Changes:

  • Make ISO build and installer logic architecture-aware (aarch64 vs x86_64), including Limine EFI binary selection and Node artifact selection.
  • Add Snapdragon/Windows-on-ARM boot and firmware handling (UKI with DTBs + staged vendor firmware extraction).
  • Add a native GitHub Actions aarch64 build workflow and update offline-mirror handling for .pkg.tar.{zst,xz}.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
configs/profiledef.sh Select aarch64 bootmodes and airootfs compression; add aarch64-only staged file permissions.
configs/packages.aarch64 New aarch64 live environment package list.
configs/grub/grub.cfg Add arm64 default entry that chainloads a UKI and Snapdragon kernel params.
configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py Limine EFI naming per-arch; stage/copy Qualcomm firmware; Node tarball selection per-arch.
configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py Default Limine EFI binary per architecture.
configs/airootfs/root/configurator Per-arch Limine binary; aarch64 mirror server handling; kernel detection for linux-aarch64; firmware staging integration.
configs/airootfs/root/.automated_script.sh Warm offline mirror for both .pkg.tar.zst and .pkg.tar.xz.
configs/aarch64/zz-aarch64-live.conf Ensure live initramfs HOOKS include archiso; drop thunderbolt module for aarch64.
configs/aarch64/live-uki.sh Build a live UKI embedding DTBs + SMBIOS hwids for Windows-on-ARM.
configs/aarch64/linux.preset No-op preset to avoid mkinitcpio failures on missing /boot/vmlinuz-linux.
configs/aarch64/customize_airootfs.sh Reconcile ALARM kernel naming, build live initramfs, and build UKI.
builder/patches/archiso-grubmodules.patch Patch archiso to filter unavailable GRUB modules on arm64-efi.
builder/patches/archiso-copy-boot-efi.patch Patch archiso to copy /boot/*.efi into the ISO (needed for the UKI).
builder/build-omarchy-packages.sh Include .pkg.tar.xz artifacts when building/copying packages.
builder/build-iso.sh Host-arch selection; ALARM keyring handling; pacman.conf filtering; aarch64 package filtering/substitution; Node arch selection.
bin/omarchy-iso-make Add --local-repo; choose Arch vs ALARM container image by host arch.
.github/workflows/aarch64-build.yml New native ARM CI workflow building pkgs then ISO on arm64 runner.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread builder/build-iso.sh
Comment thread builder/build-iso.sh
Comment thread configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py
Comment thread configs/packages.aarch64 Outdated
@birkskyum

birkskyum commented Aug 27, 2026

Copy link
Copy Markdown
Author

Package dependencies are now split and upstream-visible: omacom/omarchy-pkgs#223 provides the reusable native ARM repository artifact, omacom/omarchy-pkgs#222 provides the Quattro Limine aarch64 build, and omacom/omarchy-pkgs#221 provides safe Qualcomm firmware extraction. This branch now points at the eventual upstream master workflow and declares its own exact ISO package set; the fork-specific integration wiring remains only on the test branch.

@JimmayVV

JimmayVV commented Aug 27, 2026

Copy link
Copy Markdown

Confirming provenance. The Snapdragon commits are mine and match JimmayVV/omarchy-iso@snapdragon, and I'm happy for this to be the upstream path. Tested on the HP EliteBook Ultra G1q, details on omacom/omarchy#8672.

@birkskyum

Copy link
Copy Markdown
Author

The corrected native ARM validation is green: https://github.com/birkskyum/omarchy-iso/actions/runs/33128799299

The run built the aarch64 package repository, retained and verified archlinuxarm-keyring in the offline closure, built and inspected the ISO, and uploaded the artifact. Combined with the physical full-disk LUKS2 installation on the Yoga Slim 7x documented in the PR body, the review gate is satisfied.

@birkskyum
birkskyum marked this pull request as ready for review August 28, 2026 00:46
@JimmayVV

Copy link
Copy Markdown

Tested on an HP EliteBook Ultra G1q (Snapdragon X1E-78-100), linux-aarch64 7.2-2, from the ISO built on your integration/encrypted-yoga branch at 5c3bce4. Unencrypted install to an external NVMe on USB-A.

Works: boots via Limine into the UKI with the G1q device tree selected; display at native resolution with GPU acceleration; Wi-Fi (WCN7850, ath12k); keyboard, touchpad, touchscreen; battery reporting; Arch Linux ARM package sources configured and pacman -Sy working on first boot; zero failed units. Deep suspend and resume also works: mem_sleep was [deep], 60 seconds asleep, woke on lid open, Wi-Fi reconnected, root on USB survived.

Not yet: audio (no card registers; that's the topology/UCM follow-up I mentioned) and the function-row media keys. I haven't run the encrypted path on this machine yet.

So that's two machines for the stack, both X1E.

@HurlyDesousa

Copy link
Copy Markdown

Tested successfully on an ASUS Vivobook S15 S5507QA-MA052W (Snapdragon X Elite X1E-78-100, DTB x1e80100-asus-vivobook-s15). ISO was omarchy-iso-aarch64-5 from https://github.com/birkskyum/omarchy-iso/actions/runs/33128799299 (integration/encrypted-yoga), same native ARM build cited above.

Encrypted full-disk install to internal NVMe:

  • ESP nvme0n1p1, LUKS2 root nvme0n1p2 mapper root
  • Btrfs @ /, @home, @log, @pkg, @factory
  • Limine UKI /boot/EFI/Linux/omarchy_linux-aarch64.efi
  • Keyboard German QWERTZ, hid-over-i2c 0B05:4543; touchpad 093A:3016

Works: OLED (after GPU zap), Wi-Fi ath12k, Bluetooth, keyboard, touchpad, SSH, LUKS + Btrfs, graphical session.

Gotchas that might help this PR:

  1. After the Windows partition was wiped, firmware.sh / qcom-firmware-extract --install --no-rebuild aborted the ISO install under set -e (no DT blobs). Making that step non-fatal let the install finish.
  2. First disk boot was a black panel: missing qcom/x1e80100/ASUSTeK/vivobook-s15/qcdxkmsuc8380.mbn (dmesg: gpu hw init failed: -2). Temporary fix was copying the Lenovo 21N1 zap to that path, then replacing it with the real ASUS zap from official S5507QA Qualcomm BSP V1.318.7800.0 (SHA-256 7D8FA75780FE2A2FD90882AC7C4DCCA21152BA3CD4EB33183BFA6C72F4F20801). Do not copy extra Lenovo DSP firmware onto this board.
  3. LUKS prompt was a blank OLED with quiet splash (keyboard still worked, same class as the Yoga note). Stripping those and adding plymouth.enable=0 loglevel=7 made it visible.
  4. pkgs.omarchy.org has no aarch64 tree (404), as already noted for Build the ISO for the host architecture, adding aarch64 support #121.

Not working yet: speakers/mics (ADSP never started; also needs kernel CONFIG_RESET_GPIO and sound DT), battery percent (qcom-battmgr-bat capacity EAGAIN without ADSP + battmgr.jsn), webcam (no camera / CCI / CSIPHY nodes in the Vivobook DTS). IRIS firmware is in the BSP but CONFIG_VIDEO_QCOM_IRIS is not set.

Happy to pull more logs if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants