Boot and install Omarchy on Snapdragon X ARM64 systems - #129
Conversation
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.
There was a problem hiding this comment.
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.
|
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. |
|
Confirming provenance. The Snapdragon commits are mine and match |
|
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 |
|
Tested on an HP EliteBook Ultra G1q (Snapdragon X1E-78-100), linux-aarch64 7.2-2, from the ISO built on your 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 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. |
|
Tested successfully on an ASUS Vivobook S15 S5507QA-MA052W (Snapdragon X Elite X1E-78-100, DTB Encrypted full-disk install to internal NVMe:
Works: OLED (after GPU zap), Wi-Fi Gotchas that might help this PR:
Not working yet: speakers/mics (ADSP never started; also needs kernel Happy to pull more logs if useful. |
Summary
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:
/dev/mapper/root, with the expected@,@home,@log,@pkgand read-only@factorysubvolumescryptdevice=PARTUUID=...:root root=/dev/mapper/rooti2c-hid-of,i2c-hid,hid-multitouchand the Qualcomm DRM driverrunning, with no failed systemd unitsThat 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. Commitf263419adds 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
msminto 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.