|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
# HP ZBook Ultra G1a — Linux 7.0-rc5 kernel build |
|
# Optimized for AMD Strix Halo ISP4 Support |
|
# Supports: GRUB (Arch/CachyOS) and Limine (Omarchy) |
|
# ------------------------------------------------------- |
|
|
|
SKIP_TO_BOOTLOADER=false |
|
if [[ "${1:-}" == "--skip-to-bootloader" ]]; then |
|
SKIP_TO_BOOTLOADER=true |
|
fi |
|
|
|
KERNEL_TAG="v7.0-rc5" |
|
BRANCH_NAME="hp-zbook-ultra-g1a" |
|
LOCALVERSION="-hp_zbook_ultra_g1a" |
|
BUILD_DIR="${HOME}/.tools/kernel/linux-${BRANCH_NAME}" |
|
NPROC=$(nproc) |
|
|
|
PATCHES=( |
|
"20260320201302.3490570-1-pratap.nirujogi@amd.com" # i2c resume-probe race fix |
|
"20260311174251.3121654-1-pratap.nirujogi@amd.com" # ISP segfault fix |
|
"20260318034842.1216536-1-pratap.nirujogi@amd.com" # ACPI bus MFD modprobe fix |
|
"5986516.DvuYhMxLoT@rafael.j.wysocki" # ACPI video auxiliary bus |
|
"20260320084146.200988-1-Bin.Du@amd.com" # AMD ISP4 capture driver (7 patches) |
|
) |
|
|
|
log() { echo -e "\n\033[1;32m==> $1\033[0m\n"; } |
|
error() { echo -e "\n\033[1;31m[!] $1\033[0m\n"; exit 1; } |
|
|
|
# --- Elevate once, keep sudo alive for the duration --- |
|
log "Requesting sudo credentials" |
|
sudo -v || error "sudo authentication failed" |
|
( while kill -0 $$ 2>/dev/null; do sudo -n -v; sleep 60; done ) & |
|
SUDO_KEEPALIVE_PID=$! |
|
trap 'kill $SUDO_KEEPALIVE_PID 2>/dev/null' EXIT |
|
|
|
# --- Install build dependencies --- |
|
sudo pacman -S --needed --noconfirm b4 python-requests base-devel bc |
|
|
|
# --- Detect bootloader --- |
|
if command -v limine-mkinitcpio >/dev/null 2>&1; then |
|
BOOTLOADER="limine" |
|
elif command -v grub-mkconfig >/dev/null 2>&1; then |
|
BOOTLOADER="grub" |
|
else |
|
error "No supported bootloader found (expected grub-mkconfig or limine-mkinitcpio)" |
|
fi |
|
log "Detected bootloader: ${BOOTLOADER}" |
|
|
|
if [ "$SKIP_TO_BOOTLOADER" = true ]; then |
|
log "Skipping to bootloader install" |
|
[ -d "$BUILD_DIR" ] || error "Build directory not found: ${BUILD_DIR}" |
|
cd "$BUILD_DIR" |
|
export LOCALVERSION="" |
|
KERNELRELEASE=$(make -s kernelrelease) |
|
log "Target Kernel Release: ${KERNELRELEASE}" |
|
else |
|
|
|
# --- Cleanup Old Attempts --- |
|
log "Cleaning up old custom kernel files" |
|
sudo rm -f /boot/*"${LOCALVERSION}"* |
|
if [ "$BOOTLOADER" = "limine" ]; then |
|
sudo rm -f /boot/EFI/Linux/*_"${BRANCH_NAME}"*.efi |
|
sudo limine-entry-tool --remove-uki "${BRANCH_NAME}" --quiet 2>/dev/null || true |
|
fi |
|
if [ -d "/usr/lib/modules" ]; then |
|
for d in /usr/lib/modules/*"${LOCALVERSION}" /usr/lib/modules/.old/*"${LOCALVERSION}"*; do |
|
[ -d "$d" ] && sudo rm -rf "$d" |
|
done |
|
fi |
|
|
|
# --- Prerequisites --- |
|
log "Checking prerequisites" |
|
for cmd in git b4 make gcc mkinitcpio zstd; do |
|
command -v "$cmd" >/dev/null || error "Missing dependency: $cmd" |
|
done |
|
|
|
# --- Clone/Setup --- |
|
if [ -d "$BUILD_DIR" ]; then |
|
log "Build directory exists. Re-cloning for a clean slate..." |
|
rm -rf "$BUILD_DIR" |
|
fi |
|
|
|
log "Cloning Linux ${KERNEL_TAG}" |
|
git clone --depth 1 --branch "$KERNEL_TAG" \ |
|
https://github.com/torvalds/linux.git "$BUILD_DIR" || \ |
|
git clone --depth 1 https://github.com/torvalds/linux.git "$BUILD_DIR" |
|
|
|
cd "$BUILD_DIR" |
|
git checkout -b "$BRANCH_NAME" "$KERNEL_TAG" || git checkout -b "$BRANCH_NAME" |
|
|
|
# --- Apply patches --- |
|
log "Applying patches via b4" |
|
for msgid in "${PATCHES[@]}"; do |
|
echo "--- Fetching and applying: $msgid" |
|
# Using pipe to avoid mbox file management |
|
if b4 am --no-cover -o - "$msgid" | git am; then |
|
echo " Applied successfully." |
|
else |
|
echo " Conflict or already applied — skipping." |
|
git am --abort 2>/dev/null || true |
|
fi |
|
done |
|
|
|
# --- Configure --- |
|
log "Configuring kernel" |
|
RUNNING_KERNEL=$(uname -r) |
|
if [ -f "/usr/lib/modules/${RUNNING_KERNEL}/build/.config" ]; then |
|
cp "/usr/lib/modules/${RUNNING_KERNEL}/build/.config" .config |
|
elif [ -f /proc/config.gz ]; then |
|
zcat /proc/config.gz > .config |
|
else |
|
error "No base config found." |
|
fi |
|
|
|
# Clean and set localversion |
|
sed -i '/CONFIG_LOCALVERSION/d' .config |
|
echo "CONFIG_LOCALVERSION=\"${LOCALVERSION}\"" >> .config |
|
echo "CONFIG_LOCALVERSION_AUTO=n" >> .config |
|
echo "CONFIG_VIDEO_AMD_ISP4_CAPTURE=m" >> .config |
|
|
|
# Prevent '+' suffix due to patched (dirty) git state |
|
# Setting LOCALVERSION in env (even empty) tells scripts/setlocalversion |
|
# to skip the "+" append for untagged commits (line 201-209). |
|
# CONFIG_LOCALVERSION in .config already carries our suffix. |
|
touch .scmversion |
|
export LOCALVERSION="" |
|
|
|
make olddefconfig |
|
KERNELRELEASE=$(make -s kernelrelease) |
|
log "Target Kernel Release: ${KERNELRELEASE}" |
|
|
|
# --- Build --- |
|
log "Building kernel (${NPROC} threads)" |
|
make -j"${NPROC}" |
|
|
|
fi # end SKIP_TO_BOOTLOADER |
|
|
|
# --- Install --- |
|
PRE_ID="" |
|
if command -v snapper >/dev/null; then |
|
log "Creating pre-install snapshot" |
|
# Fix: Correctly capture the snapshot number |
|
PRE_ID=$(sudo snapper create -d "before ${KERNELRELEASE} install" -t pre --print-number) |
|
fi |
|
|
|
log "Installing modules" |
|
sudo make modules_install |
|
|
|
if [ "$BOOTLOADER" = "limine" ]; then |
|
log "Installing kernel image (vmlinuz for limine UKI)" |
|
sudo install -Dm644 arch/x86/boot/bzImage "/usr/lib/modules/${KERNELRELEASE}/vmlinuz" |
|
|
|
log "Creating pkgbase for custom kernel" |
|
echo "${BRANCH_NAME}" | sudo tee "/usr/lib/modules/${KERNELRELEASE}/pkgbase" > /dev/null |
|
|
|
# limine-mkinitcpio skips kernels not owned by pacman (pacman -Qqo check). |
|
# Build the UKI and register the boot entry manually instead. |
|
ESP_PATH=$(bootctl --print-esp-path 2>/dev/null || echo "/boot") |
|
MACHINE_ID=$(cat /etc/machine-id) |
|
UKI_DIR="${ESP_PATH}/EFI/Linux" |
|
UKI_PATH="${UKI_DIR}/${MACHINE_ID}_${BRANCH_NAME}.efi" |
|
TMP_UKI="/tmp/staging_custom_uki.efi" |
|
|
|
log "Building UKI for custom kernel" |
|
sudo install -dm700 "${UKI_DIR}" |
|
|
|
# Get cmdline from the default kernel's existing entry |
|
CMDLINE=$(sudo grep -m1 'cmdline:' /boot/limine.conf | sed 's/.*cmdline:\s*//') |
|
echo "${CMDLINE}" | sudo tee /tmp/custom-kernel-cmdline > /dev/null |
|
|
|
sudo mkinitcpio --kernel "${KERNELRELEASE}" --uki "${TMP_UKI}" --cmdline /tmp/custom-kernel-cmdline |
|
sudo mv -f "${TMP_UKI}" "${UKI_PATH}" |
|
sudo rm -f /tmp/custom-kernel-cmdline |
|
log "UKI stored in ${UKI_PATH}" |
|
|
|
log "Registering boot entry with limine-entry-tool" |
|
sudo limine-entry-tool --add-uki "${BRANCH_NAME}" "${UKI_PATH}" \ |
|
--comment "Kernel version: ${KERNELRELEASE}" |
|
|
|
log "Setting custom kernel as default boot entry" |
|
LIMINE_CONF="${ESP_PATH}/limine.conf" |
|
if [ -f "${LIMINE_CONF}" ]; then |
|
# Find the 1-based index of our entry (last /:/ section in the config) |
|
ENTRY_COUNT=$(sudo grep -c '^/:' "${LIMINE_CONF}") |
|
sudo sed -i "s/^default_entry:.*/default_entry:${ENTRY_COUNT}/" "${LIMINE_CONF}" |
|
if ! sudo grep -q '^default_entry:' "${LIMINE_CONF}"; then |
|
sudo sed -i "1i default_entry:${ENTRY_COUNT}" "${LIMINE_CONF}" |
|
fi |
|
fi |
|
else |
|
log "Installing kernel image" |
|
sudo cp arch/x86/boot/bzImage "/boot/vmlinuz-${KERNELRELEASE}" |
|
|
|
log "Generating initramfs" |
|
sudo mkinitcpio -k "${KERNELRELEASE}" -g "/boot/initramfs-${KERNELRELEASE}.img" |
|
|
|
log "Updating GRUB" |
|
sudo grub-mkconfig -o /boot/grub/grub.cfg |
|
fi |
|
|
|
if [ -n "$PRE_ID" ]; then |
|
log "Creating post-install snapshot (linked to ID: $PRE_ID)" |
|
sudo snapper create -d "after ${KERNELRELEASE} install" -t post --pre-number "$PRE_ID" |
|
fi |
|
|
|
# --- ISP suspend/resume hook --- |
|
ISP_SUSPEND_SRC="${HOME}/.tools/kernel/isp-suspend" |
|
ISP_SUSPEND_DST="/usr/lib/systemd/system-sleep/isp-suspend" |
|
if [ ! -f "$ISP_SUSPEND_DST" ] || ! cmp -s "$ISP_SUSPEND_SRC" "$ISP_SUSPEND_DST"; then |
|
log "Installing ISP suspend/resume hook" |
|
sudo cp "$ISP_SUSPEND_SRC" "$ISP_SUSPEND_DST" |
|
sudo chmod +x "$ISP_SUSPEND_DST" |
|
else |
|
log "ISP suspend/resume hook already up to date" |
|
fi |
|
|
|
# --- Firmware Check --- |
|
log "Checking for ISP4 firmware" |
|
if [ -f "/usr/lib/firmware/amdgpu/isp_4_1_1.bin.zst" ] && [ ! -f "/usr/lib/firmware/amdgpu/isp_4_1_1.bin" ]; then |
|
echo "Decompressing ISP firmware for better compatibility..." |
|
sudo zstd -d /usr/lib/firmware/amdgpu/isp_4_1_1.bin.zst -o /usr/lib/firmware/amdgpu/isp_4_1_1.bin |
|
fi |
|
|
|
log "Done! Reboot and select ${KERNELRELEASE} in ${BOOTLOADER^^}." |