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:
51
src/libs/001_core_header.sh
Normal file
51
src/libs/001_core_header.sh
Normal file
@@ -0,0 +1,51 @@
|
||||
#! /bin/sh
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# shellcheck disable=SC2317,SC2329,SC3043
|
||||
#
|
||||
# Spectre & Meltdown checker
|
||||
#
|
||||
# Check for the latest version at:
|
||||
# https://github.com/speed47/spectre-meltdown-checker
|
||||
# git clone https://github.com/speed47/spectre-meltdown-checker.git
|
||||
# or wget https://meltdown.ovh -O spectre-meltdown-checker.sh
|
||||
# or curl -L https://meltdown.ovh -o spectre-meltdown-checker.sh
|
||||
#
|
||||
# Stephane Lesimple
|
||||
#
|
||||
VERSION='0.46+'
|
||||
|
||||
# --- Common paths and basedirs ---
|
||||
readonly VULN_SYSFS_BASE="/sys/devices/system/cpu/vulnerabilities"
|
||||
readonly DEBUGFS_BASE="/sys/kernel/debug"
|
||||
readonly SYS_MODULE_BASE="/sys/module"
|
||||
readonly CPU_DEV_BASE="/dev/cpu"
|
||||
readonly BSD_CPUCTL_DEV_BASE="/dev/cpuctl"
|
||||
|
||||
trap 'exit_cleanup' EXIT
|
||||
trap 'pr_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
|
||||
# Clean up temporary files and undo module/mount side effects on exit
|
||||
exit_cleanup() {
|
||||
local saved_ret
|
||||
saved_ret=$?
|
||||
# cleanup the temp decompressed config & kernel image
|
||||
[ -n "${g_dumped_config:-}" ] && [ -f "$g_dumped_config" ] && rm -f "$g_dumped_config"
|
||||
[ -n "${g_kerneltmp:-}" ] && [ -f "$g_kerneltmp" ] && rm -f "$g_kerneltmp"
|
||||
[ -n "${g_kerneltmp2:-}" ] && [ -f "$g_kerneltmp2" ] && rm -f "$g_kerneltmp2"
|
||||
[ -n "${g_mcedb_tmp:-}" ] && [ -f "$g_mcedb_tmp" ] && rm -f "$g_mcedb_tmp"
|
||||
[ -n "${g_intel_tmp:-}" ] && [ -d "$g_intel_tmp" ] && rm -rf "$g_intel_tmp"
|
||||
[ -n "${g_linuxfw_tmp:-}" ] && [ -f "$g_linuxfw_tmp" ] && rm -f "$g_linuxfw_tmp"
|
||||
[ "${g_mounted_debugfs:-}" = 1 ] && umount "$DEBUGFS_BASE" 2>/dev/null
|
||||
[ "${g_mounted_procfs:-}" = 1 ] && umount "$g_procfs" 2>/dev/null
|
||||
[ "${g_insmod_cpuid:-}" = 1 ] && rmmod cpuid 2>/dev/null
|
||||
[ "${g_insmod_msr:-}" = 1 ] && rmmod msr 2>/dev/null
|
||||
[ "${g_kldload_cpuctl:-}" = 1 ] && kldunload cpuctl 2>/dev/null
|
||||
[ "${g_kldload_vmm:-}" = 1 ] && kldunload vmm 2>/dev/null
|
||||
exit "$saved_ret"
|
||||
}
|
||||
|
||||
# if we were git clone'd, adjust VERSION
|
||||
if [ -d "$(dirname "$0")/.git" ] && command -v git >/dev/null 2>&1; then
|
||||
g_describe=$(git -C "$(dirname "$0")" describe --tags --dirty 2>/dev/null)
|
||||
[ -n "$g_describe" ] && VERSION=$(echo "$g_describe" | sed -e s/^v//)
|
||||
fi
|
||||
198
src/libs/002_core_globals.sh
Normal file
198
src/libs/002_core_globals.sh
Normal file
@@ -0,0 +1,198 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Print command-line usage information to stdout
|
||||
show_usage() {
|
||||
# shellcheck disable=SC2086
|
||||
cat <<EOF
|
||||
Usage:
|
||||
Live mode (auto): $(basename $0) [options]
|
||||
Live mode (manual): $(basename $0) [options] <[--kernel <kimage>] [--config <kconfig>] [--map <mapfile>]> --live
|
||||
Offline mode: $(basename $0) [options] <[--kernel <kimage>] [--config <kconfig>] [--map <mapfile>]>
|
||||
|
||||
Modes:
|
||||
Two modes are available.
|
||||
|
||||
First mode is the "live" mode (default), it does its best to find information about the currently running kernel.
|
||||
To run under this mode, just start the script without any option (you can also use --live explicitly)
|
||||
|
||||
Second mode is the "offline" mode, where you can inspect a non-running kernel.
|
||||
This mode is automatically enabled when you specify the location of the kernel file, config and System.map files:
|
||||
|
||||
--kernel kernel_file specify a (possibly compressed) Linux or BSD kernel file
|
||||
--config kernel_config specify a kernel config file (Linux only)
|
||||
--map kernel_map_file specify a kernel System.map file (Linux only)
|
||||
|
||||
If you want to use live mode while specifying the location of the kernel, config or map file yourself,
|
||||
you can add --live to the above options, to tell the script to run in live mode instead of the offline mode,
|
||||
which is enabled by default when at least one file is specified on the command line.
|
||||
|
||||
Options:
|
||||
--no-color don't use color codes
|
||||
--verbose, -v increase verbosity level, possibly several times
|
||||
--explain produce an additional human-readable explanation of actions to take to mitigate a vulnerability
|
||||
--paranoid require IBPB to deem Variant 2 as mitigated
|
||||
also require SMT disabled + unconditional L1D flush to deem Foreshadow-NG VMM as mitigated
|
||||
also require SMT disabled to deem MDS vulnerabilities mitigated
|
||||
|
||||
--no-sysfs don't use the /sys interface even if present [Linux]
|
||||
--sysfs-only only use the /sys interface, don't run our own checks [Linux]
|
||||
--coreos special mode for CoreOS (use an ephemeral toolbox to inspect kernel) [Linux]
|
||||
|
||||
--arch-prefix PREFIX specify a prefix for cross-inspecting a kernel of a different arch, for example "aarch64-linux-gnu-",
|
||||
so that invoked tools will be prefixed with this (i.e. aarch64-linux-gnu-objdump)
|
||||
--batch text produce machine readable output, this is the default if --batch is specified alone
|
||||
--batch short produce only one line with the vulnerabilities separated by spaces
|
||||
--batch json produce JSON output formatted for Puppet, Ansible, Chef...
|
||||
--batch nrpe produce machine readable output formatted for NRPE
|
||||
--batch prometheus produce output for consumption by prometheus-node-exporter
|
||||
|
||||
--variant VARIANT specify which variant you'd like to check, by default all variants are checked.
|
||||
can be used multiple times (e.g. --variant 3a --variant l1tf)
|
||||
for a list of supported VARIANT parameters, use --variant help
|
||||
--cve CVE specify which CVE you'd like to check, by default all supported CVEs are checked
|
||||
can be used multiple times (e.g. --cve CVE-2017-5753 --cve CVE-2020-0543)
|
||||
--hw-only only check for CPU information, don't check for any variant
|
||||
--no-hw skip CPU information and checks, if you're inspecting a kernel not to be run on this host
|
||||
--vmm [auto,yes,no] override the detection of the presence of a hypervisor, default: auto
|
||||
--no-intel-db don't use the builtin Intel DB of affected processors
|
||||
--allow-msr-write allow probing for write-only MSRs, this might produce kernel logs or be blocked by your system
|
||||
--cpu [#,all] interact with CPUID and MSR of CPU core number #, or all (default: CPU core 0)
|
||||
--update-fwdb update our local copy of the CPU microcodes versions database (using the awesome
|
||||
MCExtractor project and the Intel firmwares GitHub repository)
|
||||
--update-builtin-fwdb same as --update-fwdb but update builtin DB inside the script itself
|
||||
--dump-mock-data used to mimick a CPU on an other system, mainly used to help debugging this script
|
||||
|
||||
Return codes:
|
||||
0 (not vulnerable), 2 (vulnerable), 3 (unknown), 255 (error)
|
||||
|
||||
IMPORTANT:
|
||||
A false sense of security is worse than no security at all.
|
||||
Please use the --disclaimer option to understand exactly what this script does.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Print the legal disclaimer about tool accuracy and limitations
|
||||
show_disclaimer() {
|
||||
cat <<EOF
|
||||
Disclaimer:
|
||||
|
||||
This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the
|
||||
collectively named "transient execution" (aka "speculative execution") vulnerabilities that started to appear
|
||||
since early 2018 with the infamous Spectre & Meltdown.
|
||||
|
||||
This tool does NOT attempt to run any kind of exploit, and can't 100% guarantee that your system is secure,
|
||||
but rather helps you verifying whether your system has the known correct mitigations in place.
|
||||
However, some mitigations could also exist in your kernel that this script doesn't know (yet) how to detect, or it might
|
||||
falsely detect mitigations that in the end don't work as expected (for example, on backported or modified kernels).
|
||||
|
||||
Your system affectability to a given vulnerability depends on your CPU model and CPU microcode version, whereas the
|
||||
mitigations in place depend on your CPU (model and microcode), your kernel version, and both the runtime configuration
|
||||
of your CPU (through bits set through the MSRs) and your kernel. The script attempts to explain everything for each
|
||||
vulnerability, so you know where your system stands. For a given vulnerability, detailed information is sometimes
|
||||
available using the \`--explain\` switch.
|
||||
|
||||
Please also note that for the Spectre-like vulnerabilities, all software can possibly be exploited, in which case
|
||||
this tool only verifies that the kernel (which is the core of the system) you're using has the proper protections
|
||||
in place. Verifying all the other software is out of the scope of this tool, as it can't be done in a simple way.
|
||||
As a general measure, ensure you always have the most up to date stable versions of all the software you use,
|
||||
especially for those who are exposed to the world, such as network daemons and browsers.
|
||||
|
||||
For more information and answers to related questions, please refer to the FAQ.md file.
|
||||
|
||||
This tool has been released in the hope that it'll be useful, but don't use it to jump to conclusions about your security.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
g_os=$(uname -s)
|
||||
|
||||
# parse options
|
||||
opt_kernel=''
|
||||
opt_config=''
|
||||
opt_map=''
|
||||
opt_live=-1
|
||||
opt_no_color=0
|
||||
opt_batch=0
|
||||
opt_batch_format='text'
|
||||
opt_verbose=1
|
||||
opt_cve_list=''
|
||||
opt_cve_all=1
|
||||
opt_no_sysfs=0
|
||||
opt_sysfs_only=0
|
||||
opt_coreos=0
|
||||
opt_arch_prefix=''
|
||||
opt_hw_only=0
|
||||
opt_no_hw=0
|
||||
opt_vmm=-1
|
||||
opt_allow_msr_write=0
|
||||
opt_cpu=0
|
||||
opt_explain=0
|
||||
opt_paranoid=0
|
||||
opt_mock=0
|
||||
opt_intel_db=1
|
||||
|
||||
g_critical=0
|
||||
g_unknown=0
|
||||
g_nrpe_vuln=''
|
||||
|
||||
# CVE Registry: single source of truth for all CVE metadata.
|
||||
# Fields: cve_id|json_key_name|affected_var_suffix|complete_name_and_aliases
|
||||
readonly CVE_REGISTRY='
|
||||
CVE-2017-5753|SPECTRE VARIANT 1|variant1|Spectre Variant 1, bounds check bypass
|
||||
CVE-2017-5715|SPECTRE VARIANT 2|variant2|Spectre Variant 2, branch target injection
|
||||
CVE-2017-5754|MELTDOWN|variant3|Variant 3, Meltdown, rogue data cache load
|
||||
CVE-2018-3640|VARIANT 3A|variant3a|Variant 3a, rogue system register read
|
||||
CVE-2018-3639|VARIANT 4|variant4|Variant 4, speculative store bypass
|
||||
CVE-2018-3615|L1TF SGX|variantl1tf_sgx|Foreshadow (SGX), L1 terminal fault
|
||||
CVE-2018-3620|L1TF OS|variantl1tf|Foreshadow-NG (OS), L1 terminal fault
|
||||
CVE-2018-3646|L1TF VMM|variantl1tf|Foreshadow-NG (VMM), L1 terminal fault
|
||||
CVE-2018-12126|MSBDS|msbds|Fallout, microarchitectural store buffer data sampling (MSBDS)
|
||||
CVE-2018-12130|MFBDS|mfbds|ZombieLoad, microarchitectural fill buffer data sampling (MFBDS)
|
||||
CVE-2018-12127|MLPDS|mlpds|RIDL, microarchitectural load port data sampling (MLPDS)
|
||||
CVE-2019-11091|MDSUM|mdsum|RIDL, microarchitectural data sampling uncacheable memory (MDSUM)
|
||||
CVE-2019-11135|TAA|taa|ZombieLoad V2, TSX Asynchronous Abort (TAA)
|
||||
CVE-2018-12207|ITLBMH|itlbmh|No eXcuses, iTLB Multihit, machine check exception on page size changes (MCEPSC)
|
||||
CVE-2020-0543|SRBDS|srbds|Special Register Buffer Data Sampling (SRBDS)
|
||||
CVE-2023-20593|ZENBLEED|zenbleed|Zenbleed, cross-process information leak
|
||||
CVE-2022-40982|DOWNFALL|downfall|Downfall, gather data sampling (GDS)
|
||||
CVE-2023-20569|INCEPTION|inception|Inception, return address security (RAS)
|
||||
CVE-2023-23583|REPTAR|reptar|Reptar, redundant prefix issue
|
||||
'
|
||||
|
||||
# Derive the supported CVE list from the registry
|
||||
g_supported_cve_list=$(echo "$CVE_REGISTRY" | grep '^CVE-' | cut -d'|' -f1)
|
||||
|
||||
# Look up a field from the CVE registry
|
||||
# Args: $1=cve_id $2=field_number (see CVE_REGISTRY format above)
|
||||
# Callers: cve2name, _is_cpu_affected_cached, pvulnstatus
|
||||
_cve_registry_field() {
|
||||
local line
|
||||
line=$(echo "$CVE_REGISTRY" | grep -E "^$1\|")
|
||||
if [ -z "$line" ]; then
|
||||
echo "$0: error: invalid CVE '$1' passed to _cve_registry_field()" >&2
|
||||
exit 255
|
||||
fi
|
||||
echo "$line" | cut -d'|' -f"$2"
|
||||
}
|
||||
|
||||
# find a sane command to print colored messages, we prefer `printf` over `echo`
|
||||
# because `printf` behavior is more standard across Linux/BSD
|
||||
# we'll try to avoid using shell builtins that might not take options
|
||||
g_echo_cmd_type='echo'
|
||||
# ignore SC2230 here because `which` ignores builtins while `command -v` doesn't, and
|
||||
# we don't want builtins here. Even if `which` is not installed, we'll fallback to the
|
||||
# `echo` builtin anyway, so this is safe.
|
||||
# shellcheck disable=SC2230
|
||||
if command -v printf >/dev/null 2>&1; then
|
||||
g_echo_cmd=$(command -v printf)
|
||||
g_echo_cmd_type='printf'
|
||||
elif which echo >/dev/null 2>&1; then
|
||||
g_echo_cmd=$(which echo)
|
||||
else
|
||||
# maybe the `which` command is broken?
|
||||
[ -x /bin/echo ] && g_echo_cmd=/bin/echo
|
||||
# for Android
|
||||
[ -x /system/bin/echo ] && g_echo_cmd=/system/bin/echo
|
||||
fi
|
||||
# still empty? fallback to builtin
|
||||
[ -z "$g_echo_cmd" ] && g_echo_cmd='echo'
|
||||
106
src/libs/100_output_print.sh
Normal file
106
src/libs/100_output_print.sh
Normal file
@@ -0,0 +1,106 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Low-level echo wrapper handling color stripping and printf/echo portability
|
||||
# Args: $1=opt(-n for no newline, '' for normal) $2...=message
|
||||
# Callers: _pr_echo, _pr_echo_nol
|
||||
_pr_echo_raw() {
|
||||
local opt msg interpret_chars ctrlchar
|
||||
opt="$1"
|
||||
shift
|
||||
msg="$*"
|
||||
|
||||
if [ "$opt_no_color" = 1 ]; then
|
||||
# strip ANSI color codes
|
||||
# some sed versions (i.e. toybox) can't seem to handle
|
||||
# \033 aka \x1B correctly, so do it for them.
|
||||
if [ "$g_echo_cmd_type" = printf ]; then
|
||||
interpret_chars=''
|
||||
else
|
||||
interpret_chars='-e'
|
||||
fi
|
||||
ctrlchar=$($g_echo_cmd $interpret_chars "\033")
|
||||
msg=$($g_echo_cmd $interpret_chars "$msg" | sed -r "s/$ctrlchar\[([0-9][0-9]?(;[0-9][0-9]?)?)?m//g")
|
||||
fi
|
||||
if [ "$g_echo_cmd_type" = printf ]; then
|
||||
if [ "$opt" = "-n" ]; then
|
||||
$g_echo_cmd "$msg"
|
||||
else
|
||||
$g_echo_cmd "$msg\n"
|
||||
fi
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
$g_echo_cmd $opt -e "$msg"
|
||||
fi
|
||||
}
|
||||
|
||||
# Print a message if the current verbosity level is high enough
|
||||
# Args: $1=minimum_verbosity_level $2...=message
|
||||
# Callers: pr_warn, pr_info, pr_verbose, pr_debug, _emit_text, toplevel batch output
|
||||
_pr_echo() {
|
||||
if [ "$opt_verbose" -ge "$1" ]; then
|
||||
shift
|
||||
_pr_echo_raw '' "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
# Print a message without trailing newline if the current verbosity level is high enough
|
||||
# Args: $1=minimum_verbosity_level $2...=message
|
||||
# Callers: pr_info_nol, pr_verbose_nol
|
||||
_pr_echo_nol() {
|
||||
if [ "$opt_verbose" -ge "$1" ]; then
|
||||
shift
|
||||
_pr_echo_raw -n "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
# Print a warning message in red to stderr (verbosity 0, always shown)
|
||||
# Args: $1...=message
|
||||
pr_warn() {
|
||||
_pr_echo 0 "\033[31m$*\033[0m" >&2
|
||||
}
|
||||
|
||||
# Print an informational message (verbosity >= 1)
|
||||
# Args: $1...=message
|
||||
pr_info() {
|
||||
_pr_echo 1 "$*"
|
||||
}
|
||||
|
||||
# Print an informational message without trailing newline (verbosity >= 1)
|
||||
# Args: $1...=message
|
||||
pr_info_nol() {
|
||||
_pr_echo_nol 1 "$*"
|
||||
}
|
||||
|
||||
# Print a verbose message (verbosity >= 2)
|
||||
# Args: $1...=message
|
||||
pr_verbose() {
|
||||
_pr_echo 2 "$*"
|
||||
}
|
||||
|
||||
# Print a verbose message without trailing newline (verbosity >= 2)
|
||||
# Args: $1...=message
|
||||
pr_verbose_nol() {
|
||||
_pr_echo_nol 2 "$*"
|
||||
}
|
||||
|
||||
# Print a debug message in blue (verbosity >= 3)
|
||||
# Args: $1...=message
|
||||
pr_debug() {
|
||||
_pr_echo 3 "\033[34m(debug) $*\033[0m"
|
||||
}
|
||||
|
||||
# Print a "How to fix" explanation when --explain is enabled
|
||||
# Args: $1...=fix description
|
||||
explain() {
|
||||
if [ "$opt_explain" = 1 ]; then
|
||||
pr_info ''
|
||||
pr_info "> \033[41m\033[30mHow to fix:\033[0m $*"
|
||||
fi
|
||||
}
|
||||
|
||||
# Convert a CVE ID to its human-readable vulnerability name
|
||||
# Args: $1=cve_id (e.g. "CVE-2017-5753")
|
||||
cve2name() {
|
||||
_cve_registry_field "$1" 4
|
||||
}
|
||||
|
||||
g_is_cpu_affected_cached=0
|
||||
465
src/libs/200_cpu_affected.sh
Normal file
465
src/libs/200_cpu_affected.sh
Normal file
@@ -0,0 +1,465 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Return the cached affected_* status for a given CVE
|
||||
# Args: $1=cve_id
|
||||
# Returns: 0 if affected, 1 if not affected
|
||||
# Callers: is_cpu_affected
|
||||
_is_cpu_affected_cached() {
|
||||
local suffix
|
||||
suffix=$(_cve_registry_field "$1" 3)
|
||||
# shellcheck disable=SC2086
|
||||
eval "return \$affected_${suffix}"
|
||||
}
|
||||
|
||||
# Determine whether the current CPU is affected by a given CVE using whitelist logic
|
||||
# Args: $1=cve_id (one of the $g_supported_cve_list items)
|
||||
# Returns: 0 if affected, 1 if not affected
|
||||
is_cpu_affected() {
|
||||
local result cpuid_hex reptar_ucode_list tuple fixed_ucode_ver affected_fmspi affected_fms ucode_platformid_mask affected_cpuid i cpupart cpuarch
|
||||
|
||||
# if CPU is Intel and is in our dump of the Intel official affected CPUs page, use it:
|
||||
if is_intel; then
|
||||
cpuid_hex=$(printf "0x%08X" $((cpu_cpuid)))
|
||||
if [ "${g_intel_line:-}" = "no" ]; then
|
||||
pr_debug "is_cpu_affected: $cpuid_hex not in Intel database (cached)"
|
||||
elif [ -z "$g_intel_line" ]; then
|
||||
g_intel_line=$(read_inteldb | grep -F "$cpuid_hex," | head -n1)
|
||||
if [ -z "$g_intel_line" ]; then
|
||||
g_intel_line=no
|
||||
pr_debug "is_cpu_affected: $cpuid_hex not in Intel database"
|
||||
fi
|
||||
fi
|
||||
if [ "$g_intel_line" != "no" ]; then
|
||||
result=$(echo "$g_intel_line" | grep -Eo ,"$(echo "$1" | cut -c5-)"'=[^,]+' | cut -d= -f2)
|
||||
pr_debug "is_cpu_affected: inteldb for $1 says '$result'"
|
||||
|
||||
# handle special case for Foreshadow SGX (CVE-2018-3615):
|
||||
# even if we are affected to L1TF (CVE-2018-3620/CVE-2018-3646), if there's no SGX on our CPU,
|
||||
# then we're not affected to the original Foreshadow.
|
||||
if [ "$1" = "CVE-2018-3615" ] && [ "$cap_sgx" = 0 ]; then
|
||||
# not affected
|
||||
return 1
|
||||
fi
|
||||
# /special case
|
||||
|
||||
if [ "$result" = "N" ]; then
|
||||
# not affected
|
||||
return 1
|
||||
elif [ -n "$result" ]; then
|
||||
# non-empty string != N means affected
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Otherwise, do it ourselves
|
||||
|
||||
if [ "$g_is_cpu_affected_cached" = 1 ]; then
|
||||
_is_cpu_affected_cached "$1"
|
||||
return $?
|
||||
fi
|
||||
|
||||
affected_variant1=''
|
||||
affected_variant2=''
|
||||
affected_variant3=''
|
||||
affected_variant3a=''
|
||||
affected_variant4=''
|
||||
affected_variantl1tf=''
|
||||
affected_msbds=''
|
||||
affected_mfbds=''
|
||||
affected_mlpds=''
|
||||
affected_mdsum=''
|
||||
affected_taa=''
|
||||
affected_itlbmh=''
|
||||
affected_srbds=''
|
||||
# Zenbleed and Inception are both AMD specific, look for "is_amd" below:
|
||||
affected_zenbleed=immune
|
||||
affected_inception=immune
|
||||
# Downfall & Reptar are Intel specific, look for "is_intel" below:
|
||||
affected_downfall=immune
|
||||
affected_reptar=immune
|
||||
|
||||
if is_cpu_mds_free; then
|
||||
[ -z "$affected_msbds" ] && affected_msbds=immune
|
||||
[ -z "$affected_mfbds" ] && affected_mfbds=immune
|
||||
[ -z "$affected_mlpds" ] && affected_mlpds=immune
|
||||
[ -z "$affected_mdsum" ] && affected_mdsum=immune
|
||||
pr_debug "is_cpu_affected: cpu not affected by Microarchitectural Data Sampling"
|
||||
fi
|
||||
|
||||
if is_cpu_taa_free; then
|
||||
[ -z "$affected_taa" ] && affected_taa=immune
|
||||
pr_debug "is_cpu_affected: cpu not affected by TSX Asynhronous Abort"
|
||||
fi
|
||||
|
||||
if is_cpu_srbds_free; then
|
||||
[ -z "$affected_srbds" ] && affected_srbds=immune
|
||||
pr_debug "is_cpu_affected: cpu not affected by Special Register Buffer Data Sampling"
|
||||
fi
|
||||
|
||||
if is_cpu_specex_free; then
|
||||
affected_variant1=immune
|
||||
affected_variant2=immune
|
||||
affected_variant3=immune
|
||||
affected_variant3a=immune
|
||||
affected_variant4=immune
|
||||
affected_variantl1tf=immune
|
||||
affected_msbds=immune
|
||||
affected_mfbds=immune
|
||||
affected_mlpds=immune
|
||||
affected_mdsum=immune
|
||||
affected_taa=immune
|
||||
affected_srbds=immune
|
||||
elif is_intel; then
|
||||
# Intel
|
||||
# https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not affected
|
||||
# https://github.com/paboldin/meltdown-exploit/issues/19 ^F E5200 => meltdown affected
|
||||
# model name : Pentium(R) Dual-Core CPU E5200 @ 2.50GHz
|
||||
if echo "$cpu_friendly_name" | grep -qE 'Pentium\(R\) Dual-Core[[:space:]]+CPU[[:space:]]+E[0-9]{4}K?'; then
|
||||
affected_variant1=vuln
|
||||
[ -z "$affected_variant2" ] && affected_variant2=immune
|
||||
affected_variant3=vuln
|
||||
fi
|
||||
if [ "$cap_rdcl_no" = 1 ]; then
|
||||
# capability bit for future Intel processor that will explicitly state
|
||||
# that they're not affected to Meltdown
|
||||
# this var is set in check_cpu()
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variantl1tf" ] && affected_variantl1tf=immune
|
||||
pr_debug "is_cpu_affected: RDCL_NO is set so not vuln to meltdown nor l1tf"
|
||||
fi
|
||||
if [ "$cap_ssb_no" = 1 ]; then
|
||||
# capability bit for future Intel processor that will explicitly state
|
||||
# that they're not affected to Variant 4
|
||||
# this var is set in check_cpu()
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "is_cpu_affected: SSB_NO is set so not vuln to affected_variant4"
|
||||
fi
|
||||
if is_cpu_ssb_free; then
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "is_cpu_affected: cpu not affected by speculative store bypass so not vuln to affected_variant4"
|
||||
fi
|
||||
# variant 3a
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
|
||||
pr_debug "is_cpu_affected: xeon phi immune to variant 3a"
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
elif [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ]; then
|
||||
# https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00115.html
|
||||
# https://github.com/speed47/spectre-meltdown-checker/issues/310
|
||||
# => silvermont CPUs (aka cherry lake for tablets and brawsell for mobile/desktop) don't seem to be affected
|
||||
# => goldmont ARE affected
|
||||
pr_debug "is_cpu_affected: silvermont immune to variant 3a"
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
fi
|
||||
fi
|
||||
# L1TF (RDCL_NO already checked above)
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_TABLET" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT_NP" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
|
||||
|
||||
pr_debug "is_cpu_affected: intel family 6 but model known to be immune to l1tf"
|
||||
[ -z "$affected_variantl1tf" ] && affected_variantl1tf=immune
|
||||
else
|
||||
pr_debug "is_cpu_affected: intel family 6 is vuln to l1tf"
|
||||
affected_variantl1tf=vuln
|
||||
fi
|
||||
elif [ "$cpu_family" -lt 6 ]; then
|
||||
pr_debug "is_cpu_affected: intel family < 6 is immune to l1tf"
|
||||
[ -z "$affected_variantl1tf" ] && affected_variantl1tf=immune
|
||||
fi
|
||||
# Downfall
|
||||
if [ "$cap_gds_no" = 1 ]; then
|
||||
# capability bit for future Intel processors that will explicitly state
|
||||
# that they're unaffected by GDS. Also set by hypervisors on virtual CPUs
|
||||
# so that the guest kernel doesn't try to mitigate GDS when it's already mitigated on the host
|
||||
pr_debug "is_cpu_affected: downfall: not affected (GDS_NO)"
|
||||
elif [ "$cpu_family" = 6 ]; then
|
||||
# list from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=64094e7e3118aff4b0be8ff713c242303e139834
|
||||
set -u
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ]; then
|
||||
pr_debug "is_cpu_affected: downfall: affected"
|
||||
affected_downfall=vuln
|
||||
elif [ "$cap_avx2" = 0 ] && [ "$cap_avx512" = 0 ]; then
|
||||
pr_debug "is_cpu_affected: downfall: no avx; immune"
|
||||
else
|
||||
# old Intel CPU (not in their DB), not listed as being affected by the Linux kernel,
|
||||
# but with AVX2 or AVX512: unclear for now
|
||||
pr_debug "is_cpu_affected: downfall: unclear, defaulting to non-affected for now"
|
||||
fi
|
||||
set +u
|
||||
fi
|
||||
# Reptar
|
||||
# the only way to know whether a CPU is vuln, is to check whether there is a known ucode update for it,
|
||||
# as the mitigation is only ucode-based and there's no flag exposed by the kernel or by an updated ucode.
|
||||
# we have to hardcode the truthtable of affected CPUs vs updated ucodes...
|
||||
# https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/redundant-prefix-issue.html
|
||||
# list taken from:
|
||||
# https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/commit/ece0d294a29a1375397941a4e6f2f7217910bc89#diff-e6fad0f2abbac6c9603b2e8f88fe1d151a83de708aeca1c1d93d881c958ecba4R26
|
||||
# both pages have a lot of inconsistencies, I've tried to fix the errors the best I could, the logic being: if it's not in the
|
||||
# blog page, then the microcode update in the commit is not related to reptar, if microcode versions differ, then the one in github is correct,
|
||||
# if a stepping exists in the blog page but not in the commit, then the blog page is right
|
||||
reptar_ucode_list='
|
||||
06-97-02/07,00000032
|
||||
06-97-05/07,00000032
|
||||
06-9a-03/80,00000430
|
||||
06-9a-04/80,00000430
|
||||
06-6c-01/10,01000268
|
||||
06-6a-06/87,0d0003b9
|
||||
06-7e-05/80,000000c2
|
||||
06-ba-02/e0,0000411c
|
||||
06-b7-01/32,0000011d
|
||||
06-a7-01/02,0000005d
|
||||
06-bf-05/07,00000032
|
||||
06-bf-02/07,00000032
|
||||
06-ba-03/e0,0000411c
|
||||
06-8f-08/87,2b0004d0
|
||||
06-8f-07/87,2b0004d0
|
||||
06-8f-06/87,2b0004d0
|
||||
06-8f-05/87,2b0004d0
|
||||
06-8f-04/87,2b0004d0
|
||||
06-8f-08/10,2c000290
|
||||
06-8c-01/80,000000b4
|
||||
06-8c-00/ff,000000b4
|
||||
06-8d-01/c2,0000004e
|
||||
06-8d-00/c2,0000004e
|
||||
06-8c-02/c2,00000034
|
||||
'
|
||||
for tuple in $reptar_ucode_list; do
|
||||
fixed_ucode_ver=$((0x$(echo "$tuple" | cut -d, -f2)))
|
||||
affected_fmspi=$(echo "$tuple" | cut -d, -f1)
|
||||
affected_fms=$(echo "$affected_fmspi" | cut -d/ -f1)
|
||||
ucode_platformid_mask=0x$(echo "$affected_fmspi" | cut -d/ -f2)
|
||||
affected_cpuid=$(
|
||||
fms2cpuid \
|
||||
0x"$(echo "$affected_fms" | cut -d- -f1)" \
|
||||
0x"$(echo "$affected_fms" | cut -d- -f2)" \
|
||||
0x"$(echo "$affected_fms" | cut -d- -f3)"
|
||||
)
|
||||
if [ "$cpu_cpuid" = "$affected_cpuid" ] && [ $((cpu_platformid & ucode_platformid_mask)) -gt 0 ]; then
|
||||
# this is not perfect as Intel never tells about their EOL CPUs, so more CPUs might be affected but there's no way to tell
|
||||
affected_reptar=vuln
|
||||
g_reptar_fixed_ucode_version=$fixed_ucode_ver
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
elif is_amd || is_hygon; then
|
||||
# AMD revised their statement about affected_variant2 => affected
|
||||
# https://www.amd.com/en/corporate/speculative-execution
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
# https://www.amd.com/en/corporate/security-updates
|
||||
# "We have not identified any AMD x86 products susceptible to the Variant 3a vulnerability in our analysis to-date."
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
if is_cpu_ssb_free; then
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "is_cpu_affected: cpu not affected by speculative store bypass so not vuln to affected_variant4"
|
||||
fi
|
||||
affected_variantl1tf=immune
|
||||
|
||||
# Zenbleed
|
||||
amd_legacy_erratum "$(amd_model_range 0x17 0x30 0x0 0x4f 0xf)" && affected_zenbleed=vuln
|
||||
amd_legacy_erratum "$(amd_model_range 0x17 0x60 0x0 0x7f 0xf)" && affected_zenbleed=vuln
|
||||
amd_legacy_erratum "$(amd_model_range 0x17 0xa0 0x0 0xaf 0xf)" && affected_zenbleed=vuln
|
||||
|
||||
# Inception (according to kernel, zen 1 to 4)
|
||||
if [ "$cpu_family" = $((0x17)) ] || [ "$cpu_family" = $((0x19)) ]; then
|
||||
affected_inception=vuln
|
||||
fi
|
||||
|
||||
elif [ "$cpu_vendor" = CAVIUM ]; then
|
||||
affected_variant3=immune
|
||||
affected_variant3a=immune
|
||||
affected_variantl1tf=immune
|
||||
elif [ "$cpu_vendor" = PHYTIUM ]; then
|
||||
affected_variant3=immune
|
||||
affected_variant3a=immune
|
||||
affected_variantl1tf=immune
|
||||
elif [ "$cpu_vendor" = ARM ]; then
|
||||
# ARM
|
||||
# reference: https://developer.arm.com/support/security-update
|
||||
# some devices (phones or other) have several ARMs and as such different part numbers,
|
||||
# an example is "bigLITTLE". we shouldn't rely on the first CPU only, so we check the whole list
|
||||
i=0
|
||||
for cpupart in $cpu_part_list; do
|
||||
i=$((i + 1))
|
||||
# do NOT quote $cpu_arch_list below
|
||||
# shellcheck disable=SC2086
|
||||
cpuarch=$(echo $cpu_arch_list | awk '{ print $'$i' }')
|
||||
pr_debug "checking cpu$i: <$cpupart> <$cpuarch>"
|
||||
# some kernels report AArch64 instead of 8
|
||||
[ "$cpuarch" = "AArch64" ] && cpuarch=8
|
||||
# some kernels report architecture with suffix (e.g. "5TEJ" for ARMv5TEJ), extract numeric prefix
|
||||
cpuarch=$(echo "$cpuarch" | grep -oE '^[0-9]+')
|
||||
if [ -n "$cpupart" ] && [ -n "$cpuarch" ]; then
|
||||
# Cortex-R7 and Cortex-R8 are real-time and only used in medical devices or such
|
||||
# I can't find their CPU part number, but it's probably not that useful anyway
|
||||
# model R7 R8 A8 A9 A12 A15 A17 A57 A72 A73 A75 A76 A77 Neoverse-N1 Neoverse-V1 Neoverse-N1 Neoverse-V2
|
||||
# part ? ? c08 c09 c0d c0f c0e d07 d08 d09 d0a d0b d0d d0c d40 d49 d4f
|
||||
# arch 7? 7? 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8
|
||||
#
|
||||
# Whitelist identified non-affected processors, use vulnerability information from
|
||||
# https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
|
||||
# Partnumbers can be found here:
|
||||
# https://github.com/gcc-mirror/gcc/blob/master/gcc/config/arm/arm-cpus.in
|
||||
#
|
||||
# Maintain cumulative check of vulnerabilities -
|
||||
# if at least one of the cpu is affected, then the system is affected
|
||||
if [ "$cpuarch" = 7 ] && echo "$cpupart" | grep -q -w -e 0xc08 -e 0xc09 -e 0xc0d -e 0xc0e; then
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "checking cpu$i: armv7 A8/A9/A12/A17 non affected to variants 3, 3a & 4"
|
||||
elif [ "$cpuarch" = 7 ] && echo "$cpupart" | grep -q -w -e 0xc0f; then
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
affected_variant3a=vuln
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "checking cpu$i: armv7 A15 non affected to variants 3 & 4"
|
||||
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd07 -e 0xd08; then
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
affected_variant3a=vuln
|
||||
affected_variant4=vuln
|
||||
pr_debug "checking cpu$i: armv8 A57/A72 non affected to variants 3"
|
||||
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd09; then
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
affected_variant4=vuln
|
||||
pr_debug "checking cpu$i: armv8 A73 non affected to variants 3 & 3a"
|
||||
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd0a; then
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
affected_variant3=vuln
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
affected_variant4=vuln
|
||||
pr_debug "checking cpu$i: armv8 A75 non affected to variant 3a"
|
||||
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd0b -e 0xd0c -e 0xd0d; then
|
||||
affected_variant1=vuln
|
||||
[ -z "$affected_variant2" ] && affected_variant2=immune
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
affected_variant4=vuln
|
||||
pr_debug "checking cpu$i: armv8 A76/A77/NeoverseN1 non affected to variant 2, 3 & 3a"
|
||||
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd40 -e 0xd49 -e 0xd4f; then
|
||||
affected_variant1=vuln
|
||||
[ -z "$affected_variant2" ] && affected_variant2=immune
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "checking cpu$i: armv8 NeoverseN2/V1/V2 non affected to variant 2, 3, 3a & 4"
|
||||
elif [ "$cpuarch" -le 7 ] || { [ "$cpuarch" = 8 ] && [ $((cpupart)) -lt $((0xd07)) ]; }; then
|
||||
[ -z "$affected_variant1" ] && affected_variant1=immune
|
||||
[ -z "$affected_variant2" ] && affected_variant2=immune
|
||||
[ -z "$affected_variant3" ] && affected_variant3=immune
|
||||
[ -z "$affected_variant3a" ] && affected_variant3a=immune
|
||||
[ -z "$affected_variant4" ] && affected_variant4=immune
|
||||
pr_debug "checking cpu$i: arm arch$cpuarch, all immune (v7 or v8 and model < 0xd07)"
|
||||
else
|
||||
affected_variant1=vuln
|
||||
affected_variant2=vuln
|
||||
affected_variant3=vuln
|
||||
affected_variant3a=vuln
|
||||
affected_variant4=vuln
|
||||
pr_debug "checking cpu$i: arm unknown arch$cpuarch part$cpupart, considering vuln"
|
||||
fi
|
||||
fi
|
||||
pr_debug "is_cpu_affected: for cpu$i and so far, we have <$affected_variant1> <$affected_variant2> <$affected_variant3> <$affected_variant3a> <$affected_variant4>"
|
||||
done
|
||||
affected_variantl1tf=immune
|
||||
fi
|
||||
|
||||
# we handle iTLB Multihit here (not linked to is_specex_free)
|
||||
if is_intel; then
|
||||
# commit f9aa6b73a407b714c9aac44734eb4045c893c6f7
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_TABLET" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ]; then
|
||||
pr_debug "is_cpu_affected: intel family 6 but model known to be immune to itlbmh"
|
||||
[ -z "$affected_itlbmh" ] && affected_itlbmh=immune
|
||||
else
|
||||
pr_debug "is_cpu_affected: intel family 6 is vuln to itlbmh"
|
||||
affected_itlbmh=vuln
|
||||
fi
|
||||
elif [ "$cpu_family" -lt 6 ]; then
|
||||
pr_debug "is_cpu_affected: intel family < 6 is immune to itlbmh"
|
||||
[ -z "$affected_itlbmh" ] && affected_itlbmh=immune
|
||||
fi
|
||||
else
|
||||
pr_debug "is_cpu_affected: non-intel not affected to itlbmh"
|
||||
[ -z "$affected_itlbmh" ] && affected_itlbmh=immune
|
||||
fi
|
||||
|
||||
pr_debug "is_cpu_affected: temp results are <$affected_variant1> <$affected_variant2> <$affected_variant3> <$affected_variant3a> <$affected_variant4> <$affected_variantl1tf>"
|
||||
[ "$affected_variant1" = "immune" ] && affected_variant1=1 || affected_variant1=0
|
||||
[ "$affected_variant2" = "immune" ] && affected_variant2=1 || affected_variant2=0
|
||||
[ "$affected_variant3" = "immune" ] && affected_variant3=1 || affected_variant3=0
|
||||
[ "$affected_variant3a" = "immune" ] && affected_variant3a=1 || affected_variant3a=0
|
||||
[ "$affected_variant4" = "immune" ] && affected_variant4=1 || affected_variant4=0
|
||||
[ "$affected_variantl1tf" = "immune" ] && affected_variantl1tf=1 || affected_variantl1tf=0
|
||||
[ "$affected_msbds" = "immune" ] && affected_msbds=1 || affected_msbds=0
|
||||
[ "$affected_mfbds" = "immune" ] && affected_mfbds=1 || affected_mfbds=0
|
||||
[ "$affected_mlpds" = "immune" ] && affected_mlpds=1 || affected_mlpds=0
|
||||
[ "$affected_mdsum" = "immune" ] && affected_mdsum=1 || affected_mdsum=0
|
||||
[ "$affected_taa" = "immune" ] && affected_taa=1 || affected_taa=0
|
||||
[ "$affected_itlbmh" = "immune" ] && affected_itlbmh=1 || affected_itlbmh=0
|
||||
[ "$affected_srbds" = "immune" ] && affected_srbds=1 || affected_srbds=0
|
||||
[ "$affected_zenbleed" = "immune" ] && affected_zenbleed=1 || affected_zenbleed=0
|
||||
[ "$affected_downfall" = "immune" ] && affected_downfall=1 || affected_downfall=0
|
||||
[ "$affected_inception" = "immune" ] && affected_inception=1 || affected_inception=0
|
||||
[ "$affected_reptar" = "immune" ] && affected_reptar=1 || affected_reptar=0
|
||||
affected_variantl1tf_sgx="$affected_variantl1tf"
|
||||
# even if we are affected to L1TF, if there's no SGX, we're not affected to the original foreshadow
|
||||
[ "$cap_sgx" = 0 ] && affected_variantl1tf_sgx=1
|
||||
pr_debug "is_cpu_affected: final results are <$affected_variant1> <$affected_variant2> <$affected_variant3> <$affected_variant3a> <$affected_variant4> <$affected_variantl1tf> <$affected_variantl1tf_sgx>"
|
||||
g_is_cpu_affected_cached=1
|
||||
_is_cpu_affected_cached "$1"
|
||||
return $?
|
||||
}
|
||||
197
src/libs/210_cpu_detect.sh
Normal file
197
src/libs/210_cpu_detect.sh
Normal file
@@ -0,0 +1,197 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Check whether the CPU is known to not perform speculative execution
|
||||
# Returns: 0 if the CPU is speculation-free, 1 otherwise
|
||||
is_cpu_specex_free() {
|
||||
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c#n882
|
||||
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL, X86_FEATURE_ANY },
|
||||
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL_TABLET, X86_FEATURE_ANY },
|
||||
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_BONNELL_MID, X86_FEATURE_ANY },
|
||||
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL_MID, X86_FEATURE_ANY },
|
||||
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_BONNELL, X86_FEATURE_ANY },
|
||||
# { X86_VENDOR_CENTAUR, 5 },
|
||||
# { X86_VENDOR_INTEL, 5 },
|
||||
# { X86_VENDOR_NSC, 5 },
|
||||
# { X86_VENDOR_ANY, 4 },
|
||||
|
||||
parse_cpu_details
|
||||
if is_intel; then
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_TABLET" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_MID" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL" ]; then
|
||||
return 0
|
||||
fi
|
||||
elif [ "$cpu_family" = 5 ]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
[ "$cpu_family" = 4 ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is known to be unaffected by microarchitectural data sampling (MDS)
|
||||
# Returns: 0 if MDS-free, 1 if affected or unknown
|
||||
is_cpu_mds_free() {
|
||||
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c
|
||||
#VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF),
|
||||
#VULNWL_INTEL(ATOM_GOLDMONT_X, NO_MDS | NO_L1TF),
|
||||
#VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF),
|
||||
|
||||
#/* AMD Family 0xf - 0x12 */
|
||||
#VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS),
|
||||
#VULNWL_AMD(0x10, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS),
|
||||
#VULNWL_AMD(0x11, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS),
|
||||
#VULNWL_AMD(0x12, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS),
|
||||
|
||||
#/* FAMILY_ANY must be last, otherwise 0x0f - 0x12 matches won't work */
|
||||
#VULNWL_AMD(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS),
|
||||
#VULNWL_HYGON(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS),
|
||||
parse_cpu_details
|
||||
if is_intel; then
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
[ "$cap_mds_no" = 1 ] && return 0
|
||||
fi
|
||||
|
||||
# official statement from AMD says none of their CPUs are affected
|
||||
# https://www.amd.com/en/corporate/product-security
|
||||
# https://www.amd.com/system/files/documents/security-whitepaper.pdf
|
||||
if is_amd; then
|
||||
return 0
|
||||
elif is_hygon; then
|
||||
return 0
|
||||
elif [ "$cpu_vendor" = CAVIUM ]; then
|
||||
return 0
|
||||
elif [ "$cpu_vendor" = PHYTIUM ]; then
|
||||
return 0
|
||||
elif [ "$cpu_vendor" = ARM ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is known to be unaffected by TSX Asynchronous Abort (TAA)
|
||||
# Returns: 0 if TAA-free, 1 if affected or unknown
|
||||
is_cpu_taa_free() {
|
||||
|
||||
if ! is_intel; then
|
||||
return 0
|
||||
# is intel
|
||||
elif [ "$cap_taa_no" = 1 ] || [ "$cap_rtm" = 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is known to be unaffected by Special Register Buffer Data Sampling (SRBDS)
|
||||
# Returns: 0 if SRBDS-free, 1 if affected or unknown
|
||||
is_cpu_srbds_free() {
|
||||
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c
|
||||
#
|
||||
# A processor is affected by SRBDS if its Family_Model and stepping is in the
|
||||
# following list, with the exception of the listed processors
|
||||
# exporting MDS_NO while Intel TSX is available yet not enabled. The
|
||||
# latter class of processors are only affected when Intel TSX is enabled
|
||||
# by software using TSX_CTRL_MSR otherwise they are not affected.
|
||||
#
|
||||
# ============= ============ ========
|
||||
# common name Family_Model Stepping
|
||||
# ============= ============ ========
|
||||
# IvyBridge 06_3AH All (INTEL_FAM6_IVYBRIDGE)
|
||||
#
|
||||
# Haswell 06_3CH All (INTEL_FAM6_HASWELL)
|
||||
# Haswell_L 06_45H All (INTEL_FAM6_HASWELL_L)
|
||||
# Haswell_G 06_46H All (INTEL_FAM6_HASWELL_G)
|
||||
#
|
||||
# Broadwell_G 06_47H All (INTEL_FAM6_BROADWELL_G)
|
||||
# Broadwell 06_3DH All (INTEL_FAM6_BROADWELL)
|
||||
#
|
||||
# Skylake_L 06_4EH All (INTEL_FAM6_SKYLAKE_L)
|
||||
# Skylake 06_5EH All (INTEL_FAM6_SKYLAKE)
|
||||
#
|
||||
# Kabylake_L 06_8EH <=0xC (MDS_NO) (INTEL_FAM6_KABYLAKE_L)
|
||||
#
|
||||
# Kabylake 06_9EH <=0xD (MDS_NO) (INTEL_FAM6_KABYLAKE)
|
||||
# ============= ============ ========
|
||||
parse_cpu_details
|
||||
if is_intel; then
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_IVYBRIDGE" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_HASWELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_HASWELL_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_HASWELL_G" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_G" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ]; then
|
||||
return 1
|
||||
elif [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] && [ "$cpu_stepping" -le 12 ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] && [ "$cpu_stepping" -le 13 ]; then
|
||||
if [ "$cap_mds_no" -eq 1 ] && { [ "$cap_rtm" -eq 0 ] || [ "$cap_tsx_ctrl_rtm_disable" -eq 1 ]; }; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
# Check whether the CPU is known to be unaffected by Speculative Store Bypass (SSB)
|
||||
# Returns: 0 if SSB-free, 1 if affected or unknown
|
||||
is_cpu_ssb_free() {
|
||||
# source1: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c#n945
|
||||
# source2: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/tree/arch/x86/kernel/cpu/common.c
|
||||
# Only list CPUs that speculate but are immune, to avoid duplication of cpus listed in is_cpu_specex_free()
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT_X },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT_MID },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_CORE_YONAH },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL },
|
||||
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM },
|
||||
#{ X86_VENDOR_AMD, 0x12, },
|
||||
#{ X86_VENDOR_AMD, 0x11, },
|
||||
#{ X86_VENDOR_AMD, 0x10, },
|
||||
#{ X86_VENDOR_AMD, 0xf, },
|
||||
parse_cpu_details
|
||||
if is_intel; then
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ]; then
|
||||
return 0
|
||||
elif [ "$cpu_model" = "$INTEL_FAM6_CORE_YONAH" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if is_amd; then
|
||||
if [ "$cpu_family" = "18" ] ||
|
||||
[ "$cpu_family" = "17" ] ||
|
||||
[ "$cpu_family" = "16" ] ||
|
||||
[ "$cpu_family" = "15" ]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if is_hygon; then
|
||||
return 1
|
||||
fi
|
||||
[ "$cpu_family" = 4 ] && return 0
|
||||
return 1
|
||||
}
|
||||
214
src/libs/220_util_update.sh
Normal file
214
src/libs/220_util_update.sh
Normal file
@@ -0,0 +1,214 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Print the tool name and version banner
|
||||
show_header() {
|
||||
pr_info "Spectre and Meltdown mitigation detection tool v$VERSION"
|
||||
pr_info
|
||||
}
|
||||
|
||||
# Convert Family-Model-Stepping triplet to a CPUID value (base-10 to stdout)
|
||||
# Args: $1=family $2=model $3=stepping
|
||||
fms2cpuid() {
|
||||
local family model stepping extfamily lowfamily extmodel lowmodel
|
||||
family="$1"
|
||||
model="$2"
|
||||
stepping="$3"
|
||||
|
||||
if [ "$((family))" -le 15 ]; then
|
||||
extfamily=0
|
||||
lowfamily=$((family))
|
||||
else
|
||||
# when we have a family > 0xF, then lowfamily is stuck at 0xF
|
||||
# and extfamily is ADDED to it (as in "+"), to ensure old software
|
||||
# never sees a lowfamily < 0xF for newer families
|
||||
lowfamily=15
|
||||
extfamily=$(((family) - 15))
|
||||
fi
|
||||
extmodel=$(((model & 0xF0) >> 4))
|
||||
lowmodel=$(((model & 0x0F) >> 0))
|
||||
echo $(((stepping & 0x0F) | (lowmodel << 4) | (lowfamily << 8) | (extmodel << 16) | (extfamily << 20)))
|
||||
}
|
||||
|
||||
# Download a file using wget, curl, or fetch (whichever is available)
|
||||
# Args: $1=url $2=output_file
|
||||
download_file() {
|
||||
local ret url file
|
||||
url="$1"
|
||||
file="$2"
|
||||
if command -v wget >/dev/null 2>&1; then
|
||||
wget -q "$url" -O "$file"
|
||||
ret=$?
|
||||
elif command -v curl >/dev/null 2>&1; then
|
||||
curl -sL "$url" -o "$file"
|
||||
ret=$?
|
||||
elif command -v fetch >/dev/null 2>&1; then
|
||||
fetch -q "$url" -o "$file"
|
||||
ret=$?
|
||||
else
|
||||
echo ERROR "please install one of \`wget\`, \`curl\` of \`fetch\` programs"
|
||||
unset file url
|
||||
return 1
|
||||
fi
|
||||
unset file url
|
||||
if [ "$ret" != 0 ]; then
|
||||
echo ERROR "error $ret"
|
||||
return $ret
|
||||
fi
|
||||
echo DONE
|
||||
}
|
||||
|
||||
[ -z "$HOME" ] && HOME="$(getent passwd "$(whoami)" | cut -d: -f6)"
|
||||
g_mcedb_cache="$HOME/.mcedb"
|
||||
# Download and update the local microcode firmware database cache
|
||||
# Sets: g_mcedb_tmp (temp file, cleaned up on exit)
|
||||
update_fwdb() {
|
||||
local previous_dbversion dbversion mcedb_revision iucode_tool nbfound linuxfw_hash mcedb_url intel_url linuxfw_url newfile line cpuid pfmask date version intel_timestamp intel_latest_date family model stepping sqlstm
|
||||
|
||||
show_header
|
||||
|
||||
set -e
|
||||
|
||||
if [ -r "$g_mcedb_cache" ]; then
|
||||
previous_dbversion=$(awk '/^# %%% MCEDB / { print $4 }' "$g_mcedb_cache")
|
||||
fi
|
||||
|
||||
# first, download the MCE.db from the excellent platomav's MCExtractor project
|
||||
g_mcedb_tmp="$(mktemp -t smc-mcedb-XXXXXX)"
|
||||
mcedb_url='https://github.com/platomav/MCExtractor/raw/master/MCE.db'
|
||||
pr_info_nol "Fetching MCE.db from the MCExtractor project... "
|
||||
download_file "$mcedb_url" "$g_mcedb_tmp" || return $?
|
||||
|
||||
# second, get the Intel firmwares from GitHub
|
||||
g_intel_tmp="$(mktemp -d -t smc-intelfw-XXXXXX)"
|
||||
intel_url="https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/archive/main.zip"
|
||||
pr_info_nol "Fetching Intel firmwares... "
|
||||
## https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
|
||||
download_file "$intel_url" "$g_intel_tmp/fw.zip" || return $?
|
||||
|
||||
# now extract MCEdb contents using sqlite
|
||||
pr_info_nol "Extracting MCEdb data... "
|
||||
if ! command -v sqlite3 >/dev/null 2>&1; then
|
||||
echo ERROR "please install the \`sqlite3\` program"
|
||||
return 1
|
||||
fi
|
||||
mcedb_revision=$(sqlite3 "$g_mcedb_tmp" "SELECT \"revision\" from \"MCE\"")
|
||||
if [ -z "$mcedb_revision" ]; then
|
||||
echo ERROR "downloaded file seems invalid"
|
||||
return 1
|
||||
fi
|
||||
sqlite3 "$g_mcedb_tmp" "ALTER TABLE \"Intel\" ADD COLUMN \"origin\" TEXT"
|
||||
sqlite3 "$g_mcedb_tmp" "ALTER TABLE \"Intel\" ADD COLUMN \"pfmask\" TEXT"
|
||||
sqlite3 "$g_mcedb_tmp" "ALTER TABLE \"AMD\" ADD COLUMN \"origin\" TEXT"
|
||||
sqlite3 "$g_mcedb_tmp" "ALTER TABLE \"AMD\" ADD COLUMN \"pfmask\" TEXT"
|
||||
sqlite3 "$g_mcedb_tmp" "UPDATE \"Intel\" SET \"origin\"='mce'"
|
||||
sqlite3 "$g_mcedb_tmp" "UPDATE \"Intel\" SET \"pfmask\"='FF'"
|
||||
sqlite3 "$g_mcedb_tmp" "UPDATE \"AMD\" SET \"origin\"='mce'"
|
||||
sqlite3 "$g_mcedb_tmp" "UPDATE \"AMD\" SET \"pfmask\"='FF'"
|
||||
|
||||
echo OK "MCExtractor database revision $mcedb_revision"
|
||||
|
||||
# parse Intel firmwares to get their versions
|
||||
pr_info_nol "Integrating Intel firmwares data to db... "
|
||||
if ! command -v unzip >/dev/null 2>&1; then
|
||||
echo ERROR "please install the \`unzip\` program"
|
||||
return 1
|
||||
fi
|
||||
(cd "$g_intel_tmp" && unzip fw.zip >/dev/null)
|
||||
if ! [ -d "$g_intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/intel-ucode" ]; then
|
||||
echo ERROR "expected the 'intel-ucode' folder in the downloaded zip file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v iucode_tool >/dev/null 2>&1; then
|
||||
if ! command -v iucode-tool >/dev/null 2>&1; then
|
||||
echo ERROR "please install the \`iucode-tool\` program"
|
||||
return 1
|
||||
else
|
||||
iucode_tool="iucode-tool"
|
||||
fi
|
||||
else
|
||||
iucode_tool="iucode_tool"
|
||||
fi
|
||||
# 079/001: sig 0x000106c2, pf_mask 0x01, 2009-04-10, rev 0x0217, size 5120
|
||||
# 078/004: sig 0x000106ca, pf_mask 0x10, 2009-08-25, rev 0x0107, size 5120
|
||||
$iucode_tool -l "$g_intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/intel-ucode" | grep -wF sig | while read -r line; do
|
||||
cpuid=$(echo "$line" | grep -Eio 'sig 0x[0-9a-f]+' | awk '{print $2}')
|
||||
cpuid=$((cpuid))
|
||||
cpuid=$(printf "%08X" "$cpuid")
|
||||
pfmask=$(echo "$line" | grep -Eio 'pf_mask 0x[0-9a-f]+' | awk '{print $2}')
|
||||
pfmask=$((pfmask))
|
||||
pfmask=$(printf "%02X" $pfmask)
|
||||
date=$(echo "$line" | grep -Eo '(19|20)[0-9][0-9]-[01][0-9]-[0-3][0-9]' | tr -d '-')
|
||||
version=$(echo "$line" | grep -Eio 'rev 0x[0-9a-f]+' | awk '{print $2}')
|
||||
version=$((version))
|
||||
version=$(printf "%08X" "$version")
|
||||
# ensure the official Intel DB always has precedence over mcedb, even if mcedb has seen a more recent fw
|
||||
sqlite3 "$g_mcedb_tmp" "DELETE FROM \"Intel\" WHERE \"origin\" != 'intel' AND \"cpuid\" = '$cpuid';"
|
||||
# then insert our version
|
||||
sqlite3 "$g_mcedb_tmp" "INSERT INTO \"Intel\" (\"origin\",\"cpuid\",\"pfmask\",\"version\",\"yyyymmdd\") VALUES ('intel','$cpuid','$pfmask','$version','$date');"
|
||||
done
|
||||
intel_timestamp=$(stat -c %Y "$g_intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/license" 2>/dev/null)
|
||||
if [ -n "$intel_timestamp" ]; then
|
||||
# use this date, it matches the last commit date
|
||||
intel_latest_date=$(date +%Y%m%d -d @"$intel_timestamp")
|
||||
else
|
||||
echo "Falling back to the latest microcode date"
|
||||
intel_latest_date=$(sqlite3 "$g_mcedb_tmp" "SELECT \"yyyymmdd\" FROM \"Intel\" WHERE \"origin\"='intel' ORDER BY \"yyyymmdd\" DESC LIMIT 1;")
|
||||
fi
|
||||
echo DONE "(version $intel_latest_date)"
|
||||
|
||||
# now parse the most recent linux-firmware amd-ucode README file
|
||||
pr_info_nol "Fetching latest amd-ucode README from linux-firmware project... "
|
||||
linuxfw_url="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/amd-ucode/README"
|
||||
g_linuxfw_tmp=$(mktemp -t smc-linuxfw-XXXXXX)
|
||||
download_file "$linuxfw_url" "$g_linuxfw_tmp" || return $?
|
||||
|
||||
pr_info_nol "Parsing the README... "
|
||||
nbfound=0
|
||||
for line in $(grep -E 'Family=0x[0-9a-f]+ Model=0x[0-9a-f]+ Stepping=0x[0-9a-f]+: Patch=0x[0-9a-f]+' "$g_linuxfw_tmp" | tr " " ","); do
|
||||
pr_debug "Parsing line $line"
|
||||
family=$(echo "$line" | grep -Eoi 'Family=0x[0-9a-f]+' | cut -d= -f2)
|
||||
model=$(echo "$line" | grep -Eoi 'Model=0x[0-9a-f]+' | cut -d= -f2)
|
||||
stepping=$(echo "$line" | grep -Eoi 'Stepping=0x[0-9a-f]+' | cut -d= -f2)
|
||||
version=$(echo "$line" | grep -Eoi 'Patch=0x[0-9a-f]+' | cut -d= -f2)
|
||||
version=$(printf "%08X" "$((version))")
|
||||
cpuid=$(fms2cpuid "$family" "$model" "$stepping")
|
||||
cpuid=$(printf "%08X" "$cpuid")
|
||||
sqlstm="INSERT INTO \"AMD\" (\"origin\",\"cpuid\",\"pfmask\",\"version\",\"yyyymmdd\") VALUES ('linux-firmware','$cpuid','FF','$version','20000101')"
|
||||
pr_debug "family $family model $model stepping $stepping cpuid $cpuid"
|
||||
pr_debug "$sqlstm"
|
||||
sqlite3 "$g_mcedb_tmp" "$sqlstm"
|
||||
nbfound=$((nbfound + 1))
|
||||
unset family model stepping version cpuid date sqlstm
|
||||
done
|
||||
echo "found $nbfound microcodes"
|
||||
unset nbfound
|
||||
|
||||
dbversion="$mcedb_revision+i$intel_latest_date"
|
||||
linuxfw_hash=$(md5sum "$g_linuxfw_tmp" 2>/dev/null | cut -c1-4)
|
||||
if [ -n "$linuxfw_hash" ]; then
|
||||
dbversion="$dbversion+$linuxfw_hash"
|
||||
fi
|
||||
|
||||
if [ "$1" != builtin ] && [ -n "$previous_dbversion" ] && [ "$previous_dbversion" = "v$dbversion" ]; then
|
||||
echo "We already have this version locally, no update needed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
pr_info_nol "Building local database... "
|
||||
{
|
||||
echo "# Spectre & Meltdown Checker"
|
||||
echo "# %%% MCEDB v$dbversion"
|
||||
# we'll use the more recent fw for Intel and AMD
|
||||
sqlite3 "$g_mcedb_tmp" "SELECT '# I,0x'||\"t1\".\"cpuid\"||',0x'||\"t1\".\"pfmask\"||',0x'||MAX(\"t1\".\"version\")||','||\"t1\".\"yyyymmdd\" FROM \"Intel\" AS \"t1\" LEFT OUTER JOIN \"Intel\" AS \"t2\" ON \"t2\".\"cpuid\"=\"t1\".\"cpuid\" AND \"t2\".\"pfmask\"=\"t1\".\"pfmask\" AND \"t2\".\"yyyymmdd\" > \"t1\".\"yyyymmdd\" WHERE \"t2\".\"yyyymmdd\" IS NULL GROUP BY \"t1\".\"cpuid\",\"t1\".\"pfmask\" ORDER BY \"t1\".\"cpuid\",\"t1\".\"pfmask\" ASC;" | grep -v '^# .,0x00000000,'
|
||||
sqlite3 "$g_mcedb_tmp" "SELECT '# A,0x'||\"t1\".\"cpuid\"||',0x'||\"t1\".\"pfmask\"||',0x'||MAX(\"t1\".\"version\")||','||\"t1\".\"yyyymmdd\" FROM \"AMD\" AS \"t1\" LEFT OUTER JOIN \"AMD\" AS \"t2\" ON \"t2\".\"cpuid\"=\"t1\".\"cpuid\" AND \"t2\".\"pfmask\"=\"t1\".\"pfmask\" AND \"t2\".\"yyyymmdd\" > \"t1\".\"yyyymmdd\" WHERE \"t2\".\"yyyymmdd\" IS NULL GROUP BY \"t1\".\"cpuid\",\"t1\".\"pfmask\" ORDER BY \"t1\".\"cpuid\",\"t1\".\"pfmask\" ASC;" | grep -v '^# .,0x00000000,'
|
||||
} >"$g_mcedb_cache"
|
||||
echo DONE "(version $dbversion)"
|
||||
|
||||
if [ "$1" = builtin ]; then
|
||||
newfile=$(mktemp -t smc-builtin-XXXXXX)
|
||||
awk '/^# %%% MCEDB / { exit }; { print }' "$0" >"$newfile"
|
||||
awk '{ if (NR>1) { print } }' "$g_mcedb_cache" >>"$newfile"
|
||||
cat "$newfile" >"$0"
|
||||
rm -f "$newfile"
|
||||
fi
|
||||
}
|
||||
285
src/libs/230_util_optparse.sh
Normal file
285
src/libs/230_util_optparse.sh
Normal file
@@ -0,0 +1,285 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Validate a command-line option that expects a readable file path
|
||||
# Args: $1=option_name $2=option_value (file path)
|
||||
parse_opt_file() {
|
||||
local option_name option_value
|
||||
option_name="$1"
|
||||
option_value="$2"
|
||||
if [ -z "$option_value" ]; then
|
||||
show_header
|
||||
show_usage
|
||||
echo "$0: error: --$option_name expects one parameter (a file)" >&2
|
||||
exit 1
|
||||
elif [ ! -e "$option_value" ]; then
|
||||
show_header
|
||||
echo "$0: error: couldn't find file $option_value" >&2
|
||||
exit 1
|
||||
elif [ ! -f "$option_value" ]; then
|
||||
show_header
|
||||
echo "$0: error: $option_value is not a file" >&2
|
||||
exit 1
|
||||
elif [ ! -r "$option_value" ]; then
|
||||
show_header
|
||||
echo "$0: error: couldn't read $option_value (are you root?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$option_value"
|
||||
exit 0
|
||||
}
|
||||
|
||||
while [ -n "${1:-}" ]; do
|
||||
if [ "$1" = "--kernel" ]; then
|
||||
opt_kernel=$(parse_opt_file kernel "$2")
|
||||
ret=$?
|
||||
[ $ret -ne 0 ] && exit 255
|
||||
shift 2
|
||||
elif [ "$1" = "--config" ]; then
|
||||
opt_config=$(parse_opt_file config "$2")
|
||||
ret=$?
|
||||
[ $ret -ne 0 ] && exit 255
|
||||
shift 2
|
||||
elif [ "$1" = "--map" ]; then
|
||||
opt_map=$(parse_opt_file map "$2")
|
||||
ret=$?
|
||||
[ $ret -ne 0 ] && exit 255
|
||||
shift 2
|
||||
elif [ "$1" = "--arch-prefix" ]; then
|
||||
opt_arch_prefix="$2"
|
||||
shift 2
|
||||
elif [ "$1" = "--live" ]; then
|
||||
opt_live=1
|
||||
shift
|
||||
elif [ "$1" = "--no-color" ]; then
|
||||
opt_no_color=1
|
||||
shift
|
||||
elif [ "$1" = "--no-sysfs" ]; then
|
||||
opt_no_sysfs=1
|
||||
shift
|
||||
elif [ "$1" = "--sysfs-only" ]; then
|
||||
opt_sysfs_only=1
|
||||
shift
|
||||
elif [ "$1" = "--coreos" ]; then
|
||||
opt_coreos=1
|
||||
shift
|
||||
elif [ "$1" = "--coreos-within-toolbox" ]; then
|
||||
# don't use directly: used internally by --coreos
|
||||
opt_coreos=0
|
||||
shift
|
||||
elif [ "$1" = "--paranoid" ]; then
|
||||
opt_paranoid=1
|
||||
shift
|
||||
elif [ "$1" = "--hw-only" ]; then
|
||||
opt_hw_only=1
|
||||
shift
|
||||
elif [ "$1" = "--no-hw" ]; then
|
||||
opt_no_hw=1
|
||||
shift
|
||||
elif [ "$1" = "--allow-msr-write" ]; then
|
||||
opt_allow_msr_write=1
|
||||
shift
|
||||
elif [ "$1" = "--no-intel-db" ]; then
|
||||
opt_intel_db=0
|
||||
shift
|
||||
elif [ "$1" = "--cpu" ]; then
|
||||
opt_cpu=$2
|
||||
if [ "$opt_cpu" != all ]; then
|
||||
if echo "$opt_cpu" | grep -Eq '^[0-9]+'; then
|
||||
opt_cpu=$((opt_cpu))
|
||||
else
|
||||
echo "$0: error: --cpu should be an integer or 'all', got '$opt_cpu'" >&2
|
||||
exit 255
|
||||
fi
|
||||
fi
|
||||
shift 2
|
||||
elif [ "$1" = "--no-explain" ]; then
|
||||
# deprecated, kept for compatibility
|
||||
opt_explain=0
|
||||
shift
|
||||
elif [ "$1" = "--update-fwdb" ] || [ "$1" = "--update-mcedb" ]; then
|
||||
update_fwdb
|
||||
exit $?
|
||||
elif [ "$1" = "--update-builtin-fwdb" ] || [ "$1" = "--update-builtin-mcedb" ]; then
|
||||
update_fwdb builtin
|
||||
exit $?
|
||||
elif [ "$1" = "--dump-mock-data" ]; then
|
||||
opt_mock=1
|
||||
shift
|
||||
elif [ "$1" = "--explain" ]; then
|
||||
opt_explain=1
|
||||
shift
|
||||
elif [ "$1" = "--batch" ]; then
|
||||
opt_batch=1
|
||||
opt_verbose=0
|
||||
opt_no_color=1
|
||||
shift
|
||||
case "$1" in
|
||||
text | short | nrpe | json | prometheus)
|
||||
opt_batch_format="$1"
|
||||
shift
|
||||
;;
|
||||
--*) ;; # allow subsequent flags
|
||||
'') ;; # allow nothing at all
|
||||
*)
|
||||
echo "$0: error: unknown batch format '$1'" >&2
|
||||
echo "$0: error: --batch expects a format from: text, nrpe, json" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
elif [ "$1" = "-v" ] || [ "$1" = "--verbose" ]; then
|
||||
opt_verbose=$((opt_verbose + 1))
|
||||
[ "$opt_verbose" -ge 2 ] && opt_mock=1
|
||||
shift
|
||||
elif [ "$1" = "--cve" ]; then
|
||||
if [ -z "$2" ]; then
|
||||
echo "$0: error: option --cve expects a parameter, supported CVEs are: $g_supported_cve_list" >&2
|
||||
exit 255
|
||||
fi
|
||||
selected_cve=$(echo "$g_supported_cve_list" | grep -iwo "$2")
|
||||
if [ -n "$selected_cve" ]; then
|
||||
opt_cve_list="$opt_cve_list $selected_cve"
|
||||
opt_cve_all=0
|
||||
else
|
||||
echo "$0: error: unsupported CVE specified ('$2'), supported CVEs are: $g_supported_cve_list" >&2
|
||||
exit 255
|
||||
fi
|
||||
shift 2
|
||||
elif [ "$1" = "--vmm" ]; then
|
||||
if [ -z "$2" ]; then
|
||||
echo "$0: error: option --vmm (auto, yes, no)" >&2
|
||||
exit 255
|
||||
fi
|
||||
case "$2" in
|
||||
auto) opt_vmm=-1 ;;
|
||||
yes) opt_vmm=1 ;;
|
||||
no) opt_vmm=0 ;;
|
||||
*)
|
||||
echo "$0: error: expected one of (auto, yes, no) to option --vmm instead of '$2'" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
shift 2
|
||||
elif [ "$1" = "--variant" ]; then
|
||||
if [ -z "$2" ]; then
|
||||
echo "$0: error: option --variant expects a parameter (see --variant help)" >&2
|
||||
exit 255
|
||||
fi
|
||||
case "$2" in
|
||||
help)
|
||||
echo "The following parameters are supported for --variant (can be used multiple times):"
|
||||
echo "1, 2, 3, 3a, 4, msbds, mfbds, mlpds, mdsum, l1tf, taa, mcepsc, srbds, zenbleed, downfall, inception"
|
||||
exit 0
|
||||
;;
|
||||
1)
|
||||
opt_cve_list="$opt_cve_list CVE-2017-5753"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
2)
|
||||
opt_cve_list="$opt_cve_list CVE-2017-5715"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
3)
|
||||
opt_cve_list="$opt_cve_list CVE-2017-5754"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
3a)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-3640"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
4)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-3639"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
msbds)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-12126"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
mfbds)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-12130"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
mlpds)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-12127"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
mdsum)
|
||||
opt_cve_list="$opt_cve_list CVE-2019-11091"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
l1tf)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-3615 CVE-2018-3620 CVE-2018-3646"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
taa)
|
||||
opt_cve_list="$opt_cve_list CVE-2019-11135"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
mcepsc)
|
||||
opt_cve_list="$opt_cve_list CVE-2018-12207"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
srbds)
|
||||
opt_cve_list="$opt_cve_list CVE-2020-0543"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
zenbleed)
|
||||
opt_cve_list="$opt_cve_list CVE-2023-20593"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
downfall)
|
||||
opt_cve_list="$opt_cve_list CVE-2022-40982"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
inception)
|
||||
opt_cve_list="$opt_cve_list CVE-2023-20569"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
reptar)
|
||||
opt_cve_list="$opt_cve_list CVE-2023-23583"
|
||||
opt_cve_all=0
|
||||
;;
|
||||
*)
|
||||
echo "$0: error: invalid parameter '$2' for --variant, see --variant help for a list" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
shift 2
|
||||
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
show_header
|
||||
show_usage
|
||||
exit 0
|
||||
elif [ "$1" = "--version" ]; then
|
||||
opt_no_color=1
|
||||
show_header
|
||||
exit 0
|
||||
elif [ "$1" = "--disclaimer" ]; then
|
||||
show_header
|
||||
show_disclaimer
|
||||
exit 0
|
||||
else
|
||||
show_header
|
||||
show_usage
|
||||
echo "$0: error: unknown option '$1'"
|
||||
exit 255
|
||||
fi
|
||||
done
|
||||
|
||||
show_header
|
||||
|
||||
if [ "$opt_no_sysfs" = 1 ] && [ "$opt_sysfs_only" = 1 ]; then
|
||||
pr_warn "Incompatible options specified (--no-sysfs and --sysfs-only), aborting"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
if [ "$opt_no_hw" = 1 ] && [ "$opt_hw_only" = 1 ]; then
|
||||
pr_warn "Incompatible options specified (--no-hw and --hw-only), aborting"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
if [ "$opt_live" = -1 ]; then
|
||||
if [ -n "$opt_kernel" ] || [ -n "$opt_config" ] || [ -n "$opt_map" ]; then
|
||||
# no --live specified and we have a least one of the kernel/config/map files on the cmdline: offline mode
|
||||
opt_live=0
|
||||
else
|
||||
opt_live=1
|
||||
fi
|
||||
fi
|
||||
21
src/libs/240_output_status.sh
Normal file
21
src/libs/240_output_status.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Print a colored status badge followed by an optional supplement
|
||||
# Args: $1=color(red|green|yellow|blue) $2=message $3=supplement(optional)
|
||||
pstatus() {
|
||||
local col
|
||||
if [ "$opt_no_color" = 1 ]; then
|
||||
pr_info_nol "$2"
|
||||
else
|
||||
case "$1" in
|
||||
red) col="\033[41m\033[30m" ;;
|
||||
green) col="\033[42m\033[30m" ;;
|
||||
yellow) col="\033[43m\033[30m" ;;
|
||||
blue) col="\033[44m\033[30m" ;;
|
||||
*) col="" ;;
|
||||
esac
|
||||
pr_info_nol "$col $2 \033[0m"
|
||||
fi
|
||||
[ -n "${3:-}" ] && pr_info_nol " ($3)"
|
||||
pr_info
|
||||
unset col
|
||||
}
|
||||
123
src/libs/250_output_emitters.sh
Normal file
123
src/libs/250_output_emitters.sh
Normal file
@@ -0,0 +1,123 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# --- Format-specific batch emitters ---
|
||||
|
||||
# Emit a single CVE result as plain text
|
||||
# Args: $1=cve $2=aka $3=status $4=description
|
||||
# Callers: pvulnstatus
|
||||
_emit_text() {
|
||||
_pr_echo 0 "$1: $3 ($4)"
|
||||
}
|
||||
|
||||
# Append CVE ID to the space-separated short output buffer
|
||||
# Args: $1=cve $2=aka $3=status $4=description
|
||||
# Sets: g_short_output
|
||||
# Callers: pvulnstatus
|
||||
_emit_short() {
|
||||
g_short_output="${g_short_output}$1 "
|
||||
}
|
||||
|
||||
# Append a CVE result as a JSON object to the batch output buffer
|
||||
# Args: $1=cve $2=aka $3=status(UNK|VULN|OK) $4=description
|
||||
# Sets: g_json_output
|
||||
# Callers: pvulnstatus
|
||||
_emit_json() {
|
||||
local is_vuln esc_name esc_infos
|
||||
case "$3" in
|
||||
UNK) is_vuln="null" ;;
|
||||
VULN) is_vuln="true" ;;
|
||||
OK) is_vuln="false" ;;
|
||||
*)
|
||||
echo "$0: error: unknown status '$3' passed to _emit_json()" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
# escape backslashes and double quotes for valid JSON strings
|
||||
esc_name=$(printf '%s' "$2" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
esc_infos=$(printf '%s' "$4" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
[ -z "$g_json_output" ] && g_json_output='['
|
||||
g_json_output="${g_json_output}{\"NAME\":\"$esc_name\",\"CVE\":\"$1\",\"VULNERABLE\":$is_vuln,\"INFOS\":\"$esc_infos\"},"
|
||||
}
|
||||
|
||||
# Append vulnerable CVE IDs to the NRPE output buffer
|
||||
# Args: $1=cve $2=aka $3=status $4=description
|
||||
# Sets: g_nrpe_vuln
|
||||
# Callers: pvulnstatus
|
||||
_emit_nrpe() {
|
||||
[ "$3" = VULN ] && g_nrpe_vuln="$g_nrpe_vuln $1"
|
||||
}
|
||||
|
||||
# Append a CVE result as a Prometheus metric to the batch output buffer
|
||||
# Args: $1=cve $2=aka $3=status $4=description
|
||||
# Sets: g_prometheus_output
|
||||
# Callers: pvulnstatus
|
||||
_emit_prometheus() {
|
||||
local esc_info
|
||||
# escape backslashes and double quotes for Prometheus label values
|
||||
esc_info=$(printf '%s' "$4" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
g_prometheus_output="${g_prometheus_output:+$g_prometheus_output\n}specex_vuln_status{name=\"$2\",cve=\"$1\",status=\"$3\",info=\"$esc_info\"} 1"
|
||||
}
|
||||
|
||||
# Update global state used to determine the program exit code
|
||||
# Args: $1=cve $2=status(UNK|VULN|OK)
|
||||
# Sets: g_unknown, g_critical
|
||||
# Callers: pvulnstatus
|
||||
_record_result() {
|
||||
case "$2" in
|
||||
UNK) g_unknown="1" ;;
|
||||
VULN) g_critical="1" ;;
|
||||
OK) ;;
|
||||
*)
|
||||
echo "$0: error: unknown status '$2' passed to _record_result()" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Print the final vulnerability status for a CVE and dispatch to batch emitters
|
||||
# Args: $1=cve $2=status(UNK|OK|VULN) $3=description
|
||||
# Sets: g_pvulnstatus_last_cve
|
||||
pvulnstatus() {
|
||||
local aka vulnstatus
|
||||
g_pvulnstatus_last_cve="$1"
|
||||
if [ "$opt_batch" = 1 ]; then
|
||||
aka=$(_cve_registry_field "$1" 2)
|
||||
|
||||
case "$opt_batch_format" in
|
||||
text) _emit_text "$1" "$aka" "$2" "$3" ;;
|
||||
short) _emit_short "$1" "$aka" "$2" "$3" ;;
|
||||
json) _emit_json "$1" "$aka" "$2" "$3" ;;
|
||||
nrpe) _emit_nrpe "$1" "$aka" "$2" "$3" ;;
|
||||
prometheus) _emit_prometheus "$1" "$aka" "$2" "$3" ;;
|
||||
*)
|
||||
echo "$0: error: invalid batch format '$opt_batch_format' specified" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
_record_result "$1" "$2"
|
||||
|
||||
# display info if we're not in quiet/batch mode
|
||||
vulnstatus="$2"
|
||||
shift 2
|
||||
pr_info_nol "> \033[46m\033[30mSTATUS:\033[0m "
|
||||
: "${g_final_summary:=}"
|
||||
case "$vulnstatus" in
|
||||
UNK)
|
||||
pstatus yellow 'UNKNOWN' "$@"
|
||||
g_final_summary="$g_final_summary \033[43m\033[30m$g_pvulnstatus_last_cve:??\033[0m"
|
||||
;;
|
||||
VULN)
|
||||
pstatus red 'VULNERABLE' "$@"
|
||||
g_final_summary="$g_final_summary \033[41m\033[30m$g_pvulnstatus_last_cve:KO\033[0m"
|
||||
;;
|
||||
OK)
|
||||
pstatus green 'NOT VULNERABLE' "$@"
|
||||
g_final_summary="$g_final_summary \033[42m\033[30m$g_pvulnstatus_last_cve:OK\033[0m"
|
||||
;;
|
||||
*)
|
||||
echo "$0: error: unknown status '$vulnstatus' passed to pvulnstatus()" >&2
|
||||
exit 255
|
||||
;;
|
||||
esac
|
||||
}
|
||||
152
src/libs/300_kernel_extract.sh
Normal file
152
src/libs/300_kernel_extract.sh
Normal file
@@ -0,0 +1,152 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# The 3 below functions are taken from the extract-linux script, available here:
|
||||
# https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux
|
||||
# The functions have been modified for better integration to this script
|
||||
# The original header of the file has been retained below
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
|
||||
#
|
||||
# Inspired from extract-ikconfig
|
||||
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
|
||||
#
|
||||
# (c) 2011 Corentin Chary <corentin.chary@gmail.com>
|
||||
#
|
||||
# Licensed under the GNU General Public License, version 2 (GPLv2).
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
g_kernel=''
|
||||
g_kernel_err=''
|
||||
# Validate whether a file looks like a valid uncompressed Linux kernel image
|
||||
# Args: $1=file_path
|
||||
# Sets: g_kernel, g_kernel_err
|
||||
check_kernel() {
|
||||
local ret file mode readelf_warnings readelf_sections kernel_size
|
||||
file="$1"
|
||||
mode="${2:-normal}"
|
||||
# checking the return code of readelf -h is not enough, we could get
|
||||
# a damaged ELF file and validate it, check for stderr warnings too
|
||||
|
||||
# the warning "readelf: Warning: [16]: Link field (0) should index a symtab section./" can appear on valid kernels, ignore it
|
||||
readelf_warnings=$("${opt_arch_prefix}readelf" -S "$file" 2>&1 >/dev/null | grep -v 'should index a symtab section' | tr "\n" "/")
|
||||
ret=$?
|
||||
readelf_sections=$("${opt_arch_prefix}readelf" -S "$file" 2>/dev/null | grep -c -e data -e text -e init)
|
||||
kernel_size=$(stat -c %s "$file" 2>/dev/null || stat -f %z "$file" 2>/dev/null || echo 10000)
|
||||
pr_debug "check_kernel: ret=$? size=$kernel_size sections=$readelf_sections warnings=$readelf_warnings"
|
||||
if [ "$mode" = desperate ]; then
|
||||
if "${opt_arch_prefix}strings" "$file" | grep -Eq '^Linux version '; then
|
||||
pr_debug "check_kernel (desperate): ... matched!"
|
||||
if [ "$readelf_sections" = 0 ] && grep -qF -e armv6 -e armv7 "$file"; then
|
||||
pr_debug "check_kernel (desperate): raw arm binary found, adjusting objdump options"
|
||||
g_objdump_options="-D -b binary -marm"
|
||||
else
|
||||
g_objdump_options="-d"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
pr_debug "check_kernel (desperate): ... invalid"
|
||||
fi
|
||||
else
|
||||
if [ $ret -eq 0 ] && [ -z "$readelf_warnings" ] && [ "$readelf_sections" -gt 0 ]; then
|
||||
if [ "$kernel_size" -ge 100000 ]; then
|
||||
pr_debug "check_kernel: ... file is valid"
|
||||
g_objdump_options="-d"
|
||||
return 0
|
||||
else
|
||||
pr_debug "check_kernel: ... file seems valid but is too small, ignoring"
|
||||
fi
|
||||
else
|
||||
pr_debug "check_kernel: ... file is invalid"
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Attempt to find and decompress a kernel image using a given compression format
|
||||
# Args: $1=magic_search $2=magic_match $3=format_name $4=decompress_cmd $5=decompress_args $6=input_file $7=output_file
|
||||
try_decompress() {
|
||||
local pos ret
|
||||
# The obscure use of the "tr" filter is to work around older versions of
|
||||
# "grep" that report the byte offset of the line instead of the pattern.
|
||||
|
||||
# Try to find the header ($1) and decompress from here
|
||||
pr_debug "try_decompress: looking for $3 magic in $6"
|
||||
for pos in $(tr "$1\n$2" "\n$2=" <"$6" | grep -abo "^$2"); do
|
||||
pr_debug "try_decompress: magic for $3 found at offset $pos"
|
||||
if ! command -v "$3" >/dev/null 2>&1; then
|
||||
if [ "$8" = 1 ]; then
|
||||
# pass1: if the tool is not installed, just bail out silently
|
||||
# and hope that the next decompression tool will be, and that
|
||||
# it'll happen to be the proper one for this kernel
|
||||
pr_debug "try_decompress: the '$3' tool is not installed (pass 1), try the next algo"
|
||||
else
|
||||
# pass2: if the tool is not installed, populate g_kernel_err this time
|
||||
g_kernel_err="missing '$3' tool, please install it, usually it's in the '$5' package"
|
||||
pr_debug "try_decompress: $g_kernel_err"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
pos=${pos%%:*}
|
||||
# shellcheck disable=SC2086
|
||||
tail -c+$pos "$6" 2>/dev/null | $3 $4 >"$g_kerneltmp" 2>/dev/null
|
||||
ret=$?
|
||||
if [ ! -s "$g_kerneltmp" ]; then
|
||||
# don't rely on $ret, sometimes it's != 0 but worked
|
||||
# (e.g. gunzip ret=2 just means there was trailing garbage)
|
||||
pr_debug "try_decompress: decompression with $3 failed (err=$ret)"
|
||||
elif check_kernel "$g_kerneltmp" "$7"; then
|
||||
g_kernel="$g_kerneltmp"
|
||||
pr_debug "try_decompress: decompressed with $3 successfully!"
|
||||
return 0
|
||||
elif [ "$3" != "cat" ]; then
|
||||
pr_debug "try_decompress: decompression with $3 worked but result is not a kernel, trying with an offset"
|
||||
[ -z "$g_kerneltmp2" ] && g_kerneltmp2=$(mktemp -t smc-kernel-XXXXXX)
|
||||
cat "$g_kerneltmp" >"$g_kerneltmp2"
|
||||
try_decompress '\177ELF' xxy 'cat' '' cat "$g_kerneltmp2" && return 0
|
||||
else
|
||||
pr_debug "try_decompress: decompression with $3 worked but result is not a kernel"
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Extract an uncompressed vmlinux from a possibly compressed kernel image
|
||||
# Args: $1=kernel_image_path
|
||||
# Sets: g_kerneltmp
|
||||
extract_kernel() {
|
||||
local pass mode
|
||||
[ -n "${1:-}" ] || return 1
|
||||
# Prepare temp files:
|
||||
g_kerneltmp="$(mktemp -t smc-kernel-XXXXXX)"
|
||||
|
||||
# Initial attempt for uncompressed images or objects:
|
||||
if check_kernel "$1"; then
|
||||
pr_debug "extract_kernel: found kernel is valid, no decompression needed"
|
||||
cat "$1" >"$g_kerneltmp"
|
||||
g_kernel=$g_kerneltmp
|
||||
return 0
|
||||
fi
|
||||
|
||||
# That didn't work, so retry after decompression.
|
||||
for pass in 1 2; do
|
||||
for mode in normal desperate; do
|
||||
pr_debug "extract_kernel: pass $pass $mode mode"
|
||||
try_decompress '\037\213\010' xy gunzip '' gunzip "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '\002\041\114\030' xyy 'lz4' '-d -l' liblz4-tool "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '\3757zXZ\000' abcde unxz '' xz-utils "$1" "$mode" "$pass" && return 0
|
||||
try_decompress 'BZh' xy bunzip2 '' bzip2 "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '\135\0\0\0' xxx unlzma '' xz-utils "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '\211\114\132' xy 'lzop' '-d' lzop "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '\177ELF' xxy 'cat' '' cat "$1" "$mode" "$pass" && return 0
|
||||
try_decompress '(\265/\375' xxy unzstd '' zstd "$1" "$mode" "$pass" && return 0
|
||||
done
|
||||
done
|
||||
# g_kernel_err might already have been populated by try_decompress() if we're missing one of the tools
|
||||
if [ -z "$g_kernel_err" ]; then
|
||||
g_kernel_err="kernel compression format is unknown or image is invalid"
|
||||
fi
|
||||
pr_verbose "Couldn't extract the kernel image ($g_kernel_err), accuracy might be reduced"
|
||||
return 1
|
||||
}
|
||||
|
||||
# end of extract-vmlinux functions
|
||||
32
src/libs/310_cpu_msr_load.sh
Normal file
32
src/libs/310_cpu_msr_load.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Mount debugfs if not already available, remembering to unmount on cleanup
|
||||
# Sets: g_mounted_debugfs
|
||||
mount_debugfs() {
|
||||
if [ ! -e "$DEBUGFS_BASE/sched_features" ]; then
|
||||
# try to mount the debugfs hierarchy ourselves and remember it to umount afterwards
|
||||
mount -t debugfs debugfs "$DEBUGFS_BASE" 2>/dev/null && g_mounted_debugfs=1
|
||||
fi
|
||||
}
|
||||
|
||||
# Load the MSR kernel module (Linux) or cpuctl (BSD) if not already loaded
|
||||
# Sets: g_insmod_msr, g_kldload_cpuctl
|
||||
load_msr() {
|
||||
[ "${g_load_msr_once:-}" = 1 ] && return
|
||||
g_load_msr_once=1
|
||||
|
||||
if [ "$g_os" = Linux ]; then
|
||||
if ! grep -qw msr "$g_procfs/modules" 2>/dev/null; then
|
||||
modprobe msr 2>/dev/null && g_insmod_msr=1
|
||||
pr_debug "attempted to load module msr, g_insmod_msr=$g_insmod_msr"
|
||||
else
|
||||
pr_debug "msr 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
|
||||
}
|
||||
177
src/libs/320_cpu_cpuid.sh
Normal file
177
src/libs/320_cpu_cpuid.sh
Normal file
@@ -0,0 +1,177 @@
|
||||
# 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
|
||||
}
|
||||
24
src/libs/330_cpu_misc.sh
Normal file
24
src/libs/330_cpu_misc.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Search dmesg for a pattern, returning nothing if the buffer has been truncated
|
||||
# Args: $1=grep_pattern
|
||||
# Sets: ret_dmesg_grep_grepped
|
||||
# Returns: 0=found, 1=not found, 2=dmesg truncated
|
||||
dmesg_grep() {
|
||||
ret_dmesg_grep_grepped=''
|
||||
if ! dmesg 2>/dev/null | grep -qE -e '(^|\] )Linux version [0-9]' -e '^FreeBSD is a registered'; then
|
||||
# dmesg truncated
|
||||
return 2
|
||||
fi
|
||||
ret_dmesg_grep_grepped=$(dmesg 2>/dev/null | grep -E "$1" | head -n1)
|
||||
# not found:
|
||||
[ -z "$ret_dmesg_grep_grepped" ] && return 1
|
||||
# found, output is in $ret_dmesg_grep_grepped
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check whether the system is running CoreOS/Flatcar
|
||||
# Returns: 0 if CoreOS, 1 otherwise
|
||||
is_coreos() {
|
||||
command -v coreos-install >/dev/null 2>&1 && command -v toolbox >/dev/null 2>&1 && return 0
|
||||
return 1
|
||||
}
|
||||
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
|
||||
}
|
||||
238
src/libs/350_cpu_detect2.sh
Normal file
238
src/libs/350_cpu_detect2.sh
Normal file
@@ -0,0 +1,238 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Detect and cache CPU vendor, family, model, stepping, microcode, and arch capabilities
|
||||
# Sets: cpu_vendor, cpu_family, cpu_model, cpu_stepping, cpu_cpuid, cpu_ucode, cpu_friendly_name, g_max_core_id, and many cap_* globals
|
||||
parse_cpu_details() {
|
||||
[ "${g_parse_cpu_details_done:-}" = 1 ] && return 0
|
||||
|
||||
local number_of_cores arch part ret
|
||||
if command -v nproc >/dev/null; then
|
||||
number_of_cores=$(nproc)
|
||||
elif echo "$g_os" | grep -q BSD; then
|
||||
number_of_cores=$(sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
||||
elif [ -e "$g_procfs/cpuinfo" ]; then
|
||||
number_of_cores=$(grep -c ^processor "$g_procfs/cpuinfo" 2>/dev/null || echo 1)
|
||||
else
|
||||
# if we don't know, default to 1 CPU
|
||||
number_of_cores=1
|
||||
fi
|
||||
g_max_core_id=$((number_of_cores - 1))
|
||||
|
||||
cap_avx2=0
|
||||
cap_avx512=0
|
||||
if [ -e "$g_procfs/cpuinfo" ]; then
|
||||
if grep -qw avx2 "$g_procfs/cpuinfo" 2>/dev/null; then cap_avx2=1; fi
|
||||
if grep -qw avx512 "$g_procfs/cpuinfo" 2>/dev/null; then cap_avx512=1; fi
|
||||
cpu_vendor=$(grep '^vendor_id' "$g_procfs/cpuinfo" | awk '{print $3}' | head -n1)
|
||||
cpu_friendly_name=$(grep '^model name' "$g_procfs/cpuinfo" | cut -d: -f2- | head -n1 | sed -e 's/^ *//')
|
||||
# special case for ARM follows
|
||||
if grep -qi 'CPU implementer[[:space:]]*:[[:space:]]*0x41' "$g_procfs/cpuinfo"; then
|
||||
cpu_vendor='ARM'
|
||||
# some devices (phones or other) have several ARMs and as such different part numbers,
|
||||
# an example is "bigLITTLE", so we need to store the whole list, this is needed for is_cpu_affected
|
||||
cpu_part_list=$(awk '/CPU part/ {print $4}' "$g_procfs/cpuinfo")
|
||||
cpu_arch_list=$(awk '/CPU architecture/ {print $3}' "$g_procfs/cpuinfo")
|
||||
# take the first one to fill the friendly name, do NOT quote the vars below
|
||||
# shellcheck disable=SC2086
|
||||
arch=$(echo $cpu_arch_list | awk '{ print $1 }')
|
||||
# shellcheck disable=SC2086
|
||||
part=$(echo $cpu_part_list | awk '{ print $1 }')
|
||||
[ "$arch" = "AArch64" ] && arch=8
|
||||
cpu_friendly_name="ARM"
|
||||
[ -n "$arch" ] && cpu_friendly_name="$cpu_friendly_name v$arch"
|
||||
[ -n "$part" ] && cpu_friendly_name="$cpu_friendly_name model $part"
|
||||
|
||||
elif grep -qi 'CPU implementer[[:space:]]*:[[:space:]]*0x43' "$g_procfs/cpuinfo"; then
|
||||
cpu_vendor='CAVIUM'
|
||||
elif grep -qi 'CPU implementer[[:space:]]*:[[:space:]]*0x70' "$g_procfs/cpuinfo"; then
|
||||
cpu_vendor='PHYTIUM'
|
||||
fi
|
||||
|
||||
cpu_family=$(grep '^cpu family' "$g_procfs/cpuinfo" | awk '{print $4}' | grep -E '^[0-9]+$' | head -n1)
|
||||
cpu_model=$(grep '^model' "$g_procfs/cpuinfo" | awk '{print $3}' | grep -E '^[0-9]+$' | head -n1)
|
||||
cpu_stepping=$(grep '^stepping' "$g_procfs/cpuinfo" | awk '{print $3}' | grep -E '^[0-9]+$' | head -n1)
|
||||
cpu_ucode=$(grep '^microcode' "$g_procfs/cpuinfo" | awk '{print $3}' | head -n1)
|
||||
else
|
||||
cpu_vendor=$(dmesg 2>/dev/null | grep -i -m1 'Origin=' | cut -f2 -w | cut -f2 -d= | cut -f2 -d\")
|
||||
cpu_family=$(dmesg 2>/dev/null | grep -i -m1 'Family=' | cut -f4 -w | cut -f2 -d=)
|
||||
cpu_family=$((cpu_family))
|
||||
cpu_model=$(dmesg 2>/dev/null | grep -i -m1 'Model=' | cut -f5 -w | cut -f2 -d=)
|
||||
cpu_model=$((cpu_model))
|
||||
cpu_stepping=$(dmesg 2>/dev/null | grep -i -m1 'Stepping=' | cut -f6 -w | cut -f2 -d=)
|
||||
cpu_friendly_name=$(sysctl -n hw.model 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Intel processors have a 3bit Platform ID field in MSR(17H) that specifies the platform type for up to 8 types
|
||||
# see https://elixir.bootlin.com/linux/v6.0/source/arch/x86/kernel/cpu/microcode/intel.c#L694
|
||||
# Set it to 8 (impossible value as it is 3 bit long) by default
|
||||
cpu_platformid=8
|
||||
if [ "$cpu_vendor" = GenuineIntel ] && [ "$cpu_model" -ge 5 ]; then
|
||||
read_msr 0x17
|
||||
ret=$?
|
||||
if [ $ret = $READ_MSR_RET_OK ]; then
|
||||
cpu_platformid=$((1 << ((ret_read_msr_value >> 18) & 7)))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${SMC_MOCK_CPU_FRIENDLY_NAME:-}" ]; then
|
||||
cpu_friendly_name="$SMC_MOCK_CPU_FRIENDLY_NAME"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu friendly name to $cpu_friendly_name"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_FRIENDLY_NAME='$cpu_friendly_name'")
|
||||
fi
|
||||
if [ -n "${SMC_MOCK_CPU_VENDOR:-}" ]; then
|
||||
cpu_vendor="$SMC_MOCK_CPU_VENDOR"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu vendor to $cpu_vendor"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_VENDOR='$cpu_vendor'")
|
||||
fi
|
||||
if [ -n "${SMC_MOCK_CPU_FAMILY:-}" ]; then
|
||||
cpu_family="$SMC_MOCK_CPU_FAMILY"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu family to $cpu_family"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_FAMILY='$cpu_family'")
|
||||
fi
|
||||
if [ -n "${SMC_MOCK_CPU_MODEL:-}" ]; then
|
||||
cpu_model="$SMC_MOCK_CPU_MODEL"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu model to $cpu_model"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_MODEL='$cpu_model'")
|
||||
fi
|
||||
if [ -n "${SMC_MOCK_CPU_STEPPING:-}" ]; then
|
||||
cpu_stepping="$SMC_MOCK_CPU_STEPPING"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu stepping to $cpu_stepping"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_STEPPING='$cpu_stepping'")
|
||||
fi
|
||||
if [ -n "${SMC_MOCK_CPU_PLATFORMID:-}" ]; then
|
||||
cpu_platformid="$SMC_MOCK_CPU_PLATFORMID"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu platformid name to $cpu_platformid"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_PLATFORMID='$cpu_platformid'")
|
||||
fi
|
||||
|
||||
# get raw cpuid, it's always useful (referenced in the Intel doc for firmware updates for example)
|
||||
if [ "$g_mocked" != 1 ] && read_cpuid 0x1 0x0 $EAX 0 0xFFFFFFFF; then
|
||||
cpu_cpuid="$ret_read_cpuid_value"
|
||||
else
|
||||
# try to build it by ourselves
|
||||
pr_debug "parse_cpu_details: build the CPUID by ourselves"
|
||||
cpu_cpuid=$(fms2cpuid "$cpu_family" "$cpu_model" "$cpu_stepping")
|
||||
fi
|
||||
|
||||
# under BSD, linprocfs often doesn't export ucode information, so fetch it ourselves the good old way
|
||||
if [ -z "$cpu_ucode" ] && [ "$g_os" != Linux ]; then
|
||||
load_cpuid
|
||||
if [ -e ${BSD_CPUCTL_DEV_BASE}0 ]; then
|
||||
# init MSR with NULLs
|
||||
cpucontrol -m 0x8b=0 ${BSD_CPUCTL_DEV_BASE}0
|
||||
# call CPUID
|
||||
cpucontrol -i 1 ${BSD_CPUCTL_DEV_BASE}0 >/dev/null
|
||||
# read MSR
|
||||
cpu_ucode=$(cpucontrol -m 0x8b ${BSD_CPUCTL_DEV_BASE}0 | awk '{print $3}')
|
||||
# convert to decimal
|
||||
cpu_ucode=$((cpu_ucode))
|
||||
# convert back to hex
|
||||
cpu_ucode=$(printf "0x%x" "$cpu_ucode")
|
||||
fi
|
||||
fi
|
||||
|
||||
# if we got no cpu_ucode (e.g. we're in a vm), fall back to 0x0
|
||||
: "${cpu_ucode:=0x0}"
|
||||
|
||||
# on non-x86 systems (e.g. ARM), these fields may not exist in cpuinfo, fall back to 0
|
||||
: "${cpu_family:=0}"
|
||||
: "${cpu_model:=0}"
|
||||
: "${cpu_stepping:=0}"
|
||||
|
||||
if [ -n "${SMC_MOCK_CPU_UCODE:-}" ]; then
|
||||
cpu_ucode="$SMC_MOCK_CPU_UCODE"
|
||||
pr_debug "parse_cpu_details: MOCKING cpu ucode to $cpu_ucode"
|
||||
g_mocked=1
|
||||
else
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CPU_UCODE='$cpu_ucode'")
|
||||
fi
|
||||
|
||||
echo "$cpu_ucode" | grep -q ^0x && cpu_ucode=$((cpu_ucode))
|
||||
g_ucode_found=$(printf "family 0x%x model 0x%x stepping 0x%x ucode 0x%x cpuid 0x%x pfid 0x%x" \
|
||||
"$cpu_family" "$cpu_model" "$cpu_stepping" "$cpu_ucode" "$cpu_cpuid" "$cpu_platformid")
|
||||
|
||||
# also define those that we will need in other funcs
|
||||
# taken from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/intel-family.h
|
||||
# curl -s 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/arch/x86/include/asm/intel-family.h' | awk '/#define INTEL_FAM6/ {print $2"=$(( "$3" )) # "$4,$5,$6,$7,$8,$9}' | sed -re 's/ +$//'
|
||||
# shellcheck disable=SC2034
|
||||
{
|
||||
readonly INTEL_FAM6_CORE_YONAH=$((0x0E)) #
|
||||
readonly INTEL_FAM6_CORE2_MEROM=$((0x0F)) #
|
||||
readonly INTEL_FAM6_CORE2_MEROM_L=$((0x16)) #
|
||||
readonly INTEL_FAM6_CORE2_PENRYN=$((0x17)) #
|
||||
readonly INTEL_FAM6_CORE2_DUNNINGTON=$((0x1D)) #
|
||||
readonly INTEL_FAM6_NEHALEM=$((0x1E)) #
|
||||
readonly INTEL_FAM6_NEHALEM_G=$((0x1F)) # /* Auburndale / Havendale */
|
||||
readonly INTEL_FAM6_NEHALEM_EP=$((0x1A)) #
|
||||
readonly INTEL_FAM6_NEHALEM_EX=$((0x2E)) #
|
||||
readonly INTEL_FAM6_WESTMERE=$((0x25)) #
|
||||
readonly INTEL_FAM6_WESTMERE_EP=$((0x2C)) #
|
||||
readonly INTEL_FAM6_WESTMERE_EX=$((0x2F)) #
|
||||
readonly INTEL_FAM6_SANDYBRIDGE=$((0x2A)) #
|
||||
readonly INTEL_FAM6_SANDYBRIDGE_X=$((0x2D)) #
|
||||
readonly INTEL_FAM6_IVYBRIDGE=$((0x3A)) #
|
||||
readonly INTEL_FAM6_IVYBRIDGE_X=$((0x3E)) #
|
||||
readonly INTEL_FAM6_HASWELL=$((0x3C)) #
|
||||
readonly INTEL_FAM6_HASWELL_X=$((0x3F)) #
|
||||
readonly INTEL_FAM6_HASWELL_L=$((0x45)) #
|
||||
readonly INTEL_FAM6_HASWELL_G=$((0x46)) #
|
||||
readonly INTEL_FAM6_BROADWELL=$((0x3D)) #
|
||||
readonly INTEL_FAM6_BROADWELL_G=$((0x47)) #
|
||||
readonly INTEL_FAM6_BROADWELL_X=$((0x4F)) #
|
||||
readonly INTEL_FAM6_BROADWELL_D=$((0x56)) #
|
||||
readonly INTEL_FAM6_SKYLAKE_L=$((0x4E)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_SKYLAKE=$((0x5E)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_SKYLAKE_X=$((0x55)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_KABYLAKE_L=$((0x8E)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_KABYLAKE=$((0x9E)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_COMETLAKE=$((0xA5)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_COMETLAKE_L=$((0xA6)) # /* Sky Lake */
|
||||
readonly INTEL_FAM6_CANNONLAKE_L=$((0x66)) # /* Palm Cove */
|
||||
readonly INTEL_FAM6_ICELAKE_X=$((0x6A)) # /* Sunny Cove */
|
||||
readonly INTEL_FAM6_ICELAKE_D=$((0x6C)) # /* Sunny Cove */
|
||||
readonly INTEL_FAM6_ICELAKE=$((0x7D)) # /* Sunny Cove */
|
||||
readonly INTEL_FAM6_ICELAKE_L=$((0x7E)) # /* Sunny Cove */
|
||||
readonly INTEL_FAM6_ICELAKE_NNPI=$((0x9D)) # /* Sunny Cove */
|
||||
readonly INTEL_FAM6_LAKEFIELD=$((0x8A)) # /* Sunny Cove / Tremont */
|
||||
readonly INTEL_FAM6_ROCKETLAKE=$((0xA7)) # /* Cypress Cove */
|
||||
readonly INTEL_FAM6_TIGERLAKE_L=$((0x8C)) # /* Willow Cove */
|
||||
readonly INTEL_FAM6_TIGERLAKE=$((0x8D)) # /* Willow Cove */
|
||||
readonly INTEL_FAM6_SAPPHIRERAPIDS_X=$((0x8F)) # /* Golden Cove */
|
||||
readonly INTEL_FAM6_ALDERLAKE=$((0x97)) # /* Golden Cove / Gracemont */
|
||||
readonly INTEL_FAM6_ALDERLAKE_L=$((0x9A)) # /* Golden Cove / Gracemont */
|
||||
readonly INTEL_FAM6_RAPTORLAKE=$((0xB7)) #
|
||||
readonly INTEL_FAM6_ATOM_BONNELL=$((0x1C)) # /* Diamondville, Pineview */
|
||||
readonly INTEL_FAM6_ATOM_BONNELL_MID=$((0x26)) # /* Silverthorne, Lincroft */
|
||||
readonly INTEL_FAM6_ATOM_SALTWELL=$((0x36)) # /* Cedarview */
|
||||
readonly INTEL_FAM6_ATOM_SALTWELL_MID=$((0x27)) # /* Penwell */
|
||||
readonly INTEL_FAM6_ATOM_SALTWELL_TABLET=$((0x35)) # /* Cloverview */
|
||||
readonly INTEL_FAM6_ATOM_SILVERMONT=$((0x37)) # /* Bay Trail, Valleyview */
|
||||
readonly INTEL_FAM6_ATOM_SILVERMONT_D=$((0x4D)) # /* Avaton, Rangely */
|
||||
readonly INTEL_FAM6_ATOM_SILVERMONT_MID=$((0x4A)) # /* Merriefield */
|
||||
readonly INTEL_FAM6_ATOM_AIRMONT=$((0x4C)) # /* Cherry Trail, Braswell */
|
||||
readonly INTEL_FAM6_ATOM_AIRMONT_MID=$((0x5A)) # /* Moorefield */
|
||||
readonly INTEL_FAM6_ATOM_AIRMONT_NP=$((0x75)) # /* Lightning Mountain */
|
||||
readonly INTEL_FAM6_ATOM_GOLDMONT=$((0x5C)) # /* Apollo Lake */
|
||||
readonly INTEL_FAM6_ATOM_GOLDMONT_D=$((0x5F)) # /* Denverton */
|
||||
readonly INTEL_FAM6_ATOM_GOLDMONT_PLUS=$((0x7A)) # /* Gemini Lake */
|
||||
readonly INTEL_FAM6_ATOM_TREMONT_D=$((0x86)) # /* Jacobsville */
|
||||
readonly INTEL_FAM6_ATOM_TREMONT=$((0x96)) # /* Elkhart Lake */
|
||||
readonly INTEL_FAM6_ATOM_TREMONT_L=$((0x9C)) # /* Jasper Lake */
|
||||
readonly INTEL_FAM6_XEON_PHI_KNL=$((0x57)) # /* Knights Landing */
|
||||
readonly INTEL_FAM6_XEON_PHI_KNM=$((0x85)) # /* Knights Mill */
|
||||
}
|
||||
g_parse_cpu_details_done=1
|
||||
}
|
||||
# Check whether the CPU vendor is Hygon
|
||||
# Returns: 0 if Hygon, 1 otherwise
|
||||
224
src/libs/360_cpu_smt.sh
Normal file
224
src/libs/360_cpu_smt.sh
Normal file
@@ -0,0 +1,224 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
is_hygon() {
|
||||
parse_cpu_details
|
||||
[ "$cpu_vendor" = HygonGenuine ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU vendor is AMD
|
||||
# Returns: 0 if AMD, 1 otherwise
|
||||
is_amd() {
|
||||
parse_cpu_details
|
||||
[ "$cpu_vendor" = AuthenticAMD ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU vendor is Intel
|
||||
# Returns: 0 if Intel, 1 otherwise
|
||||
is_intel() {
|
||||
parse_cpu_details
|
||||
[ "$cpu_vendor" = GenuineIntel ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether SMT (HyperThreading) is enabled on the system
|
||||
# Returns: 0 if SMT enabled, 1 otherwise
|
||||
is_cpu_smt_enabled() {
|
||||
local siblings cpucores
|
||||
# SMT / HyperThreading is enabled if siblings != cpucores
|
||||
if [ -e "$g_procfs/cpuinfo" ]; then
|
||||
siblings=$(awk '/^siblings/ {print $3;exit}' "$g_procfs/cpuinfo")
|
||||
cpucores=$(awk '/^cpu cores/ {print $4;exit}' "$g_procfs/cpuinfo")
|
||||
if [ -n "$siblings" ] && [ -n "$cpucores" ]; then
|
||||
if [ "$siblings" = "$cpucores" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# we can't tell
|
||||
return 2
|
||||
}
|
||||
|
||||
# Check whether the current CPU microcode version is on Intel's blacklist
|
||||
# Returns: 0 if blacklisted, 1 otherwise
|
||||
is_ucode_blacklisted() {
|
||||
local tuple model stepping ucode cpuid
|
||||
parse_cpu_details
|
||||
# if it's not an Intel, don't bother: it's not blacklisted
|
||||
is_intel || return 1
|
||||
# it also needs to be family=6
|
||||
[ "$cpu_family" = 6 ] || return 1
|
||||
# now, check each known bad microcode
|
||||
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/intel.c#n105
|
||||
# 2018-02-08 update: https://newsroom.intel.com/wp-content/uploads/sites/11/2018/02/microcode-update-guidance.pdf
|
||||
# model,stepping,microcode
|
||||
for tuple in \
|
||||
$INTEL_FAM6_KABYLAKE,0x0B,0x80 \
|
||||
$INTEL_FAM6_KABYLAKE,0x0A,0x80 \
|
||||
$INTEL_FAM6_KABYLAKE,0x09,0x80 \
|
||||
$INTEL_FAM6_KABYLAKE_L,0x0A,0x80 \
|
||||
$INTEL_FAM6_KABYLAKE_L,0x09,0x80 \
|
||||
$INTEL_FAM6_SKYLAKE_X,0x03,0x0100013e \
|
||||
$INTEL_FAM6_SKYLAKE_X,0x04,0x02000036 \
|
||||
$INTEL_FAM6_SKYLAKE_X,0x04,0x0200003a \
|
||||
$INTEL_FAM6_SKYLAKE_X,0x04,0x0200003c \
|
||||
$INTEL_FAM6_BROADWELL,0x04,0x28 \
|
||||
$INTEL_FAM6_BROADWELL_G,0x01,0x1b \
|
||||
$INTEL_FAM6_BROADWELL_D,0x02,0x14 \
|
||||
$INTEL_FAM6_BROADWELL_D,0x03,0x07000011 \
|
||||
$INTEL_FAM6_BROADWELL_X,0x01,0x0b000025 \
|
||||
$INTEL_FAM6_HASWELL_L,0x01,0x21 \
|
||||
$INTEL_FAM6_HASWELL_G,0x01,0x18 \
|
||||
$INTEL_FAM6_HASWELL,0x03,0x23 \
|
||||
$INTEL_FAM6_HASWELL_X,0x02,0x3b \
|
||||
$INTEL_FAM6_HASWELL_X,0x04,0x10 \
|
||||
$INTEL_FAM6_IVYBRIDGE_X,0x04,0x42a \
|
||||
$INTEL_FAM6_SANDYBRIDGE_X,0x06,0x61b \
|
||||
$INTEL_FAM6_SANDYBRIDGE_X,0x07,0x712; do
|
||||
model=$(echo "$tuple" | cut -d, -f1)
|
||||
stepping=$(($(echo "$tuple" | cut -d, -f2)))
|
||||
if [ "$cpu_model" = "$model" ] && [ "$cpu_stepping" = "$stepping" ]; then
|
||||
ucode=$(($(echo "$tuple" | cut -d, -f3)))
|
||||
if [ "$cpu_ucode" = "$ucode" ]; then
|
||||
pr_debug "is_ucode_blacklisted: we have a match! ($cpu_model/$cpu_stepping/$cpu_ucode)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 2024-01-09 update: https://github.com/speed47/spectre-meltdown-checker/issues/475
|
||||
# this time the tuple is cpuid,microcode
|
||||
for tuple in \
|
||||
0xB0671,0x119 \
|
||||
0xB06A2,0x4119 \
|
||||
0xB06A3,0x4119; do
|
||||
cpuid=$(($(echo "$tuple" | cut -d, -f1)))
|
||||
ucode=$(($(echo "$tuple" | cut -d, -f2)))
|
||||
if [ "$cpu_cpuid" = "$cpuid" ] && [ "$cpu_ucode" = "$ucode" ]; then
|
||||
pr_debug "is_ucode_blacklisted: we have a match! ($cpuid/$ucode)"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
pr_debug "is_ucode_blacklisted: no ($cpu_model/$cpu_stepping/$cpu_ucode)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is a Skylake/Kabylake family processor
|
||||
# Returns: 0 if Skylake-family, 1 otherwise
|
||||
is_skylake_cpu() {
|
||||
# return 0 if yes, 1 otherwise
|
||||
#if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
|
||||
# boot_cpu_data.x86 == 6) {
|
||||
# switch (boot_cpu_data.x86_model) {
|
||||
# case INTEL_FAM6_SKYLAKE_MOBILE:
|
||||
# case INTEL_FAM6_SKYLAKE_DESKTOP:
|
||||
# case INTEL_FAM6_SKYLAKE_X:
|
||||
# case INTEL_FAM6_KABYLAKE_MOBILE:
|
||||
# case INTEL_FAM6_KABYLAKE_DESKTOP:
|
||||
# return true;
|
||||
parse_cpu_details
|
||||
is_intel || return 1
|
||||
[ "$cpu_family" = 6 ] || return 1
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is vulnerable to empty RSB speculation
|
||||
# Returns: 0 if vulnerable, 1 otherwise
|
||||
is_vulnerable_to_empty_rsb() {
|
||||
if is_intel && [ -z "$cap_rsba" ]; then
|
||||
pr_warn "is_vulnerable_to_empty_rsb() called before ARCH CAPABILITIES MSR was read"
|
||||
fi
|
||||
if is_skylake_cpu || [ "$cap_rsba" = 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is from the AMD Zen family (Ryzen, EPYC, ...)
|
||||
# Returns: 0 if Zen, 1 otherwise
|
||||
is_zen_cpu() {
|
||||
parse_cpu_details
|
||||
is_amd || return 1
|
||||
[ "$cpu_family" = 23 ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU is a Hygon Moksha (Dhyana) family processor
|
||||
# Returns: 0 if Moksha, 1 otherwise
|
||||
is_moksha_cpu() {
|
||||
parse_cpu_details
|
||||
is_hygon || return 1
|
||||
[ "$cpu_family" = 24 ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Encode an AMD family/model/stepping range into a single integer (mimics Linux AMD_MODEL_RANGE macro)
|
||||
# Args: $1=family $2=model_start $3=stepping_start $4=model_end $5=stepping_end
|
||||
amd_model_range() {
|
||||
echo $((($1 << 24) | ($2 << 16) | ($3 << 12) | ($4 << 4) | ($5)))
|
||||
}
|
||||
|
||||
# Check if the current AMD CPU falls within a given model/stepping range (mimics Linux amd_legacy_erratum)
|
||||
# Args: $1=range (output of amd_model_range)
|
||||
# Returns: 0 if CPU is in range, 1 otherwise
|
||||
amd_legacy_erratum() {
|
||||
local range ms
|
||||
range="$1"
|
||||
ms=$((cpu_model << 4 | cpu_stepping))
|
||||
if [ "$cpu_family" = $((((range) >> 24) & 0xff)) ] &&
|
||||
[ $ms -ge $((((range) >> 12) & 0xfff)) ] &&
|
||||
[ $ms -le $(((range) & 0xfff)) ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check whether the CPU has a microcode version that fixes Zenbleed
|
||||
# Sets: g_zenbleed_fw, g_zenbleed_fw_required
|
||||
# Returns: 0=fixed, 1=not fixed, 2=not applicable
|
||||
has_zenbleed_fixed_firmware() {
|
||||
local tuples tuple model_low model_high fwver
|
||||
# return cached data
|
||||
[ -n "$g_zenbleed_fw" ] && return "$g_zenbleed_fw"
|
||||
# or compute it:
|
||||
g_zenbleed_fw=2 # unknown
|
||||
# only amd
|
||||
if ! is_amd; then
|
||||
g_zenbleed_fw=1
|
||||
return $g_zenbleed_fw
|
||||
fi
|
||||
# list of known fixed firmwares, from commit 522b1d69219d8f083173819fde04f994aa051a98
|
||||
tuples="
|
||||
0x30,0x3f,0x0830107a
|
||||
0x60,0x67,0x0860010b
|
||||
0x68,0x6f,0x08608105
|
||||
0x70,0x7f,0x08701032
|
||||
0xa0,0xaf,0x08a00008
|
||||
"
|
||||
for tuple in $tuples; do
|
||||
model_low=$(echo "$tuple" | cut -d, -f1)
|
||||
model_high=$(echo "$tuple" | cut -d, -f2)
|
||||
fwver=$(echo "$tuple" | cut -d, -f3)
|
||||
if [ $((cpu_model)) -ge $((model_low)) ] && [ $((cpu_model)) -le $((model_high)) ]; then
|
||||
if [ $((cpu_ucode)) -ge $((fwver)) ]; then
|
||||
g_zenbleed_fw=0 # true
|
||||
break
|
||||
else
|
||||
g_zenbleed_fw=1 # false
|
||||
g_zenbleed_fw_required=$fwver
|
||||
fi
|
||||
fi
|
||||
done
|
||||
unset tuples
|
||||
return $g_zenbleed_fw
|
||||
}
|
||||
57
src/libs/370_hw_vmm.sh
Normal file
57
src/libs/370_hw_vmm.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
# Check whether the system is running as a Xen paravirtualized guest
|
||||
# Returns: 0 if Xen PV, 1 otherwise
|
||||
is_xen() {
|
||||
local ret
|
||||
if [ ! -d "$g_procfs/xen" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# XXX do we have a better way that relying on dmesg?
|
||||
dmesg_grep 'Booting paravirtualized kernel on Xen$'
|
||||
ret=$?
|
||||
if [ "$ret" -eq 2 ]; then
|
||||
pr_warn "dmesg truncated, Xen detection will be unreliable. Please reboot and relaunch this script"
|
||||
return 1
|
||||
elif [ "$ret" -eq 0 ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check whether the system is a Xen Dom0 (privileged domain)
|
||||
# Returns: 0 if Dom0, 1 otherwise
|
||||
is_xen_dom0() {
|
||||
if ! is_xen; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -e "$g_procfs/xen/capabilities" ] && grep -q "control_d" "$g_procfs/xen/capabilities"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check whether the system is a Xen DomU (unprivileged PV guest)
|
||||
# Returns: 0 if DomU, 1 otherwise
|
||||
is_xen_domU() {
|
||||
local ret
|
||||
if ! is_xen; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# PVHVM guests also print 'Booting paravirtualized kernel', so we need this check.
|
||||
dmesg_grep 'Xen HVM callback vector for event delivery is enabled$'
|
||||
ret=$?
|
||||
if [ "$ret" -eq 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! is_xen_dom0; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
70
src/libs/380_hw_microcode.sh
Normal file
70
src/libs/380_hw_microcode.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
g_builtin_dbversion=$(awk '/^# %%% MCEDB / { print $4 }' "$0")
|
||||
if [ -r "$g_mcedb_cache" ]; then
|
||||
# we have a local cache file, but it might be older than the builtin version we have
|
||||
g_local_dbversion=$(awk '/^# %%% MCEDB / { print $4 }' "$g_mcedb_cache")
|
||||
# sort -V sorts by version number
|
||||
g_older_dbversion=$(printf "%b\n%b" "$g_local_dbversion" "$g_builtin_dbversion" | sort -V | head -n1)
|
||||
if [ "$g_older_dbversion" = "$g_builtin_dbversion" ]; then
|
||||
g_mcedb_source="$g_mcedb_cache"
|
||||
g_mcedb_info="local firmwares DB $g_local_dbversion"
|
||||
fi
|
||||
fi
|
||||
# if g_mcedb_source is not set, either we don't have a local cached db, or it is older than the builtin db
|
||||
if [ -z "${g_mcedb_source:-}" ]; then
|
||||
g_mcedb_source="$0"
|
||||
g_mcedb_info="builtin firmwares DB $g_builtin_dbversion"
|
||||
fi
|
||||
# Read the MCExtractor microcode database (from local cache or builtin) to stdout
|
||||
read_mcedb() {
|
||||
awk '{ if (DELIM==1) { print $2 } } /^# %%% MCEDB / { DELIM=1 }' "$g_mcedb_source"
|
||||
}
|
||||
|
||||
# Read the Intel official affected CPUs database (builtin) to stdout
|
||||
read_inteldb() {
|
||||
if [ "$opt_intel_db" = 1 ]; then
|
||||
awk '/^# %%% ENDOFINTELDB/ { exit } { if (DELIM==1) { print $2 } } /^# %%% INTELDB/ { DELIM=1 }' "$0"
|
||||
fi
|
||||
# otherwise don't output nothing, it'll be as if the database is empty
|
||||
}
|
||||
|
||||
# Check whether the CPU is running the latest known microcode version
|
||||
# Sets: ret_is_latest_known_ucode_latest
|
||||
# Returns: 0=latest, 1=outdated, 2=unknown
|
||||
is_latest_known_ucode() {
|
||||
local brand_prefix tuple pfmask ucode ucode_date
|
||||
parse_cpu_details
|
||||
if [ "$cpu_cpuid" = 0 ]; then
|
||||
ret_is_latest_known_ucode_latest="couldn't get your cpuid"
|
||||
return 2
|
||||
fi
|
||||
ret_is_latest_known_ucode_latest="latest microcode version for your CPU model is unknown"
|
||||
if is_intel; then
|
||||
brand_prefix=I
|
||||
elif is_amd; then
|
||||
brand_prefix=A
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
for tuple in $(read_mcedb | grep "$(printf "^$brand_prefix,0x%08X," "$cpu_cpuid")"); do
|
||||
# skip if the pfmask doesn't match our platformid
|
||||
pfmask=$(echo "$tuple" | cut -d, -f3)
|
||||
if is_intel && [ $((cpu_platformid & pfmask)) -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
ucode=$(($(echo "$tuple" | cut -d, -f4)))
|
||||
ucode_date=$(echo "$tuple" | cut -d, -f5 | sed -r 's=(....)(..)(..)=\1/\2/\3=')
|
||||
pr_debug "is_latest_known_ucode: with cpuid $cpu_cpuid has ucode $cpu_ucode, last known is $ucode from $ucode_date"
|
||||
ret_is_latest_known_ucode_latest=$(printf "latest version is 0x%x dated $ucode_date according to $g_mcedb_info" "$ucode")
|
||||
if [ "$cpu_ucode" -ge "$ucode" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
pr_debug "is_latest_known_ucode: this cpuid is not referenced ($cpu_cpuid)"
|
||||
return 2
|
||||
}
|
||||
|
||||
# Read and cache the kernel command line from /proc/cmdline or mock
|
||||
# Sets: g_kernel_cmdline
|
||||
16
src/libs/390_kernel_cmdline.sh
Normal file
16
src/libs/390_kernel_cmdline.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
# vim: set ts=4 sw=4 sts=4 et:
|
||||
get_cmdline() {
|
||||
if [ -n "${g_kernel_cmdline:-}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -n "${SMC_MOCK_CMDLINE:-}" ]; then
|
||||
g_mocked=1
|
||||
pr_debug "get_cmdline: using g_mocked cmdline '$SMC_MOCK_CMDLINE'"
|
||||
g_kernel_cmdline="$SMC_MOCK_CMDLINE"
|
||||
return
|
||||
else
|
||||
g_kernel_cmdline=$(cat "$g_procfs/cmdline")
|
||||
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_CMDLINE='$g_kernel_cmdline'")
|
||||
fi
|
||||
}
|
||||
1178
src/libs/400_hw_check.sh
Normal file
1178
src/libs/400_hw_check.sh
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user