mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-01 12:47:07 +02:00
split script in multiple files, reassembled through build.sh
This commit is contained in:
274
src/libs/340_cpu_msr.sh
Normal file
274
src/libs/340_cpu_msr.sh
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user