split script in multiple files, reassembled through build.sh

This commit is contained in:
Stéphane Lesimple
2026-03-30 20:04:16 +02:00
parent 7e660812e9
commit cebda01d05
47 changed files with 7712 additions and 7617 deletions

274
src/libs/340_cpu_msr.sh Normal file
View File

@@ -0,0 +1,274 @@
# vim: set ts=4 sw=4 sts=4 et:
readonly WRITE_MSR_RET_OK=0
readonly WRITE_MSR_RET_KO=1
readonly WRITE_MSR_RET_ERR=2
readonly WRITE_MSR_RET_LOCKDOWN=3
# Write a value to an MSR register across one or all cores
# Args: $1=msr_address $2=value(optional) $3=cpu_index(optional, default 0)
# Sets: ret_write_msr_msg
# Returns: WRITE_MSR_RET_OK | WRITE_MSR_RET_KO | WRITE_MSR_RET_ERR | WRITE_MSR_RET_LOCKDOWN
write_msr() {
local ret core first_core_ret
if [ "$opt_cpu" != all ]; then
# we only have one core to write to, do it and return the result
write_msr_one_core "$opt_cpu" "$@"
return $?
fi
# otherwise we must write on all cores
for core in $(seq 0 "$g_max_core_id"); do
write_msr_one_core "$core" "$@"
ret=$?
if [ "$core" = 0 ]; then
# save the result of the first core, for comparison with the others
first_core_ret=$ret
else
# compare first core with the other ones
if [ "$first_core_ret" != "$ret" ]; then
ret_write_msr_msg="result is not homogeneous between all cores, at least core 0 and $core differ!"
return $WRITE_MSR_RET_ERR
fi
fi
done
# if we're here, all cores agree, return the result
return $ret
}
# Write a value to an MSR register on a single CPU core
# Args: $1=core $2=msr_address $3=value
# Sets: ret_write_msr_msg
# Returns: WRITE_MSR_RET_OK | WRITE_MSR_RET_KO | WRITE_MSR_RET_ERR | WRITE_MSR_RET_LOCKDOWN
write_msr_one_core() {
local ret core msr msr_dec value value_dec mockvarname write_denied
core="$1"
msr_dec=$(($2))
msr=$(printf "0x%x" "$msr_dec")
value_dec=$(($3))
value=$(printf "0x%x" "$value_dec")
ret_write_msr_msg='unknown error'
: "${g_msr_locked_down:=0}"
mockvarname="SMC_MOCK_WRMSR_${msr}_RET"
# shellcheck disable=SC2086,SC1083
if [ -n "$(eval echo \${$mockvarname:-})" ]; then
pr_debug "write_msr: MOCKING enabled for msr $msr func returns $(eval echo \$$mockvarname)"
g_mocked=1
[ "$(eval echo \$$mockvarname)" = $WRITE_MSR_RET_LOCKDOWN ] && g_msr_locked_down=1
return "$(eval echo \$$mockvarname)"
fi
if [ ! -e $CPU_DEV_BASE/0/msr ] && [ ! -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
# try to load the module ourselves (and remember it so we can rmmod it afterwards)
load_msr
fi
if [ ! -e $CPU_DEV_BASE/0/msr ] && [ ! -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
ret_read_msr_msg="is msr kernel module available?"
return $WRITE_MSR_RET_ERR
fi
write_denied=0
if [ "$g_os" != Linux ]; then
cpucontrol -m "$msr=$value" "${BSD_CPUCTL_DEV_BASE}$core" >/dev/null 2>&1
ret=$?
else
# for Linux
# convert to decimal
if [ ! -w $CPU_DEV_BASE/"$core"/msr ]; then
ret_write_msr_msg="No write permission on $CPU_DEV_BASE/$core/msr"
return $WRITE_MSR_RET_ERR
# if wrmsr is available, use it
elif command -v wrmsr >/dev/null 2>&1 && [ "${SMC_NO_WRMSR:-}" != 1 ]; then
pr_debug "write_msr: using wrmsr"
wrmsr $msr_dec $value_dec 2>/dev/null
ret=$?
# ret=4: msr doesn't exist, ret=127: msr.allow_writes=off
[ "$ret" = 127 ] && write_denied=1
# or fallback to dd if it supports seek_bytes, we prefer it over perl because we can tell the difference between EPERM and EIO
elif dd if=/dev/null of=/dev/null bs=8 count=1 seek="$msr_dec" oflag=seek_bytes 2>/dev/null && [ "${SMC_NO_DD:-}" != 1 ]; then
pr_debug "write_msr: using dd"
awk "BEGIN{printf \"%c\", $value_dec}" | dd of=$CPU_DEV_BASE/"$core"/msr bs=8 count=1 seek="$msr_dec" oflag=seek_bytes 2>/dev/null
ret=$?
# if it failed, inspect stderrto look for EPERM
if [ "$ret" != 0 ]; then
if awk "BEGIN{printf \"%c\", $value_dec}" | dd of=$CPU_DEV_BASE/"$core"/msr bs=8 count=1 seek="$msr_dec" oflag=seek_bytes 2>&1 | grep -qF 'Operation not permitted'; then
write_denied=1
fi
fi
# or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "${SMC_NO_PERL:-}" != 1 ]; then
pr_debug "write_msr: using perl"
ret=1
perl -e "open(M,'>','$CPU_DEV_BASE/$core/msr') and seek(M,$msr_dec,0) and exit(syswrite(M,pack(v4,$value_dec)))"
[ $? -eq 8 ] && ret=0
else
pr_debug "write_msr: got no wrmsr, perl or recent enough dd!"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_WRMSR_${msr}_RET=$WRITE_MSR_RET_ERR")
ret_write_msr_msg="missing tool, install either msr-tools or perl"
return $WRITE_MSR_RET_ERR
fi
if [ "$ret" != 0 ]; then
# * Fedora (and probably Red Hat) have a "kernel lock down" feature that prevents us to write to MSRs
# when this mode is enabled and EFI secure boot is enabled (see issue #303)
# https://src.fedoraproject.org/rpms/kernel/blob/master/f/efi-lockdown.patch
# when this happens, any write will fail and dmesg will have a msg printed "msr: Direct access to MSR"
# * A version of this patch also made it to vanilla in 5.4+, in that case the message is: 'raw MSR access is restricted'
# * we don't use dmesg_grep() because we don't care if dmesg is truncated here, as the message has just been printed
# yet more recent versions of the msr module can be set to msr.allow_writes=off, in which case no dmesg message is printed,
# but the write fails
if [ "$write_denied" = 1 ]; then
pr_debug "write_msr: writing to msr has been denied"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_WRMSR_${msr}_RET=$WRITE_MSR_RET_LOCKDOWN")
g_msr_locked_down=1
ret_write_msr_msg="your kernel is configured to deny writes to MSRs from user space"
return $WRITE_MSR_RET_LOCKDOWN
elif dmesg 2>/dev/null | grep -qF "msr: Direct access to MSR"; then
pr_debug "write_msr: locked down kernel detected (Red Hat / Fedora)"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_WRMSR_${msr}_RET=$WRITE_MSR_RET_LOCKDOWN")
g_msr_locked_down=1
ret_write_msr_msg="your kernel is locked down (Fedora/Red Hat), please reboot without secure boot and retry"
return $WRITE_MSR_RET_LOCKDOWN
elif dmesg 2>/dev/null | grep -qF "raw MSR access is restricted"; then
pr_debug "write_msr: locked down kernel detected (vanilla)"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_WRMSR_${msr}_RET=$WRITE_MSR_RET_LOCKDOWN")
g_msr_locked_down=1
ret_write_msr_msg="your kernel is locked down, please reboot with lockdown=none in the kernel cmdline and retry"
return $WRITE_MSR_RET_LOCKDOWN
fi
unset write_denied
fi
fi
# normalize ret
if [ "$ret" = 0 ]; then
ret=$WRITE_MSR_RET_OK
else
ret=$WRITE_MSR_RET_KO
fi
pr_debug "write_msr: for cpu $core on msr $msr, value=$value, ret=$ret"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_WRMSR_${msr}_RET=$ret")
return $ret
}
readonly READ_MSR_RET_OK=0
readonly READ_MSR_RET_KO=1
readonly READ_MSR_RET_ERR=2
# Read an MSR register value across one or all cores
# Args: $1=msr_address $2=cpu_index(optional, default 0)
# Sets: ret_read_msr_value, ret_read_msr_msg
# Returns: READ_MSR_RET_OK | READ_MSR_RET_KO | READ_MSR_RET_ERR
read_msr() {
local ret core first_core_ret first_core_value
if [ "$opt_cpu" != all ]; then
# we only have one core to read, do it and return the result
read_msr_one_core "$opt_cpu" "$@"
return $?
fi
# otherwise we must read all cores
for core in $(seq 0 "$g_max_core_id"); do
read_msr_one_core "$core" "$@"
ret=$?
if [ "$core" = 0 ]; then
# save the result of the first core, for comparison with the others
first_core_ret=$ret
first_core_value=$ret_read_msr_value
else
# compare first core with the other ones
if [ "$first_core_ret" != "$ret" ] || [ "$first_core_value" != "$ret_read_msr_value" ]; then
ret_read_msr_msg="result is not homogeneous between all cores, at least core 0 and $core differ!"
return $READ_MSR_RET_ERR
fi
fi
done
# if we're here, all cores agree, return the result
return "$ret"
}
# Read an MSR register value from a single CPU core
# Args: $1=core $2=msr_address
# Sets: ret_read_msr_value, ret_read_msr_msg
# Returns: READ_MSR_RET_OK | READ_MSR_RET_KO | READ_MSR_RET_ERR
read_msr_one_core() {
local ret core msr msr_dec mockvarname msr_h msr_l
core="$1"
msr_dec=$(($2))
msr=$(printf "0x%x" "$msr_dec")
ret_read_msr_value=''
ret_read_msr_msg='unknown error'
mockvarname="SMC_MOCK_RDMSR_${msr}"
# shellcheck disable=SC2086,SC1083
if [ -n "$(eval echo \${$mockvarname:-})" ]; then
ret_read_msr_value="$(eval echo \$$mockvarname)"
pr_debug "read_msr: MOCKING enabled for msr $msr, returning $ret_read_msr_value"
g_mocked=1
return $READ_MSR_RET_OK
fi
mockvarname="SMC_MOCK_RDMSR_${msr}_RET"
# shellcheck disable=SC2086,SC1083
if [ -n "$(eval echo \${$mockvarname:-})" ] && [ "$(eval echo \$$mockvarname)" -ne 0 ]; then
pr_debug "read_msr: MOCKING enabled for msr $msr func returns $(eval echo \$$mockvarname)"
g_mocked=1
return "$(eval echo \$$mockvarname)"
fi
if [ ! -e $CPU_DEV_BASE/0/msr ] && [ ! -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
# try to load the module ourselves (and remember it so we can rmmod it afterwards)
load_msr
fi
if [ ! -e $CPU_DEV_BASE/0/msr ] && [ ! -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
ret_read_msr_msg="is msr kernel module available?"
return $READ_MSR_RET_ERR
fi
if [ "$g_os" != Linux ]; then
# for BSD
msr=$(cpucontrol -m "$msr" "${BSD_CPUCTL_DEV_BASE}$core" 2>/dev/null)
ret=$?
if [ $ret -ne 0 ]; then
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_RDMSR_${msr}_RET=$READ_MSR_RET_KO")
return $READ_MSR_RET_KO
fi
# MSR 0x10: 0x000003e1 0xb106dded
msr_h=$(echo "$msr" | awk '{print $3}')
msr_l=$(echo "$msr" | awk '{print $4}')
ret_read_msr_value=$((msr_h << 32 | msr_l))
else
# for Linux
if [ ! -r $CPU_DEV_BASE/"$core"/msr ]; then
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_RDMSR_${msr}_RET=$READ_MSR_RET_ERR")
ret_read_msr_msg="No read permission for $CPU_DEV_BASE/$core/msr"
return $READ_MSR_RET_ERR
# if rdmsr is available, use it
elif command -v rdmsr >/dev/null 2>&1 && [ "${SMC_NO_RDMSR:-}" != 1 ]; then
pr_debug "read_msr: using rdmsr on $msr"
ret_read_msr_value=$(rdmsr -r $msr_dec 2>/dev/null | od -t u8 -A n)
# or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "${SMC_NO_PERL:-}" != 1 ]; then
pr_debug "read_msr: using perl on $msr"
ret_read_msr_value=$(perl -e "open(M,'<','$CPU_DEV_BASE/$core/msr') and seek(M,$msr_dec,0) and read(M,\$_,8) and print" | od -t u8 -A n)
# fallback to dd if it supports skip_bytes
elif dd if=/dev/null of=/dev/null bs=8 count=1 skip="$msr_dec" iflag=skip_bytes 2>/dev/null; then
pr_debug "read_msr: using dd on $msr"
ret_read_msr_value=$(dd if=$CPU_DEV_BASE/"$core"/msr bs=8 count=1 skip="$msr_dec" iflag=skip_bytes 2>/dev/null | od -t u8 -A n)
else
pr_debug "read_msr: got no rdmsr, perl or recent enough dd!"
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_RDMSR_${msr}_RET=$READ_MSR_RET_ERR")
ret_read_msr_msg='missing tool, install either msr-tools or perl'
return $READ_MSR_RET_ERR
fi
if [ -z "$ret_read_msr_value" ]; then
# MSR doesn't exist, don't check for $? because some versions of dd still return 0!
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_RDMSR_${msr}_RET=$READ_MSR_RET_KO")
return $READ_MSR_RET_KO
fi
# remove sparse spaces od might give us
ret_read_msr_value=$((ret_read_msr_value))
fi
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_RDMSR_${msr}='$ret_read_msr_value'")
pr_debug "read_msr: MSR=$msr value is $ret_read_msr_value"
return $READ_MSR_RET_OK
}