All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 1m52s
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
356 lines
10 KiB
Bash
356 lines
10 KiB
Bash
#!/bin/bash
|
|
# SPDX-Version: 3.0
|
|
# SPDX-CreationInfo: 2025-06-17; WEIDNER, Marc S.; <msw@coresecret.dev>
|
|
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.installer.git
|
|
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
|
|
# SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; <msw@coresecret.dev>
|
|
# SPDX-FileType: SOURCE
|
|
# SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0
|
|
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
|
|
# SPDX-PackageName: CISS.debian.installer
|
|
# SPDX-Security-Contact: security@coresecret.eu
|
|
|
|
guard_sourcing
|
|
|
|
#######################################
|
|
# Install microcode updates depending on architecture (amd64, arm64, intel64) and environment (Baremetal, VM).
|
|
# Every 'apt-get install' command is invoked by adding 'export INITRD=No'
|
|
# to suppress the 'update-initramfs'-Kernel-Hooks, according to the initramfs-tools manpage:
|
|
# https://manpages.debian.org/testing/initramfs-tools-core/initramfs-tools.7.en.html
|
|
# Globals:
|
|
# TARGET
|
|
# firmware_lookup
|
|
# image
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# 0: on success
|
|
#######################################
|
|
installation_firmware() {
|
|
### Declare Arrays, HashMaps, and Variables.
|
|
declare -a ary_bus=( "acpi" "hid" "i2c" "mdio" "of" "pci" "platform" "pnp" "serio" "spi" "usb" "virtio" ) \
|
|
ary_files=() ary_mods=() ary_pkgs_resolved=()
|
|
declare var_kernel="${image#linux-image-}" var_fw_policy="${firmware_lookup:-missing}" \
|
|
dir_fw="${TARGET}/root/.ciss/cdi/log/firmware" \
|
|
var_alias="" var_bus="" var_file="" var_found="" var_fw="" var_mod="" var_pkgs="" var_re="" var_wc_alias=""
|
|
declare -r var_logfile="/root/.ciss/cdi/log/4145_installation_firmware.log"
|
|
declare -A hmp_alias_unique=() hmp_fw_present=() hmp_fw_unique=() hmp_module_unique=() hmp_want_pkgs=()
|
|
|
|
declare -A hmp_fw_map=(
|
|
["^iwlwifi/.*\\.ucode$"]="firmware-iwlwifi"
|
|
["^ath10k/.*\\.bin$"]="firmware-atheros"
|
|
["^ath11k/.*\\.bin$"]="firmware-atheros"
|
|
["^brcm/.*\\.(bin|txt)$"]="firmware-brcm80211"
|
|
["^libertas/.*"]="firmware-libertas"
|
|
["^rtlwifi/.*\\.(bin|fw)$"]="firmware-realtek"
|
|
["^rtl_bt/.*\\.(bin|fw)$"]="firmware-realtek"
|
|
["^mediatek/.*"]="firmware-misc-nonfree"
|
|
["^rtl_nic/.*\\.fw$"]="firmware-realtek"
|
|
["^bnx2/.*\\.fw$"]="firmware-bnx2"
|
|
["^bnx2x/.*\\.fw$"]="firmware-bnx2x"
|
|
["^qed/.*\\.bin$"]="firmware-qlogic"
|
|
["^qla2xxx/.*\\.bin$"]="firmware-qlogic"
|
|
["^cxgb4/.*"]="firmware-chelsio"
|
|
["^netronome/.*"]="firmware-netronome"
|
|
["^ice/.*\\.pkg$"]="firmware-misc-nonfree"
|
|
["^amdgpu/.*\\.bin$"]="firmware-amd-graphics"
|
|
["^radeon/.*\\.bin$"]="firmware-amd-graphics"
|
|
["^i915/.*\\.(bin|dmc)$"]="firmware-misc-nonfree"
|
|
["^sof/.*"]="firmware-sof-signed"
|
|
["^mrvl/.*"]="firmware-misc-nonfree"
|
|
["^aspeed/.*"]="firmware-misc-nonfree"
|
|
)
|
|
|
|
chroot_logger "${TARGET}${var_logfile}"
|
|
|
|
if [[ ! -d "${TARGET}/lib/modules/${var_kernel}" ]]; then
|
|
do_log "error" "file_only" "4145() Target modules-directory missing: ${TARGET}/lib/modules/${var_kernel}."
|
|
return 0
|
|
fi
|
|
|
|
### Step 1: Collect all module aliases per bus (deterministic inputs):
|
|
mkdir -p "${dir_fw}"
|
|
|
|
: >| "${dir_fw}/4145_s1_mod_aliases_all.txt"
|
|
|
|
for var_bus in "${ary_bus[@]}"; do
|
|
|
|
: >| "${dir_fw}/4145_s1_mod_aliases_${var_bus}.txt"
|
|
|
|
### Safe enumeration without failing on unmatched globs.
|
|
# shellcheck disable=SC2312
|
|
readarray -t ary_files < <(compgen -G "/sys/bus/${var_bus}/devices"/*/modalias)
|
|
|
|
for var_file in "${ary_files[@]}"; do
|
|
|
|
if [[ -r "${var_file}" ]]; then
|
|
|
|
var_alias=""
|
|
IFS= read -r var_alias < "${var_file}" || true
|
|
|
|
if [[ -n "${var_alias}" ]]; then
|
|
|
|
if [[ -z "${hmp_alias_unique[${var_alias}]:-}" ]]; then
|
|
|
|
hmp_alias_unique["${var_alias}"]=1
|
|
printf '%s\n' "${var_alias}" >> "${dir_fw}/4145_s1_mod_aliases_all.txt"
|
|
|
|
fi
|
|
|
|
printf '%s\n' "${var_alias}" >> "${dir_fw}/4145_s1_mod_aliases_${var_bus}.txt"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
done
|
|
|
|
### Step 2: Resolve modules from aliases against the "TARGET" kernel tree.
|
|
: >| "${dir_fw}/4145_s2_alias_to_modules.txt"
|
|
var_alias=""
|
|
|
|
for var_alias in "${!hmp_alias_unique[@]}"; do
|
|
|
|
var_wc_alias="$(wildcard_mod_alias "${var_alias}")"
|
|
|
|
### Resolve modules in the "TARGET" root (-d) and for the "var_kernel" (-S)
|
|
while IFS= read -r var_mod; do
|
|
|
|
if [[ -n "${var_mod}" ]]; then
|
|
|
|
ary_mods+=("${var_mod}")
|
|
|
|
fi
|
|
|
|
done < <(modprobe -R "${var_wc_alias}" -S "${var_kernel}" -d "${TARGET}" 2>/dev/null || true)
|
|
|
|
if [[ "${#ary_mods[@]}" -eq 0 ]]; then
|
|
|
|
printf '%s\t%s\n' "${var_alias}" "-" >> "${dir_fw}/4145_s2_alias_to_modules.txt"
|
|
|
|
else
|
|
|
|
for var_mod in "${ary_mods[@]}"; do
|
|
|
|
hmp_module_unique["${var_mod}"]=1
|
|
printf '%s\t%s\n' "${var_alias}" "${var_mod}" >> "${dir_fw}/4145_s2_alias_to_modules.txt"
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
### Step 3: Resolve modules to firmware filenames; mark present/missing in "TARGET".
|
|
: >| "${dir_fw}/4145_s3_module_to_firmware.txt"
|
|
: >| "${dir_fw}/4145_s3_firmware_present.txt"
|
|
: >| "${dir_fw}/4145_s3_firmware_missing.txt"
|
|
|
|
for var_mod in "${!hmp_module_unique[@]}"; do
|
|
|
|
### Query modinfo in the 'TARGET' base (-b) for the target kernel image (-k)
|
|
while IFS= read -r var_fw; do
|
|
|
|
if [[ -n "${var_fw}" ]]; then
|
|
|
|
var_found="1"
|
|
|
|
### Normalize to the path relative to '/lib/firmware'
|
|
### modinfo may output "amdgpu/..." or "rtl_nic/..." already relative; handle both.
|
|
if [[ "${var_fw}" == /lib/firmware/* ]]; then
|
|
|
|
var_fw="${var_fw#/lib/firmware/}"
|
|
|
|
fi
|
|
|
|
hmp_fw_unique["${var_fw}"]=1
|
|
|
|
printf '%s\t%s\n' "${var_mod}" "${var_fw}" >> "${dir_fw}/4145_s3_module_to_firmware.txt"
|
|
|
|
if [[ -e "${TARGET}/lib/firmware/${var_fw}" ]]; then
|
|
|
|
hmp_fw_present["${var_fw}"]="yes"
|
|
|
|
else
|
|
|
|
hmp_fw_present["${var_fw}"]="no"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
done < <(modinfo -k "${var_kernel}" -b "${TARGET}" -F firmware "${var_mod}" 2>/dev/null || true)
|
|
|
|
if [[ -z "${var_found}" ]]; then
|
|
|
|
printf '%s\t-\n' "${var_mod}" >> "${dir_fw}/4145_s3_module_to_firmware.txt"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
### Emit present/missing lists.
|
|
for var_fw in "${!hmp_fw_unique[@]}"; do
|
|
|
|
if [[ "${hmp_fw_present[${var_fw}]:-no}" == "yes" ]]; then
|
|
|
|
printf '%s\n' "${var_fw}" >> "${dir_fw}/4145_s3_firmware_present.txt"
|
|
|
|
else
|
|
|
|
printf '%s\n' "${var_fw}" >> "${dir_fw}/4145_s3_firmware_missing.txt"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
### Step 4: Read "${dir_fw}/4145_s3_firmware_missing.txt" and map to the package set using 'hmp_fw_map'.
|
|
var_fw=""
|
|
: >| "${dir_fw}/4145_s4_packages_resolved.txt"
|
|
|
|
if [[ -s "${dir_fw}/4145_s3_firmware_missing.txt" ]]; then
|
|
|
|
while IFS= read -r var_fw; do
|
|
|
|
if [[ -z "${var_fw}" ]]; then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
declare var_matched=""
|
|
### Iterate through the explicit map first.
|
|
for var_re in "${!hmp_fw_map[@]}"; do
|
|
|
|
if [[ "${var_fw}" =~ ${var_re} ]]; then
|
|
|
|
hmp_want_pkgs["${hmp_fw_map[${var_re}]}"]=1
|
|
var_matched="1"
|
|
break
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
if [[ -z "${var_matched}" ]]; then
|
|
|
|
### Fallback heuristics by top-level directory.
|
|
case "${var_fw}" in
|
|
iwlwifi/*) hmp_want_pkgs["firmware-iwlwifi"]=1 ;;
|
|
rtlwifi/*|rtl_*/*) hmp_want_pkgs["firmware-realtek"]=1 ;;
|
|
amdgpu/*|radeon/*) hmp_want_pkgs["firmware-amd-graphics"]=1 ;;
|
|
i915/*) hmp_want_pkgs["firmware-misc-nonfree"]=1 ;;
|
|
ath10k/*|ath11k/*) hmp_want_pkgs["firmware-atheros"]=1 ;;
|
|
brcm/*) hmp_want_pkgs["firmware-brcm80211"]=1 ;;
|
|
bnx2/*) hmp_want_pkgs["firmware-bnx2"]=1 ;;
|
|
bnx2x/*) hmp_want_pkgs["firmware-bnx2x"]=1 ;;
|
|
qed/*|qla2xxx/*) hmp_want_pkgs["firmware-qlogic"]=1 ;;
|
|
cxgb4/*) hmp_want_pkgs["firmware-chelsio"]=1 ;;
|
|
netronome/*) hmp_want_pkgs["firmware-netronome"]=1 ;;
|
|
sof/*) hmp_want_pkgs["firmware-sof-signed"]=1 ;;
|
|
ice/*) hmp_want_pkgs["firmware-misc-nonfree"]=1 ;;
|
|
*) do_log "warn" "file_only" "4145() No map entry for: '${var_fw}'." ;;
|
|
esac
|
|
|
|
fi
|
|
|
|
done < "${dir_fw}/4145_s3_firmware_missing.txt"
|
|
|
|
else
|
|
|
|
do_log "info" "file_only" "4145() No missing firmware file found."
|
|
|
|
fi
|
|
|
|
### Emit unique package list.
|
|
for var_pkgs in "${!hmp_want_pkgs[@]}"; do
|
|
|
|
printf '%s\n' "${var_pkgs}"
|
|
|
|
done | sort -u >| "${dir_fw}/4145_s4_packages_resolved.txt"
|
|
|
|
### Step 5: Install packages according to policy (always|missing|never).
|
|
: >| "${dir_fw}/4145_s5_installation_cmd.txt"
|
|
: >| "${dir_fw}/4145_s5_installation_out.txt"
|
|
|
|
if [[ "${var_fw_policy}" == "always" ]]; then
|
|
do_log "info" "file_only" "4145() Policy=always: installing broad firmware sets."
|
|
printf '%s\n' "firmware-linux" "firmware-misc-nonfree" >> "${dir_fw}/4145_s4_packages_resolved.txt"
|
|
fi
|
|
|
|
if [[ ! -s "${dir_fw}/4145_s4_packages_resolved.txt" ]]; then
|
|
do_log "info" "file_only" "4145() Nothing to install."
|
|
return 0
|
|
fi
|
|
|
|
mapfile -t ary_pkgs_resolved < "${dir_fw}/4145_s4_packages_resolved.txt"
|
|
|
|
if [[ "${var_fw_policy}" == "never" ]]; then
|
|
do_log "info" "file_only" "4145() Policy=never: Packages would be: ${ary_pkgs_resolved[*]}."
|
|
return 0
|
|
fi
|
|
|
|
chroot_script "${TARGET}" "
|
|
export INITRD=No
|
|
apt-get install -y --no-install-recommends --no-install-suggests ${ary_pkgs_resolved[*]} 2>&1 | tee -a ${var_logfile}
|
|
echo ExitCode: \$? >> ${var_logfile}
|
|
"
|
|
|
|
guard_dir && return 0
|
|
}
|
|
|
|
#######################################
|
|
# Helper: Wildcardize a module alias (bus-aware, conservative)
|
|
# Arguments:
|
|
# 1: Module alias
|
|
# Returns:
|
|
# 0: on success
|
|
#######################################
|
|
wildcard_mod_alias() {
|
|
### Keep vendor/device exact, relax subfields, fall back to original if not matched.
|
|
declare a="${1}" out=""
|
|
|
|
case "${a}" in
|
|
|
|
pci:*)
|
|
|
|
if [[ "${a}" == *sv* ]]; then
|
|
out="${a%%sv*}sv*sd*bc*sc*i*"
|
|
elif [[ "${a}" == *bc* ]]; then
|
|
out="${a%%bc*}bc*sc*i*"
|
|
else
|
|
out="${a}"
|
|
fi
|
|
;;
|
|
|
|
usb:*)
|
|
|
|
if [[ "${a}" == *ic* ]]; then
|
|
out="${a%%ic*}ic*isc*ip*in*"
|
|
elif [[ "${a}" == *dc* ]]; then
|
|
out="${a%%dc*}dc*dsc*dp*ic*isc*ip*in*"
|
|
else
|
|
out="${a}"
|
|
fi
|
|
;;
|
|
|
|
platform:*|acpi:*|of:*|i2c:*|spi:*|mdio:*|virtio:*|pnp:*|serio:*|hid:*)
|
|
|
|
out="${a}"
|
|
;;
|
|
|
|
*)
|
|
|
|
out="${a}"
|
|
;;
|
|
|
|
esac
|
|
|
|
printf '%s\n' "${out}"
|
|
|
|
return 0
|
|
}
|
|
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|