Repository navigation
Build one aarch64 image for every board - #258
Merged
Merged
Conversation
An aarch64 build had to be an N1x build: its own kernel, a package bundle on disk and local source checkouts. None of that can be built or booted by CI, and nothing else can boot the result. --platform generic builds the same image without anything board-specific: Arch Linux ARM's linux-aarch64, the published edge packages, no bundle and no NVIDIA stack. It boots machines whose firmware describes their hardware, which is what a VM is, so it is the image CI can install and test, and the base a board's kernel and packages go on top of. linux-aarch64 owns its mkinitcpio preset and names its image /boot/Image, so the live kernel name, preset and initramfs are made after pacstrap, inside the image (builder/linux-aarch64-live.sh). The approach is the dragon branch's customize_airootfs.sh. The generic image keeps both consoles, serial and panel. The N1x build is unchanged.
No aarch64 ISO has been built by CI. This builds the generic one natively on every push to this branch, checks that it carries the boot loader, the kernel, the live initramfs and the root image, and keeps it as an artifact for three days. The unit tests run beside it. The push trigger is for the branch: a manual run needs the workflow on the default branch first.
The runner's Python ships a "test" package. Discovery from the repository root, as test/all does it, then looks for test.unit inside that package and finds nothing.
Thirteen of the installer's tests stage a Node tarball named linux-x64, and the code they exercise looks for the host's architecture, so they fail on any aarch64 host. quattro runs them on x86; so does this.
On plain Ubuntu four tests fail for reasons that are the runner's, not the code's: the keyboard tests need Arch's keymaps behind localectl, and two package-signing tests meet a Python whose Path.is_file raises on a directory it may not enter. quattro's unit-test.yml runs ./test/all in an Arch container with systemd for exactly this; this is that step, plus the tools the aarch64 build test uses.
The aarch64 build test compares the dev key it wrote with cmp, which the Arch base image does not carry.
The build downloads 3.3 GB of packages into ~/.cache/omarchy on every run and prunes what it no longer needs. On Namespace that directory can live on a cache volume. The step is skipped, not failed, on a runner profile that has none.
A built ISO says nothing about whether it starts. This boots it the way a machine would, on one of Namespace's Apple-backed runners, the ARM ones with KVM: UEFI firmware, GRUB from the ISO, the live kernel and initramfs, the live root. It passes when the live system offers a login on its serial port, and keeps the serial log and a picture of the screen either way. The machine is virt with gic-version=3, its=off and acpi=off: with the interrupt translation service on, MSIs are lost under nested KVM there and virtio disks hang.
The generic aarch64 ISO started its kernel and initramfs and stopped there: mount: /run/archiso/airootfs: fsconfig() failed: Filesystem uses "zstd" compression. This is not supported. Arch Linux ARM builds linux-aarch64's squashfs without zstd. Of what that kernel reads, lz4 keeps what zstd was chosen for, fast decompression on the page-fault path. Repacked that way on the runner, the same ISO offered a login 20 s after power-on. The root image grows from 4,509 MB to 4,814 MB. Other kernels keep zstd.
An unattended install of the generic ISO stopped at its last step: "Error: bundled Node.js tarball missing from /opt/packages". The ISO carries Node for its own architecture; quattro's install/user/mise-work.sh looks for the x64 one on every architecture. The n1x branch of omarchy picks by uname -m, which is why the N1x image builds its packages from --local-source. The generic image now does the same, from the n1x branches of omarchy and omarchy-pkgs. The ISO also goes to Namespace's artifact store for the jobs that boot it. GitHub's store took 3 min 20 s to hand 4.5 GB back to a Namespace runner; it stays as the fallback and for people.
The whole path with nobody at the keyboard, on a Namespace runner with KVM: the installer takes its answers from a cidata drive, writes the disk, and the machine restarts into the installed system, which is then checked over SSH. The answers are the x86_64 integration harness's own (build_cidata), changed where the architecture shows: Limine's binary, the kernel, the mirror. Checked on a runner by hand before this: the install runs to its last step and writes a Limine entry and a unified kernel image for linux-aarch64, and after a reset the firmware starts Limine from the disk.
An unattended install of the generic aarch64 image did everything and then stopped at its last check: "Validating boot setup failed: no installed kernel found in target". The installer finds installed kernels by the pkgbase file Arch's kernel packages leave next to their modules. Arch Linux ARM's linux-aarch64 leaves none, though its modules, headers and Limine's unified kernel image were all in place. A kernel's module directory without a pkgbase is now named by the kernel the image was built to install. modules.builtin marks a kernel's own directory, so a leftover of out-of-tree modules is still not a kernel, and kernels that do record a pkgbase are found exactly as before.
With the kernel recognised, the same install stopped one check later: "/boot/EFI/Linux/omarchy_linux-aarch64.efi missing or empty". The Omarchy settings add thunderbolt to the initramfs. linux-aarch64 has no such module, mkinitcpio reports a name it cannot find as an error, and limine-mkinitcpio-hook then builds the unified kernel image and does not install it: "mkinitcpio failed for kernel 7.2.9-1-aarch64-ARCH, skipping". Before the boot image is built, the installer now asks modinfo which of the listed modules the installed kernel has, and writes a drop-in that sorts last and takes the others back out. With this on the runner, the install finished, the machine restarted into the installed system, and Hyprland was running with no failed unit.
The test waited out its whole timeout on an installer that had stopped after two minutes, and said nothing about why. It now has a way in to the live system (the live ISO runs cloud-init, so the cidata drive hands root a key), fails as soon as the installer's log says it halted, and prints the end of that log. It also checks that no unit failed, that Hyprland is running, and that the installed system's packages come from Omarchy's mirror and nowhere else.
The install test's last failing check is the installed system's mirrorlist. The Omarchy runtime writes it from default/pacman/mirrorlist-aarch64, which on n1x names Arch Linux ARM's own mirror. omarchy's arm64-generic branch is n1x plus that one line changed to arm-mirror.omarchy.org. The workflow builds from it until the line lands in n1x.
The aarch64 build test runs ssh-keygen, patch and cmp. quattro's unit-test.yml installs none of them, so it stopped at 'ssh-keygen: command not found' on every pull request into n1x. With that fixed, arm64-iso.yml no longer needs its own copy of the unit job.
* Validate Linux boot entries when Limine disables UKIs * Provide matching kernel headers in boot validation fixtures
They write an x86_64 Limine binary, and the name checked follows the host, so the tests failed on an aarch64 machine.
The file permission check used an associative array and grep -P, which bash 3.2 and BSD grep do not have, so an ARM image could not be built from a Mac. It reads the declared paths with sed and looks each one up with grep -F.
The build asked which board an aarch64 image was for, so each platform would get an image of its own. It no longer asks. The offline mirror carries every platform's packages from the lists the runtime ships, the installer runs a copy of the runtime's omarchy-hw-platform and installs the default set of the platform the machine is, and the build resolves each of those sets against the mirror. The kernel is the one choice left, as --kernel. linux-aarch64 is the default. linux-omarchy-n1x still comes with --package-dir, since no repository publishes it, and keeps what only the N1x uses on its image, namely the recovery entry, the rescue hostname, the console options and --dev-ssh. Snapdragon laptops get no device tree from their firmware, so GRUB cannot boot them by itself. Once the live kernel and initramfs exist, the build wraps them in a UKI that carries a device tree for every supported laptop, whenever the kernel package ships them, and systemd-stub picks one by the machine's SMBIOS data. A second archiso patch copies that UKI onto the ISO. GRUB boots it by default only where SMBIOS says the processor is a Snapdragon, and every other machine boots the kernel directly. The live image gets ukify for that, and the firmware tools the installer uses. A runtime without the platform detector, the Snapdragon hardware setup or a platform's package list is refused, so the ARM workflow builds against omarchy's n1x. Co-authored-by: Jimmy Van Veen <jimmy.van.veen@gmail.com>
…ritten The GPU, audio and compute DSP firmware of these laptops is signed by the vendor and ships only with Windows. The configurator copies it off the Windows partition before a full-disk install removes it, an unattended install does the same, and the copy travels into the target, where the runtime's hardware setup installs it. Without Windows on the disk, or off Snapdragon, nothing happens. Co-authored-by: Jimmy Van Veen <jimmy.van.veen@gmail.com>
The DSP driver stays out of the live initramfs, where loading it can reset the USB-C port the install media is on. Without it the live system has no USB hotplug and no battery reading. On the one laptop this is tested on, a service loads the driver after checking that the live root is entirely in RAM, with no mounted block device and no active swap.
The README gets an aarch64 section, and the N1x build commands name the kernel where they named the platform.
Install test passed
Install phases
|
…atform drivers as modules Arch Linux ARM's kernel builds the X1 clock, pin control, interconnect, PMIC, PHY and bus drivers in. Omarchy's own aarch64 kernel builds them as modules, so the live initramfs names them, each one optional. Without them the USB ports, the keyboard and the panel never come up. The device tree entry boots with iommu.passthrough=0. That kernel defaults to passthrough, under which the Yoga Slim 7x's keyboard and touchpad read back empty descriptors and never appear. On Arch Linux ARM's kernel the parameter changes nothing. The live UKI takes the device trees a kernel keeps under its own package name first, and Arch Linux ARM's shared folder otherwise.
This was referenced Oct 11, 2026
birkskyum
marked this pull request as ready for review
October 11, 2026 12:33
2 of 3 tasks
Contributor
Author
|
To see what this changes for the N1x, we built two images on the same kernel ( Identical for an N1x
Different
So what a boot on an N1x has to show is |
This was referenced Oct 12, 2026
omarchy-pkgs renames linux-omarchy-n1x to linux-omarchy (omarchy-pkgs It is the kernel for every aarch64 board, so it is what an aarch64 build uses unless told otherwise: --kernel is no longer needed, and names Arch Linux ARM's linux-aarch64 where someone wants that instead. The image's file name loses the kernel suffix with it. What the old name switched on stays as it was: the console settings for the laptops' panel, the N1x recovery entry, the live preset, and the rule that the kernel comes from a package bundle until a repository publishes it. The package filter loses a case. The N1x list used to name a kernel of its own, which an image on another kernel had to swap for its own; it now names linux-omarchy, which the filter already swaps. [Birk: applied without the install-test workflow and test-script changes of the CI-only branch, which this branch does not carry] (cherry picked from commit d788f69)
An aarch64 build could still be told to use Arch Linux ARM's linux-aarch64, and carried what that took: a script that made it the live kernel, its own console settings, an lz4 squashfs because that kernel cannot read zstd, and a fallback to the place it keeps device trees. linux-omarchy is the one kernel for every aarch64 board, so the choice goes: no --kernel, and the build refuses any other kernel. The image is built one way. The installer is unchanged. It still installs whatever kernel the image records, and its tests keep their fixtures. (cherry picked from commit 0096c4e)
2 of 3 tasks
This was referenced Oct 15, 2026
stevederico
pushed a commit
to stevederico/omarchy-iso
that referenced
this pull request
Oct 15, 2026
The image boots linux-omarchy since omacom#258, so its live kernel and initramfs are vmlinuz-linux-omarchy and initramfs-linux-omarchy.img.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One aarch64 image for every board that installs from a USB stick, with the board decided at install time as on x86_64. It replaces
--platform. Apple Silicon Macs are not part of it, since they are installed from macOS.It replaces #254 and carries everything from it. It also takes in what #247 and #255 add, so those two can close once this is in.
What changes
The build no longer asks which board the image is for, or which kernel.
install/omarchy-aarch64-*.packages). Apple Silicon is left out, since it is installed from macOS.omarchy-hw-platform, and installs that platform's default set. The sets are the onesomarchy-pkg-defaultscomposes.n1x.One kernel
The image is built on
linux-omarchy, Omarchy's kernel for every aarch64 board, and the build refuses any other. Arch Linux ARM'slinux-aarch64is no longer an option, and with it go the script that made it the live kernel, its console settings and the lz4 squashfs it needed. No channel publisheslinux-omarchyfor aarch64 yet, so the build takes it from--package-dir.The kernel builds the Snapdragon platform drivers as modules, so the live initramfs names them. The device tree entry boots with
iommu.passthrough=0and takes the device trees from the kernel's own folder.The two commits that rename the kernel here are Emir's, from #264.
The N1x
The recovery entry, the
omarchy-n1x-rescuehostname,console=tty0 acpi=nospcrand--dev-sshcame with the N1x kernel, so they are now in every aarch64 image. The N1x boot entry check runs wherever the target's own detector says the machine is an N1x.The build command changes from
--platform n1xto--arch aarch64 --package-dir DIR.Cost
The image is 6.1 GB. The platforms' own packages account for about 1 GB of that, mostly NVIDIA's driver and profilers and the Qualcomm firmware.
Tested
Before the rename all of this ran in VMs, on an image built on each kernel,
linux-aarch647.2.9-1 andlinux-omarchy-n1x7.2.8-4 as merged in omacom/omarchy-pkgs#955.omarchy-pkg-defaultsprints foraarch64,aarch64-qualcomm,aarch64-n1xandaarch64-gb10.linux-omarchy-n1xthat UKI carries the kernel package's own 32 device trees.rdma-coreand the two profilers. As a Snapdragon it is the firmware, the firmware tool,systemd-ukifyandvulkan-freedreno. As an N1x onlinux-omarchy-n1xit is that kernel, its headers and the NVIDIA driver, and DKMS builds the NVIDIA module for it. The VM has no NVIDIA GPU, so the container toolkit's two CDI units fail there, sincenvidia-smifinds no driver to talk to.After the rename the image on
linux-omarchy7.2.8-9 passes the unit tests for the build and the platform choice, its live UKI carries that package's 32 device trees, and it boots in a VM to the installer. In #264, CI builds the same commits, installs the image unattended and boots the result.On hardware, a Yoga Slim 7x boots the
linux-omarchy-n1x7.2.8-4 image from USB to the installer with its keyboard working, but not on every try. With the same stick and port some boots stay black, and we have no log from those yet. The live session there also runs without the DSP and with USB at USB 2 speed. The live root is 5.7 GB, over the 4 GiB that archiso copies to RAM by itself, and the DSP unit waits for that copy.The Yoga boots that kernel as an installed system with omacom/omarchy#14952's files in place. We have not run an install from this image on a laptop, and it is untested on an N1x and on a GB10.