limine-mkinitcpio-hook 1.39.0-2: Apple Silicon activation gate on aarch64
On aarch64 the Limine hooks and the mkinitcpio wrapper run through limine-apple-gate. Only an Apple Silicon Mac (device tree "apple,") behaves differently: Limine's kernel and removal hooks wait until Omarchy activates Limine (/var/lib/omarchy/limine.enabled and /etc/default/limine), mkinitcpio's own kernel hook keeps /boot current before and after activation, Limine's EFI deploy hook is left to omarchy-mac-boot, and the wrapper is plain mkinitcpio. Skipped hooks drain the socket pacman streams targets over. x86_64 packages exactly what 1.39.0-1 did; generic aarch64 and Snapdragon keep upstream behaviour.
This commit is contained in:
1 parent
ed6a3869c7
commit
baf6df3e80
5 files changed
+701
-3
No files matched your search
@@ -58,6 +58,7 @@ jobs:
|
||||
./tests/published-build-plan.sh
|
||||
./tests/controller.sh
|
||||
./tests/artifact-helpers.sh
|
||||
./tests/limine-mkinitcpio-hook.sh
|
||||
pacman -S --noconfirm --quiet rclone >/dev/null
|
||||
./tests/publish-artifact.sh
|
||||
'
|
||||
@@ -3,14 +3,16 @@ _pkgname="limine-entry-tool"
|
||||
pkgname="limine-mkinitcpio-hook"
|
||||
_gradle_version=9.7.1
|
||||
pkgver=1.39.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="Install kernels for the Limine bootloader."
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://gitlab.com/Zesko/limine-entry-tool"
|
||||
source=("${_pkgname}::git+${url}.git#tag=${pkgver}")
|
||||
source_x86_64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz")
|
||||
source_aarch64=("https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.2.4/graalvm-community-jdk-25i2-25.0.4_linux-aarch64_bin.tar.gz"
|
||||
"https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip")
|
||||
"https://services.gradle.org/distributions/gradle-${_gradle_version}-bin.zip"
|
||||
"limine-apple-gate"
|
||||
"mkinitcpio-install.hook")
|
||||
license=("GPL3")
|
||||
provides=('limine-entry-tool')
|
||||
options=(!debug !strip)
|
||||
@@ -34,7 +36,9 @@ conflicts=('limine-entry-tool')
|
||||
sha256sums=('6c4affb6fb6367a1222f7d0c54957a3142781d7894bbf61b1a274bd153e5d869')
|
||||
sha256sums_x86_64=('3f4a89de8eaa96f2ed677f09957c7e872cd8467aad3537f8b5394c1b8c4b942e')
|
||||
sha256sums_aarch64=('22286f7ecd21b9aedb3226b9bf797469e1bd3eefc491e12ef3dd49b452d230b7'
|
||||
'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a')
|
||||
'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a'
|
||||
'199fa2122e4a20ed80e6cf5df235b4e50739593803522bfa65700b1ef242b9ce'
|
||||
'9fc607e81d2f09aa0dfd313805277af92977535bd222f1e86caf5fbb28138cb4')
|
||||
|
||||
prepare() {
|
||||
[[ -d "${_graalvm_version}" ]] && rm -rf "${_graalvm_version}"
|
||||
@@ -77,4 +81,58 @@ package() {
|
||||
# limine hook symlinks
|
||||
ln -sf /usr/bin/limine-reset-enroll "$pkgdir/etc/boot/hooks/pre.d/10-limine-reset-enroll"
|
||||
ln -sf /usr/bin/limine-enroll-config "$pkgdir/etc/boot/hooks/post.d/90-limine-enroll-config"
|
||||
|
||||
if [[ $CARCH == aarch64 ]]; then
|
||||
_apple_silicon_hooks
|
||||
fi
|
||||
}
|
||||
|
||||
# An Apple Silicon Mac boots GRUB until Omarchy activates Limine, and keeps
|
||||
# the kernel and initramfs that mkinitcpio's presets install under /boot
|
||||
# after that too (GRUB, the boot check and the snapshot tools read them).
|
||||
# The aarch64 hooks and mkinitcpio wrapper run through limine-apple-gate,
|
||||
# which leaves every other machine as upstream ships it. On a Mac:
|
||||
# - Limine's kernel and removal hooks wait until Limine is active;
|
||||
# - mkinitcpio's own kernel hook, which Limine's takes over by name, runs from
|
||||
# a copy of mkinitcpio 42's (mkinitcpio-install.hook: keep its triggers in
|
||||
# step with mkinitcpio's), just before Limine's;
|
||||
# - Limine's EFI deploy hook stays out: omarchy-mac-boot puts Limine in
|
||||
# U-Boot's EFI/BOOT/BOOTAA64.EFI slot;
|
||||
# - the wrapper is plain mkinitcpio: it would run limine-mkinitcpio after
|
||||
# every preset build, mkinitcpio's kernel hook included.
|
||||
_apple_silicon_hooks() {
|
||||
local scripts=/usr/share/libalpm/scripts
|
||||
local gate=$scripts/limine-apple-gate
|
||||
local hooks="$pkgdir/usr/share/libalpm/hooks"
|
||||
local kernel_hook="$pkgdir/etc/pacman.d/hooks/90-mkinitcpio-install.hook"
|
||||
local mac_hook="$hooks/90-mkinitcpio-apple-install.hook"
|
||||
local wrapper="$pkgdir/usr/local/bin/mkinitcpio"
|
||||
|
||||
install -Dm755 "$srcdir/limine-apple-gate" "$pkgdir$gate"
|
||||
install -Dm644 "$srcdir/mkinitcpio-install.hook" "$mac_hook"
|
||||
sed -i -e 's|^Description = .*|Description = Updating linux initcpios under /boot (Apple Silicon)...|' \
|
||||
-e "s|^Exec = $scripts/mkinitcpio install\$|Exec = $gate --mac $scripts/mkinitcpio install|" "$mac_hook"
|
||||
sed -i "s|^Exec = $scripts/limine-mkinitcpio-|Exec = $gate $scripts/limine-mkinitcpio-|" \
|
||||
"$kernel_hook" "$hooks/60-limine-mkinitcpio-remove-pre.hook" "$hooks/90-limine-mkinitcpio-remove-post.hook"
|
||||
sed -i "s|^Exec = /usr/bin/limine-install\$|Exec = $gate --not-mac /usr/bin/limine-install|" "$hooks/80-limine-efi-deploy.hook"
|
||||
sed -i "1a $gate --mac \\&\\& exec /usr/bin/mkinitcpio \"\$@\"" "$wrapper"
|
||||
|
||||
local expected=(
|
||||
"$mac_hook|Exec = $gate --mac $scripts/mkinitcpio install"
|
||||
"$kernel_hook|Exec = $gate $scripts/limine-mkinitcpio-install"
|
||||
"$hooks/60-limine-mkinitcpio-remove-pre.hook|Exec = $gate $scripts/limine-mkinitcpio-remove pre"
|
||||
"$hooks/90-limine-mkinitcpio-remove-post.hook|Exec = $gate $scripts/limine-mkinitcpio-remove post"
|
||||
"$hooks/80-limine-efi-deploy.hook|Exec = $gate --not-mac /usr/bin/limine-install"
|
||||
)
|
||||
local entry
|
||||
for entry in "${expected[@]}"; do
|
||||
[[ $(grep '^Exec = ' "${entry%%|*}") == "${entry#*|}" ]] || {
|
||||
echo "${entry%%|*}: expected '${entry#*|}'" >&2
|
||||
return 1
|
||||
}
|
||||
done
|
||||
[[ $(sed -n 1,2p "$wrapper") == $'#!/usr/bin/env bash\n'"$gate --mac && exec /usr/bin/mkinitcpio \"\$@\"" ]] || {
|
||||
echo "the mkinitcpio wrapper does not start with the Apple Silicon gate" >&2
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# The aarch64 package runs its pacman hooks and its mkinitcpio wrapper through
|
||||
# this, so that only an Apple Silicon Mac behaves differently from upstream:
|
||||
#
|
||||
# limine-apple-gate COMMAND... COMMAND, except on a Mac that has not activated Limine
|
||||
# limine-apple-gate --mac COMMAND... COMMAND on a Mac only
|
||||
# limine-apple-gate --not-mac COMMAND... COMMAND everywhere but a Mac
|
||||
# limine-apple-gate --mac succeeds on a Mac only
|
||||
#
|
||||
# A Mac boots GRUB until Omarchy activates Limine: the gate
|
||||
# /var/lib/omarchy/limine.enabled and the /etc/default/limine the activation
|
||||
# writes, the same test as omarchy-mac-limine-active. A command that does not
|
||||
# run reads the targets pacman streams to it and succeeds quietly.
|
||||
|
||||
gate=/var/lib/omarchy/limine.enabled
|
||||
limine_default=/etc/default/limine
|
||||
compatible=(/proc/device-tree/compatible /sys/firmware/devicetree/base/compatible)
|
||||
|
||||
# Fixtures fake the machine for an unprivileged caller only; as root, which
|
||||
# pacman hooks always are, the live system decides.
|
||||
if ((EUID != 0)); then
|
||||
gate=${OMARCHY_LIMINE_GATE:-$gate}
|
||||
limine_default=${OMARCHY_LIMINE_DEFAULT:-$limine_default}
|
||||
if [[ -n ${OMARCHY_PROC_ROOT:-}${OMARCHY_SYS_ROOT:-} ]]; then
|
||||
compatible=()
|
||||
[[ -z ${OMARCHY_PROC_ROOT:-} ]] || compatible+=("$OMARCHY_PROC_ROOT/device-tree/compatible")
|
||||
[[ -z ${OMARCHY_SYS_ROOT:-} ]] || compatible+=("$OMARCHY_SYS_ROOT/firmware/devicetree/base/compatible")
|
||||
fi
|
||||
fi
|
||||
|
||||
# m1n1 names every Apple Silicon Mac "apple,<model>" in the root node's
|
||||
# NUL-separated compatible list, as omarchy-hw-platform reads it.
|
||||
is_mac() {
|
||||
local source
|
||||
for source in "${compatible[@]}"; do
|
||||
[[ -r $source ]] && tr '\0' '\n' <"$source" | grep -q '^apple,' && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# pacman writes NeedsTargets to a socket and fails the write if the hook has
|
||||
# gone; hooks without NeedsTargets get no stdin at all.
|
||||
pass() {
|
||||
if [[ -S /dev/stdin || -p /dev/stdin ]]; then
|
||||
cat >/dev/null
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
mode=limine
|
||||
case ${1:-} in
|
||||
--mac | --not-mac)
|
||||
mode=${1#--}
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
|
||||
if (($# == 0)); then
|
||||
[[ $mode == mac ]] && { is_mac; exit; }
|
||||
echo "usage: limine-apple-gate [--mac|--not-mac] COMMAND [ARG]..." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
case $mode in
|
||||
mac) is_mac || pass ;;
|
||||
not-mac) ! is_mac || pass ;;
|
||||
limine) ! is_mac || [[ -e $gate && -f $limine_default ]] || pass ;;
|
||||
esac
|
||||
exec "$@"
|
||||
@@ -0,0 +1,38 @@
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Operation = Remove
|
||||
Target = usr/lib/initcpio/*
|
||||
Target = usr/lib/firmware/*
|
||||
Target = usr/lib/modules/*/extramodules/
|
||||
Target = usr/src/*/dkms.conf
|
||||
Target = usr/lib/systemd/systemd
|
||||
Target = usr/lib/systemd/systemd-udevd
|
||||
Target = usr/bin/cryptsetup
|
||||
Target = usr/bin/lvm
|
||||
Target = usr/bin/mdadm
|
||||
Target = usr/bin/pdata_tools
|
||||
Target = usr/lib/libcryptsetup.so
|
||||
Target = usr/lib/libp11-kit.so
|
||||
Target = usr/lib/libpcsclite.so
|
||||
Target = usr/lib/modprobe.d/
|
||||
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Target = usr/lib/modules/*/vmlinuz
|
||||
|
||||
[Trigger]
|
||||
Type = Package
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Target = mkinitcpio
|
||||
Target = mkinitcpio-git
|
||||
|
||||
[Action]
|
||||
Description = Updating linux initcpios...
|
||||
When = PostTransaction
|
||||
Exec = /usr/share/libalpm/scripts/mkinitcpio install
|
||||
NeedsTargets
|
||||
Executable
+532
@@ -0,0 +1,532 @@
|
||||
#!/bin/bash
|
||||
# limine-mkinitcpio-hook: the x86_64 package is upstream's as before. On
|
||||
# aarch64, limine-apple-gate leaves every machine but an Apple Silicon Mac as
|
||||
# the x86_64 package behaves; a Mac keeps /boot current through mkinitcpio's
|
||||
# own kernel hook, and Limine waits until Omarchy activates it.
|
||||
#
|
||||
# The package() of the PKGBUILD runs over an upstream-shaped source tree
|
||||
# (LIMINE_ENTRY_TOOL_SRC may name a real limine-entry-tool checkout instead),
|
||||
# and pacman transactions replay through the packaged hooks on fixture machines.
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
RECIPE=$REPO_ROOT/pkgbuilds/limine-mkinitcpio-hook
|
||||
GATE=$RECIPE/limine-apple-gate
|
||||
TEST_ROOT=$(mktemp -d)
|
||||
trap 'rm -rf "$TEST_ROOT"' EXIT
|
||||
|
||||
fail() {
|
||||
echo "not ok - $1" >&2
|
||||
[[ $# -lt 2 ]] || printf '%s\n' "$2" >&2
|
||||
exit 1
|
||||
}
|
||||
pass() {
|
||||
echo "ok - $1"
|
||||
}
|
||||
|
||||
# pacman hands NeedsTargets to a hook over a socket, and a hook that stops
|
||||
# reading early fails the write ("unable to write to pipe").
|
||||
SOCKET_FEED='
|
||||
import socket, subprocess, sys
|
||||
parent, child = socket.socketpair()
|
||||
hook = subprocess.Popen(sys.argv[1:], stdin=child)
|
||||
child.close()
|
||||
try:
|
||||
parent.sendall(sys.stdin.buffer.read())
|
||||
parent.shutdown(socket.SHUT_WR)
|
||||
except OSError as error:
|
||||
print("unable to write to pipe (%s)" % error.strerror, file=sys.stderr)
|
||||
hook.wait()
|
||||
sys.exit(141)
|
||||
sys.exit(hook.wait())
|
||||
'
|
||||
socket_feed() { python3 -c "$SOCKET_FEED" "$@"; }
|
||||
|
||||
apple_tree() { printf 'apple,j316s\0apple,t6000\0apple,arm-platform\0'; }
|
||||
live_is_mac() {
|
||||
local source
|
||||
for source in /proc/device-tree/compatible /sys/firmware/devicetree/base/compatible; do
|
||||
[[ -r $source ]] && tr '\0' '\n' <"$source" | grep -q '^apple,' && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# The gate trusts fixtures only from an unprivileged caller: as root it reads
|
||||
# the live machine. Check that, then carry on as nobody.
|
||||
if ((EUID == 0)); then
|
||||
if ! live_is_mac; then
|
||||
mkdir -p "$TEST_ROOT/root-proc/device-tree"
|
||||
apple_tree >"$TEST_ROOT/root-proc/device-tree/compatible"
|
||||
out=$(OMARCHY_PROC_ROOT=$TEST_ROOT/root-proc bash "$GATE" echo ran </dev/null)
|
||||
[[ $out == ran ]] || fail "as root the gate ignores a fixture device tree" "$out"
|
||||
pass "as root the gate reads the live machine, not fixtures"
|
||||
fi
|
||||
rm -rf "$TEST_ROOT"
|
||||
exec setpriv --reuid=nobody --regid=nobody --clear-groups -- bash "$REPO_ROOT/tests/limine-mkinitcpio-hook.sh"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sources: the local aarch64 sources are pinned, x86_64 has none.
|
||||
bash -n "$RECIPE/PKGBUILD"
|
||||
bash -n "$GATE"
|
||||
mapfile -t sources < <(CARCH=aarch64 bash -c 'source "$1"; printf "%s\n" "${source_aarch64[@]}"' _ "$RECIPE/PKGBUILD")
|
||||
mapfile -t sums < <(CARCH=aarch64 bash -c 'source "$1"; printf "%s\n" "${sha256sums_aarch64[@]}"' _ "$RECIPE/PKGBUILD")
|
||||
((${#sources[@]} == ${#sums[@]})) || fail "every aarch64 source has a checksum"
|
||||
local_sources=0
|
||||
for i in "${!sources[@]}"; do
|
||||
[[ ${sources[i]} == *://* ]] && continue
|
||||
((++local_sources))
|
||||
[[ $(sha256sum "$RECIPE/${sources[i]}" | cut -d' ' -f1) == "${sums[i]}" ]] ||
|
||||
fail "the PKGBUILD pins ${sources[i]} by its sha256"
|
||||
done
|
||||
((local_sources == 2)) || fail "limine-apple-gate and mkinitcpio-install.hook are the aarch64 local sources"
|
||||
x86_sources=$(CARCH=x86_64 bash -c 'source "$1"; printf "%s\n" "${source[@]}" "${source_x86_64[@]}"' _ "$RECIPE/PKGBUILD")
|
||||
[[ $x86_sources != *limine-apple-gate* && $x86_sources != *mkinitcpio-install.hook* ]] ||
|
||||
fail "x86_64 builds take no Apple Silicon source"
|
||||
grep -Fxq 'Exec = /usr/share/libalpm/scripts/mkinitcpio install' "$RECIPE/mkinitcpio-install.hook" ||
|
||||
fail "mkinitcpio-install.hook is mkinitcpio's own kernel hook"
|
||||
pass "the Apple Silicon sources are aarch64-only and pinned by checksum"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# package() for both architectures over the same upstream tree.
|
||||
SRC=$TEST_ROOT/src
|
||||
upstream=$SRC/limine-entry-tool
|
||||
mkdir -p "$upstream/build/native/nativeCompile"
|
||||
if [[ -n ${LIMINE_ENTRY_TOOL_SRC:-} ]]; then
|
||||
cp -a "$LIMINE_ENTRY_TOOL_SRC/install" "$LIMINE_ENTRY_TOOL_SRC/README.md" "$LIMINE_ENTRY_TOOL_SRC/CHANGELOG.md" "$upstream/"
|
||||
else
|
||||
tool=$upstream/install/arch-linux/limine-entry-tool
|
||||
hook=$upstream/install/arch-linux/limine-mkinitcpio-hook
|
||||
mkdir -p "$tool/etc/boot/hooks/pre.d" "$tool/usr/bin" "$tool/usr/lib/limine" "$tool/usr/share/libalpm/hooks" \
|
||||
"$hook/etc/pacman.d/hooks" "$hook/usr/bin" "$hook/usr/local/bin" "$hook/usr/share/libalpm/hooks" "$hook/usr/share/libalpm/scripts"
|
||||
echo '# README' >"$upstream/README.md"
|
||||
echo '# CHANGELOG' >"$upstream/CHANGELOG.md"
|
||||
echo 'ESP_PATH=""' >"$tool/etc/limine-entry-tool.conf"
|
||||
echo 'ESP_PATH=""' >"$hook/etc/limine-entry-tool.conf"
|
||||
for name in limine-install limine-entry-tool limine-reset-enroll limine-enroll-config; do
|
||||
printf '#!/usr/bin/env bash\n' >"$tool/usr/bin/$name"
|
||||
done
|
||||
printf '#!/usr/bin/env bash\n' >"$tool/usr/lib/limine/limine-common-functions"
|
||||
for name in limine-mkinitcpio limine-update; do
|
||||
printf '#!/usr/bin/env bash\n' >"$hook/usr/bin/$name"
|
||||
done
|
||||
for name in limine-mkinitcpio-install limine-mkinitcpio-remove; do
|
||||
printf '#!/usr/bin/env bash\n' >"$hook/usr/share/libalpm/scripts/$name"
|
||||
done
|
||||
cat >"$tool/usr/share/libalpm/hooks/80-limine-efi-deploy.hook" <<'HOOK'
|
||||
[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
Target = limine
|
||||
Target = limine-git
|
||||
Target = limine-dev
|
||||
|
||||
[Action]
|
||||
Description = Deploying Limine after upgrade...
|
||||
When = PostTransaction
|
||||
Exec = /usr/bin/limine-install --no-efi-register
|
||||
HOOK
|
||||
cat >"$hook/usr/share/libalpm/hooks/80-limine-efi-deploy.hook" <<'HOOK'
|
||||
[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
Target = limine
|
||||
Target = limine-git
|
||||
Target = limine-dev
|
||||
Target = limine-mkinitcpio-hook
|
||||
Target = limine-mkinitcpio-hook-git
|
||||
|
||||
[Action]
|
||||
Description = Deploying Limine after upgrade...
|
||||
When = PostTransaction
|
||||
Exec = /usr/bin/limine-install
|
||||
HOOK
|
||||
cat >"$hook/usr/share/libalpm/hooks/60-limine-mkinitcpio-remove-pre.hook" <<'HOOK'
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Remove
|
||||
Target = usr/lib/modules/*/modules.builtin
|
||||
|
||||
[Action]
|
||||
Description = Record kernels marked for removal in Limine
|
||||
When = PreTransaction
|
||||
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-remove pre
|
||||
NeedsTargets
|
||||
HOOK
|
||||
cat >"$hook/usr/share/libalpm/hooks/90-limine-mkinitcpio-remove-post.hook" <<'HOOK'
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Remove
|
||||
Target = usr/lib/modules/*/modules.builtin
|
||||
|
||||
[Action]
|
||||
Description = Clean Limine boot entries of removed kernels
|
||||
When = PostTransaction
|
||||
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-remove post
|
||||
HOOK
|
||||
cat >"$hook/etc/pacman.d/hooks/90-mkinitcpio-install.hook" <<'HOOK'
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Operation = Remove
|
||||
Target = usr/lib/initcpio/*
|
||||
Target = usr/lib/firmware/*
|
||||
Target = usr/lib/modules/*/extramodules/
|
||||
Target = usr/src/*/dkms.conf
|
||||
Target = usr/lib/systemd/systemd
|
||||
Target = usr/bin/cryptsetup
|
||||
Target = usr/bin/lvm
|
||||
|
||||
[Trigger]
|
||||
Type = Path
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Target = usr/lib/modules/*/modules.builtin
|
||||
|
||||
[Trigger]
|
||||
Type = Package
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Target = mkinitcpio
|
||||
Target = mkinitcpio-git
|
||||
|
||||
[Action]
|
||||
Description = Updating linux initcpios...
|
||||
When = PostTransaction
|
||||
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-install
|
||||
NeedsTargets
|
||||
HOOK
|
||||
cat >"$hook/usr/local/bin/mkinitcpio" <<'WRAPPER'
|
||||
#!/usr/bin/env bash
|
||||
|
||||
/usr/bin/mkinitcpio "$@"
|
||||
|
||||
_color_reset=""
|
||||
_color_yellow=""
|
||||
colors="$(tput colors 2>/dev/null || echo 0)"
|
||||
if ((colors >= 8)); then
|
||||
_color_reset="\033[0m"
|
||||
_color_yellow="\033[1;33m"
|
||||
fi
|
||||
|
||||
if [[ " $* " == *" -P "* || " $* " == *" --allpresets "* || " $* " == *" -p "* || " $* " == *" --preset "* ]]; then
|
||||
printf '%b==> WARNING: This does not update Limine boot entries.%b\n' "${_color_yellow}" "${_color_reset}" >&2
|
||||
printf "%b Use 'limine-mkinitcpio' or 'limine-update' instead.%b\n" "${_color_yellow}" "${_color_reset}" >&2
|
||||
|
||||
read -rp "==> Would you like to run 'limine-mkinitcpio' now? [Y/n]: " answer
|
||||
case "${answer,,}" in
|
||||
"" | "y" | "yes")
|
||||
/usr/bin/limine-mkinitcpio
|
||||
;;
|
||||
*)
|
||||
# nothing
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
WRAPPER
|
||||
fi
|
||||
printf 'native image\n' >"$upstream/build/native/nativeCompile/limine-entry-tool"
|
||||
chmod -R u+w,go+rX "$upstream"
|
||||
find "$upstream/install" -path '*/bin/*' -type f -exec chmod 755 {} + -o -path '*/scripts/*' -type f -exec chmod 755 {} +
|
||||
ln -s "$RECIPE/limine-apple-gate" "$RECIPE/mkinitcpio-install.hook" "$SRC/"
|
||||
|
||||
# makepkg runs package() with errexit.
|
||||
build_package() {
|
||||
local arch=$1 out=$2
|
||||
mkdir -p "$out"
|
||||
CARCH=$arch srcdir=$SRC pkgdir=$out bash -euo pipefail -c 'source "$1"; package' _ "$RECIPE/PKGBUILD" ||
|
||||
fail "package() succeeds for $arch"
|
||||
}
|
||||
PKG_X86=$TEST_ROOT/pkg-x86_64
|
||||
PKG_ARM=$TEST_ROOT/pkg-aarch64
|
||||
build_package x86_64 "$PKG_X86"
|
||||
build_package aarch64 "$PKG_ARM"
|
||||
|
||||
manifest() {
|
||||
(cd "$1" && find . \( -type f -o -type l \) -printf '%p %m %l\n' | LC_ALL=C sort) |
|
||||
while read -r path mode target; do
|
||||
if [[ -n $target ]]; then
|
||||
echo "$path $mode -> $target"
|
||||
else
|
||||
echo "$path $mode $(sha256sum "$1/$path" | cut -d' ' -f1)"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
scripts=/usr/share/libalpm/scripts
|
||||
gate=$scripts/limine-apple-gate
|
||||
upstream_hook=$upstream/install/arch-linux/limine-mkinitcpio-hook
|
||||
for file in etc/pacman.d/hooks/90-mkinitcpio-install.hook usr/local/bin/mkinitcpio \
|
||||
usr/share/libalpm/hooks/{60-limine-mkinitcpio-remove-pre,80-limine-efi-deploy,90-limine-mkinitcpio-remove-post}.hook; do
|
||||
cmp -s "$upstream_hook/$file" "$PKG_X86/$file" || fail "x86_64 ships upstream's $file unchanged"
|
||||
done
|
||||
[[ ! -e $PKG_X86$gate && ! -e $PKG_X86/usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook ]] ||
|
||||
fail "x86_64 ships no Apple Silicon file"
|
||||
pass "x86_64 packages upstream's hooks and wrapper unchanged"
|
||||
|
||||
differences=$(diff <(manifest "$PKG_X86") <(manifest "$PKG_ARM") || true)
|
||||
changed=$(sed -n 's/^> \(\S*\) .*/\1/p' <<<"$differences")
|
||||
expected=$(printf '%s\n' ./etc/pacman.d/hooks/90-mkinitcpio-install.hook \
|
||||
./usr/local/bin/mkinitcpio \
|
||||
./usr/share/libalpm/hooks/60-limine-mkinitcpio-remove-pre.hook \
|
||||
./usr/share/libalpm/hooks/80-limine-efi-deploy.hook \
|
||||
./usr/share/libalpm/hooks/90-limine-mkinitcpio-remove-post.hook \
|
||||
./usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook \
|
||||
./usr/share/libalpm/scripts/limine-apple-gate)
|
||||
[[ $changed == "$expected" ]] || fail "aarch64 differs from x86_64 only in the gated hooks, the wrapper and the gate" "$changed"
|
||||
[[ -z $(sed -n 's/^< \(\S*\) .*/\1/p' <<<"$differences" | grep -vxF "$expected") ]] ||
|
||||
fail "aarch64 keeps every x86_64 file"
|
||||
[[ $(stat -c %a "$PKG_ARM$gate") == 755 ]] || fail "the gate is executable"
|
||||
diff <(grep -v '^Exec = \|^Description = ' "$RECIPE/mkinitcpio-install.hook") \
|
||||
<(grep -v '^Exec = \|^Description = ' "$PKG_ARM/usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook") >/dev/null ||
|
||||
fail "the Mac's kernel hook keeps mkinitcpio's triggers"
|
||||
[[ $(sed -n 2p "$PKG_ARM/usr/local/bin/mkinitcpio") == "$gate --mac && exec /usr/bin/mkinitcpio \"\$@\"" ]] ||
|
||||
fail "the aarch64 wrapper is plain mkinitcpio on a Mac"
|
||||
pass "aarch64 routes Limine's hooks and the wrapper through the gate and adds the Mac's kernel hook"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixture machines, read through OMARCHY_PROC_ROOT, OMARCHY_LIMINE_GATE and
|
||||
# OMARCHY_LIMINE_DEFAULT.
|
||||
machine() {
|
||||
local name=$1 compatible=$2 dir=$TEST_ROOT/machines/$1
|
||||
mkdir -p "$dir/proc" "$dir/state"
|
||||
if [[ -n $compatible ]]; then
|
||||
mkdir -p "$dir/proc/device-tree"
|
||||
printf "$compatible" >"$dir/proc/device-tree/compatible"
|
||||
fi
|
||||
}
|
||||
machine x86 ''
|
||||
machine generic-aarch64 'linux,dummy-virt\0'
|
||||
machine qualcomm 'lenovo,thinkpad-t14s\0qcom,x1e78100\0qcom,x1e80100\0'
|
||||
machine mac-dormant 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
|
||||
machine mac-marker-only 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
|
||||
machine mac-active 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
|
||||
: >"$TEST_ROOT/machines/mac-marker-only/state/limine.enabled"
|
||||
: >"$TEST_ROOT/machines/mac-active/state/limine.enabled"
|
||||
echo 'ESP_PATH="/boot/efi"' >"$TEST_ROOT/machines/mac-active/state/limine"
|
||||
|
||||
on() {
|
||||
local dir=$TEST_ROOT/machines/$1
|
||||
shift
|
||||
(
|
||||
export OMARCHY_PROC_ROOT=$dir/proc OMARCHY_LIMINE_GATE=$dir/state/limine.enabled OMARCHY_LIMINE_DEFAULT=$dir/state/limine
|
||||
"$@"
|
||||
)
|
||||
}
|
||||
status() {
|
||||
local rc=0
|
||||
"$@" || rc=$?
|
||||
echo "$rc"
|
||||
}
|
||||
|
||||
for m in x86 generic-aarch64 qualcomm; do
|
||||
[[ $(on "$m" status bash "$GATE" --mac) == 1 ]] || fail "$m is not a Mac"
|
||||
[[ $(printf 'a\nb\n' | on "$m" bash "$GATE" cat) == $'a\nb' ]] || fail "$m runs Limine's hooks with their targets"
|
||||
[[ $(on "$m" status bash "$GATE" bash -c 'exit 7' </dev/null) == 7 ]] || fail "$m keeps the hook's status"
|
||||
[[ -z $(printf 'a\n' | on "$m" bash "$GATE" --mac cat 2>&1) ]] || fail "$m skips the Mac's hooks quietly"
|
||||
[[ $(on "$m" bash "$GATE" --not-mac echo ran </dev/null) == ran ]] || fail "$m deploys Limine's EFI binary"
|
||||
done
|
||||
for m in mac-dormant mac-marker-only mac-active; do
|
||||
[[ $(on "$m" status bash "$GATE" --mac) == 0 ]] || fail "$m is a Mac"
|
||||
[[ $(printf 'a\n' | on "$m" bash "$GATE" --mac cat) == a ]] || fail "$m runs the Mac's hooks with their targets"
|
||||
[[ -z $(printf 'a\n' | on "$m" bash "$GATE" --not-mac cat 2>&1) ]] || fail "$m leaves Limine's EFI binary to omarchy-mac-boot"
|
||||
done
|
||||
for m in mac-dormant mac-marker-only; do
|
||||
out=$(printf 'usr/lib/modules/7.1.0/modules.builtin\n' | on "$m" bash "$GATE" bash -c 'echo ran; exit 100' 2>&1) ||
|
||||
fail "$m passes over Limine's hooks successfully"
|
||||
[[ -z $out ]] || fail "$m passes over Limine's hooks quietly" "$out"
|
||||
[[ -z $(on "$m" bash "$GATE" echo ran <&- 2>&1) ]] || fail "$m passes over a hook without targets"
|
||||
done
|
||||
# A linux-firmware upgrade streams thousands of targets, more than a socket holds.
|
||||
many_targets() { printf 'usr/lib/firmware/fixture/%s.bin\n' $(seq 60000); }
|
||||
many_targets | on mac-dormant socket_feed bash "$GATE" true || fail "a dormant Mac reads every target of a hook it passes over"
|
||||
many_targets | on x86 socket_feed bash "$GATE" --mac true || fail "other machines read every target of the Mac's hooks"
|
||||
many_targets | on mac-active socket_feed bash "$GATE" --not-mac true || fail "a Mac reads every target of the deploy hook"
|
||||
many_targets | on mac-dormant bash "$GATE" true || fail "a dormant Mac reads every target piped to a hook it passes over"
|
||||
[[ $(printf 'a\n' | on mac-active bash "$GATE" bash -c 'echo "ran $1"; cat' _ post) == $'ran post\na' ]] ||
|
||||
fail "an active Mac runs Limine's hooks with their arguments and targets"
|
||||
[[ $(on mac-active status bash "$GATE" bash -c 'exit 7' </dev/null) == 7 ]] || fail "an active Mac keeps the hook's status"
|
||||
[[ $(on x86 status bash "$GATE" </dev/null 2>/dev/null) == 2 ]] || fail "the gate needs a command"
|
||||
pass "the gate runs Limine's hooks everywhere but a Mac that has not activated Limine"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# pacman: hooks from /usr/share/libalpm/hooks, overridden by name from
|
||||
# /etc/pacman.d/hooks, run in name order, each with the targets of the
|
||||
# transaction its triggers match (sorted, over a socket for NeedsTargets,
|
||||
# else no stdin).
|
||||
# Transactions are lines of "<Operation> <Path|Package> <target>".
|
||||
fake_root() {
|
||||
local pkg=$1 root=$2 name
|
||||
cp -a "$pkg" "$root"
|
||||
cp "$RECIPE/mkinitcpio-install.hook" "$root/usr/share/libalpm/hooks/90-mkinitcpio-install.hook"
|
||||
for name in usr/share/libalpm/scripts/limine-mkinitcpio-install usr/share/libalpm/scripts/limine-mkinitcpio-remove \
|
||||
usr/bin/limine-install usr/share/libalpm/scripts/mkinitcpio usr/bin/mkinitcpio usr/bin/limine-mkinitcpio; do
|
||||
rm -f "$root/$name"
|
||||
printf '#!/bin/bash\n{ printf %%s %q; if (($#)); then printf " %%s" "$@"; fi; echo; if [[ -S /dev/stdin || -p /dev/stdin ]]; then sed "s/^/ /"; fi; } >>"$SIM_LOG"\n' \
|
||||
"${name##*/}" >"$root/$name"
|
||||
chmod 755 "$root/$name"
|
||||
done
|
||||
}
|
||||
|
||||
run_hook() {
|
||||
local root=$1 hook=$2 when=$3 transaction=$4
|
||||
local line section='' hook_when='' exec_line='' needs=0 type='' ops='' targets=''
|
||||
local -a triggers=()
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
case $line in
|
||||
'[Trigger]' | '[Action]')
|
||||
[[ $section != Trigger ]] || triggers+=("$type|$ops|$targets")
|
||||
section=${line//[][]/} type='' ops=' ' targets=''
|
||||
;;
|
||||
NeedsTargets) needs=1 ;;
|
||||
*' = '*)
|
||||
case $section/${line%% = *} in
|
||||
Trigger/Type) type=${line#* = } ;;
|
||||
Trigger/Operation) ops+="${line#* = } " ;;
|
||||
Trigger/Target) targets+="${line#* = } " ;;
|
||||
Action/When) hook_when=${line#* = } ;;
|
||||
Action/Exec) exec_line=${line#* = } ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done <"$hook"
|
||||
[[ $hook_when == "$when" ]] || return 0
|
||||
|
||||
local op kind target trigger t_type t_ops t_targets pattern
|
||||
local -a matched=() patterns=()
|
||||
while read -r op kind target; do
|
||||
for trigger in "${triggers[@]}"; do
|
||||
IFS='|' read -r t_type t_ops t_targets <<<"$trigger"
|
||||
[[ $t_type == "$kind" && $t_ops == *" $op "* ]] || continue
|
||||
read -ra patterns <<<"$t_targets"
|
||||
for pattern in "${patterns[@]}"; do
|
||||
# shellcheck disable=SC2053
|
||||
if [[ $target == $pattern ]]; then
|
||||
matched+=("$target")
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
done <"$transaction"
|
||||
((${#matched[@]})) || return 0
|
||||
|
||||
local word
|
||||
local -a argv=() command=()
|
||||
read -ra argv <<<"$exec_line"
|
||||
for word in "${argv[@]}"; do
|
||||
if [[ $word == /* && -e $root$word ]]; then command+=("$root$word"); else command+=("$word"); fi
|
||||
done
|
||||
if ((needs)); then
|
||||
printf '%s\n' "${matched[@]}" | LC_ALL=C sort -u | socket_feed "${command[@]}"
|
||||
else
|
||||
"${command[@]}" <&-
|
||||
fi
|
||||
}
|
||||
|
||||
run_transaction() {
|
||||
local root=$1 transaction=$2 when name
|
||||
local -A hooks=()
|
||||
for name in "$root"/usr/share/libalpm/hooks/*.hook "$root"/etc/pacman.d/hooks/*.hook; do
|
||||
[[ -e $name ]] && hooks[${name##*/}]=$name
|
||||
done
|
||||
for when in PreTransaction PostTransaction; do
|
||||
while read -r name; do
|
||||
run_hook "$root" "${hooks[$name.hook]}" "$when" "$transaction"
|
||||
done < <(printf '%s\n' "${!hooks[@]}" | sed 's/\.hook$//' | LC_ALL=C sort)
|
||||
done
|
||||
}
|
||||
|
||||
fake_root "$PKG_X86" "$TEST_ROOT/root-x86_64"
|
||||
fake_root "$PKG_ARM" "$TEST_ROOT/root-aarch64"
|
||||
|
||||
transaction() {
|
||||
local name=$1
|
||||
shift
|
||||
printf '%s\n' "$@" >"$TEST_ROOT/tx-$name"
|
||||
}
|
||||
transaction kernel \
|
||||
'Install Path usr/lib/modules/7.1.0-asahi/vmlinuz' \
|
||||
'Install Path usr/lib/modules/7.1.0-asahi/modules.builtin' \
|
||||
'Install Path usr/lib/modules/7.1.0-asahi/pkgbase' \
|
||||
'Remove Path usr/lib/modules/7.0.0-asahi/vmlinuz' \
|
||||
'Remove Path usr/lib/modules/7.0.0-asahi/modules.builtin' \
|
||||
'Remove Path usr/lib/modules/7.0.0-asahi/pkgbase' \
|
||||
'Upgrade Package linux-asahi'
|
||||
transaction firmware 'Upgrade Path usr/lib/firmware/brcm/fixture.bin' 'Upgrade Package linux-firmware'
|
||||
transaction modprobe 'Upgrade Path usr/lib/modprobe.d/' 'Install Path usr/lib/modprobe.d/omarchy-mac.conf' 'Upgrade Package omarchy-mac'
|
||||
transaction limine 'Upgrade Path usr/share/limine/BOOTAA64.EFI' 'Upgrade Package limine'
|
||||
|
||||
replay() {
|
||||
local arch=$1 m=$2 name=$3
|
||||
export SIM_LOG=$TEST_ROOT/log
|
||||
: >"$SIM_LOG"
|
||||
local out
|
||||
out=$(on "$m" run_transaction "$TEST_ROOT/root-$arch" "$TEST_ROOT/tx-$name" 2>&1) || fail "$name on $arch/$m replays"
|
||||
[[ -z $out ]] || fail "$name on $arch/$m prints nothing of the gate's" "$out"
|
||||
cat "$SIM_LOG"
|
||||
}
|
||||
|
||||
limine_kernel=$'limine-mkinitcpio-remove pre\n usr/lib/modules/7.0.0-asahi/modules.builtin\nlimine-mkinitcpio-remove post\nlimine-mkinitcpio-install\n usr/lib/modules/7.1.0-asahi/modules.builtin'
|
||||
stock_kernel=$'mkinitcpio install\n usr/lib/modules/7.1.0-asahi/vmlinuz'
|
||||
limine_firmware=$'limine-mkinitcpio-install\n usr/lib/firmware/brcm/fixture.bin'
|
||||
stock_firmware=$'mkinitcpio install\n usr/lib/firmware/brcm/fixture.bin'
|
||||
stock_modprobe=$'mkinitcpio install\n usr/lib/modprobe.d/'
|
||||
|
||||
declare -A want=(
|
||||
[kernel/other]=$limine_kernel
|
||||
[firmware/other]=$limine_firmware
|
||||
[modprobe/other]=''
|
||||
[limine/other]='limine-install'
|
||||
[kernel/mac-dormant]=$stock_kernel
|
||||
[firmware/mac-dormant]=$stock_firmware
|
||||
[modprobe/mac-dormant]=$stock_modprobe
|
||||
[limine/mac-dormant]=''
|
||||
[kernel/mac-active]=$'limine-mkinitcpio-remove pre\n usr/lib/modules/7.0.0-asahi/modules.builtin\nlimine-mkinitcpio-remove post\n'"$stock_kernel"$'\nlimine-mkinitcpio-install\n usr/lib/modules/7.1.0-asahi/modules.builtin'
|
||||
[firmware/mac-active]=$stock_firmware$'\n'$limine_firmware
|
||||
[modprobe/mac-active]=$stock_modprobe
|
||||
[limine/mac-active]=''
|
||||
)
|
||||
want[kernel/mac-marker-only]=${want[kernel/mac-dormant]}
|
||||
|
||||
for name in kernel firmware modprobe limine; do
|
||||
got=$(replay x86_64 x86 "$name")
|
||||
[[ $got == "${want[$name/other]}" ]] || fail "x86_64: $name transaction runs upstream's hooks" "$got"
|
||||
for m in generic-aarch64 qualcomm; do
|
||||
got=$(replay aarch64 "$m" "$name")
|
||||
[[ $got == "${want[$name/other]}" ]] || fail "aarch64 $m: $name transaction runs as on x86_64" "$got"
|
||||
done
|
||||
done
|
||||
pass "x86_64, generic aarch64 and Snapdragon transactions run Limine's hooks as upstream ships them"
|
||||
|
||||
for m in mac-dormant mac-marker-only mac-active; do
|
||||
for name in kernel firmware modprobe limine; do
|
||||
[[ -v want[$name/$m] ]] || continue
|
||||
got=$(replay aarch64 "$m" "$name")
|
||||
[[ $got == "${want[$name/$m]}" ]] || fail "$m: $name transaction" "$got"
|
||||
done
|
||||
done
|
||||
pass "a dormant Mac defers to mkinitcpio's own hook; an active Mac also writes Limine's entries after it"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The wrapper, as sudo mkinitcpio -P and mkinitcpio's hook script call it
|
||||
# (/usr/local/bin first in PATH, stdin at its end).
|
||||
wrapper() {
|
||||
local root=$1 m=$2
|
||||
sed "s|/usr/|$root/usr/|g" "$root/usr/local/bin/mkinitcpio" >"$TEST_ROOT/wrapper"
|
||||
export SIM_LOG=$TEST_ROOT/log
|
||||
: >"$SIM_LOG"
|
||||
on "$m" bash "$TEST_ROOT/wrapper" -P </dev/null >/dev/null 2>&1 || true
|
||||
cat "$SIM_LOG"
|
||||
}
|
||||
upstream_wrapper=$'mkinitcpio -P\nlimine-mkinitcpio'
|
||||
[[ $(wrapper "$TEST_ROOT/root-x86_64" x86) == "$upstream_wrapper" ]] || fail "x86_64 keeps upstream's wrapper"
|
||||
for m in generic-aarch64 qualcomm; do
|
||||
[[ $(wrapper "$TEST_ROOT/root-aarch64" "$m") == "$upstream_wrapper" ]] || fail "$m keeps upstream's wrapper"
|
||||
done
|
||||
for m in mac-dormant mac-active; do
|
||||
[[ $(wrapper "$TEST_ROOT/root-aarch64" "$m") == 'mkinitcpio -P' ]] || fail "$m runs plain mkinitcpio"
|
||||
done
|
||||
pass "the mkinitcpio wrapper is upstream's everywhere but a Mac, where it is plain mkinitcpio"
|
||||
Reference in new issue
Block a user