|
Packit |
e3b5e1 |
#! /bin/bash -eu
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
# Maintain kernel-version-specific symlinks in /lib/firmware based on
|
|
Packit |
e3b5e1 |
# configuration present in /usr/share/microcode_ctl/ucode_with_caveats.
|
|
Packit |
e3b5e1 |
#
|
|
Packit |
e3b5e1 |
# SPDX-License-Identifier: CC0-1.0
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
usage()
|
|
Packit |
e3b5e1 |
{
|
|
Packit |
e3b5e1 |
echo "Usage: update_ucode [--action {add|remove|refresh|list}]" \
|
|
Packit |
e3b5e1 |
"[--kernel KERNELVER]* [--verbose] [--dry-run]" \
|
|
Packit |
e3b5e1 |
"[--cleanup intel_ucode caveats_ucode]" \
|
|
Packit |
e3b5e1 |
"[--skip-common] [--skip-kernel-specific]" >&2
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
debug() { [ 0 = "$verbose" ] || echo "$*" >&2; }
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
MC_DIR=/usr/share/microcode_ctl
|
|
Packit |
e3b5e1 |
INTEL_UCODE_DIR=intel-ucode
|
|
Packit |
e3b5e1 |
DATA_DIR=/usr/share/microcode_ctl/ucode_with_caveats
|
|
Packit |
e3b5e1 |
FW_DIR=/lib/firmware
|
|
Packit |
e3b5e1 |
check_caveats=/usr/libexec/microcode_ctl/check_caveats
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
action=refresh
|
|
Packit |
e3b5e1 |
kernel=
|
|
Packit |
e3b5e1 |
verbose=0
|
|
Packit |
e3b5e1 |
verbose_opt=
|
|
Packit |
e3b5e1 |
dry_run=0
|
|
Packit |
e3b5e1 |
remove_cleanup=0
|
|
Packit |
e3b5e1 |
cleanup_intel=
|
|
Packit |
e3b5e1 |
cleanup_caveats=
|
|
Packit |
e3b5e1 |
skip_common=0
|
|
Packit |
e3b5e1 |
skip_caveats=0
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
while [ 1 -le "$#" ]; do
|
|
Packit |
e3b5e1 |
case "$1" in
|
|
Packit |
e3b5e1 |
-C|--skip-common)
|
|
Packit |
e3b5e1 |
skip_common=1
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-K|--skip-kernel-specific)
|
|
Packit |
e3b5e1 |
skip_caveats=1
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-a|--action)
|
|
Packit |
e3b5e1 |
shift
|
|
Packit |
e3b5e1 |
action="$1"
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-k|--kernel)
|
|
Packit |
e3b5e1 |
shift
|
|
Packit |
e3b5e1 |
kernel="$kernel $1"
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-v|--verbose)
|
|
Packit |
e3b5e1 |
verbose=1
|
|
Packit |
e3b5e1 |
verbose_opt="-v"
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-n|--dry-run)
|
|
Packit |
e3b5e1 |
dry_run=1
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
-c|--cleanup)
|
|
Packit |
e3b5e1 |
remove_cleanup=1
|
|
Packit |
e3b5e1 |
shift
|
|
Packit |
e3b5e1 |
cleanup_intel="$1"
|
|
Packit |
e3b5e1 |
shift
|
|
Packit |
e3b5e1 |
cleanup_caveats="$1"
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
*)
|
|
Packit |
e3b5e1 |
echo "Unknown argument \"$1\"" >&2
|
|
Packit |
e3b5e1 |
usage
|
|
Packit |
e3b5e1 |
exit 1
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
shift
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
cmd=
|
|
Packit |
e3b5e1 |
[ 0 -eq "$dry_run" ] || cmd=echo
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
case "$action" in
|
|
Packit |
e3b5e1 |
add|remove|refresh|list)
|
|
Packit |
e3b5e1 |
# Scan all directories in FW_DIR and all existing kernels
|
|
Packit |
e3b5e1 |
if [ -z "$kernel" ]; then
|
|
Packit |
e3b5e1 |
debug "No kernel versions provided, scanning..."
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
kvers=$(find /lib/modules/ -name '[2-9].*' -print)
|
|
Packit |
e3b5e1 |
for k_dir in $kvers; do
|
|
Packit |
e3b5e1 |
k="${k_dir#/lib/modules/}"
|
|
Packit |
e3b5e1 |
[ ! -e "${k_dir}/symvers.gz" ] || {
|
|
Packit |
e3b5e1 |
debug " Adding $k (from /lib/modules)"
|
|
Packit |
e3b5e1 |
kernel="$kernel $k"
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
kvers=$(find /lib/firmware/ -name '[2-9].*' -print)
|
|
Packit |
e3b5e1 |
for k_dir in $kvers; do
|
|
Packit |
e3b5e1 |
k="${k_dir#/lib/firmware/}"
|
|
Packit |
e3b5e1 |
[ ! -d "$k_dir" ] || {
|
|
Packit |
e3b5e1 |
debug " Adding $k (from /lib/firmware)"
|
|
Packit |
e3b5e1 |
kernel="$kernel $k"
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
kernel=$(printf "%s" "$kernel" | xargs -n 1 | sort -u)
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
*)
|
|
Packit |
e3b5e1 |
echo "Unknown action \"$action\"" >&2
|
|
Packit |
e3b5e1 |
usage
|
|
Packit |
e3b5e1 |
exit 1
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
# Generic part: managing intel ucode
|
|
Packit |
e3b5e1 |
debug "Running action \"$action\" on common Intel microcode directory"
|
|
Packit |
e3b5e1 |
while :; do
|
|
Packit |
e3b5e1 |
[ 0 -eq "$skip_common" ] || break
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ ! -e "/etc/microcode_ctl/intel-ucode-disallow" ] || {
|
|
Packit |
e3b5e1 |
debug " Skipping \"$i\":" \
|
|
Packit |
e3b5e1 |
"\"/etc/microcode_ctl/intel-ucode-disallow\"" \
|
|
Packit |
e3b5e1 |
"present"
|
|
Packit |
e3b5e1 |
break
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
[ ! -e "$FW_DIR/intel-ucode-disallow" ] || {
|
|
Packit |
e3b5e1 |
debug " Found \"$FW_DIR/intel-ucode-disallow\"," \
|
|
Packit |
e3b5e1 |
"skipping"
|
|
Packit |
e3b5e1 |
break
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
# Removing old files
|
|
Packit |
e3b5e1 |
case "$action" in
|
|
Packit |
e3b5e1 |
refresh|remove|list)
|
|
Packit |
e3b5e1 |
debug " Removing old files from ${FW_DIR}/${INTEL_UCODE_DIR}"
|
|
Packit |
e3b5e1 |
if [ 0 = "$remove_cleanup" ]; then
|
|
Packit |
e3b5e1 |
find "${MC_DIR}/${INTEL_UCODE_DIR}" \
|
|
Packit |
e3b5e1 |
-maxdepth 1 -mindepth 1 \
|
|
Packit |
e3b5e1 |
-type f -printf '%f\n'
|
|
Packit |
e3b5e1 |
else
|
|
Packit |
e3b5e1 |
cat "$cleanup_intel"
|
|
Packit |
e3b5e1 |
fi | while read -r fname; do
|
|
Packit |
e3b5e1 |
name="${FW_DIR}/${INTEL_UCODE_DIR}/${fname}"
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
# Needed in case we downgrade to a version where
|
|
Packit |
e3b5e1 |
# no symlinks in /lib/firmware were used
|
|
Packit |
e3b5e1 |
if [ 1 = "$remove_cleanup" ]; then
|
|
Packit |
e3b5e1 |
[ -L "$name" ] || continue
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ "xlist" != "x$action" ] || {
|
|
Packit |
e3b5e1 |
echo "$name"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
$cmd rm -f $verbose_opt "$name"
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
[ "xlist" = "x$action" ] || {
|
|
Packit |
e3b5e1 |
$cmd rmdir -p $verbose_opt \
|
|
Packit |
e3b5e1 |
"${FW_DIR}/${INTEL_UCODE_DIR}" 2>/dev/null \
|
|
Packit |
e3b5e1 |
|| true
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
# Adding new ones
|
|
Packit |
e3b5e1 |
case "$action" in
|
|
Packit |
e3b5e1 |
add|refresh)
|
|
Packit |
e3b5e1 |
debug " Creating symlinks in ${FW_DIR}/${INTEL_UCODE_DIR}"
|
|
Packit |
e3b5e1 |
$cmd mkdir -p $verbose_opt "${FW_DIR}/${INTEL_UCODE_DIR}"
|
|
Packit |
e3b5e1 |
$cmd find "${MC_DIR}/${INTEL_UCODE_DIR}" -maxdepth 1 -mindepth 1 \
|
|
Packit |
e3b5e1 |
-type f -exec bash -c 'ln -fs '"$verbose_opt"' '\''{}'\'' \
|
|
Packit |
e3b5e1 |
"'"${FW_DIR}/${INTEL_UCODE_DIR}/"'$(basename '\''{}'\'')"' \;
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
break
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
debug "Running action \"$action\" on kernels $kernel"
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
if [ 0 = "$remove_cleanup" ]; then
|
|
Packit |
e3b5e1 |
ls "$DATA_DIR"
|
|
Packit |
e3b5e1 |
else
|
|
Packit |
e3b5e1 |
cat "$cleanup_caveats"
|
|
Packit |
e3b5e1 |
fi | while read -r i; do
|
|
Packit |
e3b5e1 |
[ 0 -eq "$skip_caveats" ] || break
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
debug "Processing data directory \"$i\"..."
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
for k in $(echo "$kernel"); do
|
|
Packit |
e3b5e1 |
debug " Processing kernel version \"$k\""
|
|
Packit |
e3b5e1 |
{
|
|
Packit |
e3b5e1 |
out=$($check_caveats -k "$k" -c "$i" $verbose_opt)
|
|
Packit |
e3b5e1 |
ret="$?"
|
|
Packit |
e3b5e1 |
} || :
|
|
Packit |
e3b5e1 |
paths=$(printf "%s" "$out" | sed -n 's/^paths //p')
|
|
Packit |
e3b5e1 |
ignore=$(printf "%s" "$out" | sed -n 's/^skip_cfgs //p')
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ -z "$ignore" ] || {
|
|
Packit |
e3b5e1 |
debug " Configuration is ignored, skipping"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
case "$action" in
|
|
Packit |
e3b5e1 |
remove|refresh|list)
|
|
Packit |
e3b5e1 |
[ "xlist" = "x$action" ] || \
|
|
Packit |
e3b5e1 |
debug " Removing \"$paths\" (part of $action)..."
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
for p in $(printf "%s" "$paths"); do
|
|
Packit |
e3b5e1 |
find "$DATA_DIR/$i" -path "$DATA_DIR/$i/$p" \
|
|
Packit |
e3b5e1 |
-printf "%P\n"
|
|
Packit |
e3b5e1 |
done | while read -r path; do
|
|
Packit |
e3b5e1 |
[ -e "$FW_DIR/$k/readme-$i" ] || {
|
|
Packit |
e3b5e1 |
debug " \"$FW_DIR/$k/readme-$i\"" \
|
|
Packit |
e3b5e1 |
"is not found, skipping" \
|
|
Packit |
e3b5e1 |
"\"$paths\" removal"
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
break
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
if [ "xlist" = "x$action" ]; then
|
|
Packit |
e3b5e1 |
echo "$FW_DIR/$k/$path"
|
|
Packit |
e3b5e1 |
else
|
|
Packit |
e3b5e1 |
debug " Removing \"$FW_DIR/$k/$path\""
|
|
Packit |
e3b5e1 |
$cmd rm -f $verbose_opt "$FW_DIR/$k/$path"
|
|
Packit |
e3b5e1 |
$cmd rmdir -p $verbose_opt \
|
|
Packit |
e3b5e1 |
"$FW_DIR/$k/$(dirname $path)" 2>/dev/null \
|
|
Packit |
e3b5e1 |
|| true
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
if [ -e "$FW_DIR/$k/readme-$i" ]; then
|
|
Packit |
e3b5e1 |
if [ "xlist" = "x$action" ]; then
|
|
Packit |
e3b5e1 |
echo "$FW_DIR/$k/readme-$i"
|
|
Packit |
e3b5e1 |
else
|
|
Packit |
e3b5e1 |
$cmd rm -f $verbose_opt \
|
|
Packit |
e3b5e1 |
"$FW_DIR/$k/readme-$i"
|
|
Packit |
e3b5e1 |
$cmd rmdir -p $verbose_opt \
|
|
Packit |
e3b5e1 |
"$FW_DIR/$k" 2>/dev/null || true
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ 0 -eq "$ret" ] || {
|
|
Packit |
e3b5e1 |
debug " Checking for caveats failed" \
|
|
Packit |
e3b5e1 |
"(kernel version \"$k\"), skipping"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ -n "$paths" ] || {
|
|
Packit |
e3b5e1 |
debug " List of paths to add is empty, skipping"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
case "$action" in
|
|
Packit |
e3b5e1 |
add|refresh)
|
|
Packit |
e3b5e1 |
debug " Adding $paths (part of $action)..."
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
[ -e "/lib/modules/$k/symvers.gz" ] || {
|
|
Packit |
e3b5e1 |
debug " \"/lib/modules/$k/symvers.gz\"" \
|
|
Packit |
e3b5e1 |
"does not exist, skipping"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
for p in $(printf "%s" "$paths"); do
|
|
Packit |
e3b5e1 |
find "$DATA_DIR/$i" -path "$DATA_DIR/$i/$p" \
|
|
Packit |
e3b5e1 |
-printf "%P\n"
|
|
Packit |
e3b5e1 |
done | while read -r path; do
|
|
Packit |
e3b5e1 |
[ ! -e "$FW_DIR/$k/$path" ] || {
|
|
Packit |
e3b5e1 |
debug " $FW_DIR/$k/$path already" \
|
|
Packit |
e3b5e1 |
"exists, skipping"
|
|
Packit |
e3b5e1 |
continue
|
|
Packit |
e3b5e1 |
}
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
debug " Adding \"$FW_DIR/$k/$path\""
|
|
Packit |
e3b5e1 |
$cmd mkdir -p $verbose_opt \
|
|
Packit |
e3b5e1 |
"$(dirname "$FW_DIR/$k/$path")"
|
|
Packit |
e3b5e1 |
$cmd ln -fs $verbose_opt "$DATA_DIR/$i/$path" \
|
|
Packit |
e3b5e1 |
"$FW_DIR/$k/$path"
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
|
|
Packit |
e3b5e1 |
if [ -e "$FW_DIR/$k/readme-$i" ]; then
|
|
Packit |
e3b5e1 |
debug " $FW_DIR/$k/readme-$i already" \
|
|
Packit |
e3b5e1 |
"exists, skipping creation"
|
|
Packit |
e3b5e1 |
else
|
|
Packit |
e3b5e1 |
$cmd cp $verbose_opt "$DATA_DIR/$i/readme" \
|
|
Packit |
e3b5e1 |
"$FW_DIR/$k/readme-$i"
|
|
Packit |
e3b5e1 |
fi
|
|
Packit |
e3b5e1 |
;;
|
|
Packit |
e3b5e1 |
remove)
|
|
Packit |
e3b5e1 |
esac
|
|
Packit |
e3b5e1 |
done
|
|
Packit |
e3b5e1 |
done
|