mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-05 16:23:19 +02:00
178 lines
7.1 KiB
Bash
178 lines
7.1 KiB
Bash
# vim: set ts=4 sw=4 sts=4 et:
|
|
# Load the CPUID kernel module if not already loaded (Linux only)
|
|
# Sets: g_insmod_cpuid
|
|
load_cpuid() {
|
|
[ "${g_load_cpuid_once:-}" = 1 ] && return
|
|
g_load_cpuid_once=1
|
|
|
|
if [ "$g_os" = Linux ]; then
|
|
if ! grep -qw cpuid "$g_procfs/modules" 2>/dev/null; then
|
|
modprobe cpuid 2>/dev/null && g_insmod_cpuid=1
|
|
pr_debug "attempted to load module cpuid, g_insmod_cpuid=$g_insmod_cpuid"
|
|
else
|
|
pr_debug "cpuid module already loaded"
|
|
fi
|
|
else
|
|
if ! kldstat -q -m cpuctl; then
|
|
kldload cpuctl 2>/dev/null && g_kldload_cpuctl=1
|
|
pr_debug "attempted to load module cpuctl, g_kldload_cpuctl=$g_kldload_cpuctl"
|
|
else
|
|
pr_debug "cpuctl module already loaded"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# shellcheck disable=SC2034
|
|
readonly EAX=1
|
|
readonly EBX=2
|
|
readonly ECX=3
|
|
readonly EDX=4
|
|
readonly READ_CPUID_RET_OK=0
|
|
readonly READ_CPUID_RET_KO=1
|
|
readonly READ_CPUID_RET_ERR=2
|
|
# Read a CPUID register value across one or all cores
|
|
# Args: $1=leaf $2=subleaf $3=register(EAX|EBX|ECX|EDX) $4=shift $5=bit_width $6=expected_value
|
|
# Sets: ret_read_cpuid_value, ret_read_cpuid_msg
|
|
# Returns: READ_CPUID_RET_OK | READ_CPUID_RET_KO | READ_CPUID_RET_ERR
|
|
read_cpuid() {
|
|
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_cpuid_one_core "$opt_cpu" "$@"
|
|
return $?
|
|
fi
|
|
|
|
# otherwise we must read all cores
|
|
for core in $(seq 0 "$g_max_core_id"); do
|
|
read_cpuid_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_cpuid_value
|
|
else
|
|
# compare first core with the other ones
|
|
if [ "$first_core_ret" != "$ret" ] || [ "$first_core_value" != "$ret_read_cpuid_value" ]; then
|
|
ret_read_cpuid_msg="result is not homogeneous between all cores, at least core 0 and $core differ!"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
fi
|
|
done
|
|
# if we're here, all cores agree, return the result
|
|
return "$ret"
|
|
}
|
|
|
|
# Read a CPUID register value from a single CPU core
|
|
# Args: $1=core $2=leaf $3=subleaf $4=register(EAX|EBX|ECX|EDX) $5=shift $6=bit_width $7=expected_value
|
|
# Sets: ret_read_cpuid_value, ret_read_cpuid_msg
|
|
# Returns: READ_CPUID_RET_OK | READ_CPUID_RET_KO | READ_CPUID_RET_ERR
|
|
read_cpuid_one_core() {
|
|
local core leaf subleaf register shift mask wanted position ddskip odskip cpuid mockvarname reg reg_shifted
|
|
# on which core to send the CPUID instruction
|
|
core="$1"
|
|
# leaf is the value of the eax register when calling the cpuid instruction:
|
|
leaf="$2"
|
|
# subleaf is the value of the ecx register when calling the cpuid instruction:
|
|
subleaf="$3"
|
|
# eax=1 ebx=2 ecx=3 edx=4:
|
|
register="$4"
|
|
# number of bits to shift the register right to, 0-31:
|
|
shift="$5"
|
|
# mask to apply as an AND operand to the shifted register value
|
|
mask="$6"
|
|
# wanted value (optional), if present we return 0(true) if the obtained value is equal, 1 otherwise:
|
|
wanted="${7:-}"
|
|
# in any case, the read value is globally available in $ret_read_cpuid_value
|
|
ret_read_cpuid_value=''
|
|
ret_read_cpuid_msg='unknown error'
|
|
|
|
if [ $# -lt 6 ]; then
|
|
ret_read_cpuid_msg="read_cpuid: missing arguments, got only $#, expected at least 6: $*"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
if [ "$register" -gt 4 ]; then
|
|
ret_read_cpuid_msg="read_cpuid: register must be 0-4, got $register"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
if [ "$shift" -gt 32 ]; then
|
|
ret_read_cpuid_msg="read_cpuid: shift must be 0-31, got $shift"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
|
|
if [ ! -e $CPU_DEV_BASE/0/cpuid ] && [ ! -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
|
|
# try to load the module ourselves (and remember it so we can rmmod it afterwards)
|
|
load_cpuid
|
|
fi
|
|
|
|
if [ -e $CPU_DEV_BASE/0/cpuid ]; then
|
|
# Linux
|
|
if [ ! -r $CPU_DEV_BASE/0/cpuid ]; then
|
|
ret_read_cpuid_msg="Couldn't load cpuid module"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
# on some kernel versions, $CPU_DEV_BASE/0/cpuid doesn't imply that the cpuid module is loaded, in that case dd returns an error,
|
|
# we use that fact to load the module if dd returns an error
|
|
if ! dd if=$CPU_DEV_BASE/0/cpuid bs=16 count=1 >/dev/null 2>&1; then
|
|
load_cpuid
|
|
fi
|
|
# we need leaf to be converted to decimal for dd
|
|
leaf=$((leaf))
|
|
subleaf=$((subleaf))
|
|
position=$((leaf + (subleaf << 32)))
|
|
# to avoid using iflag=skip_bytes, which doesn't exist on old versions of dd, seek to the closer multiple-of-16
|
|
ddskip=$((position / 16))
|
|
odskip=$((position - ddskip * 16))
|
|
# now read the value
|
|
cpuid=$(dd if="$CPU_DEV_BASE/$core/cpuid" bs=16 skip=$ddskip count=$((odskip + 1)) 2>/dev/null | od -j $((odskip * 16)) -A n -t u4)
|
|
elif [ -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
|
|
# BSD
|
|
if [ ! -r ${BSD_CPUCTL_DEV_BASE}0 ]; then
|
|
ret_read_cpuid_msg="Couldn't read cpuid info from cpuctl"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
cpuid=$(cpucontrol -i "$leaf","$subleaf" "${BSD_CPUCTL_DEV_BASE}$core" 2>/dev/null | cut -d: -f2-)
|
|
# cpuid level 0x4, level_type 0x2: 0x1c004143 0x01c0003f 0x000001ff 0x00000000
|
|
else
|
|
ret_read_cpuid_msg="Found no way to read cpuid info"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
|
|
pr_debug "cpuid: leaf$leaf subleaf$subleaf on cpu$core, eax-ebx-ecx-edx: $cpuid"
|
|
mockvarname="SMC_MOCK_CPUID_${leaf}_${subleaf}"
|
|
# shellcheck disable=SC1083
|
|
if [ -n "$(eval echo \${"$mockvarname":-})" ]; then
|
|
cpuid="$(eval echo \$"$mockvarname")"
|
|
pr_debug "read_cpuid: MOCKING enabled for leaf $leaf subleaf $subleaf, will return $cpuid"
|
|
g_mocked=1
|
|
else
|
|
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPUID_${leaf}_${subleaf}='$cpuid'")
|
|
fi
|
|
if [ -z "$cpuid" ]; then
|
|
ret_read_cpuid_msg="Failed to get cpuid data"
|
|
return $READ_CPUID_RET_ERR
|
|
fi
|
|
|
|
# get the value of the register we want
|
|
reg=$(echo "$cpuid" | awk '{print $'"$register"'}')
|
|
# Linux returns it as decimal, BSD as hex, normalize to decimal
|
|
reg=$((reg))
|
|
# shellcheck disable=SC2046
|
|
pr_debug "cpuid: wanted register ($register) has value $reg aka "$(printf "%08x" "$reg")
|
|
reg_shifted=$((reg >> shift))
|
|
# shellcheck disable=SC2046
|
|
pr_debug "cpuid: shifted value by $shift is $reg_shifted aka "$(printf "%x" "$reg_shifted")
|
|
ret_read_cpuid_value=$((reg_shifted & mask))
|
|
# shellcheck disable=SC2046
|
|
pr_debug "cpuid: after AND $mask, final value is $ret_read_cpuid_value aka "$(printf "%x" "$ret_read_cpuid_value")
|
|
if [ -n "$wanted" ]; then
|
|
pr_debug "cpuid: wanted $wanted and got $ret_read_cpuid_value"
|
|
if [ "$ret_read_cpuid_value" = "$wanted" ]; then
|
|
return $READ_CPUID_RET_OK
|
|
else
|
|
return $READ_CPUID_RET_KO
|
|
fi
|
|
fi
|
|
|
|
return $READ_CPUID_RET_OK
|
|
}
|