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
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ jobs:
-w /workspace \
archlinux:base-devel bash -lc '
set -euo pipefail
pacman -Syu --noconfirm git jq
pacman -Syu --noconfirm git jq python-yaml xz zstd
./bin/sync-upstream self-test
./bin/sync-rebuilds --self-test
./bin/omarchy-pkgs self-test
./bin/omarchy-release self-test
shopt -s nullglob
for test in pkgbuilds/*/test.sh; do
bash "$test"
done
if [[ -d test ]]; then
runuser -u nobody -- python -m unittest discover -s test -v
fi
'
3 changes: 3 additions & 0 deletions pkgbuilds/linux-aarch64-pkgbase-shim/.omarchy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"source": "local"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Run before mkinitcpio and Limine discover installed kernels.
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Operation = Remove
Target = usr/lib/modules/*/

[Trigger]
Type = Package
Operation = Install
Operation = Upgrade
Target = linux-aarch64-pkgbase-shim

[Action]
Description = Writing pkgbase and vmlinuz for Arch Linux ARM kernels...
When = PostTransaction
Exec = /usr/share/libalpm/scripts/linux-aarch64-pkgbase-shim
NeedsTargets
26 changes: 26 additions & 0 deletions pkgbuilds/linux-aarch64-pkgbase-shim/PKGBUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Maintainer: Jimmy Van Veen

# Add kernel metadata required by mkinitcpio and Limine on Arch Linux ARM.

pkgname=linux-aarch64-pkgbase-shim
pkgver=1
pkgrel=3
pkgdesc="Writes usr/lib/modules/<ver>/{pkgbase,vmlinuz} for Arch Linux ARM kernels so mkinitcpio and Limine hooks find them"
arch=('aarch64')
url="https://github.com/omacom/omarchy-pkgs"
license=('MIT')
depends=('bash' 'diffutils' 'pacman')
optdepends=('limine-mkinitcpio-hook: rebuild the UKI and Limine entry when the shim is installed after the kernel')
options=('!debug')
source=(
'85-linux-aarch64-pkgbase-shim.hook'
'linux-aarch64-pkgbase-shim'
'README.package.md'
)
sha256sums=('SKIP' 'SKIP' 'SKIP')

package() {
install -Dm644 85-linux-aarch64-pkgbase-shim.hook "${pkgdir}/usr/share/libalpm/hooks/85-linux-aarch64-pkgbase-shim.hook"
install -Dm755 linux-aarch64-pkgbase-shim "${pkgdir}/usr/share/libalpm/scripts/linux-aarch64-pkgbase-shim"
install -Dm644 README.package.md "${pkgdir}/usr/share/doc/${pkgname}/README.md"
}
28 changes: 28 additions & 0 deletions pkgbuilds/linux-aarch64-pkgbase-shim/README.package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# linux-aarch64-pkgbase-shim

Arch Linux ARM installs its kernel as `/boot/Image` instead of
`usr/lib/modules/<ver>/vmlinuz`. Limine discovers the kernel through
`modules.builtin`, but needs `vmlinuz` beside it to build a boot entry.

This package is one pacman hook. After a kernel package is installed or
upgraded (`usr/lib/modules/*/`), and when the shim itself is installed, it
writes into each package-owned modules directory without native kernel metadata:

- `pkgbase`: compatibility metadata naming the package that owns the directory
- `vmlinuz`: a copy of `/boot/Image` (the owner must own that too)

Reinstalling a kernel refreshes the shim's copy when the image changes, even
if the module-directory version is unchanged. Package-owned files are preserved.

It runs as `85-`, before `90-mkinitcpio-install`, so the usual hook then builds
the initramfs/UKI and the Limine entry. If that hook would not run in the same
transaction (the shim installed after the kernel, or a kernel package that
does not touch `usr/lib/initcpio/`), the script runs
`limine-mkinitcpio-install` itself. Leftover directories of removed kernels
that hold nothing but these two files are cleaned up.

## Retirement

Delete this package when `linux-aarch64` ships `vmlinuz` in its kernel module
directory. Package-owned metadata is never modified, so both approaches can coexist
during the transition.
103 changes: 103 additions & 0 deletions pkgbuilds/linux-aarch64-pkgbase-shim/linux-aarch64-pkgbase-shim
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/bin/bash
# Supply pkgbase and vmlinuz metadata missing from Arch Linux ARM kernels.
set -uo pipefail

readonly SELF=linux-aarch64-pkgbase-shim
readonly ROOT=${OMARCHY_KERNEL_SHIM_ROOT:-}
readonly IMAGE=$ROOT/boot/Image
readonly LIMINE_INSTALL_SCRIPT=$ROOT/usr/share/libalpm/scripts/limine-mkinitcpio-install

declare -A DIRS=()
modules_in_transaction=0
self_in_transaction=0
rebuild=0
failed=0

owner_of() {
pacman -Qqo "${1#"$ROOT"}" 2>/dev/null
}

# NeedsTargets requires consuming every line from stdin.
while IFS= read -r line; do
case $line in
usr/lib/modules/*)
v=${line#usr/lib/modules/}
v=${v%%/*}
[[ -n $v ]] && DIRS["$ROOT/usr/lib/modules/$v"]=1
modules_in_transaction=1
;;
"$SELF")
self_in_transaction=1
;;
esac
done

# Cover kernels already present when the shim itself is installed.
if ((self_in_transaction)); then
for d in "$ROOT"/usr/lib/modules/*/; do
[[ -d $d ]] && DIRS["${d%/}"]=1
done
fi

for dir in "${!DIRS[@]}"; do
[[ -d $dir ]] || continue
# Not a packaged kernel (a leftover or DKMS-only directory).
owner=$(owner_of "$dir") || continue
if [[ $(owner_of "$IMAGE") != "$owner" ]]; then
echo "$SELF: $owner owns $dir but not $IMAGE, leaving it alone" >&2
continue
fi
# Preserve package-owned files; refresh metadata previously written by the shim.
owner_of "$dir/pkgbase" >/dev/null && continue
owner_of "$dir/vmlinuz" >/dev/null && continue
if [[ -e $dir/pkgbase && $(<"$dir/pkgbase") != "$owner" ]]; then
continue
fi
if [[ -f $dir/pkgbase ]] && cmp -s "$IMAGE" "$dir/vmlinuz"; then
continue
fi
image_tmp=$(mktemp "$dir/.vmlinuz.XXXXXX") || { failed=1; continue; }
if ! cp --preserve=mode "$IMAGE" "$image_tmp" || ! mv -f "$image_tmp" "$dir/vmlinuz"; then
echo "$SELF: failed to copy $IMAGE to $dir/vmlinuz" >&2
rm -f "$image_tmp"
failed=1
continue
fi
if ! printf '%s\n' "$owner" >"$dir/pkgbase"; then
failed=1
continue
fi
echo "$SELF: wrote pkgbase ($owner) and vmlinuz in $dir"
# Rebuild when the transaction will not trigger mkinitcpio itself.
if ! ((modules_in_transaction)) || ! pacman -Qlq "$owner" 2>/dev/null | grep '^/usr/lib/initcpio/' >/dev/null; then
rebuild=1
fi
done

# Remove directories containing only stale shim metadata.
for dir in "$ROOT"/usr/lib/modules/*/; do
dir=${dir%/}
[[ -f $dir/pkgbase && -f $dir/vmlinuz ]] || continue
owner_of "$dir" >/dev/null && continue
owner_of "$dir/pkgbase" >/dev/null && continue
owner_of "$dir/vmlinuz" >/dev/null && continue
[[ -z $(find "$dir" -mindepth 1 ! -name pkgbase ! -name vmlinuz -print -quit) ]] || continue
rm -f "$dir/pkgbase" "$dir/vmlinuz" && rmdir "$dir" && echo "$SELF: removed leftover $dir"
done

# Respect the installer's deferred boot-image build.
deferred_hook=$ROOT/etc/pacman.d/hooks/90-mkinitcpio-install.hook
if ((rebuild)) && [[ -L $deferred_hook && $(readlink "$deferred_hook") == /dev/null ]]; then
echo "$SELF: $deferred_hook is masked, leaving the rebuild to limine-update"
rebuild=0
fi

if ((rebuild)); then
if [[ -x $LIMINE_INSTALL_SCRIPT ]]; then
echo rebuild | "$LIMINE_INSTALL_SCRIPT" || failed=1
else
echo "$SELF: $LIMINE_INSTALL_SCRIPT not found; run mkinitcpio for the new kernel yourself" >&2
fi
fi

exit "$failed"
107 changes: 107 additions & 0 deletions pkgbuilds/linux-aarch64-pkgbase-shim/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/bash
set -euo pipefail

package_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
export OMARCHY_KERNEL_SHIM_ROOT=$scratch
export SHIM_NATIVE=0 SHIM_KERNEL_PRESENT=1
export SHIM_IMAGE_OWNER=linux-aarch64

pacman() {
if [[ $1 == -Qlq ]]; then
printf '/usr/lib/initcpio/install/test\n'
return 0
fi
case $2 in
/boot/Image)
printf '%s\n' "$SHIM_IMAGE_OWNER"
;;
/usr/lib/modules/test)
((SHIM_KERNEL_PRESENT)) || return 1
printf 'linux-aarch64\n'
;;
/usr/lib/modules/test/pkgbase | /usr/lib/modules/test/vmlinuz)
[[ $SHIM_NATIVE == 1 || $2 == /usr/lib/modules/test/"$SHIM_NATIVE" ]] || return 1
printf 'linux-aarch64\n'
;;
*) return 1 ;;
esac
}
export -f pacman

modules=$scratch/usr/lib/modules/test
mkdir -p "$modules" "$scratch/boot" "$scratch/etc/pacman.d/hooks" \
"$scratch/usr/share/libalpm/scripts"
printf 'first-kernel' >"$scratch/boot/Image"
cat >"$scratch/usr/share/libalpm/scripts/limine-mkinitcpio-install" <<'SCRIPT'
#!/bin/bash
cat >>"$OMARCHY_KERNEL_SHIM_ROOT/rebuilds"
SCRIPT
chmod +x "$scratch/usr/share/libalpm/scripts/limine-mkinitcpio-install"

run_shim() {
printf '%s\n' "$1" | bash "$package_dir/linux-aarch64-pkgbase-shim"
}

run_shim usr/lib/modules/test/
[[ $(<"$modules/pkgbase") == linux-aarch64 ]]
cmp "$scratch/boot/Image" "$modules/vmlinuz"
[[ $(stat -c %a "$modules/vmlinuz") == $(stat -c %a "$scratch/boot/Image") ]]
[[ ! -e $scratch/rebuilds ]]
echo 'ok - kernel installation supplies metadata before the normal rebuild hook'

printf 'replacement-kernel' >"$scratch/boot/Image"
run_shim usr/lib/modules/test/
cmp "$scratch/boot/Image" "$modules/vmlinuz"
echo 'ok - reinstalling the same kernel version refreshes its image'

printf 'shim-upgrade' >"$scratch/boot/Image"
run_shim linux-aarch64-pkgbase-shim
[[ $(<"$scratch/rebuilds") == rebuild ]]
run_shim linux-aarch64-pkgbase-shim
[[ $(wc -l <"$scratch/rebuilds") == 1 ]]
echo 'ok - unchanged images do not trigger redundant rebuilds'

ln -s /dev/null "$scratch/etc/pacman.d/hooks/90-mkinitcpio-install.hook"
printf 'deferred-kernel' >"$scratch/boot/Image"
run_shim linux-aarch64-pkgbase-shim
cmp "$scratch/boot/Image" "$modules/vmlinuz"
[[ $(wc -l <"$scratch/rebuilds") == 1 ]]
echo 'ok - the installer can defer boot-image regeneration'

for SHIM_NATIVE in pkgbase vmlinuz; do
printf 'package-owned' >"$modules/vmlinuz"
run_shim usr/lib/modules/test/
[[ $(<"$modules/vmlinuz") == package-owned ]]
done
echo 'ok - either package-owned metadata file prevents replacement'

SHIM_NATIVE=0
SHIM_IMAGE_OWNER=another-kernel
run_shim usr/lib/modules/test/
[[ $(<"$modules/vmlinuz") == package-owned ]]
SHIM_IMAGE_OWNER=linux-aarch64
echo 'ok - a different kernel image owner prevents replacement'

(
# shellcheck disable=SC2329 # Invoked by the shim in a child shell.
cp() { return 1; }
export -f cp
if run_shim usr/lib/modules/test/; then
echo 'not ok - a failed image copy was accepted' >&2
exit 1
fi
[[ $(<"$modules/vmlinuz") == package-owned ]]
)
echo 'ok - failed copies preserve the previous kernel image'

SHIM_KERNEL_PRESENT=0
mkdir -p "$modules/updates/dkms"
run_shim usr/lib/modules/test/
[[ -d $modules/updates/dkms && -f $modules/vmlinuz ]]
rmdir "$modules/updates/dkms" "$modules/updates"
echo 'ok - kernel removal preserves DKMS leftovers'
run_shim usr/lib/modules/test/
[[ ! -d $modules ]]
echo 'ok - kernel removal cleans up leftover shim metadata'