From d9955f628fc4bb6339c3725e1d1278fe07b55afa Mon Sep 17 00:00:00 2001 From: youngrok-XCENA Date: Thu, 6 Aug 2026 17:56:09 +0900 Subject: [PATCH] feat(install): build for a named target kernel, not always the running one dkms and make default to `uname -r`. That is the kernel the module will run on when install.sh runs on the target machine, and the builder's kernel when it installs into an image being assembled, where the module built that way cannot load. Take the target as input: XCENA_TARGET_KVER names the kernel version (DKMS registration, install path, depmod, initramfs) and XCENA_TARGET_KDIR names the build tree. Either one is enough -- the version resolves to /lib/modules//build, the path DKMS and the distro header packages use, and the tree resolves to its own kernelrelease. With neither, the running kernel is used exactly as before. A tree outside the default path is passed on to DKMS via --kernelsourcedir. A missing tree fails with both remedies named. Two fixes in the same area: - The clean that runs before staging the source into /usr/src used the default build tree. Installing for another kernel made it fail silently, so build artifacts such as mx_dma.mod.c were copied into the DKMS source. - `udevadm control --reload-rules` is now best-effort. udevd is not running while an image is being assembled, and the rule file it would reload is already in place there. --- install.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/install.sh b/install.sh index 532fe90..a882244 100755 --- a/install.sh +++ b/install.sh @@ -19,6 +19,30 @@ else echo "[INFO] CEDT not found – building **without** CXL (WO_CXL=1)." fi +# Kernel to build for. Installing onto this machine needs neither input below; a +# caller installing for a different kernel (image build, or a locally built tree) +# gives one of them: +# +# XCENA_TARGET_KDIR kernel build tree. Default /lib/modules//build, +# the path DKMS and the distro header packages use. +# XCENA_TARGET_KVER kernel version. Names the DKMS registration, the install +# path, depmod and initramfs. Read from the tree when only +# XCENA_TARGET_KDIR is given. +KVER="${XCENA_TARGET_KVER:-}" +KDIR="${XCENA_TARGET_KDIR:-}" +if [[ -n "$KDIR" && -z "$KVER" ]]; then + KVER="$(make -s -C "$KDIR" kernelrelease 2>/dev/null || true)" + [[ -n "$KVER" ]] || { echo "[ERROR] cannot read kernelrelease from '${KDIR}'"; exit 1; } +fi +[[ -n "$KVER" ]] || KVER="$(uname -r)" +[[ -n "$KDIR" ]] || KDIR="/lib/modules/${KVER}/build" +if [[ ! -e "$KDIR" ]]; then + echo "[ERROR] kernel build tree not found: ${KDIR}" + echo " install linux-headers-${KVER}, or set XCENA_TARGET_KDIR to the tree." + exit 1 +fi +echo "[INFO] target kernel: ${KVER} (build tree: ${KDIR})" + install_dkms() { echo "[INFO] Installing ${PACKAGE_NAME} ${PACKAGE_VERSION} via DKMS..." @@ -35,16 +59,25 @@ install_dkms() { # Force-clean DKMS tree in case remove left stale entries rm -rf "/var/lib/dkms/${PACKAGE_NAME}" 2>/dev/null || true - # Copy source to DKMS source tree (clean first to exclude build artifacts like mx_dma.mod.c) + # Copy source to DKMS source tree (clean first to exclude build artifacts like + # mx_dma.mod.c). The clean needs the target kernel's build tree too: with the + # default it runs against the running kernel, which during an image build is a + # kernel absent from this root, so the clean silently does nothing and the + # artifacts get copied. rm -rf "${SRC_DIR}" mkdir -p "${SRC_DIR}/scripts" - make clean 2>/dev/null || true + make BUILDSYSTEM_DIR="$KDIR" clean 2>/dev/null || true cp -a Makefile dkms.conf *.c *.h "${SRC_DIR}/" cp -a scripts/dkms-post-install.sh "${SRC_DIR}/scripts/" + # DKMS resolves the tree as /lib/modules//build on its own, so only a + # tree outside that path has to be spelled out. + local dkms_src=() + [[ "$KDIR" != "/lib/modules/${KVER}/build" ]] && dkms_src=(--kernelsourcedir "$KDIR") + dkms add "${PACKAGE_NAME}/${PACKAGE_VERSION}" - dkms build "${PACKAGE_NAME}/${PACKAGE_VERSION}" - dkms install "${PACKAGE_NAME}/${PACKAGE_VERSION}" --force + dkms build "${PACKAGE_NAME}/${PACKAGE_VERSION}" -k "${KVER}" "${dkms_src[@]}" + dkms install "${PACKAGE_NAME}/${PACKAGE_VERSION}" -k "${KVER}" "${dkms_src[@]}" --force echo "[INFO] DKMS installation completed. Module will auto-rebuild on kernel upgrades." } @@ -58,11 +91,11 @@ install_legacy() { fi # shellcheck disable=SC2086 - make $MAKEVAR clean + make $MAKEVAR BUILDSYSTEM_DIR="$KDIR" clean # shellcheck disable=SC2086 - make $MAKEVAR -j"$(nproc)" install + make $MAKEVAR BUILDSYSTEM_DIR="$KDIR" -j"$(nproc)" install - depmod -a + depmod -a "${KVER}" } # Install module (prefer DKMS, fallback to legacy) @@ -84,7 +117,10 @@ if [[ -f /etc/udev/rules.d/99-xcena_set_devdax_perm.rules \ || -f /usr/local/sbin/xcena_set_devdax_perm ]]; then rm -f /etc/udev/rules.d/99-xcena_set_devdax_perm.rules rm -f /usr/local/sbin/xcena_set_devdax_perm - udevadm control --reload-rules + # Best-effort: udevd is not running during an image build, and what a booted + # system reads is the rule file, which is already gone by this point. + udevadm control --reload-rules 2>/dev/null \ + || echo "[INFO] udevd not running; rule change applies at boot." echo "[INFO] Removed obsolete xcena_set_devdax_perm helper." fi @@ -122,8 +158,8 @@ fi # where configured, the bundled mx_dma module. if [[ "$INITRAMFS_BACKEND" == "initramfs-tools" ]]; then echo "[INFO] Updating initramfs..." - update-initramfs -u -k "$(uname -r)" + update-initramfs -u -k "${KVER}" elif [[ "$INITRAMFS_BACKEND" == "dracut" ]]; then echo "[INFO] Updating initramfs via dracut..." - dracut --force --kver "$(uname -r)" + dracut --force --kver "${KVER}" fi