Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 245 additions & 16 deletions builder/build-iso.sh

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions builder/build-omarchy-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ for pkg in "${packages[@]}"; do
done

mkdir -p "$offline_mirror_dir"
for package_file in "$work_dir"/*.pkg.tar.zst; do
for package_file in "$work_dir"/*.pkg.tar.zst "$work_dir"/*.pkg.tar.xz; do
[[ -e $package_file ]] || continue
destination="$offline_mirror_dir/$(basename "$package_file")"

# A cached signature belongs to the previously downloaded or locally built
Expand All @@ -82,4 +83,4 @@ done

echo
echo "Built Omarchy packages, placed in $offline_mirror_dir:"
ls "$offline_mirror_dir"/omarchy*.pkg.tar.zst | sed 's|^| |'
ls "$offline_mirror_dir"/omarchy*.pkg.tar.{zst,xz} 2>/dev/null | sed 's|^| |'
49 changes: 49 additions & 0 deletions builder/patches/archiso-grubmodules.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Filter archiso's hardcoded GRUB module list to what the target platform
# provides. at_keyboard (PS/2), keylayouts, usb and the usbserial_* drivers
# are not built for arm64-efi, so grub-mkstandalone aborts without this.
#
# Generated against the vendored archiso (v87). Belongs upstream in archiso;
# carried here until it lands there. build-iso.sh applies it with --forward,
# so it becomes a no-op once the submodule is bumped past the fix.
--- a/archiso/mkarchiso
+++ b/archiso/mkarchiso
@@ -669,6 +669,31 @@
fi
}

+# Filter a GRUB module list down to what a platform actually provides.
+# Not every module archiso asks for is built everywhere: at_keyboard is PS/2 and
+# the usbserial_* drivers are x86-oriented, so none of them exist for arm64-efi
+# and grub-mkstandalone aborts on the first one it cannot find.
+# $1: GRUB target (e.g. arm64-efi)
+# $@: module names
+# Prints the available modules, one per line.
+_filter_grubmodules() {
+ local _target="${1}"
+ shift
+ local _module _available=() _missing=()
+ for _module in "$@"; do
+ if [[ -e "/usr/lib/grub/${_target}/${_module}.mod" ]]; then
+ _available+=("${_module}")
+ else
+ _missing+=("${_module}")
+ fi
+ done
+ if (( ${#_missing[@]} )); then
+ # stdout carries the module list, so this has to go to stderr.
+ _msg_info "Skipping GRUB module(s) not built for ${_target}: ${_missing[*]}" >&2
+ fi
+ printf '%s\n' "${_available[@]}"
+}
+
# Prepare GRUB
_make_bootmode_uefi.grub() {
local grub_target grubmodules=() files_to_copy=()
@@ -691,6 +716,7 @@
minicmd normal ntfs ntfscomp part_apple part_gpt part_msdos png read reboot regexp search \
search_fs_file search_fs_uuid search_label serial sleep tpm udf usb usbserial_common usbserial_ftdi \
usbserial_pl2303 usbserial_usbdebug video xfs zstd)
+ mapfile -t grubmodules < <(_filter_grubmodules "$grub_target" "${grubmodules[@]}")
grub-mkstandalone -O "$grub_target" \
--modules="${grubmodules[*]}" \
--locales="en@quot" \
100 changes: 100 additions & 0 deletions configs/aarch64/customize_airootfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
# Runs inside the live-ISO chroot after packages are installed.
#
# WHAT THIS IS FOR
# ----------------
# Arch Linux ARM's kernel packages name things differently from Arch's, and
# archiso and GRUB both look for the Arch names. This reconciles the two.
#
# It is deliberately *not* responsible for generating the initramfs. On Arch
# that happens by itself: the kernel package installs
# usr/lib/modules/<ver>/{vmlinuz,pkgbase}, 90-mkinitcpio-install.hook triggers
# on the former, and /usr/share/libalpm/scripts/mkinitcpio reads the latter to
# learn the kernel's name. ALARM installs neither file, so the hook never fires
# -- proposed upstream as archlinuxarm/PKGBUILDs#2215. With that applied the
# hook does run, but two naming gaps remain and are what this script closes:
#
# * ALARM's preset is PRESETS=('default') writing /boot/initramfs-linux.img,
# while the GRUB entries load initramfs-linux-aarch64.img.
# * Nothing creates /boot/vmlinuz-*, and mkarchiso copies the kernel with
# `install -- "${pacstrap_dir}/boot/vmlinuz-"*`, which would match nothing.
#
# It also sets archiso_config in the preset, which is how x86's linux-t2.preset
# keeps an installed system's HOOKS out of the live initramfs.
#
# NOTE: mkarchiso prints a deprecation warning for customize_airootfs.sh. There
# is currently no replacement hook that runs inside the chroot after packages
# install, which is required here because the kernel image only exists then.
# Shipping the preset in airootfs/ does not work either: the overlay is copied
# before pacstrap, so linux-aarch64's own preset overwrites it.
set -uo pipefail

# Everything here addresses differences in Arch Linux ARM's kernel packaging.
# On any other architecture this script is a no-op.
if [[ $(uname -m) != aarch64 ]]; then
exit 0
fi

# Omarchy forces the thunderbolt module; ALARM's aarch64 kernel does not build
# it, and mkinitcpio treats an unresolvable MODULES entry as an error.
if [[ -f /etc/mkinitcpio.conf.d/thunderbolt_module.conf ]]; then
sed -i 's/^MODULES+=(thunderbolt)/#MODULES+=(thunderbolt) # not built for aarch64/' \
/etc/mkinitcpio.conf.d/thunderbolt_module.conf
fi

# ALARM installs the kernel as /boot/Image. mkarchiso copies /boot/vmlinuz-*
# into the ISO, so without this name the kernel never reaches the image.
if [[ ! -e /boot/vmlinuz-linux-aarch64 ]]; then
if [[ -e /boot/Image ]]; then
cp -a /boot/Image /boot/vmlinuz-linux-aarch64
else
echo "customize_airootfs: no kernel image at /boot/Image or /boot/vmlinuz-linux-aarch64" >&2
exit 1
fi
fi

# Drop presets whose kernel image is absent: `mkinitcpio -P` aborts on them.
for preset in /etc/mkinitcpio.d/*.preset; do
[[ -e $preset ]] || continue
kname="$(basename "$preset" .preset)"
if [[ $kname != linux-aarch64 && ! -e /boot/vmlinuz-$kname ]]; then
echo "customize_airootfs: dropping $kname.preset (no kernel image)"
rm -f "$preset"
fi
done

# Replace ALARM's preset, which writes /boot/initramfs-linux.img, with one that
# writes the name the GRUB entries load. archiso_config additionally makes
# mkinitcpio read archiso.conf as its configuration, bypassing
# /etc/mkinitcpio.conf.d entirely -- the same trick x86's linux-t2.preset uses
# to keep omarchy_hooks.conf, which describes an *installed* system, out of the
# *live* initramfs.
cat > /etc/mkinitcpio.d/linux-aarch64.preset <<'PRESET'
# Live-ISO preset for Arch Linux ARM's linux-aarch64.
PRESETS=('archiso')

ALL_kver='/boot/vmlinuz-linux-aarch64'
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'

archiso_image="/boot/initramfs-linux-aarch64.img"
PRESET

echo "customize_airootfs: building the live initramfs from archiso.conf"
rm -f /boot/initramfs-linux*.img

# mkinitcpio exits non-zero on this platform even when it produces a complete
# image: 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). Judge the result on the artefact rather than the exit status.
mkinitcpio -p linux-aarch64 || \
echo "customize_airootfs: mkinitcpio reported errors; checking for the image"

# The GRUB entries boot this file by name. If it is missing the ISO builds
# cleanly and then fails to boot, so fail the build here instead.
if [[ ! -s /boot/initramfs-linux-aarch64.img ]]; then
echo "customize_airootfs: no live initramfs was produced; the ISO would not boot" >&2
exit 1
fi
Comment thread
greptile-apps[bot] marked this conversation as resolved.
echo "customize_airootfs: initramfs present ($(stat -c %s /boot/initramfs-linux-aarch64.img) bytes)"

exit 0
5 changes: 5 additions & 0 deletions configs/aarch64/linux.preset
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# The live ISO boots linux-aarch64 (/boot/Image). A stock linux.preset points at
# /boot/vmlinuz-linux, which does not exist here, and `mkinitcpio -P` fails with
# ERROR: Invalid option -k -- '/boot/vmlinuz-linux' must be readable
# An empty PRESETS array makes this preset a no-op.
PRESETS=()
32 changes: 32 additions & 0 deletions configs/aarch64/zz-aarch64-live.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Live-ISO overrides. Sorts last in /etc/mkinitcpio.conf.d, so it has the final
# word, and is unowned by any package so pacman cannot clobber it.
#
# WHY THIS EXISTS
# ---------------
# archiso.conf sets the HOOKS needed to boot a live ISO:
# base udev microcode modconf kms memdisk archiso archiso_loop_mnt
# archiso_pxe_* block filesystems keyboard
# omarchy-settings then ships omarchy_hooks.conf, which sets the HOOKS for an
# *installed* system (autodetect, encrypt, fsck, btrfs-overlayfs...).
#
# "omarchy_hooks.conf" sorts after "archiso.conf", so it wins -- and the live
# initramfs ends up with no `archiso` hook. The ISO then boots the kernel and
# panics in the initramfs:
# ERROR: Failed to mount '' on real root
# Restore the live hooks, keeping Omarchy's plymouth (the ISO installs
# omarchy-settings specifically so its plymouthd.conf lands before mkinitcpio).
#
# `microcode` is omitted: on aarch64 that hook only emits
# WARNING: architecture 'aarch64' not supported, skipping hook
HOOKS=(base udev plymouth modconf kms memdisk archiso archiso_loop_mnt
archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs
block filesystems keyboard)

# thunderbolt is x86-oriented hardware; ALARM's aarch64 kernel does not build
# the module, and a missing MODULES entry is fatal to mkinitcpio.
_keep=()
for _m in "${MODULES[@]:-}"; do
[[ $_m == "thunderbolt" || -z $_m ]] || _keep+=("$_m")
done
MODULES=("${_keep[@]}")
unset _keep _m
2 changes: 1 addition & 1 deletion configs/airootfs/root/.automated_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ warm_offline_mirror() {
((spent_kb + size_kb > budget_kb)) && continue
cat -- "$path" >/dev/null 2>&1 || true
spent_kb=$((spent_kb + size_kb))
done < <(du -k "$mirror"/*.pkg.tar.zst 2>/dev/null | sort -rn)
done < <(du -k "$mirror"/*.pkg.tar.zst "$mirror"/*.pkg.tar.xz 2>/dev/null | sort -rn)
}

warm_offline_mirror &
Expand Down
48 changes: 36 additions & 12 deletions configs/airootfs/root/configurator
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ OMARCHY_SETTINGS_PACKAGE="${OMARCHY_SETTINGS_PACKAGE:-omarchy-settings}"

LOGO_PATH="$OMARCHY_PATH/logo.txt"

# Limine installs BOOT<ARCH>.EFI as limine_<arch>.efi. The Arch package ships
# every architecture's binary, so the x64 name resolves on ARM too and the
# install silently registers an unbootable x86-64 loader. Derive it instead.
case "$(uname -m)" in
x86_64) limine_efi_binary="limine_x64.efi" ;;
i?86) limine_efi_binary="limine_ia32.efi" ;;
aarch64) limine_efi_binary="limine_aa64.efi" ;;
riscv64) limine_efi_binary="limine_riscv64.efi" ;;
loongarch64) limine_efi_binary="limine_loongarch64.efi" ;;
*) limine_efi_binary="limine_x64.efi" ;;
esac

# archinstall prepends these to the target's mirrorlist. They are Arch x86_64
# mirrors laid out as $repo/os/$arch; Arch Linux ARM uses $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. Leave that list alone
# there.
case "$(uname -m)" in
aarch64) mirror_custom_servers='[]' ;;
*) mirror_custom_servers='[
{"url": "https://mirror.omarchy.org/$repo/os/$arch"},
{"url": "https://mirror.rackspace.com/archlinux/$repo/os/$arch"},
{"url": "https://geo.mirror.pkgbuild.com/$repo/os/$arch"}
]' ;;
esac

# The setup form — keyboard, account, hostname, and timezone questions, plus the
# rules their answers are checked against — shared verbatim with the first-boot
# owner setup that finishes a deferred install. build-iso.sh vendors it out of
Expand Down Expand Up @@ -412,6 +438,12 @@ to_gb() {

# T2 Macs need their own kernel for keyboard/wifi drivers.
detect_kernel() {
# Arch Linux ARM ships no bare `linux` package -- its kernel is
# `linux-aarch64`, and that is also the pkgbase every bootloader hook and
# validate_boot() looks for. T2 detection is x86-only by construction.
case "$(uname -m)" in
aarch64) echo "linux-aarch64"; return ;;
esac
if lspci -nn 2>/dev/null | grep -q "106b:180[12]"; then
echo "linux-t2"
else
Expand Down Expand Up @@ -805,7 +837,7 @@ run_partition_execute() {
"boot": {
"esp_mount": "$esp_mount_in_target",
"esp_path": "/EFI/limine",
"efi_binary": "limine_x64.efi",
"efi_binary": "$limine_efi_binary",
"enable_fallback": false
},
"storage": {
Expand Down Expand Up @@ -836,11 +868,7 @@ run_partition_execute() {
},
"mirror_config": {
"custom_repositories": [],
"custom_servers": [
{"url": "https://mirror.omarchy.org/\$repo/os/\$arch"},
{"url": "https://mirror.rackspace.com/archlinux/\$repo/os/\$arch"},
{"url": "https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"}
],
"custom_servers": $mirror_custom_servers,
"mirror_regions": {},
"optional_repositories": []
},
Expand Down Expand Up @@ -1144,7 +1172,7 @@ cat <<-_EOF_ >user_configuration.json
"boot": {
"esp_mount": "/boot",
"esp_path": "/EFI/limine",
"efi_binary": "limine_x64.efi",
"efi_binary": "$limine_efi_binary",
"enable_fallback": true
},
"storage": {
Expand Down Expand Up @@ -1225,11 +1253,7 @@ cat <<-_EOF_ >user_configuration.json
},
"mirror_config": {
"custom_repositories": [],
"custom_servers": [
{"url": "https://mirror.omarchy.org/\$repo/os/\$arch"},
{"url": "https://mirror.rackspace.com/archlinux/\$repo/os/\$arch"},
{"url": "https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"}
],
"custom_servers": $mirror_custom_servers,
"mirror_regions": {},
"optional_repositories": []
},
Expand Down
17 changes: 16 additions & 1 deletion configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,26 @@

import json
import os
import platform
import secrets
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

# Limine ships BOOT{X64,IA32,AA64,RISCV64,LOONGARCH64}.EFI and the Arch package
# installs *all* of them, so hardcoding the x64 name does not fail on ARM -- it
# silently installs an x86-64 binary the firmware cannot execute. Derive the
# name instead, matching limine-common-functions' limine_efi_arch().
_LIMINE_EFI_ARCH = {
"x86_64": "X64",
"i686": "IA32",
"aarch64": "AA64",
"riscv64": "RISCV64",
"loongarch64": "LOONGARCH64",
}.get(platform.machine(), "X64")
_LIMINE_SOURCE_EFI = f"BOOT{_LIMINE_EFI_ARCH}.EFI"
_LIMINE_EFI_BINARY = f"limine_{_LIMINE_EFI_ARCH.lower()}.efi"


@dataclass
class InstallContext:
Expand Down Expand Up @@ -184,7 +199,7 @@ def _default_omarchy_install(user_configuration: dict) -> dict[str, Any]:
"boot": {
"esp_mount": "/boot",
"esp_path": "/EFI/limine",
"efi_binary": "limine_x64.efi",
"efi_binary": _LIMINE_EFI_BINARY,
"enable_fallback": mode == "full_disk",
},
"storage": {},
Expand Down
Loading