#!/bin/sh # # Copyright (C) 1995 - 1998, Ian A. Murdock # Copyright (C) 1998, 1999, Guy Maor # Copyright (C) 2002, Matthew Wilcox # Copyright (C) 2002, 2004, 2005, 2007, 2009 Clint Adams # Copyright (C) 2009 Manoj Srivasta # Copyright 2020-2024 Gentoo Authors # # Install the kernel on a Linux system. # # This script is called by the kernel's "make install" if it is installed as # /sbin/installkernel. It is also called by kernel-install.eclass. : "${SYSTEMD_KERNEL_INSTALL:=0}" : "${INSTALLKERNEL_VERBOSE:=0}" _ik_install_all=0 _ik_remaining_optargs= _ik_arg_index=0 # Use same defaults as systemd's kernel-install _ik_arg_1="$(uname -r)" _ik_arg_2=/lib/modules/${_ik_arg_1}/vmlinuz _ik_arg_3=/lib/modules/${_ik_arg_1}/System.map _ik_arg_4=/boot # Of course, powerpc has to be all different, and passes in a fifth # argument, just because it is "special". We ignore the fifth argument, # and do not flag is as an error, which it would be for any arch apart # from powerpc _ik_arg_5= while [ ${#} -gt 0 ]; do case "${1}" in --help|-h) echo "Gentoo's helper utility to install new kernel versions" echo echo "Usage: installkernel " echo echo "Optional arguments:" echo " -h, --help: display this help text" echo " -v, --verbose: run in verbose mode and export \$INSTALLKERNEL_VERBOSE" echo " -a, --all: iteratively install all versions available in /lib/modules/" echo " --systemd, --no-systemd: offload installation to systemd's kernel-install" echo echo "Other optional arguments are passed on to systemd's kernel-install if this is enabled" echo "via the --systemd argument. The default value for --systemd|--no-systemd is controlled" echo "by the value of the \"systemd\" USE flag on the sys-kernel/installkernel package." if command -v kernel-install >/dev/null; then echo kernel-install --help fi echo echo "See the installkernel(8) man page for details." exit 0 ;; --verbose|-v) INSTALLKERNEL_VERBOSE=1 _ik_remaining_optargs="${_ik_remaining_optargs} ${1}" ;; --all|-a) _ik_install_all=1 ;; --systemd) SYSTEMD_KERNEL_INSTALL=1 ;; --no-systemd) SYSTEMD_KERNEL_INSTALL=0 ;; -*) _ik_remaining_optargs="${_ik_remaining_optargs} ${1}" ;; *) _ik_arg_index=$((_ik_arg_index+1)) if [ ${_ik_arg_index} -eq 1 ]; then _ik_arg_1=${1} elif [ ${_ik_arg_index} -eq 2 ]; then _ik_arg_2=${1} elif [ ${_ik_arg_index} -eq 3 ]; then _ik_arg_3=${1} elif [ ${_ik_arg_index} -eq 4 ]; then _ik_arg_4=${1} elif [ ${_ik_arg_index} -eq 5 ]; then _ik_arg_5=${1} else echo "Too many arguments." echo "Usage: installkernel " echo " [--verbose] [--all] [--systemd|--no-systemd] [--help]" exit 1 fi ;; esac shift done if [ "${SYSTEMD_KERNEL_INSTALL}" -eq 1 ] && command -v kernel-install >/dev/null then # If the ${0} of kernel-install is installkernel it takes its arguments # in the same way we do here. We could use "exec -a ${0} .. ${@}" for this, # but the -a argument is not POSIX, only bash. # TODO: maybe revisit this if we ever bashify the script. if [ ${_ik_arg_index} -le 3 ] || [ "${_ik_arg_4}" = "/boot" ]; then # kernel-install does not support relocation (ignores $4, see manual) if [ ${_ik_install_all} -eq 1 ]; then # shellcheck disable=SC2086 exec kernel-install add-all ${_ik_remaining_optargs} else # shellcheck disable=SC2086 exec kernel-install add ${_ik_remaining_optargs} "${_ik_arg_1}" "${_ik_arg_2}" fi else echo "WARNING: A custom installation directory is specified as fourth argument." echo "WARNING: Systemd kernel-install does not support this. Falling back" echo "WARNING: to legacy installkernel. SYSTEMD_KERNEL_INSTALL is ignored." fi fi set -e # Used to find pre and post install hooks dropindirs_sort() { for d; do for i in "${d}/"*; do [ -e "${i}" ] && echo "${i##*/}" done done | sort -Vu | while read -r f; do for d; do if [ -e "${d}/${f}" ]; then [ -x "${d}/${f}" ] && echo "${d}/${f}" continue 2 fi done done } # Create backups of older versions before installing updatever() { oldsuffix="${3}.old" if [ "${3%.efi}" != "${3}" ]; then # Some UEFIs enforce .efi suffix, so if using efi files retain suffix oldsuffix="-old${3}" fi if [ -f "${dir}/${1}-${ver}${3}" ]; then # If they are hardlinked together, mv will fail. rm -f "${dir}/${1}-${ver}${oldsuffix}" mv "${dir}/${1}-${ver}${3}" "${dir}/${1}-${ver}${oldsuffix}" fi install -m 0644 "${2}" "${dir}/${1}-${ver}${3}" # Update static version-less symlink/copy if [ -f "${dir}/${1}${3}" ] || [ -L "${dir}/${1}${3}" ]; then # The presence of "${dir}/${1}${3}" is unusual in modern installations, and # the results are mostly unused. So only recreate them if they # already existed. if [ -L "${dir}/${1}${3}" ]; then # If we were using links, continue to use links, updating if # we need to. if [ "$(readlink -f "${dir}/${1}${3}")" = "${dir}/${1}-${ver}${3}" ]; then # Yup, we need to change ln -sf "${1}-${ver}${oldsuffix}" "${dir}/${1}${oldsuffix}" else # If they are hardlinked together, mv will fail. rm -f "${dir}/${1}${oldsuffix}" mv "${dir}/${1}${3}" "${dir}/${1}${oldsuffix}" fi ln -sf "${1}-${ver}${3}" "${dir}/${1}${3}" else # No links # If they are hardlinked together, mv will fail. rm -f "${dir}/${1}${oldsuffix}" mv "${dir}/${1}${3}" "${dir}/${1}${oldsuffix}" install -m 0644 "${2}" "${dir}/${1}${3}" fi fi } cleanup() { # shellcheck disable=SC2317 if [ -n "${INSTALLKERNEL_STAGING_AREA}" ]; then rm -rf "${INSTALLKERNEL_STAGING_AREA}" fi } trap cleanup EXIT # Main installation script that we will call later main() { ver=${1:?} img=${2:?} map=${3:?} dir=${4:-/boot} if [ ${#} -gt 4 ]; then echo "Too many arguments." return 1 fi INSTALLKERNEL_STAGING_AREA="$(mktemp -d -t installkernel.staging.XXXXXXX)" export INSTALLKERNEL_STAGING_AREA suffix= if [ "${INSTALLKERNEL_LAYOUT}" = "efistub" ]; then if [ ${#} -le 3 ] || [ "${4%/boot}" != "${4}" ] then # Relocate to ESP for candidate in "${dir}/EFI" "${dir}/efi" "${dir}" "${dir%/boot}/efi" do if [ -d "${candidate}/EFI/${NAME}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Found vendor directory on ESP ${candidate}" fi dir=${candidate}/EFI/${NAME} suffix=.efi # backwards compatibility if [ -f "/boot/intel-uc.img" ]; then install -m 0644 "/boot/intel-uc.img" "${dir}/intel-uc.img" fi if [ -f "/boot/amd-uc.img" ]; then install -m 0644 "/boot/amd-uc.img" "${dir}/amd-uc.img" fi else continue fi done if [ "${dir}" = "/boot" ] || [ "${dir}" = "${4}" ] then # No vendor dir found, try to create one for candidate in "${dir}/EFI" "${dir}/efi" "${dir}" "${dir%/boot}/efi" do if [ -d "${candidate}/EFI" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Creating vendor directory on ESP ${candidate}" fi mkdir -p "${candidate}/EFI/${NAME}" dir=${candidate}/EFI/${NAME} suffix=.efi # backwards compatibility if [ -f "/boot/intel-uc.img" ]; then install -m 0644 "/boot/intel-uc.img" "${dir}/intel-uc.img" fi if [ -f "/boot/amd-uc.img" ]; then install -m 0644 "/boot/amd-uc.img" "${dir}/amd-uc.img" fi else continue fi done fi if [ "${dir}" = "/boot" ] || [ "${dir}" = "${4}" ] then # Still no vendor dir, warn and fallback echo "WARNING: layout=efistub set, but did not find vendor directory on ESP." echo "WARNING: Please create the EFI/${NAME} directory on the EFI System partition manually." fi fi fi base_dir=$(dirname "${img}") prebuilt_initrd=${base_dir}/initrd prebuilt_uki=${base_dir}/uki.efi initrd=${INSTALLKERNEL_STAGING_AREA}/initrd uki=${INSTALLKERNEL_STAGING_AREA}/uki.efi # Copy prebuilt initrd, uki.efi at kernel location if [ -f "${prebuilt_initrd}" ]; then cp --dereference --preserve=all "${prebuilt_initrd}" "${initrd}" fi if [ -f "${prebuilt_uki}" ]; then cp --dereference --preserve=all "${prebuilt_uki}" "${uki}" fi # If installing in the usual directory, run the same scripts that hook # into kernel package installation. Also make sure the PATH includes # /usr/sbin and /sbin, just as dpkg would. if [ ${#} -le 3 ] || [ "${4}" = "/boot" ] then err=0 ( #shellcheck disable=SC2030 export LC_ALL=C PATH="${PATH}:/usr/sbin:/sbin" if [ -z "${INSTALLKERNEL_PREINST_PLUGINS}" ]; then INSTALLKERNEL_PREINST_PLUGINS="$( dropindirs_sort \ "/etc/kernel/preinst.d" \ "/usr/lib/kernel/preinst.d" )" fi for plugin in ${INSTALLKERNEL_PREINST_PLUGINS}; do if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo echo "Running ${plugin} ${ver} ${img}..." echo fi "${plugin}" "${ver}" "${img}" || exit ${?} if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo echo "Hook ${plugin} finished successfully" echo fi done ) || err=${?} [ ${err} -eq 77 ] && return 0 [ ${err} -ne 0 ] && return ${err} else echo "WARNING: A custom installation directory is specified as fourth argument." echo "WARNING: In this configuration running installation hooks is not supported." echo "WARNING: All pre-installation hooks are ignored." fi if [ "${img%vmlinux*}" != "${img}" ]; then img_dest=vmlinux else img_dest=vmlinuz fi # If we found a uki.efi, install it instead of kernel+initrd if [ -f "${uki}" ] && [ "${INSTALLKERNEL_LAYOUT}" = "uki" ]; then suffix=.efi if [ ${#} -le 3 ] || [ "${4%/boot}" != "${4}" ] then # Relocate to ESP for candidate in "${dir}/EFI" "${dir}/efi" "${dir}" "${dir%/boot}/efi" do if [ -d "${candidate}/EFI/Linux" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Found UKI directory on ESP ${candidate}" fi dir=${candidate}/EFI/Linux img_dest=${ID} else continue fi done if [ "${dir}" = "/boot" ] || [ "${dir}" = "${4}" ] then # No UKI dir found, try to create one for candidate in "${dir}/EFI" "${dir}/efi" "${dir}" "${dir%/boot}/efi" do if [ -d "${candidate}/EFI" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Creating UKI directory on ESP ${candidate}" fi mkdir -p "${candidate}/EFI/Linux" dir=${candidate}/EFI/Linux img_dest=${ID} else continue fi done fi if [ "${dir}" = "/boot" ] || [ "${dir}" = "${4}" ] then # Still no UKI dir, warn and fallback echo "WARNING: layout=uki set, but did not find the UKI directory on ESP" echo "WARNING: Please create the EFI/Linux directory on the EFI System partition manually" fi fi if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing Unified Kernel Image for ${ver}..." fi updatever "${img_dest}" "${uki}" "${suffix}" else if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing kernel image for ${ver}..." fi updatever "${img_dest}" "${img}" "${suffix}" if [ -f "${initrd}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing initramfs image for ${ver}..." fi updatever initramfs "${initrd}" .img fi if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing System.map for ${ver}..." fi updatever System.map "${map}" basedir=$(dirname "${map}") if [ -f "${basedir}/.config" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing config for ${ver}..." fi updatever config "${basedir}/.config" elif [ -f "${basedir}/config" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Installing config for ${ver}..." fi updatever config "${basedir}/config" fi fi # If installing in the usual directory, run the same scripts that hook # into kernel package installation. Also make sure the PATH includes # /usr/sbin and /sbin, just as dpkg would. if [ ${#} -le 3 ] || [ "${4}" = "/boot" ] then err=0 ( #shellcheck disable=SC2031 export LC_ALL=C PATH="${PATH}:/usr/sbin:/sbin" if [ -z "${INSTALLKERNEL_POSTINST_PLUGINS}" ]; then INSTALLKERNEL_POSTINST_PLUGINS="$( dropindirs_sort \ "/etc/kernel/postinst.d" \ "/usr/lib/kernel/postinst.d" )" fi for plugin in ${INSTALLKERNEL_POSTINST_PLUGINS}; do if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo echo "Running ${plugin} ${ver} ${dir}/${img_dest}-${ver}${suffix}..." echo fi "${plugin}" "${ver}" "${dir}/${img_dest}-${ver}${suffix}" || exit ${?} if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo echo "Hook ${plugin} finished successfully" echo fi done ) || err=${?} [ ${err} -eq 77 ] && return 0 [ ${err} -ne 0 ] && return ${err} else echo "WARNING: A custom installation directory is specified as fourth argument." echo "WARNING: In this configuration running installation hooks is not supported." echo "WARNING: All post-installation hooks are ignored." fi return 0 } # Find and read the install.conf if [ -n "${INSTALLKERNEL_CONF_ROOT}" ]; then install_conf="${INSTALLKERNEL_CONF_ROOT}/install.conf" elif [ -f /etc/kernel/install.conf ]; then install_conf=/etc/kernel/install.conf elif [ -f /run/kernel/install.conf ]; then install_conf=/run/kernel/install.conf elif [ -f /usr/local/lib/kernel/install.conf ]; then install_conf=/usr/local/lib/kernel/install.conf elif [ -f /usr/lib/kernel/install.conf ]; then install_conf=/usr/lib/kernel/install.conf else install_conf= fi if [ -f "${install_conf}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "Reading ${install_conf}..." fi # shellcheck source=/dev/null . "${install_conf}" if [ -n "${layout}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "${install_conf} configures layout=${layout}" fi if [ -z "${INSTALLKERNEL_LAYOUT}" ]; then INSTALLKERNEL_LAYOUT="${layout}" fi else echo "No layout= configured by ${install_conf}" exit 1 fi if [ -n "${initrd_generator}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "${install_conf} configures initrd_generator=${initrd_generator}" fi if [ -z "${INSTALLKERNEL_INITRD_GENERATOR}" ]; then INSTALLKERNEL_INITRD_GENERATOR="${initrd_generator}" fi else echo "No initrd_generator= configured by ${install_conf}" exit 1 fi if [ -n "${uki_generator}" ]; then if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "${install_conf} configures uki_generator=${uki_generator}" fi if [ -z "${INSTALLKERNEL_UKI_GENERATOR}" ]; then INSTALLKERNEL_UKI_GENERATOR="${uki_generator}" fi else echo "No uki_generator= configured by ${install_conf}" exit 1 fi else echo "No install.conf found at ${install_conf}" exit 1 fi export INSTALLKERNEL_LAYOUT export INSTALLKERNEL_INITRD_GENERATOR export INSTALLKERNEL_UKI_GENERATOR export INSTALLKERNEL_VERBOSE if [ -f /etc/os-release ]; then # shellcheck source=/dev/null . /etc/os-release elif [ -f /usr/lib/os-release ]; then # shellcheck source=/dev/null . /usr/lib/os-release fi # Set sane default if no or broken os-release export NAME="${NAME:=Linux}" export ID="${ID:=linux}" export PRETTY_NAME="${PRETTY_NAME:=Linux}" # Now we actually run the install main_err=0 if [ ${_ik_install_all} -eq 1 ]; then for ver in /lib/modules/*; do ver=$(basename "${ver}") img=/lib/modules/${ver}/vmlinuz map=/lib/modules/${ver}/System.map if [ -f "${img}" ] && [ -f "${map}" ]; then main "${ver}" "${img}" "${map}" "${_ik_arg_4}" || { main_err=${?}; echo "WARNING: Installing ${ver} failed"; } cleanup else if [ "${INSTALLKERNEL_VERBOSE}" -gt 0 ]; then echo "WARNING: Skipping ${ver}, no kernel image or system map available." fi fi done else if [ -f "${_ik_arg_2}" ] && [ -f "${_ik_arg_3}" ]; then main "${_ik_arg_1}" "${_ik_arg_2}" "${_ik_arg_3}" "${_ik_arg_4}" || { main_err=${?}; echo "ERROR: Installing ${_ik_arg_1} failed"; } cleanup else echo "ERROR: No such kernel image or system map." exit 1 fi fi exit ${main_err}