#!/bin/bash Version=0.9.65 InstallLocation=/usr/local/src # change to /tmp if you want tools to be deleted after reboot Main() { export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/vc/bin PathToMe="$( cd "$(dirname "$0")" || exit 1 ; pwd -P )/${0##*/}" unset LC_ALL LC_MESSAGES LC_NUMERIC LANGUAGE LANG 2>/dev/null # prevent localisation of decimal points and similar stuff NO_COLOR="${NO_COLOR:-}" Netio="${Netio:-}" Smartpower="${Smartpower:-}" Linpack="${Linpack:-}" # use colours and bold when outputting to a terminal CheckTerminal # The following allows to use sbc-bench on real ARM devices where for # whatever reasons a fake /usr/bin/vcgencmd is lying around -- see for # an example here: https://github.com/ThomasKaiser/sbc-bench/pull/13 VCGENCMD=$(command -v vcgencmd) if [ -z "${USE_VCGENCMD}" ] && [ -x "${VCGENCMD}" ]; then # this seems to be a Raspberry Pi where we need to query # ThreadX on the VC via vcgencmd to get real information # Double check via device-tree compatible string: [ -f /proc/device-tree/compatible ] && \ grep -a -q raspberrypi /proc/device-tree/compatible if [ $? -eq 0 ]; then USE_VCGENCMD=true else USE_VCGENCMD=false fi else USE_VCGENCMD=false fi ProcCPUFile="${CPUINFOFILE:-/proc/cpuinfo}" [ -r "${ProcCPUFile}" ] && ProcCPU="$(cat "${ProcCPUFile}")" [ -r /etc/os-release ] && source /etc/os-release # check in which mode we're supposed to run while getopts 'uSchjkmtTrRsgNPG' c ; do case ${c} in m) # monitoring mode SleepInterval=${2:-5} if [ -n "${Netio}" ] && [ -z "${NetioConsumptionFile}" ]; then QueryNetioDevice if [ "X${OutputCurrents[*]}" != "X" ]; then # We are in Netio monitoring mode as such provide consumption info as well echo -e "Power monitoring on socket ${NetioSocket} of ${NetioName} (Netio ${NetioModel}, FW v${Firmware}, XML API v${XmlVer}, ${InputVoltage}V @ ${Frequency}Hz)\n" CreateTempDir NetioConsumptionFile="${TempDir}/netio.current" echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${OutputCurrent[$(( ${NetioSocket} - 1 ))]}" ) * 10 )) >"${NetioConsumptionFile}" export NetioConsumptionFile NetioInterval=$(LC_ALL=C awk '{printf ("%0.1f",$1/2); }' <<<"${SleepInterval}") NetioSamples=$(( SleepInterval * 3 )) /bin/bash "${PathToMe}" -N ${NetioDevice} ${NetioSocket} ${NetioConsumptionFile} "${NetioInterval}" "${NetioSamples}" >/dev/null 2>&1 & NetioMonitoringPID=$! fi fi MonitorMode=TRUE MonitorBoard [ -n "${Netio}" ] && [ -z "${NetioConsumptionFile}" ] && kill ${NetioMonitoringPID} >/dev/null 2>&1 exit 0 ;; c) # Run Cpuminer test (NEON/SSE/AVX) ExecuteCpuminer=yes ;; s) # Run Stockfish test (NEON/SSE/AVX/RAM) NeuralNetwork=$2 ExecuteStockfish=yes ;; S) # check storage, only for testing, will disappear later DMESG="$(dmesg | grep "mmc")" SMARTDrives="$(ls /dev/sd* /dev/nvme* 2>/dev/null)" if [ "X${SMARTDrives}" != "X" ]; then # warn about absence of smartctl and running not as root only when needed command -v smartctl >/dev/null 2>&1 || echo -e "${BOLD}Warning: smartmontools not installed${NC}\n" >&2 [ ${UID} = 0 ] || echo -e "${BOLD}Warning: to query all drive info this tool needs to be run as root${NC}\n" >&2 fi CheckStorage | sed 's/000Mbps/Gbps/' exit 0 ;; h) # print help DisplayUsage exit 0 ;; u) # replace script with latest version from Github UpdateMe exit 0 ;; j|r) # Jeff Geerling or Jean-Luc Aufranc mode. Help in reviewing devices REVIEWMODE=true SleepInterval=${2:-5} RunBenchmarks=TRUE [ ${UID} = 0 ] || { echo -e "${BOLD}Warning: for useable results this tool needs to be run as root${NC}\n" >&2 ; exit 1 ; } ProvideReviewInfo exit 0 ;; R) # Review mode w/o basic benchmarking and thermal throttling tests REVIEWMODE=true SleepInterval=${2:-5} [ ${UID} = 0 ] || { echo -e "${BOLD}Warning: for useable results this tool needs to be run as root${NC}\n" >&2 ; exit 1 ; } ProvideReviewInfo exit 0 ;; k) # Kernel info. Check kernel version against https://endoflife.date/linux # and issue warnings if used kernel is not an official one but vendor/BSP PrintKernelInfo exit 0 ;; t|T) # temperature tests sufficient for heatsink/fan and throttling settings testing # 2nd argument (integer in degree C) is the value we wait for the board to cool # down prior to next test TargetTemp=${2:-50} [ ${UID} = 0 ] || { echo -e "${BOLD}Warning: for useable results this tool needs to be run as root${NC}\n" >&2 ; exit 1 ; } TempTest exit 0 ;; g) # graph performance chart instead of doing standard 7-zip benchmarks, thereby # walking through different cpufreq OPP. An additional parameter in taskset's # --cpu-list format can be provided, eg. '-g 0' to graph only cpu0 or '-g 4' # to graph only cpu4 (which might be an A72 on big.LITTLE systems). Mixing # CPUs from different clusters (e.g. '-g 0,4' on a RK3399) will result in # garbage since big and little cores have other cpufreq OPP. # 3 special modes exist: cores, clusters and all. # * '-g cores' will test single-threaded through cluster's cores (0 and 4 on # RK3399, 0 and 2 on S922X and so on) # * '-g clusters' tests all cores of each cluster (0-3 and 4-5 on RK3399, # 0-1 and 2-5 on S922X and so on) # * '-g all' performs both tests from above and then runs the test on all # cores as well. This will take ages. PlotCpufreqOPPs=yes CPUList=${2} [ "X${CPUList}" = "X" ] || [ "X${CPUList}" = "Xall" ] || [ "X${CPUList}" = "Xcores" ] || [ "X${CPUList}" = "Xclusters" ] || [ "X${CPUList}" = "Xcoreclusters" ] \ || taskset -c ${CPUList} echo "foo" >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\nInvalid option \"-g ${CPUList}\". Please check taskset manual page for --cpu-list format" >&2 DisplayUsage exit 1 fi ;; G) # Geekbench piggyback mode. Geekbench lacks sanity checks (measuring # clockspeeds) and environment monitoring (too much background activity # or throttling for example). MODE="gb" ;; P) # Phoronix Test Suite piggyback mode. PTS lacks sanity checks (measuring # clockspeeds) and environment monitoring (too much background activity # or throttling for example). You need to install Phoronix Test Suite by # yourself (see https://www.phoronix-test-suite.com/?k=downloads) and run # 'phoronix-test-suite batch-setup' once to configure batch operation mode: # # Save test results when in batch mode (Y/n): y # Open the web browser automatically when in batch mode (y/N): n # Auto upload the results to OpenBenchmarking.org (Y/n): Y # Prompt for test identifier (Y/n): n # Prompt for test description (Y/n): n # Prompt for saved results file-name (Y/n): n # Run all test options (Y/n): n # # If you want PTS progress info use 'tail -f /tmp/sbc-bench.*/temp.log' # in another shell. You'll need this anyway since if 'Run all test options' # has been answered with no, then PTS of course expects you giving manual # feedback even if in batch-benchmark mode. # # root@rock-5b:/tmp/sbc-bench.Pvhatc# tail -f temp.log # SQLite 3.30.1: # pts/sqlite-2.1.0 # Disk Test Configuration # 1: 1 # 2: 8 # 3: 32 # 4: 64 # 5: 128 # 6: Test All Options # ** Multiple items can be selected, delimit by a comma. ** # # In the invoking shell you need to enter the desired config then PTS # proceeds. MODE="pts" shift PTSArguments="$*" ;; N) # internal Netio monitor mode. Do not use this unless you really know # what you're doing. Requires enabled 'XML API' on your device (read-only # w/o password: https://www.netio-products.com/en/glossary/xml-over-https) # Needs address/name and socket number and a file where results will be # written to. For example '-N powerbox-1.local 2 /tmp/my-consumption' if # your Netio device is accessible as powerbox-1.local, this device is # plugged into its 2nd socket and you want consumption to be written to # /tmp/my-consumption where it can be picked up by tools like RPi Monitor. # The last 2 arguments are optional: sleep time (defaults to 0.8) and # count of samples (defaults to 30). The defaults already put significant # load on the device (compare http://ix.io/3E91 and http://ix.io/3Ebd) so # to monitor idle consumption better choose 4.8 and 30 and deal with a 150 # seconds average value. MonitorNetio "$2" "$3" "$4" "$5" "$6" exit 0 ;; *) # invalid flags DisplayUsage exit 1 ;; esac done [ ${UID} = 0 ] || echo -e "${BOLD}Warning: when not being executed as root strange things will happen${NC}\n" >&2 # ensure other sbc-bench instances are terminated for PID in $( (pgrep -f "${PathToMe}" ; jobs -p) | sort | uniq -u) ; do if [ ${PID} -ne $$ ]; then kill ${PID} 2>/dev/null fi done # do a normal release check only if not in PTS or GB mode, in review mode warn if # distro is not rather recent if [ "X${MODE}" = "Xpts" ] || [ "X${MODE}" = "Xgb" ]; then : elif [ "X${REVIEWMODE}" = "Xtrue" ]; then CheckOSRelease ForReview else CheckOSRelease fi unset SPACING CreateTempDir [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && \ read OriginalCPUFreqGovernor /dev/null [ "${OriginalCPUFreqGovernor}" = "ondemand" ] && \ io_is_busy=( $(find /sys/devices/system/cpu/cpufreq -name io_is_busy | while read ; do cat "${REPLY}"; done) ) CheckLoadAndDmesg BasicSetup performance >/dev/null 2>&1 GovernorState="$(HandleGovernors | grep -v cpufreq-policy)" [ "X${GovernorState}" != "X" ] && echo -e "Status of performance related governors found below /sys (w/o cpufreq):\n${GovernorState}\n" GetTempSensor [ "X${MODE}" = "Xpts" ] && CheckPTS InstallPrerequisits [ "X${MODE}" = "Xgb" ] && CheckGB InitialMonitoring CheckClockspeedsAndSensors CheckTimeInState before if [ "${PlotCpufreqOPPs}" = "yes" ]; then PlotPerformanceGraph else CheckNetio [ "X${MODE}" = "Xpts" ] || [ "X${MODE}" = "Xgb" ] || RunTinyMemBench RunRamlat if [ "X${MODE}" = "Xpts" ]; then RunPTS elif [ "X${MODE}" = "Xgb" ]; then RunGB else RunOpenSSLBenchmark "128 192 256" Run7ZipBenchmark if [ "${ExecuteCpuminer}" = "yes" ] || [ "X${MODE}" = "Xextensive" ]; then if [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then ExecuteCpuminer=yes RunCpuminerBenchmark else ExecuteCpuminer=no echo -e "\x08\x08 Done.\n(${InstallLocation}/cpuminer-multi/cpuminer missing or not executable)...\c" fi fi if [ "${ExecuteStockfish}" = "yes" ] || [ "X${MODE}" = "Xextensive" ]; then if [ -x "${InstallLocation}/Stockfish-sf_15/src/stockfish" ]; then ExecuteStockfish=yes RunStockfishBenchmark else ExecuteStockfish=no echo -e "\x08\x08 Done.\n(${InstallLocation}/Stockfish-sf_15/src/stockfish missing or not executable)...\c" fi fi fi fi CheckTimeInState after CheckClockspeedsAndSensors # test again loaded system after heating the SoC to the max SummarizeResults UploadResults CheckAgeOfScript BasicSetup ${OriginalCPUFreqGovernor} >/dev/null 2>&1 } # Main CheckTerminal() { # Check if we're outputting to a terminal. If yes try to use bold and colors for messages # unless NO_COLOR is exported by the user and set to 1: https://no-color.org if test -t 1; then ncolors=$(tput colors) if [ -n "${ncolors}" ] && [ "${ncolors}" -ge 8 ] && [ "X${NO_COLOR}" != "X1" ]; then BOLD="$(tput bold)" NC='\033[0m' # No Color LGREEN='\033[1;32m' LRED='\e[0;91m' fi fi } # CheckTerminal UpdateMe() { # function to replace this script with latest version from here: # https://raw.githubusercontent.com/ThomasKaiser/sbc-bench/master/sbc-bench.sh # Also check/update the github projects except of stockfish since there we use # a static and not latest version [ -w "${PathToMe}" ] || { echo "Permission problem accessing ${PathToMe}. Wrong user? Aborting." >&2 ; exit 1 ; } command -v curl >/dev/null 2>&1 || { echo "curl not found. Please install and try again. Aborting." >&2 ; exit 1 ; } TempFile="$(mktemp /tmp/update-sbc-bench.XXXXXX)" [ -w "${TempFile}" ] || { echo "Temp file creation not possible. Aborting." >&2 ; exit 1 ; } curl -s "https://raw.githubusercontent.com/ThomasKaiser/sbc-bench/master/sbc-bench.sh" >"${TempFile}" \ || { echo "Can not access Github. Network problem? Aborting." >&2 ; exit 1 ; } VersionFromInternet="$(head "${TempFile}" | awk -F"=" '/^Version/ {print $2}')" VersionCompare="$(echo -e "${VersionFromInternet}\n${Version}" | sort -V 2>/dev/null | tail -n1)" if [ "X${VersionFromInternet}" = "X" ]; then echo -e "Not able to download from Github. Updating not possible." >&2 exit 1 elif [ "X${Version}" = "X${VersionFromInternet}" ]; then echo -e "Replaced ${Version} with ${Version} from Github...\c" elif [ "X${VersionCompare}" = "X${VersionFromInternet}" ]; then echo -e "Upgraded from ${Version} to ${VersionFromInternet}...\c" elif [ "X${VersionCompare}" = "X${Version}" ]; then echo -e "Downgraded from ${Version} to ${VersionFromInternet}...\c" fi cat "${TempFile}" >"${PathToMe}" && rm "${TempFile}" echo -e "\x08\x08 Done." # Only try to update git projects if git is already installed command -v git >/dev/null 2>&1 || exit 0 for GitProject in mhz ramspeed tinymembench cpuminer-multi cpufetch p7zip ; do if [ -x "${InstallLocation}/${GitProject}" ]; then if [ -r "${InstallLocation}/${GitProject}/.git/config" ]; then GitURL="$(awk -F" = " '/url = / {print $2}' "${InstallLocation}/${GitProject}/.git/config")" echo -e "Checking/updating ${GitURL}...\c" else echo -e "Checking/updating ${GitProject} from Github...\c" fi cd "${InstallLocation}/${GitProject}" || exit 1 GitResult="$(git pull 2>/dev/null)" case "${GitResult}" in *"up to date"*) echo -e "\x08\x08 ${GitResult}" ;; *) case ${GitProject} in cpuminer-multi) rm "${InstallLocation}"/cpuminer-multi/.do-not-try-to-build-again 2>/dev/null ./build.sh >/dev/null 2>&1 case $? in 0) echo -e "\x08\x08 Done." ;; *) echo -e "\x08\x08 Failed." ;; esac ;; *) make clean >/dev/null 2>&1 make >/dev/null 2>&1 echo -e "\x08\x08 Done." ;; esac esac fi done } # UpdateMe CheckAgeOfScript() { # checks modification of this file on disk, compares to 'time now' and if difference # is a month or more then suggests running 'sbc-bench -u' for an update check FileTime=$(stat -c %Y -- "${PathToMe}" 2>/dev/null) TimeNow=$(date "+%s") FileAge=$(( $(( ${TimeNow} - ${FileTime} )) / 86400 )) if [ ${FileAge:-0} -ge 30 ]; then [ "X${SUDO_USER}" != "X" ] && Sudoprefix="sudo " echo -e "\nJust for your information: this script's version on disk is ${FileAge} days old." echo -e "Maybe checking for an update: ${BOLD}${Sudoprefix}${PathToMe} -u${NC}\n" fi } # CheckAgeOfScript snore() { # https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever local IFS [[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null || { # workaround for MacOS and similar systems local fifo fifo=$(mktemp -u) mkfifo -m 700 "$fifo" exec {_snore_fd}<>"$fifo" rm "$fifo" } read ${1:+-t "$1"} -u $_snore_fd || : } # snore GetARMCore() { # List originally obtained from util-linux project but later appended by looking at # https://github.com/gcc-mirror/gcc/blob/master/gcc/config/aarch64/aarch64-cores.def # and https://github.com/llvm/llvm-project/blob/release/15.x/llvm/lib/Support/Host.cpp # and https://github.com/torvalds/linux/blob/master/arch/arm64/include/asm/cputype.h # and https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/uarch.c # and https://github.com/pytorch/cpuinfo/blob/main/src/arm/linux/midr.c # and https://github.com/hrw/arm-socs-table/blob/main/data/cpu_cores.yml grep "${1}/${2}:" <<<"41:Arm 41/810:ARM810 41/920:ARM920 41/922:ARM922 41/926:ARM926 41/940:ARM940 41/946:ARM946 41/966:ARM966 41/a20:ARM1020 41/a22:ARM1022 41/a26:ARM1026 41/b02:ARM11 MPCore 41/b36:ARM1136 41/b56:ARM1156 41/b76:ARM1176 41/c05:Cortex-A5 41/c07:Cortex-A7 41/c08:Cortex-A8 41/c09:Cortex-A9 41/c0c:Cortex-A12 41/c0d:Cortex-A17 41/c0e:Cortex-A17 41/c0f:Cortex-A15 41/c14:Cortex-R4 41/c15:Cortex-R5 41/c17:Cortex-R7 41/c18:Cortex-R8 41/c20:Cortex-M0 41/c21:Cortex-M1 41/c23:Cortex-M3 41/c24:Cortex-M4 41/c27:Cortex-M7 41/c60:Cortex-M0+ 41/d01:Cortex-A32 41/d02:Cortex-A34 41/d03:Cortex-A53 41/d04:Cortex-A35 41/d05:Cortex-A55 41/d06:Cortex-A65 41/d07:Cortex-A57 41/d08:Cortex-A72 41/d09:Cortex-A73 41/d0a:Cortex-A75 41/d0b:Cortex-A76 41/d0c:Neoverse-N1 41/d0d:Cortex-A77 41/d0e:Cortex-A76AE 41/d13:Cortex-R52 41/d15:Cortex-R82 41/d16:Cortex-R52+ 41/d20:Cortex-M23 41/d21:Cortex-M33 41/d22:Cortex-M55 41/d23:Cortex-M85 41/d40:Neoverse-V1 41/d41:Cortex-A78 41/d42:Cortex-A78AE 41/d43:Cortex-A65AE 41/d44:Cortex-X1 41/d46:Cortex-A510 41/d47:Cortex-A710 41/d48:Cortex-X2 41/d49:Neoverse-N2 41/d4a:Neoverse-E1 41/d4b:Cortex-A78C 41/d4c:Cortex-X1C 41/d4d:Cortex-A715 41/d4e:Cortex-X3 41/d4f:Neoverse-V2 41/d80:Cortex-A520 41/d81:Cortex-A720 41/d82:Cortex-X4 41/d84:Neoverse-V3 41/d8e:Neoverse-N3 42:Broadcom 42/00f:Broadcom Brahma B15 42/100:Broadcom Brahma B53 42/516:Broadcom ThunderX2 43:Cavium 43/0a0:Cavium ThunderX 43/0a1:Cavium ThunderX 88XX 43/0a2:Cavium ThunderX 81XX 43/0a3:Cavium ThunderX 83XX 43/0af:Cavium ThunderX2 99xx 43/0b0:Cavium OcteonTX2 43/0b1:Cavium OcteonTX2 98XX 43/0b2:Cavium OcteonTX2 96XX 43/0b3:Cavium OcteonTX2 95XX 43/0b4:Cavium OcteonTX2 95XXN 43/0b5:Cavium OcteonTX2 95XXMM 43/0b6:Cavium OcteonTX2 95XXO 43/0b8:Cavium ThunderX3 T110 44:DEC 44/a10:DEC SA110 44/a11:DEC SA1100 46:Fujitsu 46/001:A64FX 48:HiSilicon 48/d01:TaiShan v110 48/d02:TaiShan v120 48/d40:HiSilicon-A76 48/d41:HiSilicon-A77 48/d42:HiSilicon-A710 49:Infineon 4d:Motorola/Freescale 4e:NVidia 4e/000:NVidia Denver 4e/003:NVidia Denver 2 4e/004:NVidia Carmel 50:APM 50/000:APM X-Gene 51:Qualcomm 51/001:Qualcomm Oryon 1 51/002:Qualcomm Oryon 2 51/00f:Qualcomm Scorpion 51/02d:Qualcomm Scorpion 51/04d:Qualcomm Krait 51/06f:Qualcomm Krait 51/201:Qualcomm Kryo 51/205:Qualcomm Kryo 51/211:Qualcomm Kryo 51/800:Qualcomm Falkor V1/Kryo 51/801:Qualcomm Kryo V2 51/802:Qualcomm Kryo 3XX Gold 51/803:Qualcomm Kryo 3XX Silver 51/804:Qualcomm Kryo 4XX Gold 51/805:Qualcomm Kryo 4XX Silver 51/c00:Qualcomm Falkor 51/c01:Qualcomm Saphira 53:Samsung 53/001:Samsung Exynos-m1 53/002:Samsung Exynos-m3 53/003:Samsung Exynos-m4 53/004:Samsung Exynos-m5 56:Marvell 56/131:Marvell Feroceon 88FR131 56/581:Marvell PJ4/PJ4b 56/584:Marvell PJ4B-MP 61:Apple 61/000:Apple Swift 61/001:Apple Cyclone 61/002:Apple Typhoon 61/003:Apple Typhoon/Capri 61/004:Apple Twister 61/005:Apple Twister/Elba/Malta 61/006:Apple Hurricane 61/007:Apple Hurricane/Myst 61/008:Apple Monsoon 61/009:Apple Mistral 61/00b:Apple Vortex 61/00c:Apple Tempest 61/00f:Apple Tempest-M9 61/010:Apple Vortex-Aruba 61/011:Apple Tempest-Aruba 61/012:Apple Lightning 61/013:Apple Thunder 61/020:Apple Icestorm A14 61/021:Apple Firestorm A14 61/022:Apple Icestorm M1 61/023:Apple Firestorm M1 61/024:Apple Icestorm M1Pro 61/025:Apple Firestorm M1Pro 61/026:Apple Thunder-M10 61/028:Apple Icestorm M1Max 61/029:Apple Firestorm M1Max 61/030:Apple Blizzard A15 61/031:Apple Avalanche A15 61/032:Apple Blizzard M2 61/033:Apple Avalanche M2 61/034:Apple Blizzard M2Pro 61/035:Apple Avalanche M2Pro 61/036:Apple Sawtooth A16 61/037:Apple Everest A16 61/038:Apple Blizzard M2Max 61/039:Apple Avalanche M2Max 61/046:Apple Sawtooth M11 61/048:Apple Sawtooth M3Max 61/049:Apple Everest M3Max 61/000:Virtualized Apple Silicon 00/000:Virtualized Apple Silicon 66:Faraday 66/526:Faraday FA526 66/626:Faraday FA626 68:Huaxintong Semiconductor (HXT) 68/000:HXT Phecda 69:Intel 69/200:Intel i80200 69/210:Intel PXA250A 69/212:Intel PXA210A 69/242:Intel i80321-400 69/243:Intel i80321-600 69/290:Intel PXA250B/PXA26x 69/292:Intel PXA210B 69/2c2:Intel i80321-400-B0 69/2c3:Intel i80321-600-B0 69/2d0:Intel PXA250C/PXA255/PXA26x 69/2d2:Intel PXA210C 69/411:Intel PXA27x 69/41c:Intel IPX425-533 69/41d:Intel IPX425-400 69/41f:Intel IPX425-266 69/682:Intel PXA32x 69/683:Intel PXA930/PXA935 69/688:Intel PXA30x 69/689:Intel PXA31x 69/b11:Intel SA1110 69/c12:Intel IPX1200 6d:Microsoft 6d/d49:Azure Cobalt 100 70:Phytium 70/303:Phytium FTC310 70/660:Phytium FTC660 70/661:Phytium FTC661 70/662:Phytium FTC662 70/663:Phytium FTC663 70/664:Phytium FTC664 70/862:Phytium FTC862 c0:Ampere c0/ac3:Ampere Ampere-1 c0/ac4:Ampere Ampere-1a" | cut -f2 -d: } # GetARMCore GetCoreType() { # function that returns name of CPU cores on several platforms # $1 is the CPU in question, 1st CPU is always cpu0 case ${CPUArchitecture} in arm*|aarch*) if [ -n "${ARMTypes}" ]; then GetARMCore "${ARMTypes[$(( $1 * 2 ))]}" "${ARMTypes[$(( $(( $1 * 2 )) + 1 ))]}" | tail -n1 fi ;; riscv*) # TODO: newer kernels now expose more information so we could use this: # (mvendorid corresponds to manufacturer's JEDEC ID) # https://gist.github.com/ThomasKaiser/14002f473d354360bf6d87c652c209aa # relying on uarch doesn't work with older RISC-V kernels since missing grep -q '^uarch' <<< "${ProcCPU}" case $? in 0) awk -F": " '/^uarch/ {print $2}' <<< "${ProcCPU}" | sed -n $(( $1 + 1 ))p ;; *) awk -F": " '/^isa/ {print $2}' <<< "${ProcCPU}" | sed -n $(( $1 + 1 ))p ;; esac ;; loongarch*) ModelName="$(awk -F": " '/^model name/ {print $2}' <<< "${ProcCPU}" | sed -n $(( $1 + 1 ))p)" case ${ModelName} in *3?5000*) echo "LA464" ;; *3?6000*) echo "LA664" ;; "") # fallback to cpu model if existing grep -q 'cpu model' <<< "${ProcCPU}" && awk -F": " '/^cpu model/ {print $2}' <<< "${ProcCPU}" | sed -n $(( $1 + 1 ))p ;; *\(*) # we use just the part in brackets: Loongson-3A R4 (Loongson-3A4000) @ 1500MHz awk -F"[()]" '{print $2}' <<<"${ModelName}" ;; *) echo "${ModelName}" ;; esac ;; mips*) awk -F": " '/^cpu model/ {print $2}' <<< "${ProcCPU}" | sed -n $(( $1 + 1 ))p ;; x86_64) # on hybrid x86 designs print core type if [ ${#ClusterConfig[@]} -gt 1 ]; then if [ $1 -lt ${ClusterConfig[1]} ]; then echo "${PCores}" else echo "${ECores}" fi fi ;; esac } # GetCoreType GetARMStepping() { # Parse '^CPU variant|^CPU revision' fields from /proc/cpuinfo and transform them # into 'Stepping' like lscpu does (the latter in older versions only showing info for # cpu0 so partially useless on systems with different CPU clusters) if [ -n "${ARMStepping}" ]; then echo "r$(awk -Wposix '{printf("%d", $1)}' <<<${ARMStepping[$(( $1 * 2 ))]})p${ARMStepping[$(( $(( $1 * 2 )) + 1 ))]}" fi } # GetARMStepping GetCPUInfo() { # function that returns ARM/RISC-V core type in brackets if possible otherwise empty string CoreType="$(GetCoreType $1)" [ -n "${CoreType}" ] && echo " (${CoreType})" } # GetCPUInfo GetLastClusterCore() { NextCore=${ClusterConfig[$1]} [ "${NextCore}" = "" ] && NextCore=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}") echo -n $(( ${NextCore} - 1 )) } # GetLastClusterCore GetLastClusterCoreByType() { NextCore=${ClusterConfigByCoreType[$1]} [ "${NextCore}" = "" ] && NextCore=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}") echo -n $(( ${NextCore} - 1 )) } # GetLastClusterCoreByType BashBench(){ # quick integer performance assessment using a simple bash loop. # Depends not only on CPU performance but also on bash version. # # 5.0.17 / E5-2665 @ 2.40GHz: 147485025 # 4.4.20 / Xeon Silver 4110 @ 3.00GHz: 64434988 # 5.0.3 / Xeon Silver 4110 @ 3.00GHz: 38958875 # 5.0.17 / N5100 @ 2.8GHz: 56864747 # 4.4.20 / Allwinner H3: 566944214 # 5.0.3 / BCM2711: 152325522 # 5.0.17 / Apple Firestorm: 28319838 # 5.2.15 / BCM2712 @ 3.0 GHz: 41210070 local i StartTime=$(date +"%s%N") i=0 while [ $i -lt 10000 ] do ((i++)) done FinishedTime=$(date +"%s%N") RawTime=$(( ${FinishedTime} - ${StartTime} )) case ${BASH_VERSION} in 5.*) # multiply by 1.2 since this version seems faster awk '{printf ("%0.0f",$1*1.2); }' <<<${RawTime} ;; *) echo -n ${RawTime} ;; esac } # BashBench HandleGovernors() { # check and report governors that might affect performance behaviour. Stuff like # memory/GPU/NPU governors. On RK3588 it looks like this for example: # # Status of performance related governors found below /sys: # cpufreq-policy0: ondemand / 408 MHz (conservative ondemand userspace powersave performance schedutil / 408 600 816 1008 1200 1416 1608 1800) # cpufreq-policy4: ondemand / 408 MHz (conservative ondemand userspace powersave performance schedutil / 408 600 816 1008 1200 1416 1608 1800 2016 2208 2256 2304 2352 2400) # cpufreq-policy6: ondemand / 408 MHz (conservative ondemand userspace powersave performance schedutil / 408 600 816 1008 1200 1416 1608 1800 2016 2208 2256 2304 2352 2400) # dmc: dmc_ondemand / 528 MHz (rknpu_ondemand dmc_ondemand userspace powersave performance simple_ondemand / 528 1068 1560 2112) # fb000000.gpu: simple_ondemand / 300 MHz (rknpu_ondemand dmc_ondemand userspace powersave performance simple_ondemand / 300 400 500 600 700 800 900 1000) # fdab0000.npu: rknpu_ondemand / 1000 MHz (rknpu_ondemand dmc_ondemand userspace powersave performance simple_ondemand / 300 400 500 600 700 800 900 1000) # use colours and bold when outputting to a terminal [ -z "${BOLD}" ] && CheckTerminal Governors="$(find /sys -name "*governor" 2>/dev/null | grep -E -v '/sys/module|cpuidle|watchdog')" if [ "X${Governors}" = "X" ] || [ "${CPUArchitecture}" = "x86_64" ]; then # skip if no governors found or on x86 where lots of cpufreq governors are # listed to no avail since the actual work is done layers below. return elif [ "X$1" != "X" ]; then # try to set all governors to $1 echo "${Governors}" | while read ; do echo $1 >"${REPLY}" 2>/dev/null done # If in adjust mode we also tune ASPM since this can massively affect performance # of components behind PCIe buses (NVMe SSDs, PCIe attached HBAs, NICs and so on) if [ -w /sys/module/pcie_aspm/parameters/policy ] && [ -d /sys/bus/pci_express ]; then read OriginalASPMSetting /sys/module/pcie_aspm/parameters/policy 2>/dev/null fi return fi # process governors: echo "${Governors}" | while read ; do read Governor <"${REPLY}" if [ "X${Governor}" != "X" ]; then SysFSNode="$(sed 's/cpufreq\//cpufreq-/' <<<"${REPLY%/*}")" AvailableGovernorsSysFSNode="$(ls -d "${REPLY%/*}"/*available_governors 2>/dev/null)" AvailableFrequenciesSysFSNode="$(ls -d "${REPLY%/*}"/*available_frequencies 2>/dev/null)" if [ -r "${REPLY%/*}/cpuinfo_cur_freq" ]; then Curfreq=" / $(awk '{printf ("%0.0f",$1/1000); }' <"${REPLY%/*}/cpuinfo_cur_freq") MHz" Divider=1000 elif [ -r "${REPLY%/*}/scaling_cur_freq" ]; then Curfreq=" / $(awk '{printf ("%0.0f",$1/1000); }' <"${REPLY%/*}/scaling_cur_freq") MHz" Divider=1000 elif [ -r "${REPLY%/*}/cur_freq" ]; then read Rawfreq <"${REPLY%/*}/cur_freq" if [ ${Rawfreq} -gt 10000000 ]; then Curfreq=" / $(awk '{printf ("%0.0f",$1/1000000); }' <<<"${Rawfreq}") MHz" Divider=1000000 else Curfreq=" / $(awk '{printf ("%0.0f",$1/1000); }' <<<"${Rawfreq}") MHz" Divider=1000 fi else Curfreq="" fi if [ -r "${AvailableGovernorsSysFSNode}" ]; then # with some kernels powersave and performance are only listed as available # after the governor itself has been set to these. So try this just not # with cpufreq since there it always works as designed case "${SysFSNode##*/}" in *cpufreq*) : ;; *) echo performance >"${REPLY}" 2>/dev/null echo powersave >"${REPLY}" 2>/dev/null echo "${Governor}" >"${REPLY}" 2>/dev/null ;; esac read AvailableGovernors <"${AvailableGovernorsSysFSNode}" if [ "X${AvailableGovernors}" != "X${Governor}" ]; then [ -r "${AvailableFrequenciesSysFSNode}" ] \ && AvailableFrequencies=" / $(tr ' ' '\n' <"${AvailableFrequenciesSysFSNode}" | sort -n | sed '/^$/d' | awk "{printf (\"%0.0f\\n\",\$1/${Divider}); }" | tr '\n' ' ' | sed -e 's/\ $//' -e 's/^ //')" \ || AvailableFrequencies="" printf "${SysFSNode##*/}: " grep -q "performance" <<<"${AvailableGovernors}" GovStatus=$? if [ ${GovStatus} -eq 0 ] && [ "X${Governor}" = "Xperformance" ]; then echo -e "${LGREEN}performance${NC}${Curfreq} (${AvailableGovernors}${AvailableFrequencies})" elif [ ${GovStatus} -eq 0 ] && [ "X${Governor}" != "Xperformance" ]; then echo -e "${LRED}${Governor}${NC}${Curfreq} ($(sed "s/performance/\x1b\x5b1mperformance\x1b\x5b0m/" <<<"${AvailableGovernors}")${AvailableFrequencies})" else echo "${Governor}${Curfreq} (${AvailableGovernors}${AvailableFrequencies})" fi fi else echo "${SysFSNode##*/}: ${Governor}${Curfreq}" fi fi done | sort -n } # HandleGovernors HandlePolicies() { # report available sysfs policies that might affect performance behaviour. Stuff like # pcie_aspm, gpu/core_availability_policy, gpu/power_policy or iopolicy. With RK3588 # this might look like this for example: # # Status of performance related policies found below /sys: # /sys/devices/platform/fb000000.gpu/power_policy: [coarse_demand] always_on # /sys/module/pcie_aspm/parameters/policy: default performance [powersave] powersupersave # use colours and bold when outputting to a terminal [ -z "${BOLD}" ] && CheckTerminal # If script is sourced we need to read in relevant dmesg contents [ -z "${DMESG}" ] && DMESG="$(dmesg | grep pcie)" # process policies SysFSPolicies="$(find /sys -name "*policy" 2>/dev/null | grep -E -v 'hotplug|cpufreq|thermal_zone|apparmor|hostap|/sys/kernel|/sys/devices/pci|mobile_lpm_|xmit_hash_|fr_policy|selinux')" if [ "X${SysFSPolicies}" = "X" ]; then # skip if no policies found return fi echo "${SysFSPolicies}" | while read ; do read Policy <"${REPLY}" if [ "X${Policy}" != "X" ]; then case "${REPLY}" in *pcie_aspm*) # PCIe's ASPM gets special treatment since values are standardized # so we can use coloured output. We also need to check for 'pcie' # occurences in kernel ring buffer since reporting ASPM settings on # devices lacking PCIe capabilities is pointless grep -q pcie <<<"${DMESG}" PCIeInDmesg=$? if [ -d /sys/bus/pci_express ] && [ ${PCIeInDmesg} -eq 0 ]; then case ${Policy} in *"[performance]"*) echo -e "${REPLY}: ${LGREEN}${Policy}${NC}" ;; *"[powersave]"*|*"[powersupersave]"*) echo -e "${REPLY}: ${LRED}${Policy}${NC}" ;; *) echo -e "${REPLY}: ${Policy}" ;; esac fi ;; *) # report policy if there is more than one value possible, otherwise skip grep -q ' ' <<<"${Policy}" && echo -e "${REPLY}: ${Policy}" ;; esac fi done | sort -n } # HandlePolicies PlotPerformanceGraph() { # function that walks through all cpufreq OPP and plots a performance graph using # 7-ZIP MIPS. Needs gnuplot and htmldoc (Debian/Ubuntu: gnuplot-nox htmldoc packages) local i if [ ! -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ]; then # no cpufreq support -> no way to test through different clockspeeds. Stop echo -e " Done.\nNo cpufreq support available. Not able to draw performance graph(s)." exit 1 fi # repeat every measurement at least this many times and do not measure any cpufreq below MinRepetitions=3 # how many times should each measurement be repeated Repetitions=$(( 1000000000 / ${QuickAndDirtyPerformance} )) [ ${Repetitions} -lt ${MinRepetitions} ] && Repetitions=${MinRepetitions} SkipBelow=${MinKHz:-600000} # minimum cpufreq in KHz to measure if [ "X${OutputCurrents[*]}" != "X" ]; then # We are in Netio monitoring mode, so measure idle consumption first, # set all governors and PCIe ASPM to lowest clockspeed / powersave HandleGovernors powersave NetioConsumptionFile="${TempDir}/netio.current" echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${OutputCurrent[$(( ${NetioSocket} - 1 ))]}" ) * 10 )) >"${NetioConsumptionFile}" export NetioConsumptionFile /bin/bash "${PathToMe}" -N "${NetioDevice}" "${NetioSocket}" "${NetioConsumptionFile}" "4.8" "30" >/dev/null 2>&1 & NetioMonitoringPID=$! echo -e "\x08\x08 Done.\nTrying to determine idle consumption...\c" echo -e "System health while idling for 4 minutes:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 30 >>"${MonitorLog}" & MonitoringPID=$! snore 240 read IdleConsumption <"${NetioConsumptionFile}" kill ${NetioMonitoringPID} ${MonitoringPID} IdleTemp=$(ReadSoCTemp) echo -e "\n##########################################################################\n\nIdle temperature: ${IdleTemp}°C, idle consumption: $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${IdleConsumption}" ) * 10 )) mW" >>"${ResultLog}" fi CheckNetio # check if cpulist parameter has been provided as well: if [ "X${CPUList}" = "X" ]; then # -g has been used without further restrictions, we run performance test on all cores CheckPerformance "all CPU cores" "$(tr -d '[:space:]' <<<"${ClusterConfig[@]}")" PlotGraph "all CPU cores" "$(tr -d '[:space:]' <<<"${ClusterConfig[@]}")" RenderPDF else # -g with additional options has been called case ${CPUList} in cores) # check each core of every cluster, on RK3399 for example 0 and 4 for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} CheckPerformance "CPU ${FirstCore}" "${FirstCore}" "${FirstCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}${CPUInfo}" "${FirstCore}" done RenderPDF ;; clusters) # check all cores of every cluster, on RK3399 for example 0-3 and 4-5 for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} LastCore=$(GetLastClusterCore $(( $i + 1 ))) CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}" done RenderPDF ;; coreclusters) # check all identical cores of every cluster, on RK3588 for example 0-3 and 4-7 # though this SoC consists of 3 clusters: 0-3 (A55), 4-5 (A76) and 6-7 (A76) for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do FirstCore=${ClusterConfigByCoreType[$i]} LastCore=$(GetLastClusterCoreByType $(( $i + 1 ))) CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}" done RenderPDF ;; all) # check each core of every cluster individually, check cores of each cluster, # if real clusters and 'clusters of same type' (see RK3588 example above) differ # then check 'clusters of same type' and then check all cores for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} CheckPerformance "CPU ${FirstCore}" "${FirstCore}" "${FirstCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}${CPUInfo}" "${FirstCore}" done for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} LastCore=$(GetLastClusterCore $(( $i + 1 ))) CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}" done if [ ${#ClusterConfigByCoreType[@]} -ne ${#ClusterConfig[@]} ]; then for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do FirstCore=${ClusterConfigByCoreType[$i]} LastCore=$(GetLastClusterCoreByType $(( $i + 1 ))) CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}" CPUInfo="$(GetCPUInfo ${FirstCore})" PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}" done fi if [ ${#ClusterConfig[@]} -gt 1 ]; then # more than one CPU cluster, we test using all cores simultaneously CheckPerformance "all CPU cores" "$(tr -d '[:space:]' <<<"${ClusterConfig[@]}")" PlotGraph "all CPU cores" "$(tr -d '[:space:]' <<<"${ClusterConfig[@]}")" fi RenderPDF ;; *) # individual taskset options have been provided, e.g. 0-2 or 3 if [ ${#CPUList} -eq 1 ]; then # single core to be tested, we need to determine correct policy node for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} LastCore=$(GetLastClusterCore $(( $i + 1 ))) if [ ${CPUList} -le ${LastCore} ]; then CheckPerformance "CPU core(s) ${CPUList}" "${FirstCore}" "${CPUList}" CPUInfo="$(GetCPUInfo ${CPUList})" PlotGraph "core ${CPUList}${CPUInfo}" "${FirstCore}" break fi done else CheckPerformance "CPU core(s) ${CPUList}" "${CPUList:0:1}" "${CPUList}" PlotGraph "core(s) ${CPUList}" "${CPUList:0:1}" fi RenderPDF ;; esac fi exit 0 } # PlotPerformanceGraph CheckPerformance() { # function that gets provided with two or three arguments: # * $1 test focus to be displayed # * $2 policy cores: the cores that need to be adjusted when measuring, e.g. "0" for # cpu0, "4" for cpu4 or for example on an RK3399 "04" to handle both cpu clusters # at the same time # * $3 taskset options as provided via the -g switch when calling sbc-bench local i if [ -n "${3}" ]; then # if taskset options are provided ensure that '-mmt=1' is set when only a single # core is tested. TasksetOptions="taskset -c ${3} " if [ ${#3} -eq 1 ]; then SevenZIPOptions="-mmt=1" elif [ ${#3} -eq 3 ]; then # something like 0-3 or 4-5, determine count of cpu cores: HowManyCores=$(( $(( ${3:2:1} - ${3:0:1} )) + 1 )) SevenZIPOptions="-mmt=${HowManyCores}" else SevenZIPOptions="" fi else TasksetOptions="" SevenZIPOptions="" fi Clusters="$(ls -d /sys/devices/system/cpu/cpufreq/policy[${2}])" echo -e "\x08\x08 Done.\nChecking ${1}: \c" echo -e "\nSystem health while testing through ${1} ${Repetitions} times:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m $(( 30 * ${Repetitions} )) >>"${MonitorLog}" & MonitoringPID=$! SocTemp=$(ReadSoCTemp) CpufreqDat="${TempDir}/cpufreq${2}.dat" CpufreqLog="${TempDir}/cpufreq${2}.log" if [ -s "${NetioConsumptionFile}" ]; then NetioHeader=" / Watt" echo -e "0\t0\t${IdleTemp}\t${IdleConsumption}" >"${CpufreqDat}" echo -n "" >"${CpufreqDat}" else echo -n "" >"${CpufreqDat}" fi if [ ${USE_VCGENCMD} = true ] ; then echo -e "Testing through ${1} ${Repetitions} times:\n\nSysfs/ThreadX/Tested: MIPS / Temp${NetioHeader}" >"${CpufreqLog}" else echo -e "Testing through ${1} ${Repetitions} times:\n\nSysfs/Tested: MIPS / Temp${NetioHeader}" >"${CpufreqLog}" fi # adjust min and max speeds (set max speeds on unaffected clusters to min speed) for Cluster in /sys/devices/system/cpu/cpufreq/policy? ; do [[ -e "${Cluster}" ]] || break echo userspace >${Cluster}/scaling_governor read MinSpeed <${Cluster}/cpuinfo_min_freq read MaxSpeed <${Cluster}/cpuinfo_max_freq echo ${MinSpeed} >${Cluster}/scaling_setspeed done for Cluster in ${Clusters}; do echo userspace >${Cluster}/scaling_governor read MinSpeed <${Cluster}/cpuinfo_min_freq read MaxSpeed <${Cluster}/cpuinfo_max_freq echo ${MinSpeed} >${Cluster}/scaling_setspeed done # now walk through higher cluster since this is supposed to provide more cpufreq OPP. # On ARM usually little cores are the cores with lower numbers. BiggestCluster="$(sort -n -r <<<${Clusters} | head -n1)" for i in $((tr " " "\n" <${BiggestCluster}/scaling_available_frequencies ; tr " " "\n" <${BiggestCluster}/scaling_boost_frequencies) 2>/dev/null | sort -n | uniq | sed '/^[[:space:]]*$/d') ; do # skip measuring cpufreq OPPs below $SkipBelow KHz if [ $i -lt ${SkipBelow} ]; then continue fi # try to set this speed on all affected cpufreq policies if [ "X${3}" = "X" ]; then for Cluster in /sys/devices/system/cpu/cpufreq/policy? ; do [[ -e "${Cluster}" ]] || break [ -f ${Cluster}/scaling_setspeed ] && echo ${i} >${Cluster}/scaling_setspeed done else for Cluster in $(seq ${3:0:1} ${3:2:1}); do [ -f /sys/devices/system/cpu/cpufreq/policy${Cluster}/scaling_setspeed ] && echo ${i} >/sys/devices/system/cpu/cpufreq/policy${Cluster}/scaling_setspeed done fi snore 0.1 # if TasksetOptions is not provided measure clockspeed on highest core: if [ "X${TasksetOptions}" = "X" ]; then MeasureCore=$(awk '{print substr($0,length,1)}' <<<"${BiggestCluster}") MeasuredSpeed=$(( $(taskset -c ${MeasureCore} "${InstallLocation}"/mhz/mhz 3 100000 | awk -F" cpu_MHz=" '{s+=$2} END {printf "%.0f", s}') / 3 )) else MeasuredSpeed=$(( $(${TasksetOptions} "${InstallLocation}"/mhz/mhz 3 100000 | awk -F" cpu_MHz=" '{s+=$2} END {printf "%.0f", s}') / 3 )) fi RoundedSpeed=$(( $(awk '{printf ("%0.0f",$1/10+0.5); }' <<<"${MeasuredSpeed}") * 10 )) SysfsSpeed=$(( $i / 1000 )) if [ ${USE_VCGENCMD} = true ] ; then # On RPi we query ThreadX about clockspeeds too ThreadXFreq=$("${VCGENCMD}" measure_clock arm 2>/dev/null | awk -F"=" '{printf ("%0.0f",$2/1000000); }' ) CoreVoltage=$("${VCGENCMD}" measure_volts uncached 2>/dev/null | cut -f2 -d=) [ "X${CoreVoltage}" = "X" ] && CoreVoltage=$("${VCGENCMD}" measure_volts 2>/dev/null | cut -f2 -d=) echo -e "$(printf "%4s" ${SysfsSpeed}) / $(printf "%4s" ${ThreadXFreq}) /$(printf "%6s" ${RoundedSpeed}):\c" >>"${CpufreqLog}" echo -e "${ThreadXFreq}\t\c" >>"${CpufreqDat}" echo -e "${ThreadXFreq}MHz, \c" else echo -e "$(printf "%4s" ${SysfsSpeed}) / $(printf "%4s" ${RoundedSpeed}) :\c" >>"${CpufreqLog}" echo -e "${MeasuredSpeed}\t\c" >>"${CpufreqDat}" echo -e "${SysfsSpeed}MHz, \c" fi echo -n "" >"${TempDir}/plotvalues" for _check in $(seq 1 ${Repetitions}) ; do # run 7-zip benchmark ${TasksetOptions} "${SevenZip}" b ${SevenZIPOptions} >"${TempLog}" 2>/dev/null if [ -s "${NetioConsumptionFile}" ]; then read ConsumptionNow <"${NetioConsumptionFile}" fi SocTemp=$(ReadSoCTemp) MIPS="$(awk -F" " '/^Tot:/ {print $4}' <"${TempLog}")" echo -e "${MIPS} ${SocTemp} ${ConsumptionNow}" >>"${TempDir}/plotvalues" done AveragedMIPS=$(( $(awk -F" " '{s+=$1} END {printf "%.0f", s}' <"${TempDir}/plotvalues") / ${Repetitions} )) TempSum=$(awk -F" " '{s+=$2} END {printf "%.1f", s}' <"${TempDir}/plotvalues") AveragedTemp=$(awk "{printf (\"%0.1f\",\$1/${Repetitions}); }" <<<"${TempSum}") if [ -s "${NetioConsumptionFile}" ]; then AveragedWatts=$(( $(awk -F" " '{s+=$3} END {printf "%.0f", s}' <"${TempDir}/plotvalues") / ${Repetitions} )) ConsumptionInfo="$(printf "%6s" ${AveragedWatts})mW" fi echo "$(printf "%6s" ${AveragedMIPS}) $(printf "%5s" ${AveragedTemp})°C${ConsumptionInfo}" >>"${CpufreqLog}" echo -e "${AveragedMIPS}\t${AveragedTemp}\t${AveragedWatts}" >>"${CpufreqDat}" done kill ${MonitoringPID} >/dev/null 2>&1 } # CheckPerformance PlotGraph() { # function that gets two arguments provided to graph the performance results: # * $1 test focus to be displayed # * $2 policy cores: the cores that need to be adjusted when measuring, e.g. "0" for # cpu0, "4" for cpu4 or for example on an RK3399 "04" to handle both cpu clusters # at the same time # create random filename for graph png GraphPNG="$(mktemp ${TempDir}/graph.XXXXXX)" mv "${GraphPNG}" "${GraphPNG}.png" # adjust y axis range by highest value MaxMIPS=$(awk '{print $2}' <"${CpufreqDat}" | sort -n | tail -n1) MaxTemp=$(awk '{print $3}' <"${CpufreqDat}" | sort -n | tail -n1) Y2Range=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxTemp}") # Count columns in plot data CountofColums=$(awk '{print NF}' "${CpufreqDat}" | head -n1) case ${CountofColums} in 3) # no consumption numbers, only plot MIPS and temp YLabel="7-Zip MIPS" YRange=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxMIPS}") PlotCommand="plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2" ;; 4) # also consumption numbers so include them in the graph too YLabel="7-Zip MIPS / mW" MaxWatt=$(awk '{print $4}' <"${CpufreqDat}" | sort -n | tail -n1) YRangeWatt=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxWatt}") YRangeMIPS=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxMIPS}") if [ ${MaxWatt} -gt ${MaxMIPS} ]; then YRange=${YRangeWatt} else YRange=${YRangeMIPS} fi PlotCommand="plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:4 lt rgb 'black' w l title 'Consumption in mW' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2" ;; esac # add individual results and ASCII graph to results log echo -e "\n##########################################################################\n\n$(cat ${TempDir}/cpufreq${2}.log)" >>"${ResultLog}" gnuplot-nox -p -e \ "set terminal dumb size 75, 30; set autoscale; set yrange [0:${YRangeMIPS}]; plot '${CpufreqDat}' using 1:2 with lines notitle" \ >>"${ResultLog}" # plot PNG with gnuplot PNGWidth=900 PNGHeight=450 cat <<- EOF | gnuplot-nox set title '${DeviceName}: ${1}' set ylabel '${YLabel}' set ytics nomirror set y2tics 0,10 set y2label 'degree celsius' set xlabel 'CPU clockspeed in MHz' set datafile sep '\t' set key top left autotitle columnheader set grid set autoscale set yrange [0:${YRange}] set y2range [0:${Y2Range}] set terminal png size ${PNGWidth},${PNGHeight} set output '${GraphPNG}.png' ${PlotCommand} plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2 EOF } # PlotGraph RenderPDF() { # fire up monitoring tasks to get device's health after executing the benchmark # CheckTimeInState after "${SevenZip}" b >/dev/null 2>&1 & # run 7-zip bench in the background CheckClockspeedsAndSensors # test again loaded system after heating the SoC to the max SummarizeResults >/dev/null # use HTMLdoc to combine graph and text cat <<- EOF >${TempDir}/report.html sbc-bench performance graph

sbc-bench v${Version} - ${DeviceName} - $(date)

sbc-bench has been called with -g ${CPUList} EOF ls -r --time=atime "${TempDir}"/*.png | while read Graph ; do echo -e "" >>${TempDir}/report.html done cat <<- EOF >>${TempDir}/report.html
$(sed '/^$/N;/^\n$/D' <"${ResultLog}")
EOF htmldoc --charset utf-8 --headfootfont helvetica-oblique --headfootsize 7 --header ..c --tocheader . --firstpage c1 --quiet --browserwidth 900 --pagemode outline --fontsize 8 --format pdf14 --bodyfont helvetica --bottom 1cm --pagelayout single --left 2.5cm --right 2cm --top 1.7cm --linkstyle plain --linkcolor blue --textcolor black --bodycolor white --links --size 210x297mm --portrait --compression=9 --jpeg=95 --webpage -f "${TempDir}/report.pdf" "${TempDir}/report.html" if [ -s "${TempDir}/report.pdf" ]; then PDFName="sbc-bench-v${Version}-$(tr ' ' '-' <<<"${DeviceName,,}")-${CPUList}" FinalPDF="$(mktemp "/tmp/${PDFName}".XXXXXX)" cat "${TempDir}/report.pdf" >"${FinalPDF}" mv "${FinalPDF}" "${FinalPDF}.pdf" chmod 644 "${FinalPDF}.pdf" # make a copy in a permanent location since stuff in /tmp/ gets lost after reboot cp "${FinalPDF}.pdf" "${HOME}/" echo -e "\x08\x08 Done.\n\nPlease check ${FinalPDF}.pdf" else echo -e "\x08\x08 Done.\n\nSomething went wrong" fi } # RenderPDF GetTempSensor() { # In Armbian we can not rely on /etc/armbianmonitor/datasources/soctemp at all any more # since nobody is left there who cares about /usr/lib/armbian/armbian-hardware-monitor. # Armbian always chooses /sys/class/hwmon/hwmon0 which can be something totally different # than the SoC temperature, for example: # * http://ix.io/3Q5y --> sun4i_ts (touch controller) # * http://ix.io/3MFz --> w1_slave_temp (1-wire sensor) # * http://ix.io/411x --> gpu_thermal (obviously _not_ cpu_thermal) # * http://ix.io/41IL --> iwlwifi_1 (Wi-Fi card) # * http://ix.io/4nt8 --> acpitz (whatever sensor remaining at 26.8°C) if [ -f /etc/armbianmonitor/datasources/soctemp ] && [ "${CPUArchitecture}" != "x86_64" ]; then TempSource=/etc/armbianmonitor/datasources/soctemp ThermalNode="$(readlink /etc/armbianmonitor/datasources/soctemp)" if [ -f "${ThermalNode%/*}/name" ]; then read ThermalSource <"${ThermalNode%/*}/name" 2>/dev/null elif [ -f "${ThermalNode%/*}/type" ]; then read ThermalSource <"${ThermalNode%/*}/type" 2>/dev/null fi case ${ThermalSource} in # check name/type of thermal node Armbian 'has chosen' (it's an unmaintained # mess since 2018) aml_thermal|cpu|cpu_thermal*|cpu-thermal*|cpu0-thermal*|cpu0_thermal*|soc_thermal*|soc-thermal*|CPU-therm|x86_pkg_temp) # Seems like a good find TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})" ;; *) # Quick results check within few months showed the following types which # smell all not that good if it's about CPU or SoC temperatures: # scpi_sensors, w1_slave_temp, iio_hwmon, sun4i_ts, gpu_thermal, iwlwifi_1 NodeGuess=$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | sort -n | grep -E "cpu|soc|CPU-therm|x86_pkg_temp|thermal-fan-est" | head -n1) if [ "X${NodeGuess}" != "X" ]; then # let's use this thermal node instead if [ -d "${TempDir}" ]; then TempSource="${TempDir}/soctemp" else TempSource="$(mktemp /tmp/soctemp.XXXXXX)" trap 'rm -f "${TempSource}" ; exit 0' 0 fi ThermalZone="$(GetThermalZone "${NodeGuess}")" ln -fs ${ThermalZone}/temp ${TempSource} if [ -f "${ThermalNode%/*}/temp1_label" ]; then read TempLabel <"${ThermalNode%/*}/temp1_label" case "${TempLabel}" in aml_thermal|cpu|cpu_thermal*|cpu-thermal*|cpu0-thermal*|cpu0_thermal*|soc_thermal*|soc-thermal*|CPU-therm|x86_pkg_temp|k10temp|k8temp|coretemp) # looks like a legit thermal sensor TempInfo="Thermal source: ${ThermalZone}/ (${NodeGuess})" ;; *) # looks weird so print a warning TempInfo="Thermal source: ${ThermalZone}/ (${NodeGuess})\n (Armbian wants to use ${ThermalNode%/*} instead, that\n zone is named ${ThermalSource}. Please check and if wrong\n file a bug here: https://github.com/armbian/build/issues/)" ;; esac fi else # use Armbian's 'choice' since no better match was found OtherTempZones="$(cat /sys/devices/virtual/thermal/thermal_zone?/type | grep -v "${ThermalSource}" | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//')" if [ "X${OtherTempZones}" = "X" ]; then # no other thermal zones available TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})" else # report other thermal zones as well TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})\n (other sensors found: ${OtherTempZones})" fi fi ;; esac else if [ -d "${TempDir}" ]; then TempSource="${TempDir}/soctemp" else TempSource="$(mktemp /tmp/soctemp.XXXXXX)" trap 'rm -f "${TempSource}" ; exit 0' 0 fi # check platform case $(lscpu | awk -F" " '/^Architecture/ {print $2}') in x86*|i686) cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | grep -q x86_pkg_temp case $? in 0) # use x86_pkg_temp sensor if available ThermalZone="$(GetThermalZone x86_pkg_temp)" ln -fs ${ThermalZone}/temp ${TempSource} TempInfo="Thermal source: ${ThermalZone}/ (x86_pkg_temp)" ;; *) # try to get hwmon node based on thermal driver loaded (idea/feedback courtesy # @linuxium and @dan-and: https://github.com/ThomasKaiser/sbc-bench/issues/33) find /sys -name 'temp?_input' -type f 2>/dev/null | while read ; do read NodeName <"${REPLY%/*}/name" case "${NodeName}" in k10temp|k8temp|coretemp) ln -fs "${REPLY}" ${TempSource} TempInfo="Thermal source: ${REPLY%/*}/ (${NodeName})" break ;; esac done if [ ! -h "${TempSource}" ]; then # still no thermal source found. If there is at least one thermal_zone # with 'cpu' in its name then use it CPUSensor="$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | grep -i cpu | head -n1)" if [ "X${CPUSensor}" != "X" ]; then ThermalZone="$(GetThermalZone "${CPUSensor}")" ln -fs ${ThermalZone}/temp ${TempSource} TempInfo="Thermal source: ${ThermalZone}/ (${CPUSensor})" else echo 0 >${TempSource} fi fi ;; esac ;; *) if [[ -d "/sys/devices/platform/a20-tp-hwmon" ]]; then # Allwinner A20 with old 3.4 kernel ln -fs /sys/devices/platform/a20-tp-hwmon/temp1_input ${TempSource} read ThermalSource /dev/null && \ TempInfo="Thermal source: /sys/devices/platform/a20-tp-hwmon/ (${ThermalSource})" else ThermalNodeGuess=$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | grep -E -i "aml_thermal|cpu|soc" | tail -n1) HWMonNodeGuess=$(cat /sys/class/hwmon/hwmon?/name 2>/dev/null | grep -E -i "120e0000|cpu-hwmon" | tail -n1) if [ "X${ThermalNodeGuess}" != "X" ]; then # let's use this thermal node ThermalZone="$(GetThermalZone "${ThermalNodeGuess}")" ln -fs ${ThermalZone}/temp ${TempSource} TempInfo="Thermal source: ${ThermalZone}/ (${ThermalNodeGuess})" elif [ "X${HWMonNodeGuess}" != "X" ]; then # temporary hack to support thermal readouts on JH71x0 boards running # StarVision vendor kernel and Loongson CPUs with 4.19.0 vendor kernel ThermalZone="$(GetHWNode "${HWMonNodeGuess}")" if [ -r ${ThermalZone}/temp1_input ]; then ln -fs ${ThermalZone}/temp1_input ${TempSource} TempInfo="Thermal source: ${ThermalZone}/ (${HWMonNodeGuess})" else echo 0 >${TempSource} fi else echo 0 >${TempSource} fi fi ;; esac fi export TempSource TempInfo InitialTemp=$(ReadSoCTemp) } # GetTempSensor GetThermalZone() { # get thermal zone for specific type string ($1) for zone in /sys/devices/virtual/thermal/thermal_zone* ; do grep -q "${1}" <"${zone}/type" case $? in 0) echo ${zone} return ;; esac done } # GetThermalZone GetHWNode() { # get hwmon node for specific name string ($1) for node in /sys/class/hwmon/hwmon* ; do grep -q "${1}" <"${node}/name" case $? in 0) echo ${node} return ;; esac done } # GetHWNode CheckNetio() { # Function that checks connection with a Netio powermeter if $Netio is set and if # successful spawns another execution of this script with -N (Netio monitor mode.). if [ "X${Netio}" != "X" ]; then # Try to fetch XML XMLOutput="$(curl -q --connect-timeout 1 "http://${NetioDevice}/netio.xml" 2>/dev/null | tr '\015' '\012')" if [ "X${XMLOutput}" = "X" ] && [ "X${MODE}" != "Xunattended" ]; then echo -e "\nError: not able to fetch \"http://${NetioDevice}/netio.xml\" within a second.\nPlease check parameters and connection manually." >&2 DisplayUsage exit 1 else # check current reading of the socket we're supposed to be plugged into OutputCurrents=($(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' ')) if [ ${OutputCurrents[$(( ${NetioSocket} - 1 ))]} -eq 0 ] && [ "X${MODE}" != "Xunattended" ]; then echo -e "\nWarning: socket ${NetioSocket} of Netio device ${NetioDevice} provides zero current.\n" fi NetioConsumptionFile="${TempDir}/netio.current" [ -f "${NetioConsumptionFile}" ] || echo -n 0 >"${NetioConsumptionFile}" export NetioConsumptionFile /bin/bash "${PathToMe}" -N "${NetioDevice}" "${NetioSocket}" "${NetioConsumptionFile}" >/dev/null 2>&1 & NetioMonitoringPID=$! trap 'kill ${NetioMonitoringPID} ; rm -rf "${TempDir}" ; exit 0' 0 fi fi } # CheckNetio MonitorNetio() { # Poll Netio powermeter accessible at $1 every ~1 second, extract current and # powerfactor readouts for socket $2, write most recent 30 readouts to a file # and generate an average number from this to be written to $3 # By also providing $4 and $5 you can adjust poll interval and sample count. NetioDevice="$1" NetioSocket="$2" ConsumptionFile="$3" SleepInterval=${4:-0.8} CountofSamples=${5:-30} # Try to renice to 19 to not interfere with benchmark behaviour renice 19 $BASHPID >/dev/null 2>&1 while true ; do XMLOutput="$(curl -q --connect-timeout 1 "http://${NetioDevice}/netio.xml" 2>/dev/null | tr '\015' '\012')" InputVoltage=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') OutputCurrents=($(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' ')) OutputCurrent=${OutputCurrents[$(( ${NetioSocket} - 1 ))]} OutputPowerFactors=($(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' ')) OutputPowerFactor=${OutputPowerFactors[$(( ${NetioSocket} - 1 ))]} Consumption=$(awk -F" " '{printf ("%0.0f",$1*$2*$3); }' <<<"${InputVoltage} ${OutputCurrent} ${OutputPowerFactor}") # create a file consisting of $CountofSamples entries with last consumption readouts so we # can generate an average value based this and $SleepInterval touch ${TempDir}/netio-socket-${NetioSocket} PriorValues="$(tail -n${CountofSamples} ${TempDir}/netio-socket-${NetioSocket})" echo -e "${PriorValues}\n${Consumption}" | sed '/^[[:space:]]*$/d' >${TempDir}/netio-socket-${NetioSocket} CountOfEntries="$(wc -l <${TempDir}/netio-socket-${NetioSocket})" SumOfEntries=$(awk '{s+=$1} END {printf "%.0f", s}' <${TempDir}/netio-socket-${NetioSocket}) AverageConsumption=$(( ${SumOfEntries} / ${CountOfEntries} )) echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${AverageConsumption}" ) * 10 )) >"${ConsumptionFile}" snore ${SleepInterval} done } # MonitorNetio PrintCPUInfo() { BasicSetup nochange [ -z "${DTCompatible}" ] && [ -r /proc/device-tree/compatible ] && DTCompatible="$(tr "\000" "\n" /dev/null)" [ -z "${LSCPU}" ] && LSCPU="$(lscpu)" [ -z "${CPUArchitecture}" ] && CPUArchitecture="$(awk -F" " '/^Architecture/ {print $2}' <<<"${LSCPU}")" [ -z "${VoltageSensor}" ] && VoltageSensor="$(GetVoltageSensor)" [ -z "${CPUTopology}" ] && CPUTopology="$(PrintCPUTopology)" [ -z "${CPUSignature}" ] && CPUSignature="$(GetCPUSignature)" [ -z "${X86CPUName}" ] && X86CPUName="$(sed 's/ \{1,\}/ /g' <<<"${LSCPU}" | awk -F": " '/^Model name/ {print $2}' | sed -e 's/1.th Gen //' -e 's/.th Gen //' -e 's/Core(TM) //' -e 's/ Processor//' -e 's/Intel(R) Xeon(R) CPU //' -e 's/Intel(R) //' -e 's/(R)//' -e 's/CPU //' -e 's/ 0 @/ @/' -e 's/AMD //' -e 's/Authentic //' -e 's/ with .*//')" [ -f /sys/devices/soc0/family ] && read SoC_Family /dev/null 2>&1 && Userland=", Userland: $(dpkg --print-architecture 2>/dev/null)" [ "X${VirtWhat}" != "X" ] && [ "X${VirtWhat}" != "Xnone" ] && VirtOrContainer=" / ${BOLD}${VirtWhat}${NC}" echo -e "Kernel: ${CPUArchitecture}${VirtOrContainer}${Userland}" echo -e "\n${CPUTopology}\n" if [ "X${TempInfo}" != "X" ]; then echo -e "${TempInfo}\n" fi } # PrintCPUInfo MonitorBoard() { BasicSetup nochange [ -z "${TempSource}" ] && GetTempSensor [ -z "${DTCompatible}" ] && [ -r /proc/device-tree/compatible ] && DTCompatible="$(tr "\000" "\n" /dev/null)" [ -z "${LSCPU}" ] && LSCPU="$(lscpu)" [ -z "${CPUArchitecture}" ] && CPUArchitecture="$(awk -F" " '/^Architecture/ {print $2}' <<<"${LSCPU}")" [ -z "${VoltageSensor}" ] && VoltageSensor="$(GetVoltageSensor)" [ -z "${CPUTopology}" ] && CPUTopology="$(PrintCPUTopology)" [ -z "${CPUSignature}" ] && CPUSignature="$(GetCPUSignature)" [ -z "${X86CPUName}" ] && X86CPUName="$(sed 's/ \{1,\}/ /g' <<<"${LSCPU}" | awk -F": " '/^Model name/ {print $2}' | sed -e 's/1.th Gen //' -e 's/.th Gen //' -e 's/Core(TM) //' -e 's/ Processor//' -e 's/Intel(R) Xeon(R) CPU //' -e 's/Intel(R) //' -e 's/(R)//' -e 's/CPU //' -e 's/ 0 @/ @/' -e 's/AMD //' -e 's/Authentic //' -e 's/ with .*//')" [ -z "${CPUFetchInfo}" ] && CPUFetchInfo="$(GrabCPUFetchInfo)" if test -t 1; then # when called from a terminal we print some system information first and insert # empty line + header every 15 lines. On x86 include cpufetch info if available if [ -x "${InstallLocation}"/cpufetch/cpufetch ] && [ "${CPUArchitecture}" = "x86_64" ]; then CPUFetchInfo="$("${InstallLocation}"/cpufetch/cpufetch)" [[ ${CPUFetchInfo} != *Unknown* ]] && echo -e "Information courtesy of cpufetch:\n${CPUFetchInfo}\n" fi PrintCPUInfo SPACING=yes else ClusterConfigByCoreType=($(GetCoreClusters)) ClusterConfig=($(GetRelevantCPUClusters)) if [ -f "${TempDir}/Pcores" ]; then read PCores <"${TempDir}/Pcores" read ECores <"${TempDir}/Ecores" fi [ ${#ClusterConfig[@]} -eq 0 ] && ClusterConfig=(0) fi # Background monitoring -- try to renice to 19 to not interfere with benchmark behaviour renice 19 $BASHPID >/dev/null 2>&1 # switch to scaling_cur_freq if cpuinfo_cur_freq is not readable if [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq ]; then CpuFreqToQuery=cpuinfo_cur_freq else CpuFreqToQuery=scaling_cur_freq fi LastUserStat=0 LastNiceStat=0 LastSystemStat=0 LastIdleStat=0 LastIOWaitStat=0 LastIrqStat=0 LastSoftIrqStat=0 LastTotal=0 # check if we're in Netio consumption monitoring mode [ -s "${NetioConsumptionFile}" ] && NetioHeader=" mW" # check whether input voltage can be read [ "X${VoltageSensor}" != "X" ] && VoltageHeader=" DC(V)" # Check whether PMIC ADCs are readable on RPi 5 "${VCGENCMD}" pmic_read_adc >/dev/null 2>&1 case $? in 0) PMIC_ADC=true VoltageHeader=" DC(V)" ;; *) PMIC_ADC=false ;; esac if [ ${USE_VCGENCMD} = true ] && [ ${PMIC_ADC} = true ] ; then DisplayHeader="Time fake/real load %cpu %sys %usr %nice %io %irq Temp VCore PMIC${VoltageHeader}${NetioHeader}" CPUs=raspberrypi elif [ ${USE_VCGENCMD} = true ] ; then DisplayHeader="Time fake/real load %cpu %sys %usr %nice %io %irq Temp VCore${VoltageHeader}${NetioHeader}" CPUs=raspberrypi elif [ ${#ClusterConfig[@]} -eq 1 ] && [ -r /sys/devices/system/cpu/cpufreq/policy0/${CpuFreqToQuery} ] ; then DisplayHeader="Time CPU load %cpu %sys %usr %nice %io %irq Temp${VoltageHeader}${NetioHeader}" CPUs=singlecluster elif [ ${#ClusterConfig[@]} -eq 3 ] && [ -r /sys/devices/system/cpu/cpufreq/policy${ClusterConfig[0]}/${CpuFreqToQuery} ] && [ -r /sys/devices/system/cpu/cpufreq/policy${ClusterConfig[1]}/${CpuFreqToQuery} ] && [ -r /sys/devices/system/cpu/cpufreq/policy${ClusterConfig[2]}/${CpuFreqToQuery} ] ; then DisplayHeader="Time cpu${ClusterConfig[0]}/cpu${ClusterConfig[1]}/cpu${ClusterConfig[2]} load %cpu %sys %usr %nice %io %irq Temp${VoltageHeader}${NetioHeader}" CPUs=triplecluster elif [ -r /sys/devices/system/cpu/cpufreq/policy${ClusterConfig[1]}/${CpuFreqToQuery} ]; then ClusterCount=$(( ${#ClusterConfig[@]} -1 )) DisplayHeader="Time big.LITTLE load %cpu %sys %usr %nice %io %irq Temp${VoltageHeader}${NetioHeader}" read FirstCluster /dev/null) RealFreq=$("${VCGENCMD}" measure_clock arm 2>/dev/null | awk -F"=" '{printf ("%0.0f",$2/1000000); }' ) if [ ${PMIC_ADC} = true ]; then ADC_Readouts="$("${VCGENCMD}" pmic_read_adc 2>/dev/null)" InputVoltage="$(awk -F"=" '/EXT5V_V/ {printf ("%0.2f",$2); }' <<<"${ADC_Readouts}")V" CoreVoltage="$(awk -F"=" '/VDD_CORE_V/ {printf ("%0.4f",$2); }' <<<"${ADC_Readouts}")V" [ "X${CoreVoltage}" = "X" ] && CoreVoltage=$("${VCGENCMD}" measure_volts 2>/dev/null | cut -f2 -d=) PMIC_Whole_Consumption=$(Parse_ADC_Readouts "${ADC_Readouts}" | awk '{s+=$1*$2} END {printf "%.1f", s}') printf "%s: %4s/%4sMHz %5s %s %8s %8s %6s %7s %s\n" "$(date "+%H:%M:%S")" "${FakeFreq}" "${RealFreq}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${CoreVoltage}" "${PMIC_Whole_Consumption}W" "${InputVoltage}" "${NetioColumn}" else CoreVoltage=$("${VCGENCMD}" measure_volts uncached 2>/dev/null | cut -f2 -d=) [ "X${CoreVoltage}" = "X" ] && CoreVoltage=$("${VCGENCMD}" measure_volts 2>/dev/null | cut -f2 -d=) printf "%s: %4s/%4sMHz %5s %s %8s %8s %s\n" "$(date "+%H:%M:%S")" "${FakeFreq}" "${RealFreq}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${CoreVoltage}" "${NetioColumn}" fi ;; biglittle) BigFreq=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) LittleFreq=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) printf "%s: %4s/%4sMHz %5s %s %8s %s %s\n" "$(date "+%H:%M:%S")" "${BigFreq}" "${LittleFreq}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${VoltageColumn}" "${NetioColumn}" ;; littlebig) BigFreq=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) LittleFreq=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) printf "%s: %4s/%4sMHz %5s %s %8s %s %s\n" "$(date "+%H:%M:%S")" "${BigFreq}" "${LittleFreq}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${VoltageColumn}" "${NetioColumn}" ;; triplecluster) Freq0=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) Freq1=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) Freq2=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) printf "%s: %4s/%4s/%4sMHz %5s %s %8s %s %s\n" "$(date "+%H:%M:%S")" "${Freq0}" "${Freq1}" "${Freq2}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${VoltageColumn}" "${NetioColumn}" ;; singlecluster) CpuFreq=$(awk '{printf ("%0.0f",$1/1000); }' /dev/null) printf "%s: %4sMHz %5s %s %8s %s %s\n" "$(date "+%H:%M:%S")" "${CpuFreq}" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${VoltageColumn}" "${NetioColumn}" ;; notavailable) printf "%s: n/aMHz %7s %s %8s %s %s\n" "$(date "+%H:%M:%S")" "${LoadAvg}" "${procStats}" "${SocTemp}°C" "${VoltageColumn}" "${NetioColumn}" ;; esac snore ${SleepInterval} done } # MonitorBoard TempTest() { [ "X${TempSource}" = "X" ] && GetTempSensor SocTemp=$(ReadSoCTemp 2>/dev/null | cut -f1 -d'.') [ "X${SocTemp}" = "X" ] && \ { echo -e "${LRED}${BOLD}WARNING: No temperature source found. Aborting.${NC}" >&2 ; exit 1 ; } [ ${SocTemp} -lt 20 ] && \ echo -e "${LRED}${BOLD}WARNING: sysfs thermal readout is ominously low: ${SocTemp}°C.${NC}\n" >&2 BasicSetup nochange >/dev/null 2>&1 InstallPrerequisits InitialMonitoring echo -e "\n${BOLD}Thermal efficiency test using $(readlink "${TempSource}")${NC}" echo -e "\nInstalling needed tools. This may take some time...\c" # get/build mhz and cpuminer if not already there if [ ! -x "${InstallLocation}"/mhz/mhz ]; then cd "${InstallLocation}" || exit 1 git clone https://github.com/wtarreau/mhz >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download mhz. Please try again later.${NC}" >&2 exit 1 fi cd mhz || exit 1 make >/dev/null 2>&1 if [ ! -x "${InstallLocation}"/mhz/mhz ]; then echo -e "${LRED}${BOLD}Not able to build necessary tools. Aborting.${NC}\nMost probably gcc and make packages are missing." >&2 exit 1 fi else grep -q 'among 5 runs of the short loop' "${InstallLocation}"/mhz/mhz.c if [ $? -ne 0 ]; then # rebuild mhz due to https://github.com/wtarreau/mhz/commit/6620e45f41429afe577aa3bb80614ac3934afd82 echo -e "\x08\x08\x08, mhz...\c" cd "${InstallLocation}/mhz" || exit 1 git pull >/dev/null 2>&1 make >/dev/null 2>&1 fi fi InstallCpuminer >/dev/null 2>&1 if [ ! -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then echo -e "${LRED}${BOLD}WARNING: Not able to execute cpuminer. Aborting.${NC}" >&2 exit 1 fi # Start with testing CheckClockspeedsAndSensors SocTemp=$(ReadSoCTemp | cut -f1 -d'.') echo " Done" case $c in T) if [ ${SocTemp} -le ${TargetTemp} ]; then echo -e "System health while heating up the SoC:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 10 >>"${MonitorLog}" & MonitoringPID=$! "${InstallLocation}"/cpuminer-multi/cpuminer --benchmark --cpu-priority=2 >/dev/null & MinerPID=$! while [ ${SocTemp} -le ${TargetTemp} ]; do echo "Heating SoC from current ${SocTemp}°C to ${TargetTemp}°C. This may take some time..." printf "\x1b[1A" snore 2 SocTemp=$(ReadSoCTemp | cut -f1 -d'.') done echo -e "Heating SoC from current ${TargetTemp}°C to ${TargetTemp}°C. This may take some time...\c" kill ${MinerPID} ${MonitoringPID} else echo -e "Skipping heating up the SoC since already at ${SocTemp}°C\n" >>"${MonitorLog}" echo -e "No need to heat the SoC to ${TargetTemp}°C since already at ${SocTemp}°C...\c" fi ;; t) if [ ${SocTemp} -gt ${TargetTemp} ]; then echo -e "System health while waiting for the SoC to cool down:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 10 >>"${MonitorLog}" & MonitoringPID=$! while [ ${SocTemp} -gt ${TargetTemp} ]; do echo "Waiting for the SoC cooling down from current ${SocTemp}°C to ${TargetTemp}°C. This may take some time..." printf "\x1b[1A" snore 2 SocTemp=$(ReadSoCTemp | cut -f1 -d'.') done echo -e "Waiting for the SoC cooling down from current ${SocTemp}°C to ${TargetTemp}°C. This may take some time...\c" kill ${MonitoringPID} else echo -e "No need to wait for the SoC chilling since already at ${SocTemp}°C\n" >>"${MonitorLog}" echo -e "No need to wait for the SoC chilling since already at ${SocTemp}°C...\c" fi ;; esac CheckTimeInState before RunCpuminerBenchmark CheckTimeInState after echo -e " Done.\n\007\007\007" # Prepare test results CheckForThrottling echo -e "\n##########################################################################\n" >>"${ResultLog}" cat "${MonitorLog}" >>"${ResultLog}" [ -f "${TempDir}/throttling_info.txt" ] && cat "${TempDir}/throttling_info.txt" >>"${ResultLog}" cat "${ResultLog}" } # TempTest ReadSoCTemp() { if [ -h "${TempSource}" ]; then read RawTemp <"${TempSource}" if [ ${RawTemp:-0} -ge 1000 ]; then RawTemp=$(awk '{printf ("%0.1f",$1/1000); }' <<<${RawTemp}) fi echo -e ${RawTemp} fi } # ReadSoCTemp ProcessStats() { # Generate detailed usage statistics based on /proc/stat procStatLine=(`sed -n 's/^cpu\s//p' /proc/stat`) UserStat=${procStatLine[0]} NiceStat=${procStatLine[1]} SystemStat=${procStatLine[2]} IdleStat=${procStatLine[3]} IOWaitStat=${procStatLine[4]} IrqStat=${procStatLine[5]} SoftIrqStat=${procStatLine[6]} Total=0 for eachstat in ${procStatLine[@]}; do Total=$(( ${Total} + ${eachstat} )) done UserDiff=$(( ${UserStat} - ${LastUserStat} )) NiceDiff=$(( ${NiceStat} - ${LastNiceStat} )) SystemDiff=$(( ${SystemStat} - ${LastSystemStat} )) IOWaitDiff=$(( ${IOWaitStat} - ${LastIOWaitStat} )) IrqDiff=$(( ${IrqStat} - ${LastIrqStat} )) SoftIrqDiff=$(( ${SoftIrqStat} - ${LastSoftIrqStat} )) diffIdle=$(( ${IdleStat} - ${LastIdleStat} )) diffTotal=$(( ${Total} - ${LastTotal} )) diffX=$(( ${diffTotal} - ${diffIdle} )) CPULoad=$(( ${diffX}* 100 / ${diffTotal} )) UserLoad=$(( ${UserDiff}* 100 / ${diffTotal} )) SystemLoad=$(( ${SystemDiff}* 100 / ${diffTotal} )) NiceLoad=$(( ${NiceDiff}* 100 / ${diffTotal} )) IOWaitLoad=$(( ${IOWaitDiff}* 100 / ${diffTotal} )) IrqCombined=$(( ${IrqDiff} + ${SoftIrqDiff} )) IrqCombinedLoad=$(( ${IrqCombined}* 100 / ${diffTotal} )) LastUserStat=${UserStat} LastNiceStat=${NiceStat} LastSystemStat=${SystemStat} LastIdleStat=${IdleStat} LastIOWaitStat=${IOWaitStat} LastIrqStat=${IrqStat} LastSoftIrqStat=${SoftIrqStat} LastTotal=${Total} procStats=$(echo -e "$(printf "%3s" ${CPULoad})%$(printf "%4s" ${SystemLoad})%$(printf "%4s" ${UserLoad})%$(printf "%4s" ${NiceLoad})%$(printf "%4s" ${IOWaitLoad})%$(printf "%4s" ${IrqCombinedLoad})%") } # ProcessStats CheckOSRelease() { # check OS OperatingSystem="$(GetOSRelease)" # Display warning when not executed on recent Debian, Fedora, Ubuntu or Rocky Linux distros case ${OperatingSystem,,} in *buster*|*bullseye*|*bookworm*|*focal*|*jammy*|*mantic*|*noble*|*thirty*|"fedora linux 3"[8-9]*|"rocky linux 9"*) : ;; *) # only inform/ask user if $MODE != unattended if [ "X${MODE}" != "Xunattended" ]; then if [ "${1}" = "ForReview" ]; then echo -e "${LRED}${BOLD}WARNING: This tool is meant to assist with device reviews but ${OperatingSystem} seems a bit too old for this.${NC}\n" echo -e "Press [ctrl]-[c] to stop or ${BOLD}[enter]${NC} to continue.\c" read else echo -e "${LRED}${BOLD}WARNING: This tool is meant to run only on recent Debian, Fedora, Ubuntu or Rocky Linux distros.${NC}\n" echo -e "When executed on ${BOLD}${OperatingSystem}${NC} results might be partially meaningless.\nPlease see https://github.com/ThomasKaiser/sbc-bench/issues/81 for reasons.\n\nPress [ctrl]-[c] to stop or ${BOLD}[enter]${NC} to continue.\c" read fi fi ;; esac } # CheckOSRelease GetOSRelease() { # try to get human friendly OS release name [ "X${UBUNTU_CODENAME}" = "X" ] || UbuntuSuffix=" (${UBUNTU_CODENAME})" OS="$(hostnamectl 2>/dev/null | awk -F": " '/Operating System:/ {print $2}')" if [ "X${OS}" = "X" ] && [ "X${PRETTY_NAME}" != "X" ]; then OS="${PRETTY_NAME}" fi case "${OS}" in Armbian*|"Orange Pi"*) # After replacing Canonical's advertising behaviour with own advertisings # (https://github.com/armbian/build/pull/4303) the distro clowns over at # Armbian now even start to manipulate base files of the operating systems # their images base on most probably to fool their users into believing Armbian # would be an operating system just like Debian or Ubuntu instead of just a # build system. They simply overwrite the PRETTY_NAME definition in Debian's # and Ubuntu's /etc/os-release file with some nonsense that will break certain # 3rd party installation routines and confuse users. But that's what you get # when a project is driven by 'ignorance and stupidity'. The fix to restore # the distro's base-files package doesn't work any more since Armbian hobbyists # once again replace distro packages with their own. grep -q "${UBUNTU_CODENAME:-sucks}" <<<"${OS}" && echo "Ubuntu ${VERSION} tampered by ${OS}" || echo "Debian ${VERSION} tampered by ${OS}" ;; "") echo "n/a" ;; *) grep -q -i Gentoo <<<"${OS}" && read OS &2 exit 1 fi export TempDir # Create temporary files TempLog="${TempDir}/temp.log" ResultLog="${TempDir}/results.log" MonitorLog="${TempDir}/monitor.log" touch "${TempLog}" "${ResultLog}" "${MonitorLog}" # delete $TempDir by default but not if in extensive mode: [ "X${MODE}" = "Xextensive" ] || trap 'rm -rf "${TempDir}" ; exit 0' 0 } # CreateTempDir CheckLoadAndDmesg() { # Check if kernel ring buffer contains boot messages. These help identifying HW. DMESG="$(dmesg | grep -E "Linux|pvtm|rockchip-dmc|rockchip-cpuinfo|Amlogic Meson|sun50i|pcie|mmc|leakage")" grep -q -E '] Booting Linux|] Linux version ' <<<"${DMESG}" case $? in 1) grep -q "sunxi" <<<"${DMESG}" IsAllwinner=$? grep -q -E "AuthenticAMD|GenuineIntel" <<<"${ProcCPU}" IsX86=$? if [ "X${MODE}" != "Xunattended" ] && [ ${IsAllwinner} -ne 0 ] && [ ${IsX86} -ne 0 ]; then # print warning on other platforms than Allwinner/x86 and if MODE != unattended echo -e "${LRED}${BOLD}WARNING: dmesg output does not contain early boot messages which\nhelp in identifying hardware details.${NC}\n" echo -e "It is recommended to reboot now and then execute the benchmarks.\nPress ${BOLD}[ctrl]-[c]${NC} to stop or ${BOLD}[enter]${NC} to continue.\c" read fi ;; esac # check for CPU cores being offline [ -z "${LSCPU}" ] && LSCPU="$(lscpu)" OfflineCores=$(awk -F" " '/^Off-line CPU/ {print $4}' <<<"${LSCPU}") if [ "X${OfflineCores}" != "X" ] && [ "X${MODE}" != "Xunattended" ]; then echo -e "${LRED}${BOLD}WARNING: One or more CPU cores are offline: ${OfflineCores}${NC}\n" echo -e "Press ${BOLD}[ctrl]-[c]${NC} to stop or ${BOLD}[enter]${NC} to continue.\c" read fi # skip whole load check on systems with more than 63 cores. CPUCores=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}") [ ${CPUCores} -gt 63 ] && return # Only continue if average load is less than 0.1 or averaged CPU utilization is lower # than 2.5% for 30 sec. Please note that average load on Linux is *not* the same as CPU # utilization: https://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html # switch to performance cpufreq governor in standard operation modes since this helps # lowering load and CPU utilization in less time if [ -d /sys/devices/system/cpu/cpufreq/policy0 ] && [ "X${NOTUNING}" != "Xyes" ]; then for Cluster in /sys/devices/system/cpu/cpufreq/policy? ; do [[ -e "${Cluster}" ]] || break [ -w ${Cluster}/scaling_governor ] && echo performance >${Cluster}/scaling_governor 2>/dev/null done fi AvgLoad1Min=$(awk -F" " '{print $1*100}' < /proc/loadavg) if [ $AvgLoad1Min -ge 10 ]; then echo -e "\nAverage load and/or CPU utilization too high (too much background activity). Waiting...\n" /bin/bash "${PathToMe}" -m 5 >"${TempDir}/wait-for-loadavg.log" & MonitoringPID=$! while [ $AvgLoad1Min -ge 10 ] && [ ${CPUSum:-100} -ge 10 ]; do snore 5 CPUutilization="$(awk -F" " '/^[0-9]/ {print $4}' <"${TempDir}/wait-for-loadavg.log" | sed 's/%//' | tail -n6)" LogLength=$(wc -l <<<"${CPUutilization}") if [ ${LogLength} -gt 5 ]; then CPUSum="$(awk '{s+=$1} END {printf "%.0f", s}' <<<"${CPUutilization}")" else CPUSum=100 fi echo -e "Too busy for benchmarking:$(uptime), cpu: $(tail -n1 <<<"${CPUutilization}")%" AvgLoad1Min=$(awk -F" " '{print $1*100}' < /proc/loadavg) done kill ${MonitoringPID} fi echo "" } # CheckLoadAndDmesg GetRelevantCPUClusters() { # If all CPU cores are of same type then just check for NUMA nodes and ignore # cpufreq settings or package IDs (server SoCs like Ampere Altra or NXP LX2160A # show rather weird package IDs, see http://ix.io/4kiu or http://ix.io/4uaA) # Try to handle special case of crappy S912 SoC consisting of 8 identical A53 # cores of which 4 are crippled (1 GHz vs. 1.4 GHz while the SoC's boot BLOB # fakes 1.5 GHz for the latter) # Also try to cope with big.LITTLE/DynamIQ SoC bring-up situations where cpufreq # isn't working yet while all cores share same ID, see http://ix.io/4Bc8 for example. local ClustersByCpufreqOrPackageIDs=($(GetCPUClusters)) if [ ${#ClusterConfigByCoreType[@]} -eq 1 ] && [ ${#ClustersByCpufreqOrPackageIDs[@]} -ne 2 ]; then NumaNodes=$(awk -F" " '/^NUMA node[0-9]/ {print $4}' <<<"${LSCPU}" | cut -f1 -d'-' | cut -f1 -d',') [ "X${NumaNodes}" = "X" ] && echo 0 || echo "${NumaNodes}" elif [ ${#ClustersByCpufreqOrPackageIDs[@]} -lt ${#ClusterConfigByCoreType[@]} ] ; then # SoC bring-up stage, different CPU clusters are not properly defined echo "${ClusterConfigByCoreType[@]}" else echo "${ClustersByCpufreqOrPackageIDs[@]}" fi } # GetRelevantCPUClusters GetCPUClusters() { # try to report different CPU clusters. Cores of same type might be handled differently. # For example on RK3588 with 4 x A55 and 4 x A76 the latter are two clusters behaving # differently wrt clockspeeds due to PVTM (see below at GetCoreClusters function # that reports all cores of same type as single cluster) if [ "X${VirtWhat}" != "X" ] && [ "X${VirtWhat}" != "Xnone" ]; then # in virtualized environments we only check cpu0 echo "0" else case ${CPUArchitecture} in aarch*|arm*) if [ -d /sys/devices/system/cpu/cpufreq/policy0 ]; then ls -ld /sys/devices/system/cpu/cpufreq/policy? | awk -F"policy" '{print $2}' else # check for different CPU types based on package ids. This allows to test through # different cores even on systems with no cpufreq support. for PKG_ID in $(cat /sys/devices/system/cpu/cpu*/topology/physical_package_id | sort | uniq); do dirname -- $(dirname -- $(grep "^${PKG_ID}$" /sys/devices/system/cpu/cpu*/topology/physical_package_id | head -n1)) | tr -d -c '[:digit:]' echo " " done fi ;; x86_64) Getx86ClusterDetails ;; *) # check for different package ids and use if existent otherwise treat all # CPU cores as identical if [ -f /sys/devices/system/cpu/cpu0/topology/physical_package_id ]; then CountOfPackageIDs=$(sort /sys/devices/system/cpu/cpu*/topology/physical_package_id | uniq | wc -l) if [ ${CountOfPackageIDs} -gt 1 ]; then for PKG_ID in $(cat /sys/devices/system/cpu/cpu*/topology/physical_package_id | sort | uniq); do dirname -- $(dirname -- $(grep "^${PKG_ID}$" /sys/devices/system/cpu/cpu*/topology/physical_package_id | head -n1)) | tr -d -c '[:digit:]' echo " " done else echo "0" fi else echo "0" fi ;; esac fi } # GetCPUClusters GetCoreClusters() { # function to determine clusters by CPU core type used by MODE=extensive # and Geekbench mode # # With x86 we rely on name based cluster detection while on ARM and other # platforms we 'bundle' CPU core types by occurence: # # Amlogic S912 for example contains 2 quad-core A53 clusters with different # cpufreq scaling properties but it's just 8 boring A53 and no big.LITTLE # so there's no reason to treat S912 as '2 clusters CPU'. # # RK3588 for example consists of 4 x A55 cores and 4 x A76 but the latter # are handled as two different clusters sharing same properties except # clockspeed and throttling differences for a reason called PVTM: # https://github.com/ThomasKaiser/Knowledge/blob/master/articles/Quick_Preview_of_ROCK_5B.md#pvtm if [ "${CPUArchitecture}" = "x86_64" ]; then Getx86ClusterDetails else local i for i in $(seq 0 $(( ${CPUCores} - 1 )) ) ; do ThisCore="$(GetCPUInfo $i)" if [ "X${ThisCore}" != "X${LastCore}" ]; then echo "${i}" LastCore="${ThisCore}" fi done fi } # GetCoreClusters Getx86ClusterDetails() { # Since they can't be differentiated by either CPU ID or physical_package_id get # Alder/Raptor/Meteor Lake E/P core clusters from ark.intel.com. HFI might be an option # in the future but only with most recent kernels: https://docs.kernel.org/x86/intel-hfi.html # Fortunately cache sizes do differ between E/P cores and with ACPI correctly set up # /sys/devices/system/cpu/cpu*/acpi_cppc/nominal_perf should differ as well. The SKU # differentiation of these hybrid CPU models follow this scheme: # https://gist.github.com/ThomasKaiser/91ce41d44da14b577b3681bd09075a03 # Check amount of available CPU cores first and whether virtualization has been detected # since when running in a virtualized environment it doesn't make sense trying to # distinguish between efficiency and performance cores if [ ${CPUCores} -eq 1 ] || [ "X${VirtWhat}" != "Xnone" ]; then echo "0" return fi # Check for hyper threading first since affecting size of P logical cluster (the 1st) [ -f /sys/devices/system/cpu/smt/active ] && read HT "${TempDir}/Ecores" echo "Sunny Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 2" || echo "0 1" ;; i9-13900K|i9-13900KF|i9-13900F|i9-13900T|i9-13900KS|i9-13900HX|i9-13950HX|i9-13980HX|i9-13900|i9-13900TE|i9-13900E|i9-14900K|i9-14900KF|i9-14900|i9-14900HX|i9-14900T|i9-14900F) # Raptor Lake, 8/16 cores, 32 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 16" || echo "0 8" ;; i7-13850HX|i7-14700K|i7-14700KF|i7-14700|i7-14700F|i7-14700HX|i7-14700T) # Raptor Lake, 8/12 cores, 28 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 16" || echo "0 8" ;; i9-12900T|i9-12900TE|i9-12900HX|i9-12950HX|i9-12900KS|i9-12900E|i9-12900F|i9-12900|i9-12900K|i9-12900KF|i7-12850HX|i7-12800HX) # Alder Lake, 8/8 cores, 24 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 16" || echo "0 8" ;; i7-13700K|i7-13700KF|i7-13700F|i7-13700T|i7-13700HX|i7-13700E|i7-13700|i7-13700TE|i7-14650HX) # Raptor Lake, 8/8 cores, 24 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 16" || echo "0 8" ;; i7-12700|i7-12700F|i7-12700E|i7-12700TE|i7-12700T|i7-12700K|i7-12700KF) # Alder Lake, 8/4 cores, 20 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 16" || echo "0 8" ;; i9-12900HK|i9-12900H|i7-12700HL|i7-12800HL|i7-12650HX|i7-1280P|i7-12700H|i7-12800H|i7-12800HE) # Alder Lake, 6/8 cores, 20 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 12" || echo "0 6" ;; i9-13900HK|i7-13700H|i5-13600K|i5-13600KF|i5-13500|i9-13905H|i9-13900H|i7-13800H|i7-1370P|i7-13705H|i7-13650HX|i5-13500HX|i5-13600HX|i7-1370PE|i7-13800HE|i5-13500T|i5-13500E|i5-13500TE|i5-13600|i5-13600T|i5-14600K|i5-14600KF|i5-14500|i5-14500HX|i5-14600|i7-1370PRE|i7-1375PRE|i7-13800HRE|i5-14600T|i5-14500T) # Raptor Lake, 6/8 cores, 20 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 12" || echo "0 6" ;; i7-1270PE|i7-1270P|i7-1260P|i5-12600HL|i5-12500HL|i5-12600HX|i5-1250PE|i5-1250P|i5-1240P|i5-12600HE|i5-12500H|i5-12600H) # Alder Lake, 4/8 cores, 16 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 8" || echo "0 4" ;; i7-1360P|i5-13500H|i5-13600H|i5-1340P|i5-13505H|i5-1350P|i5-1340PE|i5-13600HE|i5-1350PE|i5-1350PRE|i5-13600HRE) # Raptor Lake, 4/8 cores, 16 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 8" || echo "0 4" ;; i7-1265UL|i7-1255UL|i7-1265UE|i7-1260U|i7-1250U|i7-1255U|i7-1265U|i5-1245UL|i5-1235UL|i5-1245UE|i5-1230U|i5-1240U|i5-1235U|i5-1245U|i3-1220P) # Alder Lake, 2/8 cores, 12 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4" || echo "0 2" ;; i7-1355U|i7-1365U|i5-1335U|i5-1334U|i5-1345U|i7-1365UE|i5-1335UE|i5-1345UE|"5 120U"*|"7 150U"*) # Raptor Lake, 2/8 cores, 12 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4" || echo "0 2" ;; i7-12650H|i5-12600K|i5-12600KF) # Alder Lake, 6/4 cores, 16 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 12" || echo "0 6" ;; i5-13400|i5-13400F|i7-13620H|i5-13450HX|i5-13400E|i5-13400T|i5-14400|i5-14400F|i5-14450HX|i5-14400T) # Raptor Lake, 6/4 cores, 16 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 12" || echo "0 6" ;; *"Ultra 7 155H"*|*"Ultra 7 165H"*|*"Ultra 7 1002H"*|*"Ultra 9 185H"*) # Meteor Lake, 6/8/2 cores, 22 threads echo "Crestmont" >"${TempDir}/Ecores" echo "Redwood Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 12 20" || echo "0 6 14" ;; i5-12450HX|i5-12450H|i3-12300HL|i3-1220PE|i3-12300HE) # Alder Lake, 4/4 cores, 12 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 8" || echo "0 4" ;; i5-13420H|i3-1320PE|i3-13300HE|i3-1320PRE|i3-13300HRE) # Raptor Lake, 4/4 cores, 12 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 8" || echo "0 4" ;; *"Ultra 5 125H"*|*"Ultra 5 135H"*) # Meteor Lake, 4/8/2 cores, 18 threads echo "Crestmont" >"${TempDir}/Ecores" echo "Redwood Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 8 16" || echo "0 4 12" ;; i3-1215UL|i3-1215UE|i3-1210U|i3-1215U) # Alder Lake, 2/4 cores, 8 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4" || echo "0 2" ;; i3-1315U|i3-1315UE|i3-1315URE|"3 100U"*) # Raptor Lake, 2/4 cores, 8 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4" || echo "0 2" ;; i5-1345URE|i7-1365URE|i7-1366URE) # Raptor Lake, 2/8 cores, 12 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4" || echo "0 2" ;; *"Ultra 5 125U"*|*"Ultra 5 134U"*|*"Ultra 5 135U"*|*"Ultra 7 155U"*|*"Ultra 7 164U"*|*"Ultra 7 165U"*) # Meteor Lake, 2/8/2 cores, 14 threads echo "Crestmont" >"${TempDir}/Ecores" echo "Redwood Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 4 12" || echo "0 2 10" ;; *Gold*850*|*Celeron*730*) # Alder Lake, 1/4 cores, 6 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Golden Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 2" || echo "0 1" ;; i3-1305U|U300|U300E) # Raptor Lake, 1/4 cores, 6 threads echo "Gracemont" >"${TempDir}/Ecores" echo "Raptor Cove" >"${TempDir}/Pcores" [ ${HT} -eq 1 ] && echo "0 2" || echo "0 1" ;; 13100|13100E|13100F|13100T|13100TE|14100|14100F|14100T) # Raptor Lake, 4/0 cores, 8 threads echo "0" ;; *) # unknown CPU, try to check cache sizes since at least on currently known Intel # CPUs efficiency and performance cores have differing cache sizes. echo "Efficiency" >"${TempDir}/Ecores" echo "Performance" >"${TempDir}/Pcores" if [ -d /sys/devices/system/cpu/cpu0/cache ] ; then local i for i in $(seq 0 $(( ${CPUCores} - 1 )) ) ; do ThisCache="$(find /sys/devices/system/cpu/cpu${i}/cache -name size 2>/dev/null | sort -V | while read ; do echo -n "$(cat "${REPLY}")" ; done)" if [ "X${ThisCache}" != "X${LastCache}" ]; then echo "${i}" LastCache="${ThisCache}" fi done else echo "0" fi ;; esac } # Getx86ClusterDetails ParseOPPTables() { # TODO: parse/process opp-peak-kBps values [ -d /sys/firmware/devicetree/base ] && DVFS="$(for OPPTable in /sys/firmware/devicetree/base/*opp* ; do grep -q -E -- "opp-|opp_|-table|_table" <<<"${OPPTable}" && echo "${OPPTable}"; done | sort -n)" if [ "X${DVFS}" = "X" ]; then return fi echo -e "\n##########################################################################" # report I2C accessible microvolt readings first Measurements="$(find /sys/devices/platform -name microvolts 2>/dev/null | grep -i i2c)" if [ "X${Measurements}" != "X" ]; then echo "" echo "${Measurements}" | while read ; do if [ -f "${REPLY%/*}/name" ]; then read NodeName <"${REPLY%/*}/name" case ${NodeName} in *cpu*|*gpu*|*center*|*npu*|*ddr*|*dmc*) # try to report only performance relevant supply voltage readings MicroVolts=$(awk '{printf ("%0.0f",$1/1000); }' <"${REPLY}") if [ -f "${REPLY%/*}/max_microvolts" ]; then MaxMicroVolts=$(awk '{printf ("%0.0f",$1/1000); }' <"${REPLY%/*}/max_microvolts") echo -e " ${NodeName}: ${MicroVolts} mV (${MaxMicroVolts} mV max)" else echo -e " ${NodeName}: ${MicroVolts} mV" fi ;; esac fi done | sort -n fi for OPPTable in ${DVFS}; do read OPPTableName <"${OPPTable}/name" echo -e "\n ${OPPTableName}:" find "${OPPTable}" -type d -name "opp*" 2>/dev/null | grep "${OPPTableName}/" | sort | while read ; do [ -f "${REPLY}/opp-hz" ] && OPPHz="$(printf "%d\n" 0x$(od --endian=big -x <"${REPLY}/opp-hz" | cut -c9- | tr -d ' ') | awk '{printf ("%0.0f",$1/1000000); }')" || OPPHz="" if [ -f "${REPLY}/opp-microvolt" ]; then OPPVolt="$(printf "%d\n" 0x$(od --endian=big -x <"${REPLY}/opp-microvolt" | cut -c9- | tr -d ' ' | cut -c-8 | head -n1))" if [ -f "${REPLY}/opp-supported-hw" ]; then OPPSupportedHW="$(od --endian=big -x <"${REPLY}/opp-supported-hw" | cut -c9- | sed 's/0000 //g' | head -n1)" [ "X${OPPHz}" != "X" ] && printf "%10s MHz %8s mV (%s)\n" ${OPPHz} $(awk '{printf ("%0.1f",$1/1000); }' <<<"${OPPVolt}") "${OPPSupportedHW}" else [ "X${OPPHz}" != "X" ] && printf "%10s MHz %8s mV\n" ${OPPHz} $(awk '{printf ("%0.1f",$1/1000); }' <<<"${OPPVolt}") fi elif [ -f "${REPLY}/opp-microvolt-speed0" ]; then printf "%10s MHz " ${OPPHz} for SpeedBin in $(ls "${REPLY}"/opp-microvolt-speed* | sort -n) ; do OPPVolt="$(printf "%d\n" 0x$(od --endian=big -x <"${SpeedBin}" | cut -c9- | tr -d ' ' | cut -c-8 | head -n1))" [ "X${OPPHz}" != "X" ] && printf "%8s mV" $(awk '{printf ("%0.1f",$1/1000); }' <<<"${OPPVolt}") done echo "" else printf "%10s MHz -\n" ${OPPHz} fi done | sort -n done } # ParseOPPTables ParseRawOPPTables() { DVFS="$(for OPPTable in /sys/firmware/devicetree/base/*opp* ; do grep -q -E -- "opp-|opp_|-table|_table" <<<"${OPPTable}" && echo "${OPPTable}"; done | sort -n)" if [ "X${DVFS}" = "X" ]; then return fi echo -e "\n##########################################################################" for OPPTable in ${DVFS}; do ls -laR "${OPPTable}" echo "" find "${OPPTable}" -type f 2>/dev/null | while read file ; do echo -e "${file}\t$(od --endian=big -x <"${file}" | cut -c9- | tr -d ' ')" done done } # ParseRawOPPTables BasicSetup() { # set cpufreq governor based on $1 (defaults to ondemand if not provided) if [ "$1" != "nochange" ]; then if [ -d /sys/devices/system/cpu/cpufreq/policy0 ]; then for Cluster in /sys/devices/system/cpu/cpufreq/policy? ; do [[ -e "${Cluster}" ]] || break [ -w ${Cluster}/scaling_governor ] && echo ${1:-ondemand} >${Cluster}/scaling_governor 2>/dev/null done fi fi # detect environment LSCPU="$(lscpu)" CPUArchitecture="$(awk -F" " '/^Architecture/ {print $2}' <<<"${LSCPU}")" X86CPUName="$(sed 's/ \{1,\}/ /g' <<<"${LSCPU}" | awk -F": " '/^Model name/ {print $2}' | sed -e 's/1.th Gen //' -e 's/.th Gen //' -e 's/Core(TM) //' -e 's/ Processor//' -e 's/Intel(R) Xeon(R) CPU //' -e 's/Intel(R) //' -e 's/(R)//' -e 's/CPU //' -e 's/ 0 @/ @/' -e 's/AMD //' -e 's/Authentic //' -e 's/ with .*//')" VirtWhat="$(systemd-detect-virt 2>/dev/null)" RK_NVMEM_FILE="$(find /sys/bus/nvmem/devices/rockchip*/* -name nvmem 2>/dev/null | head -n1)" [ -r "${RK_NVMEM_FILE}" ] && RK_NVMEM="$(hexdump -C <"${RK_NVMEM_FILE}" 2>/dev/null | grep -E "52 4b |52 56 " | head -n1)" AW_NVMEM_FILE="/sys/bus/nvmem/devices/sunxi-sid0/nvmem" [ -r "${AW_NVMEM_FILE}" ] && AW_NVMEM="$(hexdump -C <"${AW_NVMEM_FILE}" 2>/dev/null | head -n1)" CPUFetchInfo="$(GrabCPUFetchInfo)" [[ ${CPUFetchInfo} != *Unknown* ]] && CPUFetchGuess="$(awk -F": " '/SoC:/ {print $2}' <<<"${CPUFetchInfo}" | sed "s/^[ \t]*//")" CPUCores=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}") # Might not work with RISC-V on old kernels, see # https://github.com/ThomasKaiser/sbc-bench/issues/46 [ ${CPUCores} -eq 0 ] && CPUCores=$(grep -c '^hart' <<< "${ProcCPU}") # try to bring CPU cores back online that were sent offline when idle for i in $(seq 0 $(( ${CPUCores} - 1 )) ); do taskset -c ${i} echo "" >/dev/null 2>&1 done # try to derive DeviceName from device-tree if available if [ -f /proc/device-tree/model ]; then read DeviceName /dev/null | tr -d '[:space:]' | \ grep -q 'c0000000000000000000000040' && BCM2711="B0" || BCM2711="C0 or later" DeviceName="$(sed 's/Raspberry Pi/RPi/' <<<"${DeviceName}") / BCM2711 Rev ${BCM2711}" ;; "nexell soc") # FriendlyELEC SBC based on Nexell S5P6818, on Armbian use BOARD_NAME if [ -f /etc/armbian-release ]; then . /etc/armbian-release DeviceName="${BOARD_NAME}" fi ;; "FriendlyARM "*) # Remove vendor string since outdated anyway DeviceName="$(sed 's/FriendlyARM //' <<<"${DeviceName}")" ;; esac if [ "${VirtWhat}" = "wsl" ]; then # Differentiate between WSL and WSL2 VMs uname -r | grep -q WSL2 && DeviceName="${HostName} WSL2 VM" || DeviceName="${HostName} WSL VM" elif [ ! -f /proc/device-tree/model ] && [ "X${DMISysVendor}" != "X" ] && [ -z "${DeviceName}" ]; then # if there's no device-tree support but DMI info available use this for DeviceName DeviceName="${DMISysVendor} ${DMIProductName} ${DMIProductVersion}" fi ;; x86*|i686) # if no DeviceName is already assigned then try to construct it from DMI data if [ -z "${DeviceName}" ]; then if [ "${VirtWhat}" = "wsl" ]; then uname -r | grep -q WSL2 && DeviceName="${X86CPUName} WSL2 VM" || DeviceName="${X86CPUName} WSL VM" elif [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ]; then # seems bare metal, but we double check grep -q -i "Virtual" <<<"${DMIProductName}" && \ DeviceName="${DMISysVendor} ${DMIProductName} ${DMIProductVersion} VM" || \ DeviceName="${DMISysVendor} ${DMIProductName} ${DMIProductVersion} / ${X86CPUName}" else # seems virtualized and not already caught by DMISysVendor case construct DeviceName="${DMISysVendor} ${DMIProductName} ${DMIProductVersion} ${VirtWhat} VM" fi fi ;; mips*|loongarch*) # do nothing here and use what /proc/device-tree/model provides : ;; *) echo "${CPUArchitecture} not supported. Aborting." >&2 exit 1 ;; esac ClusterConfigByCoreType=($(GetCoreClusters)) ClusterConfig=($(GetRelevantCPUClusters)) if [ -f "${TempDir}/Pcores" ]; then read PCores <"${TempDir}/Pcores" read ECores <"${TempDir}/Ecores" fi [ ${#ClusterConfig[@]} -eq 0 ] && ClusterConfig=(0) } # BasicSetup CheckMissingPackages() { # check for missing packages and construct installation string with list of needed packages case ${OperatingSystem,,} in "arch linux"*|*manjaro*) # Arch/Manjaro command -v pacman >/dev/null 2>&1 if [ $? -eq 0 ]; then # Arch/Manjaro: check whether packages need updates and suggest to run a # 'pacman -Syu' manually before continuing. Simply installing packages on # a highly outdated Arch install ended with all programs refusing to run # due to 'error while loading shared libraries: libcrypto.so.1.1: cannot # open shared object file: No such file or directory' packages="$(pacman -S -u --print-format %n,%v | sort -n)" count_of_packages=$(wc -l <<<"${packages}") if [ ${count_of_packages:-0} -gt 1 ]; then echo -e "\x08Aborting. ${count_of_packages} packages need updates first (or reboot due to updated kernel):\n\n${packages}\n\nPlease run \"pacman -Syu\" before trying again." >&2 echo "stop" return 1 fi if [[ $(pacman -Q linux | cut -d" " -f2) > $(uname -r) ]]; then echo -e "\x08Aborting. Kernel version mismatch: $(pacman -Q linux | cut -d" " -f2) installed but $(uname -r | cut -f1 -d"-") running\nPlease reboot before trying again." >&2 echo "stop" return 1 fi echo -e "pacman --noconfirm -Syq \c" command -v gcc >/dev/null 2>&1 || echo -e "gcc make base-devel \c" command -v sensors >/dev/null 2>&1 if [ $? -ne 0 ]; then # package name seems to have changed so try both variants pacman -Si lm_sensors >/dev/null 2>&1 && echo -e "lm_sensors \c" pacman -Si lm-sensors >/dev/null 2>&1 && echo -e "lm-sensors \c" fi else echo -e "echo \c" fi ;; *fedora*) command -v dnf >/dev/null 2>&1 if [ $? -eq 0 ]; then # Fedora packages="$(dnf check-update 2>/dev/null | grep "[0-9]\.")" count_of_packages=$(wc -l <<<"${packages}") if [ ${count_of_packages:-0} -gt 1 ]; then echo -e "\x08Aborting. ${count_of_packages} packages need updates first (or reboot due to updated kernel):\n\n${packages}\n\nPlease run \"dnf update -y\" before trying again." >&2 echo "stop" return 1 fi echo -e "dnf install -q -y \c" command -v gcc >/dev/null 2>&1 || dnf group install -y -q "C Development Tools and Libraries" >/dev/null 2>"${TempDir}/packages.log" command -v sensors >/dev/null 2>&1 || echo -e "lm_sensors \c" command -v lsb_release >/dev/null 2>&1 || echo -e "redhat-lsb-core \c" else echo -e "echo \c" fi ;; *"rocky linux"*) command -v dnf >/dev/null 2>&1 if [ $? -eq 0 ]; then # Rocky Linux packages="$(dnf check-update 2>/dev/null | grep "[0-9]\.")" count_of_packages=$(wc -l <<<"${packages}") if [ ${count_of_packages:-0} -gt 1 ]; then echo -e "\x08Aborting. ${count_of_packages} packages need updates first (or reboot due to updated kernel):\n\n${packages}\n\nPlease run \"dnf update -y\" before trying again." >&2 echo "stop" return 1 fi # check whether "Extra Packages for Enterprise Linux" repo needs to be activated dnf repolist | grep -q ^epel || dnf install -q -y epel-release echo -e "dnf install -q -y \c" command -v gcc >/dev/null 2>&1 || dnf group install -y -q "Development Tools" >/dev/null 2>"${TempDir}/packages.log" command -v sensors >/dev/null 2>&1 || echo -e "lm_sensors \c" else echo -e "echo \c" fi ;; *suse*) command -v zypper >/dev/null 2>&1 if [ $? -eq 0 ]; then # Some Suse variant with zypper present echo -e "zypper install -y \c" command -v gcc >/dev/null 2>&1 || zypper install -y -t pattern devel_basis >/dev/null 2>"${TempDir}/packages.log" command -v sensors >/dev/null 2>&1 || echo -e "sensors \c" else echo -e "echo \c" fi ;; *) # other distro, let's check for apt command -v apt-get >/dev/null 2>&1 if [ $? -eq 0 ]; then # Debian/Ubuntu/Linux Mint echo -e "apt-get -f -qq -y install \c" for dpkg in gcc make build-essential lm-sensors ; do dpkg -s ${dpkg} >/dev/null 2>&1 || echo -e "${dpkg} \c" done command -v powercap-info >/dev/null 2>&1 [ $? -ne 0 ] && [ -d /sys/devices/virtual/powercap ] && echo -e "powercap-utils \c" # Check for hexdump only on Rockchip and Allwinner platforms where NVMEM is # readable. In Debian based distros the tool is part of bsdmainutils package, # no idea about other distros. if [ -r "${RK_NVMEM_FILE}" ] || [ -r "${AW_NVMEM_FILE}" ]; then command -v hexdump >/dev/null 2>&1 || echo -e "bsdmainutils \c" fi else echo -e "echo \c" fi ;; esac command -v curl >/dev/null 2>&1 || echo -e "curl \c" command -v dmidecode >/dev/null 2>&1 || echo -e "dmidecode \c" command -v git >/dev/null 2>&1 || echo -e "git \c" command -v iostat >/dev/null 2>&1 || echo -e "sysstat \c" command -v lshw >/dev/null 2>&1 || echo -e "lshw \c" command -v openssl >/dev/null 2>&1 || echo -e "openssl \c" command -v uptime >/dev/null 2>&1 || echo -e "procps \c" command -v links >/dev/null 2>&1 || echo -e "links \c" if [ "${ExecuteStockfish}" = "yes" ]; then command -v g++ >/dev/null 2>&1 || echo -e "g++ \c" fi if [ "X${MODE}" = "Xextensive" ]; then command -v decode-dimms >/dev/null 2>&1 || echo -e "i2c-tools \c" fi if [ "X${REVIEWMODE}" = "Xtrue" ]; then command -v lspci >/dev/null 2>&1 || echo -e "pciutils \c" command -v lsusb >/dev/null 2>&1 || echo -e "usbutils \c" command -v mmc >/dev/null 2>&1 if [ $? -ne 0 ]; then apt-cache show mmc-utils >/dev/null 2>&1 && echo -e "mmc-utils \c" fi command -v smartctl >/dev/null 2>&1 || echo -e "smartmontools \c" command -v udevadm >/dev/null 2>&1 || echo -e "udev \c" case ${OperatingSystem,,} in "arch linux"*|*manjaro*) command -v stress >/dev/null 2>&1 || echo -e "stress \c" ;; *) command -v stress-ng >/dev/null 2>&1 || echo -e "stress-ng \c" ;; esac fi } # CheckMissingPackages CheckPTS() { command -v phoronix-test-suite >/dev/null 2>&1 if [ $? -ne 0 ]; then command -v php >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "Neither phoronix-test-suite nor php found in \$PATH." else echo "No phoronix-test-suite found in \$PATH." fi echo -e "Please install PTS manually from http://phoronix-test-suite.com/?k=downloads\nThen execute \"phoronix-test-suite batch-setup\"." exit 1 fi } # CheckPTS CheckGB() { # try to download most recent GB version for the platform and check prerequisits TotalMem=$(free | awk -F" " '/^Mem: / {print $7}' | tail -n1) if [ ${TotalMem:-1200000} -lt 1200000 ]; then echo -e "\x08\x08 \n${LRED}${BOLD}WARNING: This machine is low on memory. Likely Geekbench will be oom-killed${NC}\n" echo -e "Press [ctrl]-[c] to stop or ${BOLD}[enter]${NC} to continue.\c" read fi # get latest version string from blog, new major versions are advertised as e.g. # 'Geekbench 6' and not 'Geekbench 6.0' which makes version string guessing a bit # tricky GBBlogContents="$(links -dump "https://www.geekbench.com/blog/")" NewMajorVersion="$(grep "Geekbench [567]$" <<<"${GBBlogContents}" | head -n1 | awk -F" " '{print $2}').0" LatestMinorVersion="$(awk -F" " '/ Geekbench [567].[0-9]/ {print $2}' <<<"${GBBlogContents}" | head -n1)" GBVersion="$(echo -e "${NewMajorVersion}\n${LatestMinorVersion}" | sort -V | tail -n1)" case ${GBVersion} in 7*) echo -e "\x08\x08 No support for Geekbench ${GBVersion} yet. Exiting" exit 1 ;; 5*|6*) : ;; *) echo -e "\x08\x08 Not able to determine Geekbench version. Exiting" exit 1 ;; esac # check platform since download URLs differ: # RISC-V: https://cdn.geekbench.com/Geekbench-5.4.4-LinuxRISCVPreview.tar.gz # 404 # ARM: https://cdn.geekbench.com/Geekbench-5.4.4-LinuxARMPreview.tar.gz # https://cdn.geekbench.com/Geekbench-6.0.0-LinuxARMPreview.tar.gz # x86: https://cdn.geekbench.com/Geekbench-5.4.4-Linux.tar.gz # https://cdn.geekbench.com/Geekbench-6.0.0-Linux.tar.gz ARCH=$(dpkg --print-architecture 2>/dev/null) || \ ARCH=$(awk -F'"' '/^CARCH/ {print $2}' /etc/makepkg.conf 2>/dev/null) || \ ARCH="$(uname -m)" case $ARCH in armhf|armv7l) # 32-bit ARM: here the geekbench5 binary fails to launch the ARMv7 binary DLSuffix="LinuxARMPreview" GBBinaryName="geekbench_armv7" FirstOfflineCPU=1 GBVersion="5.5" # latest version for this platform ;; arm*|aarch64) DLSuffix="LinuxARMPreview" GBBinaryName="geekbench${GBVersion:0:1}" FirstOfflineCPU=0 ;; riscv64) DLSuffix="LinuxRISCVPreview" GBBinaryName="geekbench_riscv64" FirstOfflineCPU=1 ;; amd64|*x86*) DLSuffix="Linux" GBBinaryName="geekbench${GBVersion:0:1}" FirstOfflineCPU=1 ;; *) echo -e "\x08\x08 Not able to execute Geekbench on ${ARCH}. Exiting" exit 1 ;; esac cd "${InstallLocation}" || exit 1 for i in $(seq 9 -1 0); do GBDir="Geekbench-${GBVersion}.${i}-${DLSuffix}" GBBinary="${InstallLocation}/${GBDir}/${GBBinaryName}" # if there's already an executable stop here [ -x "${GBBinary}" ] && return # try to download version ${GBVersion}.${i} TryoutURL="https://cdn.geekbench.com/${GBDir}.tar.gz" Downloadfile="${InstallLocation}/${GBDir}.tar.gz" [ -f "${Downloadfile}" ] || curl -f -O -L -s -q --connect-timeout 10 "${TryoutURL}" 2>/dev/null if [ -s "${Downloadfile}" ]; then # tarball could be downloaded, proceed with untar/remove echo -e "\x08\x08\x08, geekbench ${GBVersion}.${i}...\c" tar xf "${Downloadfile}" rm "${Downloadfile}" break elif [ -f "${Downloadfile}" ]; then rm "${Downloadfile}" fi done # create symlink with version number, to keep different major versions on same install ln -sf "${GBBinary}" "/usr/local/bin/geekbench${GBVersion:0:1}" } # CheckGB InstallPrerequisits() { case ${MODE} in gb) echo -e "sbc-bench v${Version} taking care of Geekbench\n\nInstalling needed tools: \c" ;; pts) echo -e "sbc-bench v${Version} taking care of Phoronix Test Suite\n\nInstalling needed tools: \c" ;; *) echo -e "sbc-bench v${Version}\n\nInstalling needed tools: \c" ;; esac # Determine missing packages and install them with a single command MissingPackages="$(CheckMissingPackages | sed 's/\ $//')" [ "X${MissingPackages}" = "Xstop" ] && exit 1 [ "X${UBUNTU_CODENAME}" != "X" ] && UBUNTU_MAJOR_VERSION="${VERSION_ID%.*}" if [ ${UBUNTU_MAJOR_VERSION:-0} -lt 24 ]; then # On Ubuntus prior to 24.04 and all other distros search for p7zip in $PATH and # if not found add p7zip to list of packages to be installed SevenZip=$(command -v 7za || command -v 7zr) if [ -z "${SevenZip}" ]; then MissingPackages="${MissingPackages} p7zip" fi fi # add universe repository if needed and install all necessary packages in a batch case "${MissingPackages}" in *install|*"install -q -y"|*"install -y"|*"Syq") # no packages to be installed echo -e "\x08distro packages already installed...\c" ;; *) echo -e "\x08\x08 ${MissingPackages}...\c" [ -r /etc/apt/sources.list ] && grep -v '^#' /etc/apt/sources.list | grep -q universe || add-apt-repository -y universe >/dev/null 2>&1 ${MissingPackages} >/dev/null 2>>"${TempDir}/packages.log" if [ $? -eq 100 ]; then # apt cache might be too outdated so update and try again echo -e "\x08\x08 Updating APT cache...\c" AptMessage="$(apt update 2>&1)" MissingPubKeys="$(tr "N" "\n" <<<"${AptMessage}" | awk -F" " '/^O_PUBKEY/ {print $2}' | sort | uniq | tr "\n" " ")" if [ "X${MissingPubKeys}" != "X" ]; then # try to add missing public keys and retry (Ubuntu's key server also has Debian keys) echo -e "\x08\x08 Try to add missing public keys ${MissingPubKeys}...\c" apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ${MissingPubKeys} >/dev/null 2>&1 echo -e "\x08\x08 Updating APT cache again...\c" apt update >/dev/null 2>&1 echo -e "\x08\x08 Installing packages...\c" fi ${MissingPackages} >/dev/null 2>&1 elif [ $? -ne 0 ] && [ -s "${TempDir}/packages.log" ]; then echo -e "\x08\x08 Something went wrong:\n" cat "${TempDir}/packages.log" echo -e "\nTrying to continue...\c" fi ;; esac if [ ${UBUNTU_MAJOR_VERSION:-0} -lt 24 ]; then # On Ubuntus prior to 24.04 and all other distros search for p7zip in $PATH SevenZip=$(command -v 7za || command -v 7zr) # Validate 7-zip version (only 16.02 allows for comparable scores with results from years ago) if [ -x "${SevenZip}" ]; then Output7Zip="$("${SevenZip}" lalala 2>&1)" Valid7Zip="$(grep '^p7zip Version' <<<"${Output7Zip}" | head -n1 | awk -F" " '{print $3}')" [ -z "${Valid7Zip}" ] && Valid7Zip="$(grep '^7-Zip ' <<<"${Output7Zip}" | head -n1 | awk -F" " '{print $3}')" if [ "X${Valid7Zip}" != "X16.02" ]; then # try to install/build p7zip 16.02 with Debian (security) patches applied. If this fails still # use distro provided 7-zip binary (results validation will hint on problematic version later) if [ ! -x "${InstallLocation}/p7zip/bin/7za" ]; then grep -q "p7zip" <<<"${MissingPackages}" && echo -e "\x08\x08\x08 ${Valid7Zip}...\c" InstallOldP7zip fi [ -x "${InstallLocation}/p7zip/bin/7za" ] && SevenZip="${InstallLocation}/p7zip/bin/7za" fi fi else # On Ubuntu 24.x onwards directly build/use p7zip 16.02 on our own InstallOldP7zip [ -x "${InstallLocation}/p7zip/bin/7za" ] && SevenZip="${InstallLocation}/p7zip/bin/7za" fi if [ -z "${SevenZip}" ]; then echo -e "\x08\x08\x08 failed. ${LRED}${BOLD}No 7-zip binary found and could not be installed. Aborting${NC}" >&2 exit 1 fi if [ "${PlotCpufreqOPPs}" = "yes" ]; then command -v htmldoc >/dev/null 2>&1 || apt -f -qq -y --no-install-recommends install htmldoc >/dev/null 2>&1 command -v gnuplot >/dev/null 2>&1 || apt -f -qq -y --no-install-recommends install gnuplot-nox >/dev/null 2>&1 fi # get/build tinymembench if not already there [ -d "${InstallLocation}" ] || mkdir -p "${InstallLocation}" if [ -f "${InstallLocation}/tinymembench/.git/config" ]; then # exchange abandoned ssvb version with v0.4.9-nuumio grep -q github.com/ssvb/tinymembench "${InstallLocation}/tinymembench/.git/config" && \ rm -rf "${InstallLocation}/tinymembench" fi if [ ! -x "${InstallLocation}"/tinymembench/tinymembench ]; then echo -e "\x08\x08\x08, tinymembench...\c" if [ -d "${InstallLocation}"/tinymembench ]; then cd "${InstallLocation}"/tinymembench || exit 1 git pull >/dev/null 2>&1 else cd "${InstallLocation}" || exit 1 git clone https://github.com/nuumio/tinymembench >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download tinymembench. Please try again later.${NC}" >&2 exit 1 fi cd tinymembench || exit 1 fi make >/dev/null 2>&1 if [ ! -x "${InstallLocation}"/tinymembench/tinymembench ]; then echo -e "${LRED}${BOLD}Not able to build necessary tools. Aborting.${NC}\nMost probably gcc and make packages are missing." >&2 exit 1 fi fi # get/build ramlat benchmark if not already built, rebuild if formerly compiled w/o GCC optimizations: # https://www.cnx-software.com/2022/06/05/nanopi-r5s-preview-part-2-ubuntu-20-04-friendlycore/#comment-593141 if [ ! -x "${InstallLocation}"/ramspeed/ramlat ] || [ ! -f "${InstallLocation}"/ramspeed/Makefile ]; then echo -e "\x08\x08\x08, ramlat...\c" if [ ! -d "${InstallLocation}"/ramspeed ]; then cd "${InstallLocation}" || exit 1 && git clone https://github.com/wtarreau/ramspeed >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download ramspeed. Please try again later.${NC}" >&2 exit 1 fi fi [ -d "${InstallLocation}"/ramspeed ] && cd "${InstallLocation}"/ramspeed || exit 1 ; git pull >/dev/null 2>&1; make >/dev/null 2>&1 fi # get/build mhz if not already there if [ ! -x "${InstallLocation}"/mhz/mhz ]; then echo -e "\x08\x08\x08, mhz...\c" cd "${InstallLocation}" || exit 1 git clone https://github.com/wtarreau/mhz >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download mhz. Please try again later.${NC}" >&2 exit 1 fi cd mhz || exit 1 make >/dev/null 2>&1 if [ ! -x "${InstallLocation}"/mhz/mhz ]; then echo -e "${LRED}${BOLD}Not able to build necessary tools. Aborting.${NC}\nMost probably gcc and make packages are missing." >&2 exit 1 fi else grep -q 'among 5 runs of the short loop' "${InstallLocation}"/mhz/mhz.c if [ $? -ne 0 ]; then # rebuild mhz due to https://github.com/wtarreau/mhz/commit/6620e45f41429afe577aa3bb80614ac3934afd82 echo -e "\x08\x08\x08, mhz...\c" cd "${InstallLocation}/mhz" || exit 1 git pull >/dev/null 2>&1 make >/dev/null 2>&1 fi fi # get/build/update cpufetch if not already there if [ -x "${InstallLocation}"/cpufetch/cpufetch ]; then cd "${InstallLocation}/cpufetch" || exit 1 GitResult="$(git pull 2>/dev/null)" if [[ ${GitResult} != *"up to date"* ]]; then # always update cpufetch if something has changed on Github echo -e "\x08\x08\x08, updating cpufetch...\c" make >/dev/null 2>&1 fi else echo -e "\x08\x08\x08, cpufetch...\c" cd "${InstallLocation}" || exit 1 git clone https://github.com/Dr-Noob/cpufetch >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download cpufetch. Please try again later.${NC}" >&2 exit 1 fi cd cpufetch || exit 1 make >/dev/null 2>&1 if [ ! -x "${InstallLocation}"/cpufetch/cpufetch ]; then echo -e "${LRED}${BOLD}Not able to build necessary tools. Aborting.${NC}\nMost probably gcc and make packages are missing." >&2 exit 1 fi fi # if called with -s or with MODE=extensive we also use stockfish if [ "${ExecuteStockfish}" = "yes" ] || [ "X${MODE}" = "Xextensive" ]; then InstallStockfish && DownloadNeuralNet fi # if called with -c or as 'sbc-bench neon' or with MODE=extensive we also use cpuminer if [ "${ExecuteCpuminer}" = "yes" ] || [ "X${MODE}" = "Xextensive" ]; then InstallCpuminer fi } # InstallPrerequisits InstallOldP7zip() { if [ ! -x "${InstallLocation}/p7zip/bin/7za" ]; then echo -e "\x08\x08\x08, p7zip 16.02...\c" cd "${InstallLocation}" || exit 1 if [ -d "${InstallLocation}/p7zip/.git" ]; then cd "${InstallLocation}/p7zip/" || exit 1 && git pull >/dev/null 2>&1 else git clone https://github.com/ThomasKaiser/p7zip >/dev/null 2>&1 fi if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download p7zip 16.02. Please try again later.${NC}" >&2 exit 1 fi cd "${InstallLocation}/p7zip/" || exit 1 && make clean >/dev/null 2>&1 && make -j${CPUCores} INSTALL=install CC=gcc CXX=g++ OPTFLAGS='-O3' >/dev/null 2>&1 fi if [ ! -x "${InstallLocation}"/p7zip/bin/7za ]; then echo -e "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08 (can't build p7zip 16.02) \c" fi } # InstallOldP7zip InstallCpuminer() { # get/(re)build cpuminer if not already there or based on tkinjo1985 version [ -f "${InstallLocation}"/cpuminer-multi/.git/config ] && grep -q tkinjo1985 "${InstallLocation}"/cpuminer-multi/.git/config >/dev/null 2>&1 if [ $? -eq 0 ]; then # remove the old installation rm -rf "${InstallLocation}"/cpuminer-multi fi if [ ! -x "${InstallLocation}"/cpuminer-multi/cpuminer ] && [ ! -f "${InstallLocation}"/cpuminer-multi/.do-not-try-to-build-again ]; then echo -e "\x08\x08\x08, cpuminer...\c" cd "${InstallLocation}" || exit 1 zypper install -y automake autoconf pkg-config libcurl4-openssl-dev libjansson-dev libssl-dev libgmp-dev make g++ zlib1g-dev >/dev/null 2>&1 apt-get -f -qq -y install automake autoconf pkg-config libcurl4-openssl-dev libjansson-dev libssl-dev libgmp-dev make g++ zlib1g-dev >/dev/null 2>&1 pacman --noconfirm -Sq automake autoconf pkg-config libcurl4-openssl-dev libjansson-dev libssl-dev libgmp-dev make g++ zlib1g-dev >/dev/null 2>&1 dnf install -q -y openssl-devel curl-devel zlib-devel >/dev/null 2>&1 if [ -d "${InstallLocation}/cpuminer-multi/.git" ]; then cd "${InstallLocation}/cpuminer-multi/" || exit 1 && git pull >/dev/null 2>&1 && make clean >/dev/null 2>&1 else git clone https://github.com/tpruvot/cpuminer-multi >/dev/null 2>&1 fi if [ $? -ne 0 ]; then echo -e "\n\n${LRED}${BOLD}Temporary Github problem. Not able to download cpuminer. Please try again later.${NC}" >&2 exit 1 fi cd "${InstallLocation}/cpuminer-multi/" || exit 1 && ./build.sh >/dev/null 2>&1 fi if [ ! -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then echo -e "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08 (can't build cpuminer) \c" touch "${InstallLocation}"/cpuminer-multi/.do-not-try-to-build-again fi } # InstallCpuminer InstallStockfish() { # try to be compatible with Phoronix pts/stockfish-1.4.0 test profile as such grab # https://github.com/official-stockfish/Stockfish/archive/refs/tags/sf_15.tar.gz # and default net https://tests.stockfishchess.org/api/nn/nn-6877cd24400e.nnue if [ ! -x "${InstallLocation}/Stockfish-sf_15/src/stockfish" ]; then echo -e "\x08\x08\x08, stockfish...\c" cd "${InstallLocation}" || exit 1 curl -f -L -s -q --connect-timeout 10 "https://github.com/official-stockfish/Stockfish/archive/refs/tags/sf_15.tar.gz" >"${InstallLocation}/sf_15.tar.gz" 2>/dev/null [ -s "${InstallLocation}/sf_15.tar.gz" ] && tar xf sf_15.tar.gz [ -d "${InstallLocation}/Stockfish-sf_15/src" ] && cd "${InstallLocation}/Stockfish-sf_15/src" || exit 1 case ${CPUArchitecture} in x86_64) gcc_enabled=$(c++ -Q -march=native --help=target | grep "\[enabled\]") gcc_arch=$(c++ -Q -march=native --help=target | grep "march") if [[ "${gcc_enabled}" =~ "-mavx512vnni " && "${gcc_enabled}" =~ "-mavx512dq " && "${gcc_enabled}" =~ "-mavx512f " && "${gcc_enabled}" =~ "-mavx512bw " && "${gcc_enabled}" =~ "-mavx512vl " ]] ; then ARCH="x86-64-vnni256" elif [[ "${gcc_enabled}" =~ "-mavx512f " && "${gcc_enabled}" =~ "-mavx512bw " ]] ; then ARCH="x86-64-avx512" elif [[ "${gcc_enabled}" =~ "-mbmi2 " && ! "${gcc_arch}" =~ "znver1" && ! "${gcc_arch}" =~ "znver2" ]] ; then ARCH="x86-64-bmi2" elif [[ "${gcc_enabled}" =~ "-mavx2 " ]] ; then ARCH="x86-64-avx2" elif [[ "${gcc_enabled}" =~ "-mpopcnt " && "${gcc_enabled}" =~ "-msse4.1 " ]] ; then ARCH="x86-64-modern" elif [[ "${gcc_enabled}" =~ "-mssse 3 " ]] ; then ARCH="x86-64-ssse3" elif [[ "${gcc_enabled}" =~ "-mpopcnt " && "${gcc_enabled}" =~ "-msse3 " ]] ; then ARCH="x86-64-sse3-popcnt" else ARCH="x86-64-avx2" fi ;; i686) ARCH=x86-32 ;; ppc64) ARCH=ppc-64 ;; armv7*) ARCH=armv7-neon ;; aarch*) grep -q apple <<<"${DTCompatible,,}" && ARCH=apple-silicon || ARCH=armv8 ;; riscv64) ARCH=riscv64 ;; *) ARCH=general-64 ;; esac make profile-build ARCH=${ARCH} >"${TempDir}/stockfish-install.log" 2>&1 if [ ! -x "${InstallLocation}/Stockfish-sf_15/src/stockfish" ]; then echo -e "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08 (can't build stockfish) \c" return 1 else [ -f "${InstallLocation}/sf_15.tar.gz" ] && rm "${InstallLocation}/sf_15.tar.gz" fi fi } # InstallStockfish DownloadNeuralNet() { if [ -d "${InstallLocation}/Stockfish-sf_15/src" ]; then cd "${InstallLocation}/Stockfish-sf_15/src" || exit 1 case ${NeuralNetwork} in ????????????) # prefix with nn- and suffix with .nnue NeuralNetwork="nn-${NeuralNetwork}.nnue" ;; ????????????.nnue) # prefix with nn- NeuralNetwork="nn-${NeuralNetwork}" ;; nn-????????????) # suffix with .nnue NeuralNetwork="${NeuralNetwork}.nnue" ;; nn-????????????.nnue) # name fits already : ;; *) # use default net FallbackNotification=" (fallback default net)" NeuralNetwork="nn-6877cd24400e.nnue" ;; esac # download neural network if missing if [ ! -f "${NeuralNetwork}" ]; then echo -e "\x08\x08\x08, stockfish NN...\c" curl -f -O -L -s -q --connect-timeout 10 "https://tests.stockfishchess.org/api/nn/${NeuralNetwork}" 2>/dev/null fi # check neural network if [ -f "${NeuralNetwork}" ]; then NetHeader="$(od <"${NeuralNetwork}" | head -n1 | cut -c9-28)" if [ "X${NetHeader}" != "X027440 075363 027362" ]; then echo -e "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08 (can't use ${NeuralNetwork}${FallbackNotification}) \c" return fi else echo -e "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08 (can't download ${NeuralNetwork}${FallbackNotification}) \c" fi fi } # DownloadNeuralNet InitialMonitoring() { # download Linux kernel end-of-life data curl -s -q --connect-timeout 10 https://raw.githubusercontent.com/endoflife-date/endoflife.date/master/products/linuxkernel.md \ >"${TempDir}/linuxkernel.md" & # record start time BenchmarkStartTime=$(date +"%s") # empty caches echo 3 >/proc/sys/vm/drop_caches # q&d performance assessment to estimate duration QuickAndDirtyPerformance="$(BashBench)" TinymembenchDuration=$(( $(( 3 + $(( ${QuickAndDirtyPerformance} / 300000000 )) )) * ${#ClusterConfig[@]} )) RunHowManyTimes=3 # how many times should the multi-threaded tests be repeated SingleThreadedDuration=$(( 20 + $(( ${QuickAndDirtyPerformance} * ${#ClusterConfig[@]} / 5000000 )) )) MultiThreadedDuration=$(( ${RunHowManyTimes} * $(( 20 + $(( ${QuickAndDirtyPerformance} / 5000000 )) )) / ${CPUCores} )) if [ "${ExecuteCpuminer}" = "yes" ] && [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then EstimatedDuration=$(( ${TinymembenchDuration} + $(( $(( ${SingleThreadedDuration} + ${MultiThreadedDuration} )) / 60 )) + 8 )) else EstimatedDuration=$(( ${TinymembenchDuration} + $(( $(( ${SingleThreadedDuration} + ${MultiThreadedDuration} )) / 60 )) + 3 )) fi # collect CPU information CPUTopology="$(PrintCPUTopology)" echo -e "${CPUTopology}\n" >"${TempDir}/cpu-topology.log" & CPUSignature="$(GetCPUSignature)" [ -r /proc/device-tree/compatible ] && DTCompatible="$(tr "\000" "\n" /dev/null)" OPPTables="$(ParseOPPTables)" # try to identify ARM/RISC-V/Loongson SoCs [ "${CPUArchitecture}" = "x86_64" ] && GuessedSoC="n/a" || GuessedSoC="$(GuessARMSoC)" # upload raw /proc/cpuinfo contents and device-tree compatible entry to ix.io ping -c1 -w2 ix.io >/dev/null 2>&1 if [ $? -eq 0 ] && [ "X${ProcCPUFile}" = "X/proc/cpuinfo" ]; then UploadScheme="f:1=<-" UploadServer="ix.io" UploadAnswer="$( (echo -e "/proc/cpuinfo ${Version}\n\n$(uname -a) / ${DeviceName}\n" ; cat /proc/cpuinfo ; echo -e "\n${CPUTopology}\n\n${CPUSignature} / ${GuessedSoC} / ${CPUFetchGuess}$(cut -c9- <<<"${RK_NVMEM}") ${AW_NVMEM:19:2}${AW_NVMEM:16:2}${AW_NVMEM:13:2}${AW_NVMEM:10:2} ${AW_NVMEM:31:2}${AW_NVMEM:28:2}${AW_NVMEM:25:2}${AW_NVMEM:22:2} ${AW_NVMEM:44:2}${AW_NVMEM:41:2}${AW_NVMEM:38:2}${AW_NVMEM:35:2} ${AW_NVMEM:56:2}${AW_NVMEM:53:2}${AW_NVMEM:50:2}${AW_NVMEM:47:2}\n\n${DTCompatible}\n\n${OPPTables}\n\n$(grep . /sys/devices/virtual/thermal/thermal_zone?/* 2>/dev/null)\n\n$(grep . /sys/class/hwmon/hwmon?/* 2>/dev/null)") 2>/dev/null | curl -s -F ${UploadScheme} ${UploadServer} 2>&1)" case "${UploadAnswer}" in *ix.io/*) # URL returned, so everything's fine : ;; *) # something's wrong, e.g. "down for a while due to DDOS. thanks, buddy!" in early Nov. 2023 ping -c1 -w2 sprunge.us >/dev/null 2>&1 && UploadScheme="sprunge=<-" ; UploadServer="sprunge.us" ;; esac else # upload location fallback to sprunge.us if possible ping -c1 -w2 sprunge.us >/dev/null 2>&1 && UploadScheme="sprunge=<-" ; UploadServer="sprunge.us" fi # Log version and device info [ -r /etc/hostname ] && read HostName /dev/null if [ "X${MODE}" = "Xunattended" ] || [ "X${MODE}" = "Xextensive" ] || [ "X${MODE}" = "Xpts" ] || [ "X${MODE}" = "Xgb" ]; then echo -e "sbc-bench v${Version} ${DeviceName:-$HostName} ${MODE} ($(date -R))\n" | sed 's/ / /g' >"${ResultLog}" elif [ "X${REVIEWMODE}" = "Xtrue" ] && [ "X${NOTUNING}" != "Xyes" ]; then echo -e "sbc-bench v${Version} ${DeviceName:-$HostName} ${MODE} ($(date -R))\n" | sed 's/ / /g' >"${ResultLog}" elif [ "X${REVIEWMODE}" = "Xtrue" ]; then echo -e "sbc-bench v${Version} ${DeviceName:-$HostName} NOTUNING review ($(date -R))\n" | sed 's/ / /g' >"${ResultLog}" else echo -e "sbc-bench v${Version} ${DeviceName:-$HostName} ($(date -R))\n" | sed 's/ / /g' >"${ResultLog}" fi # get distribution info command -v lsb_release >/dev/null 2>&1 && (lsb_release -a 2>/dev/null | grep -v "n/a") >>"${ResultLog}" || \ echo -e "Description:\t${OperatingSystem}" >>"${ResultLog}" ARCH=$(dpkg --print-architecture 2>/dev/null) || \ ARCH=$(awk -F'"' '/^CARCH/ {print $2}' /etc/makepkg.conf 2>/dev/null) || \ ARCH="$(uname -m)" # Log Raspberry OS info if available [ -f /etc/apt/sources.list.d/raspi.list ] && \ echo "Build system: $(grep -v '#' /etc/apt/sources.list.d/raspi.list | head -n1 | sed 's/deb //')" >>"${ResultLog}" # Log Build system info if available if [ -r /etc/radxa_image_fingerprint ]; then # Radxa's rbuild . /etc/radxa_image_fingerprint echo "Build system: Radxa rbuild ${RBUILD_REVISION}, ${RBUILD_COMMAND#* }, ${RBUILD_UBOOT} ${RBUILD_UBOOT_VERSION}, $(awk -F" " '{print $2" "$3" "$4}' <<<"${RBUILD_BUILD_DATE}")" >>"${ResultLog}" elif [ -r /etc/fenix-release ]; then # Khadas' Fenix (mostly based on an old Armbian fork) # they put image build information in /proc/cmdline like e.g. # khadas_board=VIM3 hwver=VIM3.V12 coherent_pool=2M pci=pcie_bus_perf imagetype=EMMC_MBR uboottype=mainline . /etc/fenix-release if [ -r /proc/cmdline ]; then read cmdline >"${ResultLog}" else echo "Build system: Khadas Fenix ${VERSION}" >>"${ResultLog}" fi elif [ -r /boot/dietpi/.version ]; then # DietPi . /boot/dietpi/.version echo -e "Build system: DietPi ${G_DIETPI_VERSION_CORE}.${G_DIETPI_VERSION_SUB}.${G_DIETPI_VERSION_RC}, https://github.com/${G_GITOWNER}/DietPi/tree/${G_GITBRANCH}" >>"${ResultLog}" elif [ -r /etc/orangepi-release ]; then # Xunlong's forked Armbian scripts . /etc/orangepi-release elif [ -r /etc/armbian-release ]; then # Armbian or other forks . /etc/armbian-release fi [ "X${BOARD_NAME}" != "X" ] && \ echo "Build system: ${BUILD_REPOSITORY_URL:-https://github.com/armbian/build}, ${VERSION}, ${BOARD_NAME}, ${BOARDFAMILY}, ${LINUXFAMILY}" >>"${ResultLog}" # Log system info and BIOS/UEFI versions if available: command -v dmidecode >/dev/null 2>&1 if [ $? -eq 0 ]; then SystemInfo="$(dmidecode -t system 2>/dev/null | grep -E "Manufacturer: |Product Name: |Version: |Family: |SKU Number: " | grep -E -v ": $|O.E.M.|123456789|0000000000000000|: Not |Default|default|System Product Name|System manufacturer|System Version|BAD INDEX")" UEFIInfo="$(dmidecode -t bios 2>/dev/null | grep -E "Vendor:|Version:|Release Date:|Revision:")" fi if [ "X${SystemInfo}" != "X" ]; then # Skip worthless SMBIOS/DMI emulation on RPi grep -i -q "raspberrypi" <<<"${SystemInfo}" || echo -e "\nDevice Info:\n${SystemInfo}" >>"${ResultLog}" fi if [ "X${UEFIInfo}" != "X" ]; then echo -e "\nBIOS/UEFI:\n${UEFIInfo}" >>"${ResultLog}" fi # On Raspberries we also collect 'firmware' information and on RPi 4 check SoC revision # against config.txt contents: if [ ${USE_VCGENCMD} = true ] ; then ThreadXVersion="$("${VCGENCMD}" version 2>/dev/null)" if [ "X${ThreadXVersion}" = "X" ]; then # dealing with a fake vcgencmd not implementing version reporting USE_VCGENCMD=false else # only report ThreadX stuff when 'vcgencmd version' outputs something if [ -f /boot/config.txt ]; then grep -q 'DO NOT EDIT THIS FILE' /boot/config.txt && ThreadXConfig=/boot/firmware/config.txt || ThreadXConfig=/boot/config.txt else ThreadXConfig=/boot/firmware/config.txt fi grep -q "arm_boost=1" ${ThreadXConfig} 2>/dev/null || (grep -q "C0 or later" <<<"${DeviceName}" && \ echo -e "\nWarning: this Raspberry Pi is powered by BCM2711 Rev. ${BCM2711} but arm_boost=1\nis not set in ${ThreadXConfig}. Some (mis)information about what you are missing:\nhttps://www.raspberrypi.com/news/bullseye-bonus-1-8ghz-raspberry-pi-4/" >>"${ResultLog}") echo -e "\nRaspberry Pi ThreadX version:\n${ThreadXVersion}" >>"${ResultLog}" [ -f ${ThreadXConfig} ] && echo -e "\nThreadX configuration (${ThreadXConfig}):\n$(grep -v '#' ${ThreadXConfig} | sed '/^\s*$/d')" >>"${ResultLog}" ActualThreadXsettings="$("${VCGENCMD}" get_config int)" echo -e "\nActual ThreadX settings:\n${ActualThreadXsettings}" >>"${ResultLog}" fi fi # Log gcc version if not in Geekbench mode if [ "X${MODE}" != "Xgb" ]; then GCC="$(command -v gcc)" GCC_Version="$(${GCC} --version | sed 's/gcc\ //' | head -n1)" echo -e "\n${GCC} ${GCC_Version}" >>"${ResultLog}" fi # Some basic system info needed to interpret system health later VoltageSensor="$(GetVoltageSensor)" if [ "X${VoltageSensor}" != "X" ]; then InputVoltage="$(GetInputVoltage "${VoltageSensor}")" echo -e "\nUptime:$(uptime), ${InitialTemp}°C, ${InputVoltage}V, ${QuickAndDirtyPerformance}\n\n$(iostat | grep -E -v "^loop|boot0|boot1|mtdblock")\n\n$(free -h)\n\n$(swapon -s)\n" | sed '/^$/N;/^\n$/D' >>"${ResultLog}" else echo -e "\nUptime:$(uptime), ${InitialTemp}°C, ${QuickAndDirtyPerformance}\n\n$(iostat | grep -E -v "^loop|boot0|boot1|mtdblock")\n\n$(free -h)\n\n$(swapon -s)\n" | sed '/^$/N;/^\n$/D' >>"${ResultLog}" fi ShowZswapStats 2>/dev/null >>"${ResultLog}" # set up Netio consumption monitoring if requested. Device address and socket # need to be available as Netio (environment) variable. if [ "X${Netio}" != "X" ]; then QueryNetioDevice echo -e "\nPower monitoring on socket ${NetioSocket} of ${NetioName} (Netio ${NetioModel}, FW v${Firmware}, XML API v${XmlVer}, ${InputVoltage}V @ ${Frequency}Hz) " >>"${ResultLog}" fi # log benchmark start in dmesg output echo "sbc-bench started" >/dev/kmsg # get status of swap devices to spot swapping activity ruining benchmark scores SwapBefore="$(awk -F" " '{print $4}' /dev/null | tr '\015' '\012')" OutputCurrents=($(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' ')) InputVoltage=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') Frequency=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') NetioModel=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') NetioName=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') Firmware=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') XmlVer=$(grep '^' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g') } # QueryNetioDevice GetVoltageSensor() { case "${DTCompatible,,}" in *nanopi-r6s*) # NanoPi R6S running with Rockchip's 5.10 BSP kernel? if [ -f /sys/devices/iio_sysfs_trigger/subsystem/devices/iio\:device0/in_voltage2_raw ]; then echo /sys/devices/iio_sysfs_trigger/subsystem/devices/iio\:device0/in_voltage2_raw fi ;; *rock-5b*|*rock-5-itx*) # Rock 5B running with Rockchip's 5.10 BSP kernel? if [ -f /sys/devices/iio_sysfs_trigger/subsystem/devices/iio\:device0/in_voltage6_raw ]; then echo /sys/devices/iio_sysfs_trigger/subsystem/devices/iio\:device0/in_voltage6_raw fi ;; *) # test for Allwinner A20 + PMU if [ -f /sys/power/axp_pmu/ac/voltage ]; then # Allwinner A20 powered via DC-IN running with mainline kernel + Armbian patches InputVoltage="$(GetInputVoltage /sys/power/axp_pmu/ac/voltage)" case ${InputVoltage} in 0|0.*) # board seems to be powered through either battery or USB, so report the latter echo /sys/power/axp_pmu/vbus/voltage ;; *) echo /sys/power/axp_pmu/ac/voltage ;; esac elif [ -f /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/voltage_now ]; then # Allwinner A20 powered via DC-IN running 3.4 kernel InputVoltage="$(GetInputVoltage /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/voltage_now)" case ${InputVoltage} in 0|0.*) # board seems to be powered through either battery or USB, so report the latter echo /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/voltage_now ;; *) echo /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/ac/voltage_now ;; esac elif [ -f /sys/power/axp_pmu/vbus/voltage ]; then # Allwinner A20 powered through USB running with mainline kernel + Armbian patches echo /sys/power/axp_pmu/vbus/voltage elif [ -f /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/voltage_now ]; then # Allwinner A20 powered through USB running 3.4 kernel echo /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/usb/voltage_now fi ;; esac } # GetVoltageSensor GetInputVoltage() { case ${1##*/} in in_voltage6_raw) # Rock 5B running with Rockchip's 5.10 BSP kernel awk '{printf ("%0.2f",$1/173.5); }' <"${1}" | sed 's/,/./' ;; in_voltage2_raw) case "${DTCompatible,,}" in *nanopi-r6s*) # NanoPi R6S running with Rockchip's 5.10 BSP kernel awk '{printf ("%0.2f",$1/206.2); }' <"${1}" | sed 's/,/./' ;; esac ;; voltage|voltage_now) # Allwinner A20 awk '{printf ("%0.2f",$1/1000000); }' <"${1}" | sed 's/,/./' ;; esac } # GetInputVoltage CheckClockspeedsAndSensors() { if [ -x "${InstallLocation}"/mhz/mhz ]; then echo -e "\n##########################################################################" >>"${ResultLog}" echo -e "\c" >"${TempDir}/cpufreq" if [ -s "${MonitorLog}" ]; then # 2nd check after most demanding benchmark has been run. echo -e "\x08\x08 Done.\nChecking cpufreq OPP again...\c" echo -e "\nTesting maximum cpufreq again, still under full load. System health now:\n" >>"${ResultLog}" grep 'Time' "${MonitorLog}" | tail -n 1 >>"${ResultLog}" grep ':' "${MonitorLog}" | tail -n 1 >>"${ResultLog}" OnlyCPUFreqMax=YES else echo -e "\x08\x08 Done.\nChecking cpufreq OPP...\c" # 1st check, try to get info about Intel P-States and HFI (Hardware Feedback Interface) if [ "${CPUArchitecture}" = "x86_64" ]; then PStateStatus="$(journalctl -b 2>/dev/null | awk -F": " '/intel_pstate:/ {print $3}' | sed ':a;N;$!ba;s/\n/, /g')" if [ "X${PStateStatus}" != "X" ]; then echo -e "\nIntel P-States: ${PStateStatus}" >>"${ResultLog}" fi HFIEnabled=$(awk -F":" '/^flags/ {print $2}' <<< "${ProcCPU}" | grep -c " hfi") if [ ${HFIEnabled} -gt 0 ]; then if [ -f /sys/devices/system/cpu/cpu0/acpi_cppc/nominal_perf ]; then DifferentCores=$(cat /sys/devices/system/cpu/cpu*/acpi_cppc/nominal_perf | sort | uniq | wc -l) else DifferentCores=1 fi if [ ${DifferentCores} -gt 1 ]; then echo -e "\nIntel Hardware Feedback Interface enabled (${DifferentCores} core types)" >>"${ResultLog}" else echo -e "\nIntel Hardware Feedback Interface enabled" >>"${ResultLog}" fi fi fi # if powercapping seems to be available on Intel then add a hint # https://www.cnx-software.com/2022/09/08/how-to-check-tdp-pl1-and-pl2-power-limits-in-windows-and-linux/ if [ -d /sys/devices/virtual/powercap/intel-rapl ]; then grep -q -i GenuineIntel <<< "${ProcCPU}" && \ echo -e "\nPowercap present. You might want to check with \"powercap-info -p intel-rapl\"" >>"${ResultLog}" fi # if running on a Ryzen CPU add a hint to RyzenAdj grep -q -i "AMD Ryzen" <<< "${ProcCPU}" && \ echo -e "\nAMD Ryzen detected. For power limits visit https://github.com/FlyGoat/RyzenAdj" >>"${ResultLog}" fi if [ ${#ClusterConfig[@]} -eq 1 ]; then # all CPU cores have same package id, we only need to test one core CPUInfo="$(GetCPUInfo 0)" echo -e "\nChecking cpufreq OPP${CPUInfo}:\n" >>"${ResultLog}" echo -e "cpu0${CPUInfo}: \c" >>"${TempDir}/cpufreq" CheckCPUCluster 0 >>"${ResultLog}" else # different package ids, we walk through all clusters for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do FirstCore=${ClusterConfig[$i]} LastCore=$(GetLastClusterCore $(( $i + 1 ))) CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" CoresOnline ${FirstCore} ${LastCore} case $? in 0) if [ ${FirstCore} -eq ${LastCore} ]; then echo -e "\nChecking cpufreq OPP for cpu${FirstCore}${CPUInfo}:\n" >>"${ResultLog}" echo -e "cpu${FirstCore}${CPUInfo}: \c" >>"${TempDir}/cpufreq" else echo -e "\nChecking cpufreq OPP for cpu${FirstCore}-cpu${LastCore}${CPUInfo}:\n" >>"${ResultLog}" echo -e "cpu${FirstCore}-cpu${LastCore}${CPUInfo}: \c" >>"${TempDir}/cpufreq" fi CheckCPUCluster ${FirstCore} >>"${ResultLog}" ;; *) if [ ${FirstCore} -eq ${LastCore} ]; then echo -e "\nSkipping cpu${FirstCore}${CPUInfo} since cores are offline: ${OfflineCores}" >>"${ResultLog}" else echo -e "\nSkipping cpu${FirstCore}-cpu${LastCore}${CPUInfo} since cores are offline: ${OfflineCores}" >>"${ResultLog}" fi ;; esac done fi if [ "X${MODE}" = "Xextensive" ] && [ ! -s "${MonitorLog}" ]; then # in this mode we also check all CPU cores in parallel, 1st in idle echo -e "\nMeasuring highest clockspeeds for all cores in parallel (idle):\n" >>"${ResultLog}" CheckAllCores idle >>"${ResultLog}" # measure maximum clockspeeds under full load again if [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then echo -e "\nMeasuring highest clockspeeds for all cores in parallel (full load):\n" >>"${ResultLog}" timeout 15 "${InstallLocation}"/cpuminer-multi/cpuminer --benchmark --cpu-priority=2 >/dev/null & snore 10 CheckAllCores full-load >>"${ResultLog}" fi fi else echo -e "\x08\x08\x08 not possible since ${InstallLocation}/mhz/mhz not executable...\c" fi # if lm-sensors is present and reports anything add this to results.log LMSensorsOutput="$(sensors -A 2>/dev/null | grep -v " +0.0 C" | sed -e 's/rpi_volt-isa-0000//' -e 's/in0: N\/A //')" if [ "X${LMSensorsOutput}" != "X" ]; then echo -e "\n##########################################################################\n" >>"${ResultLog}" echo -e "Hardware sensors:\n\n${LMSensorsOutput}" >>"${ResultLog}" # if temperature sensors can be read from disks, report them SmartCtl="$(command -v smartctl 2>/dev/null)" Disks="$(ls /dev/sd? /dev/nvme? 2>/dev/null | sort)" if [ "X${SmartCtl}" != "X" ] && [ "X${Disks}" != "X" ]; then echo "" >>"${ResultLog}" # try to restrict SMART queries to 15 sec duration due to buggy devices command -v timeout >/dev/null 2>&1 && SmartCtl="timeout 15 ${SmartCtl}" for Disk in ${Disks} ; do case ${Disk} in /dev/sd*) DiskTemp="$(${SmartCtl} -a ${Disk} | awk -F" " '/Temperature/ {print $10" "$2}' | head -n1 | sed -e 's/_/ /g' -e 's/^ *//g')" [ "X${DiskTemp}" != "X" ] && echo -e "${Disk}:\t${DiskTemp}" ;; /dev/nvme*) echo -e "${Disk}:\t$(${SmartCtl} -a ${Disk} | awk -F" " '/Temperature:/ {print $2" "$3}' | head -n1)" ;; esac done | sed -e 's/ Airflow//' -e 's/ Temperature//' -e 's/ Celsius$/°C/' -e 's/ Cel$/°C/' >>"${ResultLog}" fi fi } # CheckClockspeedsAndSensors CoresOnline() { # check whether CPU hotplug is supported on cores != 0 (cpu0 can't be offline) # $1 -> first core to check # $2 -> last core to check, if missing only $1 will be checked local i if [ ${1} -ne 0 ] && [ -f /sys/devices/system/cpu/cpu${1}/online ]; then for i in $(seq ${1} ${2:-$1}) ; do read IsOnline "${StatFile%/*}/reset" ;; esac Number=$(tr -c -d '[:digit:]' <<<${StatFile}) read MaxSpeed >${TempDir}/time_in_state_${1}_${Number} echo ${REPLY} >>${TempDir}/full_time_in_state_${1}_${Number} elif [ ${Cpufreq} -le ${MaxSpeed} ]; then echo ${REPLY} >>${TempDir}/full_time_in_state_${1}_${Number} fi done done fi } # CheckTimeInState CheckCPUCluster() { # check whether there's cpufreq support or not if [ -d /sys/devices/system/cpu/cpufreq/policy${1} ]; then # walk through all cpufreq OPP and report clockspeeds (kernel vs. measured) read MinSpeed /sys/devices/system/cpu/cpufreq/policy${1}/scaling_min_freq 2>/dev/null if [ "X${OnlyCPUFreqMax}" = "XYES" ]; then OPPtoCheck="${MaxSpeed}" elif [ -f /sys/devices/system/cpu/cpufreq/policy${1}/scaling_available_frequencies ]; then OPPtoCheck=$((tr " " "\n" /dev/null | sort -n -r | uniq | sed '/^[[:space:]]*$/d') elif [ ${MinSpeed} -eq 0 ]; then OPPtoCheck="${MaxSpeed}" else OPPtoCheck="${MaxSpeed} ${MinSpeed}" fi for i in ${OPPtoCheck} ; do # if MaxKHz environment variable is set then skip any higher cpufreq OPP, # works similar for MinKHz if [ ${i} -le ${MaxKHz:-90000000} ] && [ ${i} -ge ${MinKHz:-0} ]; then echo ${i} >/sys/devices/system/cpu/cpufreq/policy${1}/scaling_max_freq # instead of 'snore 0.1' fire up some real workload in a try to compensate for SoC # firmwares that might do their own thing wrt clockspeeds (keep them low when idle) taskset -c $1 "${InstallLocation}"/mhz/mhz 1 1000000 >/dev/null MeasuredSpeed=$(taskset -c $1 "${InstallLocation}"/mhz/mhz 3 1000000 | awk -F" cpu_MHz=" '{print $2}' | awk -F" " '{print $1}' | sort -r -n | tr '\n' '/' | sed 's|/$||') SpeedSum=$(tr '/' '\n' <<<"${MeasuredSpeed}" | tr -d '.' | awk '{s+=$1} END {printf "%.0f", s}') RoundedSpeed=$(( ${SpeedSum} / 3000 )) SysfsSpeed=$(( $i / 1000 )) # report differences in cpufreq OPP ('advertised' clockspeed) and measured # clockspeed if difference exceeds 1% MeasuredDiff=$(awk '{printf ("%0.3f",$1/$2); }' <<<"${RoundedSpeed}000 ${SysfsSpeed}000" | tr -d '.') if [ ${MeasuredDiff} -lt 990 ]; then # measured clockspeed lower than 1% than cpufreq OPP DiffPercentage=$(awk '{printf ("%0.0f",$1-$2); }' <<<"1000 ${MeasuredDiff}" | awk '{printf ("%0.1f",$1/10); }') PrettyDiff="$(printf "%10s" \(-${DiffPercentage})%)" CpufreqPrefix="${LRED}" elif [ ${MeasuredDiff} -gt 1010 ]; then # measured clockspeed higher than 1% than cpufreq OPP DiffPercentage=$(awk '{printf ("%0.0f",$1-$2); }' <<<"${MeasuredDiff} 1000" | awk '{printf ("%0.1f",$1/10); }') PrettyDiff="$(printf "%10s" \(+${DiffPercentage})%)" CpufreqPrefix="${LGREEN}" else PrettyDiff="" CpufreqPrefix="" fi # Check whether Linpack reliability measurement should be done as well (aarch64 only) [ -x "${Linpack}/xhpl64" ] && [ -r "${Linpack}/HPL.dat" ] && [ "$(uname -m)" = "aarch64" ] && LinpackResult=" ($(MeasureWithLinpack))" || LinpackResult="" if [ ${USE_VCGENCMD} = true ] ; then # On RPi we query ThreadX about clockspeeds and Vcore voltage too, on RPi 5 # we even use the ADC measurements. Also we create an OPP table on the fly # derived from ThreadX config via 'measure_volts' and 'measure_clock arm': ThreadXFreq=$("${VCGENCMD}" measure_clock arm 2>/dev/null | awk -F"=" '{printf ("%0.0f",$2/1000000); }' ) CoreVoltage=$("${VCGENCMD}" measure_volts uncached 2>/dev/null | cut -f2 -d=) [ "X${CoreVoltage}" = "X" ] && CoreVoltage=$("${VCGENCMD}" measure_volts 2>/dev/null | cut -f2 -d=) [ "X${OnlyCPUFreqMax}" = "XYES" ] || printf "%10s MHz %8s mV\n" ${ThreadXFreq} $(awk '{printf ("%0.1f",$1*1000); }' <<<${CoreVoltage}) >>"${TempDir}/opp-table-threadx-${1}" ADC_Readouts="$("${VCGENCMD}" pmic_read_adc 2>/dev/null)" case $? in 0) MeasuredCoreVoltage="$(awk -F"=" '/VDD_CORE_V/ {printf ("%0.4f",$2); }' <<<"${ADC_Readouts}")V" echo -e "Cpufreq OPP: $(printf "%4s" ${SysfsSpeed}) ThreadX: $(printf "%4s" ${ThreadXFreq}) Measured: $(printf "%4s" ${RoundedSpeed}) @ ${CoreVoltage}/${MeasuredCoreVoltage}${PrettyDiff}${LinpackResult}" ;; *) echo -e "Cpufreq OPP: $(printf "%4s" ${SysfsSpeed}) ThreadX: $(printf "%4s" ${ThreadXFreq}) Measured: $(printf "%4s" ${RoundedSpeed}) @ ${CoreVoltage}${PrettyDiff}${LinpackResult}" ;; esac [ $i -eq ${MaxSpeed} ] && echo -e "OPP: $(printf "%4s" ${SysfsSpeed}), ThreadX: $(printf "%4s" ${ThreadXFreq}), Measured: $(printf "%4s" ${RoundedSpeed}) ${CpufreqPrefix}${PrettyDiff}${NC}">>"${TempDir}/cpufreq" else echo -e "Cpufreq OPP: $(printf "%4s" ${SysfsSpeed}) Measured: $(printf "%4s" ${RoundedSpeed}) $(printf "%27s" \(${MeasuredSpeed}))${PrettyDiff}${LinpackResult}" [ $i -eq ${MaxSpeed} ] && echo -e "OPP: $(printf "%4s" ${SysfsSpeed}), Measured: $(printf "%4s" ${RoundedSpeed}) ${CpufreqPrefix}${PrettyDiff}${NC}">>"${TempDir}/cpufreq" fi fi done if [ "X${MaxKHz}" = "X" ]; then echo ${MaxSpeed} >/sys/devices/system/cpu/cpufreq/policy${1}/scaling_max_freq else echo ${MaxKHz} >/sys/devices/system/cpu/cpufreq/policy${1}/scaling_max_freq fi else # no cpufreq support: measure speeds on cpu0 on single core machines, otherwise on # next cpu core to not interfere with probable bad IRQ/SMP affinitiy settings. case ${CPUCores} in 1) CpuToCheck=0 ;; *) CpuToCheck=$(( $1 + 1 )) ;; esac taskset -c ${CpuToCheck} "${InstallLocation}"/mhz/mhz 1 1000000 >/dev/null MeasuredSpeed=$(taskset -c ${CpuToCheck} "${InstallLocation}"/mhz/mhz 3 1000000 | awk -F" cpu_MHz=" '{print $2}' | awk -F" " '{print $1}' | sort -r -n | tr '\n' '/' | sed 's|/$||') SpeedSum=$(tr '/' '\n' <<<"${MeasuredSpeed}" | tr -d '.' | awk '{s+=$1} END {printf "%.0f", s}') RoundedSpeed=$(( ${SpeedSum} / 3000 )) echo -e "No cpufreq support available. Measured on cpu${CpuToCheck}: ${RoundedSpeed} MHz (${MeasuredSpeed})" echo -e "Measured: $(printf "%4s" ${RoundedSpeed})">>"${TempDir}/cpufreq" fi } # CheckCPUCluster MeasureWithLinpack() { # function that chdirs into ${Linpack}, then runs xhpl64 and provides the resulting # Gflops number when passing or FAILED otherwise. # https://github.com/raspberrypi/firmware/issues/1876#issuecomment-2002634934 cd "${Linpack}" || return 1 LinpackResult="$("./xhpl64" 2>&1)" grep -q "PASSED" <<<"${LinpackResult}" case $? in 0) awk -F' ' '/^WR02R2L2/ {print $7}' <<<"${LinpackResult}" ;; *) echo "FAILED" ;; esac cd "${OLDPWD}" || return 1 } # MeasureWithLinpack CheckAllCores() { for i in $(seq 0 $(( ${CPUCores} -1 )) ) ; do taskset -c ${i} "${InstallLocation}"/mhz/mhz 3 1000000 >"${TempDir}/CheckAllCores-${1}.${i}" & done if [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq ]; then CpuFreqToQuery=cpuinfo_cur_freq elif [ -r /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ]; then CpuFreqToQuery=scaling_cur_freq fi # if in full-load mode wait 8 seconds [ "X${1}" = "Xfull-load" ] && snore 8 for i in $(seq 0 $(( ${CPUCores} -1 )) ) ; do [ -f /sys/devices/system/cpu/cpu${i}/cpufreq/${CpuFreqToQuery} ] && \ cat /sys/devices/system/cpu/cpu${i}/cpufreq/${CpuFreqToQuery} >"${TempDir}/Reported-${1}.${i}" done for job in $(jobs -p) ; do wait ${job} done for i in $(seq 0 $(( ${CPUCores} -1 )) ) ; do MeasuredSpeed=$(awk -F" cpu_MHz=" '{print $2}' <"${TempDir}/CheckAllCores-${1}.${i}" | awk -F" " '{print $1}' | sort -r -n | tr '\n' '/' | sed 's|/$||') SpeedSum=$(tr '/' '\n' <<<"${MeasuredSpeed}" | tr -d '.' | awk '{s+=$1} END {printf "%.0f", s}') RoundedSpeed=$(( ${SpeedSum} / 3000 )) if [ -s "${TempDir}/Reported-${1}.${i}" ]; then read ReportedSpeed <"${TempDir}/Reported-${1}.${i}" echo -e "cpu${i}: ${RoundedSpeed}/$(( ${ReportedSpeed} / 1000 )) MHz (${MeasuredSpeed})" else echo -e "cpu${i}: ${RoundedSpeed} MHz (${MeasuredSpeed})" fi done } # CheckAllCores RunTinyMemBench() { if [ "X${MODE}" = "Xextensive" ] || [ "X${REVIEWMODE}" = "Xtrue" ] || [ "X${ExecuteStockfish}" = "Xyes" ]; then # extensive/review mode or yet unknown stockfish run time, do not print any duration estimates echo -e "\x08\x08 Done." else echo -e "\x08\x08 Done (results will be available in $(( ${EstimatedDuration} * 120 / 100 ))-$(( ${EstimatedDuration} * 180 / 100 )) minutes)." fi # if we're not running with MODE=extensive shorten tinymembench execution drastically. # TODO: Adjust time estimates [ "X${MODE}" = "Xextensive" ] || TMBOptions="-b 2 -B 3 -l 3 -c 1000000" echo -e "Executing tinymembench...\c" echo -e "System health while running tinymembench:\n" >"${MonitorLog}" if [ "X${MODE}" = "Xextensive" ]; then /bin/bash "${PathToMe}" -m $(( 40 * ${#ClusterConfig[@]} )) >>"${MonitorLog}" & else /bin/bash "${PathToMe}" -m $(( 10 * ${#ClusterConfig[@]} )) >>"${MonitorLog}" & fi MonitoringPID=$! echo -n "" >"${TempLog}" for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CoresOnline ${ClusterConfig[$i]} if [ $? -eq 0 ]; then CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e "\nExecuting benchmark on cpu${ClusterConfig[$i]}${CPUInfo}:\n" >>"${TempLog}" [ -s "${NetioConsumptionFile}" ] && snore 10 taskset -c ${ClusterConfig[$i]} "${InstallLocation}"/tinymembench/tinymembench ${TMBOptions} >>"${TempLog}" 2>&1 fi done kill ${MonitoringPID} echo -e "\n##########################################################################" >>"${ResultLog}" cat "${TempLog}" >>"${ResultLog}" MemCpyScore="$(awk -F" " '/^ libc memcpy copy/ {print $5}' <"${TempLog}" | sort -n | tail -n1)" MemSetScore="$(awk -F" " '/^ libc memset fill/ {print $5}' <"${TempLog}" | sort -n | tail -n1)" # round results MemBenchScore="$(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${MemCpyScore}" ) * 10 )) | $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${MemSetScore}" ) * 10 ))" } # RunTinyMemBench RunRamlat() { if [ -x "${InstallLocation}"/ramspeed/ramlat ]; then echo -e "\x08\x08 Done.\nExecuting RAM latency tester...\c" echo -e "\nSystem health while running ramlat:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m $(( ${#ClusterConfig[@]} * 3 )) >>"${MonitorLog}" & MonitoringPID=$! echo -n "" >"${TempLog}" for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CoresOnline ${ClusterConfig[$i]} if [ $? -eq 0 ]; then CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e "\nExecuting ramlat on cpu${ClusterConfig[$i]}${CPUInfo}, results in ns:\n" >>"${TempLog}" taskset -c ${ClusterConfig[$i]} "${InstallLocation}"/ramspeed/ramlat -n -w "12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27" 200 \ | sed -e 's/^/ /' >>"${TempLog}" 2>&1 fi done kill ${MonitoringPID} echo -e "\n##########################################################################" >>"${ResultLog}" cat "${TempLog}" >>"${ResultLog}" fi } # RunRamlat Run7ZipBenchmark() { echo -e "\x08\x08 Done.\nExecuting 7-zip benchmark...\c" echo -e "\nSystem health while running 7-zip single core benchmark:\n" >>"${MonitorLog}" echo -e "\c" >"${TempLog}" # determine available RAM and adjust monitoring interval + dictionary size if necessary TotalMem=$(free | awk -F" " '/^Mem: / {print $7}' | tail -n1) [ ${TotalMem:-350000} -lt 350000 ] && DictSize="-md=2m" MemAdjustment=$(( $(( 1250000 / ${TotalMem} )) / 3 )) if [ ${MemAdjustment} = 0 ]; then MonInterval=$(( ${SingleThreadedDuration} / 8 )) else MonInterval=$(( ${SingleThreadedDuration} / $(( 8 * ${MemAdjustment} )) )) fi [ ${MonInterval:-0} -lt 5 ] && MonInterval=5 [ ${MonInterval:-0} -gt 15 ] && MonInterval=15 /bin/bash "${PathToMe}" -m ${MonInterval} >>"${MonitorLog}" & MonitoringPID=$! if [ ${#ClusterConfig[@]} -eq 1 ]; then # Only cpu0 or single CPU cluster CPUInfo="$(GetCPUInfo 0)" echo -e "\nExecuting benchmark single-threaded on cpu0${CPUInfo}" >>"${TempLog}" [ -s "${NetioConsumptionFile}" ] && snore 10 taskset -c 0 "${SevenZip}" b ${DictSize} -mmt=1 >>"${TempLog}" 2>/dev/null else # test each cluster individually for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CoresOnline ${ClusterConfig[$i]} if [ $? -eq 0 ]; then CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e "\nExecuting benchmark single-threaded on cpu${ClusterConfig[$i]}${CPUInfo}" >>"${TempLog}" [ -s "${NetioConsumptionFile}" ] && snore 10 taskset -c ${ClusterConfig[$i]} "${SevenZip}" b ${DictSize} -mmt=1 >>"${TempLog}" 2>/dev/null fi done fi kill ${MonitoringPID} echo -e "\n##########################################################################" >>"${ResultLog}" cat "${TempLog}" >>"${ResultLog}" ZipScoreSingleThreaded=$(awk -F" " '/^Tot:/ {print $4}' "${TempLog}" | sort -n | tail -n1) if [ ${CPUCores} -gt 1 ]; then # run multi-threaded test only if there's more than one CPU core echo -e "\nSystem health while running 7-zip multi core benchmark:\n" >>"${MonitorLog}" echo -e "\c" >"${TempLog}" if [ ${MemAdjustment} = 0 ]; then MonInterval=$(( ${MultiThreadedDuration} / 3 )) else MonInterval=$(( ${MultiThreadedDuration} / $(( 3 * ${MemAdjustment} )) )) fi [ ${MonInterval:-0} -lt 10 ] && MonInterval=10 [ ${MonInterval:-0} -gt 30 ] && MonInterval=30 /bin/bash "${PathToMe}" -m ${MonInterval} >>"${MonitorLog}" & MonitoringPID=$! echo -e "Executing benchmark ${RunHowManyTimes} times multi-threaded on CPUs $(cat /sys/devices/system/cpu/online)" >>"${TempLog}" for ((i=1;i<=RunHowManyTimes;i++)); do "${SevenZip}" b ${DictSize} -mmt=${CPUCores} >>"${TempLog}" 2>/dev/null done kill ${MonitoringPID} echo -e "\n##########################################################################\n" >>"${ResultLog}" cat "${TempLog}" >>"${ResultLog}" # create average score from all finished benchmark runs (7-zip can get oom-killed) FinishedRuns=$(grep -c '^Tot:' "${TempLog}") [ ${FinishedRuns} -ne 0 ] && CombinedScore=$(( $(awk -F" " '/^Tot:/ {s+=$4} END {printf "%.0f", s}' <"${TempLog}") / ${FinishedRuns} )) else # use the single score instead on a single core machine CombinedScore=$(awk -F" " '/^Tot:/ {print $4}' <"${TempLog}") fi sed -i 's/|//' "${TempLog}" CompScore=$(awk -F" " '/^Avr:/ {print $4}' <"${TempLog}" | tr '\n' ', ' | sed 's/,$//') DecompScore=$(awk -F" " '/^Avr:/ {print $7}' <"${TempLog}" | tr '\n' ', ' | sed 's/,$//') TotScore=$(awk -F" " '/^Tot:/ {print $4}' <"${TempLog}" | tr '\n' ', ' | sed 's/,$//') echo -e "\nCompression: ${CompScore}" >>"${ResultLog}" echo -e "Decompression: ${DecompScore}" >>"${ResultLog}" echo -e "Total: ${TotScore}" >>"${ResultLog}" # round result ZipScore="$(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${CombinedScore}" ) * 10 ))" if [ ${#ClusterConfigByCoreType[@]} -ne 1 ] && [ "X${MODE}" = "Xextensive" ]; then # extensive mode: we're testing additionally through each CPU core cluster echo -e "\nSystem health while running 7-zip cluster benchmarks:\n" >>"${MonitorLog}" echo -e "\c" >"${TempLog}" /bin/bash "${PathToMe}" -m $(( ${MonInterval} * ${#ClusterConfigByCoreType[@]} )) >>"${MonitorLog}" & MonitoringPID=$! for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do CoresOnline ${ClusterConfigByCoreType[$i]} if [ $? -eq 0 ]; then FirstCore=${ClusterConfigByCoreType[$i]} CPUInfo="$(GetCPUInfo ${FirstCore})" LastCore=$(GetLastClusterCoreByType $(( $i + 1 ))) HowManyCores=$(( $(( ${LastCore} - ${FirstCore} )) + 1 )) if [ ${FirstCore} -eq ${LastCore} ]; then echo -e "\nExecuting benchmark ${RunHowManyTimes} times single-threaded on CPU ${FirstCore}${CPUInfo}" >>"${TempLog}" else echo -e "\nExecuting benchmark ${RunHowManyTimes} times multi-threaded on CPUs ${FirstCore}-${LastCore}${CPUInfo}" >>"${TempLog}" fi for ((o=1;o<=RunHowManyTimes;o++)); do taskset -c ${FirstCore}-${LastCore} "${SevenZip}" b ${DictSize} -mmt=${HowManyCores} >>"${TempLog}" 2>/dev/null done fi done kill ${MonitoringPID} echo -e "\n##########################################################################\n" >>"${ResultLog}" cat "${TempLog}" >>"${ResultLog}" fi } # Run7ZipBenchmark RunOpenSSLBenchmark() { echo -e "\x08\x08 Done.\nExecuting OpenSSL benchmark...\c" echo -e "\nSystem health while running OpenSSL benchmark:\n" >>"${MonitorLog}" echo -e "\n##########################################################################\n" >>"${ResultLog}" /bin/bash "${PathToMe}" -m 16 >>"${MonitorLog}" & MonitoringPID=$! OpenSSLLog="${TempDir}/openssl.log" if [ ${#ClusterConfig[@]} -eq 1 ]; then # all CPU cores have same package id, we execute openssl twice CPUInfo="$(GetCPUInfo 0)" echo -e "Executing benchmark twice on cluster 0${CPUInfo}\n" >>"${ResultLog}" for bytelength in $1 ; do openssl speed -elapsed -evp aes-${bytelength}-cbc 2>/dev/null openssl speed -elapsed -evp aes-${bytelength}-cbc 2>/dev/null done | tr '[:upper:]' '[:lower:]' >"${OpenSSLLog}" # add both scores and divide by two to get an average AES256=$(( $(awk '/^aes-256-cbc/ {print $7}' <"${OpenSSLLog}" | awk -F"." '{s+=$1} END {printf "%.0f", s}') / 2 )) [ "X${MODE}" = "Xextensive" ] && openssl speed -elapsed -evp aes-256-gcm 2>/dev/null | tr '[:upper:]' '[:lower:]' >>"${OpenSSLLog}" else # different package ids, we walk through all clusters echo -e "Executing benchmark on each cluster individually\n" >>"${ResultLog}" for bytelength in $1 ; do for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CoresOnline ${ClusterConfig[$i]} if [ $? -eq 0 ]; then CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" taskset -c ${ClusterConfig[$i]} openssl speed -elapsed -evp aes-${bytelength}-cbc 2>/dev/null \ | tr '[:upper:]' '[:lower:]' | sed "/^aes/ s/$/${CPUInfo}/" if [ "X${MODE}" = "Xextensive" ] && [ ${bytelength} -eq 256 ]; then openssl speed -elapsed -evp aes-256-gcm 2>/dev/null | tr '[:upper:]' '[:lower:]' | sed "/^aes/ s/$/${CPUInfo}/" fi fi done done >"${OpenSSLLog}" # check aes-256-cbc scores and choose highest for reporting AES256=$(awk '/^aes-256-cbc/ {print $7}' <"${OpenSSLLog}" | awk -F"." '{print $1}' | sort -n | tail -n1) fi kill ${MonitoringPID} echo -e "$(openssl version | awk -F" " '{print $1" "$2", built on "$3" "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13" "$14" "$15}' | sed 's/ *$//')\n$(grep '^type' "${OpenSSLLog}" | head -n1)" >>"${ResultLog}" grep '^aes-' ${OpenSSLLog} >>"${ResultLog}" # round result OpenSSLScore="$(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${AES256}") * 10 ))" if [ "X${MODE}" = "Xextensive" ]; then # in extensive mode run openssl benchmarks on all cores in parallel echo -e "\nSystem health while running parallel OpenSSL benchmarks:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 4 >>"${MonitorLog}" & MonitoringPID=$! for ((o=1;o<=CPUCores;o++)); do taskset -c $(( ${o} - 1 )) openssl speed -elapsed -evp aes-256-cbc 2>/dev/null >"${TempDir}"/openssl.log.cbc.${o} & done snore 25 for ((o=1;o<=CPUCores;o++)); do taskset -c $(( ${o} - 1 )) openssl speed -elapsed -evp aes-256-gcm 2>/dev/null >"${TempDir}"/openssl.log.gcm.${o} & done snore 25 grep '^aes-256-cbc' "${TempDir}"/openssl.log.* | cut -f2 -d':' | sort -r -n | sed "/^aes/ s/$/ (fully parallel)/" >>"${ResultLog}" grep '^aes-256-gcm' "${TempDir}"/openssl.log.* | cut -f2 -d':' | sort -r -n | sed "/^aes/ s/$/ (fully parallel)/" >>"${ResultLog}" kill ${MonitoringPID} fi } # RunOpenSSLBenchmark RunCpuminerBenchmark() { if [ "X${REVIEWMODE}" = "Xtrue" ]; then echo -e "\x08\x08 Done.\nThrottling test: heating up the device, 5 more minutes to wait...\c" else echo -e "\x08\x08 Done.\nExecuting cpuminer. 5 more minutes to wait...\c" fi echo -e "\nSystem health while running cpuminer:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 40 >>"${MonitorLog}" & MonitoringPID=$! "${InstallLocation}"/cpuminer-multi/cpuminer --benchmark --cpu-priority=2 >"${TempLog}" & MinerPID=$! snore 300 kill ${MinerPID} ${MonitoringPID} echo -e "\n##########################################################################\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" >>"${ResultLog}" # Summarized 'Total:' scores, we need to skip the 1st or even better the two first since # sometimes significantly lower which could then be misinterpreted as silent throttling MinerRuns=$(grep -c " Total:" "${TempLog}") if [ ${MinerRuns:-0} -ge 10 ]; then # skip two first scores when more than 10 cpuminer scores could be generated TotalScores="$(awk -F" " '/Total:/ {print $4}' "${TempLog}" | tail -n +3 | sort -r -n | uniq | tr '\n' ', ' | sed 's/,$//')" else # skip only 1st score TotalScores="$(awk -F" " '/Total:/ {print $4}' "${TempLog}" | tail -n +2 | sort -r -n | uniq | tr '\n' ', ' | sed 's/,$//')" fi echo -e "\nTotal Scores: ${TotalScores}" >>"${ResultLog}" CpuminerScore="$(awk -F"," '{print $2}' <<<"${TotalScores}")" } # RunCpuminerBenchmark RunStressNG() { case ${OperatingSystem,,} in "arch linux"*|*manjaro*) StressCommand="stress --cpu ${CPUCores} --io ${CPUCores} --vm ${CPUCores} --vm-bytes 128M" ;; *) StressCommand="stress-ng --matrix 0" ;; esac if [ "X${REVIEWMODE}" = "Xtrue" ]; then echo -e "\x08\x08 Done.\nThrottling test: heating up the device, 5 more minutes to wait...\c" else echo -e "\x08\x08 Done.\nExecuting ${StressCommand%% *}. 5 more minutes to wait...\c" fi echo -e "\nSystem health while running ${StressCommand%% *}:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 40 >>"${MonitorLog}" & MonitoringPID=$! echo -e "Executing \"${StressCommand}\" for 5 minutes:\n" >"${TempLog}" ${StressCommand} -t 300 >>"${TempLog}" 2>&1 kill ${MonitoringPID} echo -e "\n##########################################################################\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" >>"${ResultLog}" } # RunStressNG RunStockfishBenchmark() { echo -e "\x08\x08 Done.\nExecuting stockfish benchmark...\c" echo -e "\nSystem health while running stockfish:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 60 >>"${MonitorLog}" & MonitoringPID=$! cd "${InstallLocation}/Stockfish-sf_15/src" || exit 1 echo "" >"${TempLog}" for ((i=1;i<=RunHowManyTimes;i++)); do echo -e "setoption name EvalFile value ${NeuralNetwork} \n bench 128 ${CPUCores} 24 default depth" | ./stockfish 2>>"${TempLog}" >/dev/null done kill ${MonitoringPID} echo -e "\n##########################################################################\n" >>"${ResultLog}" echo -e "Executing Stockfish 15 using ${NeuralNetwork} ${RunHowManyTimes} times in a row:\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" | sed -e '/^$/d' -e '/^============/i \ ' -e '/second/a \ ' >>"${ResultLog}" StockfishScores="$(awk -F": " '/^Nodes\/second/ {print $2}' <"${TempLog}" | tr '\n' ', ' | sed 's/,$//')" echo -e "Total Scores (${NeuralNetwork}): ${StockfishScores}" >>"${ResultLog}" } # RunStockfishBenchmark RunPTS() { # executing phoronix-test-suite echo -e "\x08\x08 Done.\nExecuting phoronix-test-suite...\c" # if the CPU contains clusters of different CPU cores, test them individually first if [ ${#ClusterConfigByCoreType[@]} -ne 1 ]; then echo -e "\nSystem health while running Phoronix Test Suite cluster benchmarks:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m $(( 60 * ${#ClusterConfigByCoreType[@]} )) >>"${MonitorLog}" & MonitoringPID=$! for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do CoresOnline ${ClusterConfigByCoreType[$i]} if [ $? -eq 0 ]; then FirstCore=${ClusterConfigByCoreType[$i]} CPUInfo="$(GetCPUInfo ${FirstCore})" LastCore=$(GetLastClusterCoreByType $(( $i + 1 ))) HowManyCores=$(( $(( ${LastCore} - ${FirstCore} )) + 1 )) # try to send as much cores as possible offline (cpu0 can't be sent offline) for o in $(seq ${FirstOfflineCPU} $(( ${CPUCores} - 1 )) ); do if [ $o -lt ${FirstCore} ] || [ $o -gt ${LastCore} ]; then echo 0 > /sys/devices/system/cpu/cpu${o}/online 2>/dev/null fi done PTS_SILENT_MODE=1 phoronix-test-suite batch-run ${PTSArguments} >"${TempLog}" 2>&1 ResultsURL="$(awk -F"https" '/Results Uploaded To/ {print $2}' <"${TempLog}")" if [ "X${ResultsURL}" = "X" ]; then echo -e "\x08\x08 Failed.\n$(cat "${TempLog}")\n" grep -q 'is not installed' "${TempLog}" && \ echo -e "\nTry to manually resolve problems by \"phoronix-test-suite install ${PTSArguments}\"" exit 1 else echo -e "\n##########################################################################\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" >>"${ResultLog}" fi # bring back offline cores for o in $(seq ${FirstOfflineCPU} $(( ${CPUCores} - 1 )) ); do echo 1 >/sys/devices/system/cpu/cpu${o}/online [ -f /sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor ] && \ echo performance >/sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor 2>/dev/null snore 0.5 [ -f /sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor ] && \ echo performance >/sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor 2>/dev/null done fi done kill ${MonitoringPID} fi echo -e "\nSystem health while running Phoronix Test Suite:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m 60 >>"${MonitorLog}" & MonitoringPID=$! PTS_SILENT_MODE=1 phoronix-test-suite batch-run ${PTSArguments} >"${TempLog}" kill ${MonitoringPID} ResultsURL="$(awk -F"https" '/Results Uploaded To/ {print $2}' <"${TempLog}")" if [ "X${ResultsURL}" = "X" ]; then echo -e "\x08\x08 Failed.\n$(cat "${TempLog}")\n" grep -q 'is not installed' "${TempLog}" && \ echo -e "\nTry to manually resolve problems by \"phoronix-test-suite install ${PTSArguments}\"" exit 1 else echo -e "\n##########################################################################\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" >>"${ResultLog}" fi } # RunPTS BoostGB() { snore 1 && for i in $(pgrep geekbench) ; do renice -20 "${i}" >/dev/null 2>&1 ; done } # BoostGB RunGB() { # executing geekbench5 in a sane and monitored environment echo -e "\x08\x08 Done.\nExecuting Geekbench...\n" MonitorInterval=$(( ${QuickAndDirtyPerformance} / 10000000 )) # if the CPU contains clusters of different CPU cores, test them individually first if [ ${#ClusterConfigByCoreType[@]} -ne 1 ]; then echo -e "\nSystem health while running Geekbench cluster benchmarks:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m $(( ${MonitorInterval} * ${#ClusterConfigByCoreType[@]} )) >>"${MonitorLog}" & MonitoringPID=$! for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do CoresOnline ${ClusterConfigByCoreType[$i]} if [ $? -eq 0 ]; then FirstCore=${ClusterConfigByCoreType[$i]} CPUInfo="$(GetCPUInfo ${FirstCore})" LastCore=$(GetLastClusterCoreByType $(( $i + 1 ))) HowManyCores=$(( $(( ${LastCore} - ${FirstCore} )) + 1 )) # try to send as much cores as possible offline (cpu0 can't be sent offline) for o in $(seq ${FirstOfflineCPU} $(( ${CPUCores} - 1 )) ); do if [ $o -lt ${FirstCore} ] || [ $o -gt ${LastCore} ]; then echo 0 > /sys/devices/system/cpu/cpu${o}/online 2>/dev/null fi done echo -e "\n${BOLD}Executing on cores ${FirstCore}-${LastCore}${NC}\n" BoostGB & taskset -c ${FirstCore}-${LastCore} "${GBBinary}" 2>&1 | tee "${TempLog}" ResultsURL="$(awk -F"https" '/browser.geekbench.com/ {print $2}' <"${TempLog}" | head -n1)" if [ "X${ResultsURL}" = "X" ]; then echo -e "\x08\x08 Failed...\c" else echo -e "\n##########################################################################\n" >>"${ResultLog}" if [ ${FirstCore} -eq ${LastCore} ]; then echo -e "Executing Geekbench on core ${FirstCore}${CPUInfo}\n" >>"${ResultLog}" ResultList="${TempDir}/${FirstCore}.lst" else echo -e "Executing Geekbench on cores ${FirstCore}-${LastCore}${CPUInfo}\n" >>"${ResultLog}" ResultList="${TempDir}/${FirstCore}-${LastCore}.lst" fi echo -e "\n https${ResultsURL}\n" >>"${ResultLog}" links -dump "https${ResultsURL}" >"${TempLog}" case ${GBVersion} in 5.*) grep ' Score ' "${TempLog}" | sed '/Multi-Core*/i \ \ \ ' | sed 's/^\ //' >>${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog}" | grep -v -E '/sec| FPS| Score' | head -n46 >>${ResultList} ;; 6.*) grep ' Score ' "${TempLog}" | sed 's/^\ //' >>${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog}" | grep -v -E '/sec| FPS| Score' | head -n36 >>${ResultList} ;; esac cat ${ResultList} >>"${ResultLog}" fi # bring back offline cores for o in $(seq ${FirstOfflineCPU} $(( ${CPUCores} - 1 )) ); do echo 1 >/sys/devices/system/cpu/cpu${o}/online [ -f /sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor ] && \ echo performance >/sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor 2>/dev/null snore 0.5 [ -f /sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor ] && \ echo performance >/sys/devices/system/cpu/cpufreq/policy${o}/scaling_governor 2>/dev/null done fi done kill ${MonitoringPID} fi # run Geekbench on all cores twice echo -e "\nSystem health while running Geekbench on all cores:\n" >>"${MonitorLog}" /bin/bash "${PathToMe}" -m ${MonitorInterval} >>"${MonitorLog}" & MonitoringPID=$! echo -e "\n${BOLD}Executing on all cores 1st time${NC}\n" BoostGB & "${GBBinary}" 2>&1 | tee "${TempLog}" echo "" GBFullString="$(awk -F" : " "/^Geekbench ${GBVersion}./ {print \$1}" "${TempLog}")" GBSystemInfo="$(grep -A25 "Gathering system information" "${TempLog}" | tail -n +2 | grep -B25 " Size ")" TempLog2="${TempDir}/temp2.log" echo -e "${BOLD}Executing on all cores 2nd time${NC}\n" BoostGB & "${GBBinary}" 2>&1 | tee "${TempLog2}" kill ${MonitoringPID} ResultsURL="$(awk -F"https" '/browser.geekbench.com/ {print $2}' <"${TempLog}" | head -n1)" ResultsURL2="$(awk -F"https" '/browser.geekbench.com/ {print $2}' <"${TempLog2}" | head -n1)" ClaimURL="$(awk -F"https" '/browser.geekbench.com/ {print $2}' <"${TempLog2}" | tail -n1)" if [ "X${ResultsURL}" = "X" ] || [ "X${ResultsURL2}" = "X" ]; then echo -e "\x08\x08 Failed.\c..." else echo -e "\n##########################################################################\n" >>"${ResultLog}" echo -e "Executing Geekbench on all cores twice\n" >>"${ResultLog}" ResultList="${TempDir}/all-1st.lst" links -dump "https${ResultsURL}" >"${TempLog}" links -dump "https${ResultsURL2}" >"${TempLog2}" case ${GBVersion} in 5.*) sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempLog}" | sed '/add this result to your profile/,+3 d' | \ sed '/Geekbench 5 license/,+4 d' | sed '/active Internet connection/,+2 d' | \ sed '/preview build/,+1 d' | sed '/Single-Core/,+22 d' | sed '/Multi-Core/,+22 d' | \ sed '/Uploading results/,+4 d' | sed 's|: https://www.geekbench.com/||' >>"${ResultLog}" grep ' Score ' "${TempLog}" | sed '/Multi-Core*/i \ \ \ ' | sed 's/^\ //' >${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog}" | grep -v -E '/sec| FPS| Score' | head -n46 >>${ResultList} cat ${ResultList} >>"${ResultLog}" ResultList="${TempDir}/all-2nd.lst" echo -e "\n https${ClaimURL}\n" >>"${ResultLog}" grep ' Score ' "${TempLog2}" | sed '/Multi-Core*/i \ \ \ ' | sed 's/^\ //' >${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog2}" | grep -v -E '/sec| FPS| Score' | head -n46 >>${ResultList} cat ${ResultList} >>"${ResultLog}" ;; 6.*) echo -e "${GBFullString}\n\n${GBSystemInfo}\n" >>"${ResultLog}" grep ' Score ' "${TempLog}" | sed 's/^\ //' >${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog}" | grep -v -E '/sec| FPS| Score' | head -n36 >>${ResultList} cat ${ResultList} >>"${ResultLog}" ResultList="${TempDir}/all-2nd.lst" echo -e "\n https${ClaimURL}\n" >>"${ResultLog}" grep ' Score ' "${TempLog2}" | sed 's/^\ //' >${ResultList} echo -e "\n Single-Core Performance" >>${ResultList} sed '1,/^ Single-Core Performance$/d' "${TempLog2}" | grep -v -E '/sec| FPS| Score' | head -n36 >>${ResultList} cat ${ResultList} >>"${ResultLog}" ;; esac # create a results table on SoCs with different clusters if [ ${#ClusterConfigByCoreType[@]} -ne 1 ]; then CreateGBResultsTable | sed 's/ HTML /HTML5 /' >>"${ResultLog}" fi CompareURL="https://browser.geekbench.com/v${GBVersion:0:1}/cpu/compare/${ResultsURL##*/}?baseline=${ResultsURL2##*/}" echo -e "\n\n${CompareURL}" >>"${ResultLog}" fi echo -e "\nGeekbench execution...\c" } # RunGB CreateGBResultsTable() { cd "${TempDir}/" || exit 1 echo -e "\n##########################################################################\n" echo -e "All Geekbench results:\n\n \c" for o in *.lst ; do printf "%10s" "${o%.*}" | sed 's/all-/all /g' done echo "" for i in $(seq 1 $(wc -l <"${ResultList}") ); do for o in *.lst ; do case $o in 0*) printf "\n%25s" "$(sed -n ${i}p <$o | cut -c-26 | tr -d '[:digit:]')" ;; esac printf "%10s" $(sed -n ${i}p <$o | cut -c25- | tr -d -c '[:digit:]') done done } # CreateGBResultsTable PrintCPUTopology() { # prints list of CPU cores, clusters and cpufreq policy nodes CPUFreqPolicy="none" echo "CPU sysfs topology (clusters, cpufreq members, clockspeeds)" echo " cpufreq min max" echo " CPU cluster policy speed speed core type" FirstCoreName="$(GetCoreType 0)" FirstCoreStepping="$(GetARMStepping 0)" for i in $(seq 0 $(( ${CPUCores} - 1 )) ); do CoreName="$(GetCoreType $i)" # check if CoreName is empty if [ "X${CoreName}" = "X" ]; then case ${CPUArchitecture} in *86*) # try to return microarchitecture instead of CPU name on x86 if available if [[ ${CPUFetchInfo} != *Unknown* ]]; then uArch="$(awk -F':' '/Microarchitecture:|uArch:/ {print $2}' <<<"${CPUFetchInfo}" | sed "s/^[ \t]*//")" if [ "X${uArch}" = "X" ]; then CoreName="${X86CPUName}" else CoreName="${uArch}" fi fi ;; *arm*|*aarch*) # On ARM with older kernels core/stepping info might only be available # for cpu0 so use this instead [ -n "${FirstCoreName}" ] && CoreName="${FirstCoreName} / ${FirstCoreStepping}" ;; esac else CoreStepping="$(GetARMStepping $i)" if [ "X${CoreStepping}" != "X" ]; then CoreName="${CoreName} / ${CoreStepping}" fi fi [ -f /sys/devices/system/cpu/cpu${i}/topology/physical_package_id ] && \ read CPUCluster >"${ResultLog}" # Prepare benchmark results echo -e "\n##########################################################################\n" >>"${ResultLog}" # add thermal info if available if [ "X${TempInfo}" != "X" ]; then echo -e "${TempInfo}\n" >>"${ResultLog}" fi # include monitoring (filter out broken thermal readouts) sed 's/ 0°C$/ --/' <"${MonitorLog}" >>"${ResultLog}" # add throttling info if available if [ -f "${TempDir}/throttling_info.txt" ]; then echo -e "\n##########################################################################" >>"${ResultLog}" cat "${TempDir}/throttling_info.txt" >>"${ResultLog}" ThrottlingWarning="${LRED}${BOLD} (throttled)${NC}" fi # add dmesg output since start of the benchmark if something relevant is there TimeStamp="$(dmesg | tr -d '[' | tr -d ']' | awk -F" " '/sbc-bench started/ {print $1}' | tail -n1)" dmesg | sed "/${TimeStamp}/,\$!d" | grep -E -v 'sbc-bench started|started with executable stack' >"${TempDir}/dmesg" if [ -s "${TempDir}/dmesg" ]; then echo -e "\n##########################################################################\n\ndmesg output while running the benchmarks:\n" >>"${ResultLog}" cat "${TempDir}/dmesg" >>"${ResultLog}" fi echo -e "\n##########################################################################\n" >>"${ResultLog}" echo -e "$(iostat | grep -E -v "^loop|boot0|boot1|mtdblock")\n\n$(free -h)\n\n$(swapon -s)\n" | sed '/^$/N;/^\n$/D' >>"${ResultLog}" ShowZswapStats 2>/dev/null >>"${ResultLog}" echo >>"${ResultLog}" cat "${TempDir}/cpu-topology.log" >>"${ResultLog}" echo "${LSCPU}" >>"${ResultLog}" LogEnvironment >>"${ResultLog}" # Throttling detection/reporting if [ "X${PlotCpufreqOPPs}" != "Xyes" ]; then if [ -f /sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq ]; then # Check for throttling for first: MeasuredClockspeedStart=$(grep -A2 "^Checking cpufreq OPP" "${ResultLog}" | awk -F" " '/Measured/ {print $5}' | sed -n ${#ClusterConfig[@]}p) MeasuredClockspeedFinished=$(grep -A2 "^Checking cpufreq OPP" "${ResultLog}" | awk -F" " '/Measured/ {print $5}' | sed -n $(( ${#ClusterConfig[@]} * 2 ))p) MeasuredDifference=$(( 100 * MeasuredClockspeedStart / ${MeasuredClockspeedFinished:-MeasuredClockspeedStart} )) if [ ${MeasuredDifference:-100} -gt 103 ] || [ -f "${TempDir}/throttling_info.txt" ]; then # if measured clockspeed differs by more than 3% comparing begin and end of # benchmarking or if throttling_info.txt exists, then add a throttling warning ThrottlingWarning="${LRED}${BOLD} (throttled)${NC}" fi HighestClock=$(sort -n -r /sys/devices/system/cpu/cpufreq/policy?/cpuinfo_max_freq | head -n1) LowestClock=$(sort -n -r /sys/devices/system/cpu/cpufreq/policy?/cpuinfo_max_freq | tail -n1) ClockDifference=$(( 100000 * MeasuredClockspeedStart / HighestClock )) if [ ${ClockDifference:-100} -lt 98 ] || [ ${ClockDifference:-100} -gt 102 ]; then # if measured clockspeed differs by more than 2% compared to cpuinfo_max_freq # then report this value slightly rounded instead of cpufreq sysfs entries MHz="~$(( $(awk '{printf ("%0.0f",$1/10); }' <<<${MeasuredClockspeedStart} ) * 10 ))" elif [ ${HighestClock} -eq ${LowestClock} ]; then MHz="$(( HighestClock / 1000 )) MHz" else MHz="$(( HighestClock / 1000 ))/$(( LowestClock / 1000 )) MHz" fi else # no cpufreq support, we check first measured value and use it if available MeasuredClockspeedStart=$(awk -F": " '/No cpufreq support available. Measured on cpu/ {print $2}' <"${ResultLog}" | cut -f1 -d' ' | head -n 1) if [ "X${MeasuredClockspeedStart}" = "X" ]; then MHz="no cpufreq support" else # slightly round up measured clockspeed MHz="~$(( $(awk '{printf ("%0.0f",$1/10+0.5); }' <<<"${MeasuredClockspeedStart}") * 10 )) MHz" # check additionally for throttling MeasuredClockspeedFinished=$(awk -F": " '/No cpufreq support available. Measured on cpu/ {print $2}' <"${ResultLog}" | cut -f1 -d' ' | tail -n1) MeasuredDifference=$(( 100 * MeasuredClockspeedStart / MeasuredClockspeedFinished )) if [ ${MeasuredDifference:-100} -gt 103 ]; then # if measured clockspeed differs by more than 3% comparing begin and end of # benchmarking then add a throttling warning ThrottlingWarning="${LRED}${BOLD} (throttled)${NC}" fi fi fi fi # Add a line suitable for Results.md on Github if not in efficiency plotting or PTS, GB or review mode if [ "X${PlotCpufreqOPPs}" != "Xyes" ] && [ "X${MODE}" != "Xpts" ] && [ "X${MODE}" != "Xgb" ] && [ "X${REVIEWMODE}" != "Xtrue" ]; then KernelArch="$(uname -m | sed -e 's/armv7l/armhf/' -e 's/aarch64/arm64/')" if [ "X${KernelArch}" = "X" ] || [ "X${KernelArch}" = "X${ARCH}" ]; then DistroInfo="${OperatingSystem} ${ARCH}" else DistroInfo="${OperatingSystem} ${KernelArch}/${ARCH}" fi echo -e "\n| ${DeviceName:-$HostName} | ${MHz}${ThrottlingWarning} | ${ShortKernelVersion} | ${DistroInfo} | ${ZipScore} | ${ZipScoreSingleThreaded} | ${OpenSSLScore} | ${MemBenchScore} | ${CpuminerScore:-"-"} |\c" | sed -e 's/ / /g' -e "s,\x1B\[[0-9;]*[a-zA-Z],,g" >>"${ResultLog}" fi } # SummarizeResults LogEnvironment() { # Log processor info if available and we're not running virtualized: if [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ] || [ "X${DMIProductName}" = "XApple Virtualization Generic Platform" ]; then command -v dmidecode >/dev/null 2>&1 && \ ProcessorInfo="$(dmidecode -t processor 2>/dev/null | grep -E -v -i "^#|^Handle|Serial|O.E.M.|1234567|SMBIOS|: Not |Unknown|OUT OF SPEC|00 00 00 00 00 00 00 00|: None|Scanning ")" if [ "X${ProcessorInfo}" != "X" ]; then echo -e "\n${ProcessorInfo}\n" fi fi # report ARM/RISC-V/Loongson SoCs if possible if [ "X${GuessedSoC}" != "Xn/a" ]; then echo -e "\nSoC guess: ${GuessedSoC}" elif [ "X${CPUSignature}" != "X" ]; then if [ "X${CPUFetchGuess}" != "X" ]; then echo -e "\nSoC guess: ${CPUFetchGuess} (${CPUSignature})" else echo -e "\nSignature: ${CPUSignature}" fi else echo "" fi # check whether it's a Rockchip BSP kernel with dmc enabled DMCGovernor="/sys/devices/platform/dmc/devfreq/dmc/governor" if [ -f "${DMCGovernor}" ]; then read UsedDMCGovernor <"${DMCGovernor}" case ${UsedDMCGovernor} in userspace|powersave|performance) # report also configured RAM clock read DRAMClock <"${DMCGovernor%/*}/cur_freq" DMCGovernorSettings="${UsedDMCGovernor} ($(( ${DRAMClock} / 1000000 )) MHz)" ;; *) # check whether uptreshold and downdifferential values exist and if true report it if [ -f "${DMCGovernor%/*}/upthreshold" ]; then # report DMC governor and upthreshold value read upthreshold <"${DMCGovernor%/*}/upthreshold" if [ -f "${DMCGovernor%/*}/downdifferential" ]; then # report DMC governor and upthreshold/downdifferential values read downdifferential <"${DMCGovernor%/*}/downdifferential" DMCGovernorSettings="${UsedDMCGovernor} (upthreshold: ${upthreshold}, downdifferential: ${downdifferential})" else # report DMC governor and upthreshold value DMCGovernorSettings="${UsedDMCGovernor} (upthreshold: ${upthreshold})" fi else # report only DMC governor DMCGovernorSettings="${UsedDMCGovernor}" fi ;; esac echo -e " DMC gov: ${DMCGovernorSettings}" fi # log /proc/device-tree/compatible contents if available if [ "X${DTCompatible}" != "X" ]; then echo "DT compat: $(head -n1 <<<"${DTCompatible}")" tail -n +2 <<<"${DTCompatible}" | sed -e 's/^/ /' fi # Log compiler version if not in Geekbench mode if [ "X${MODE}" != "Xgb" ]; then GCC_Info="$(${GCC} -v 2>&1 | grep -E "^Target|^Configured")" echo -e " Compiler: ${GCC} ${GCC_Version} / $(awk -F": " '/^Target/ {print $2}' <<< "${GCC_Info}")" fi # Log userland architecture if available [ "X${ARCH}" != "X" ] && echo " Userland: ${ARCH}" # Log ThreadX version if available if [ "X${ThreadXVersion}" != "X" ]; then echo -e " ThreadX: $(awk '/^version/ {print $2}' <<<"${ThreadXVersion}") / $(head -n1 <<<"${ThreadXVersion}")" vcgencmd_mem_reloc_stats="$("${VCGENCMD}" mem_reloc_stats 2>/dev/null)" grep -q compactions <<<"${vcgencmd_mem_reloc_stats}" && echo "${vcgencmd_mem_reloc_stats}" | while read ; do echo " ${REPLY}" done fi # check for VM/container mode to add this to kernel info [ "X${VirtWhat}" != "X" ] && [ "X${VirtWhat}" != "Xnone" ] && VirtOrContainer=" (${VirtWhat})" # kernel info KernelVersion="$(uname -r)" ShortKernelVersion="$(awk -F"." '{print $1"."$2}' <<<"${KernelVersion}")" if [ -r /proc/self/smaps ]; then KernelPageSize="$(awk -F" " '/KernelPageSize/ {print $2$3}' /proc/self/smaps 2>/dev/null | head -n1)" [ "${KernelPageSize}" != "4kB" ] && PageSize=" (${KernelPageSize})" || PageSize="" fi echo -e " Kernel: ${KernelVersion}/${CPUArchitecture}${PageSize}${VirtOrContainer}" # kernel config KernelConfig="/boot/config-${KernelVersion}" if [ -f "${KernelConfig}" ] ; then grep -E "^CONFIG_HZ|^CONFIG_PREEMPT" "${KernelConfig}" | sed -e 's/^/ /' | sort -V else modprobe configs 2>/dev/null [ -f /proc/config.gz ] && zgrep -E "^CONFIG_HZ|^CONFIG_PREEMPT" /proc/config.gz | sed -e 's/^/ /' | sort -V fi # with Rockchip BSP kernels try to report PVTM settings (Process-Voltage-Temperature Monitor) grep -E "cpu.*pvtm|leakage" <<<"${DMESG}" | awk -F'] ' '{print " "$2}' # Add kernel version info / warnings KernelInfo="$(CheckKernelVersion "${KernelVersion}")" [ -z "${KernelInfo}" ] || echo -e "\n##########################################################################\n"; \ sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"${KernelInfo}" # cache and DIMM info CacheAndDIMMs="$(CacheAndDIMMDetails)" [ -z "${CacheAndDIMMs}" ] || echo -e "\n##########################################################################\n${CacheAndDIMMs}" >>"${ResultLog}" # Always include OPP tables if [ "X${USE_VCGENCMD}" = "Xtrue" ]; then # check whether we collected RPi OPP tables when walking through the cpufreq OPP and # if so include them using the usual sbc-bench format. The whole DVFS thing on RPi # happens solely inside the closed source ThreadX world. echo -e "\n##########################################################################" >>"${ResultLog}" for OPPTable in "${TempDir}"/opp-table-threadx-* ; do [[ -e "${OPPTable}" ]] || break echo -e "\n ${OPPTable##*/}:" >>"${ResultLog}" sort -n <"${OPPTable}" >>"${ResultLog}" done else echo -e "${OPPTables}" >>"${ResultLog}" fi # results validation: echo -e "\n##########################################################################\n" IsValid="$(ValidateResults | sed -e 's/^/ * /')" [ -n "${IsValid}" ] && echo -e "Results validation:\n\n${IsValid}\n" | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" # add performance relevant governors/policies to results if available: GovernorStateNow="$(HandleGovernors | grep -v cpufreq-policy)" PolicyStateNow="$(HandlePolicies)" if [ "X${GovernorStateNow}" != "X" ] && [ "X${PolicyStateNow}" != "X" ]; then echo -e "Status of performance related governors found below /sys (w/o cpufreq):\n" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"${GovernorStateNow}" | sed -e 's/^/ * /' echo -e "\nStatus of performance related policies found below /sys:\n" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"${PolicyStateNow}" | sed -e 's/^/ * /' elif [ "X${GovernorStateNow}" != "X" ]; then echo -e "Status of performance related governors found below /sys (w/o cpufreq):\n" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"${GovernorStateNow}" | sed -e 's/^/ * /' elif [ "X${PolicyStateNow}" != "X" ]; then echo -e "Status of performance related policies found below /sys:\n" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"${PolicyStateNow}" | sed -e 's/^/ * /' fi } # LogEnvironment ValidateResults() { # function that checks whether benchmark scores have been invalidated # check 7-zip version (only 16.02 is ok to compare with results list) Valid7Zip="$(grep '^p7zip Version' "${ResultLog}" | head -n1 | awk -F" " '{print $3}')" [ -z "${Valid7Zip}" ] && Valid7Zip="$(grep '^7-Zip ' "${ResultLog}" | head -n1 | awk -F" " '{print $3}')" [ "X${Valid7Zip}" = "X16.02" ] || [ "X${Valid7Zip}" = "X" ] || echo -e "${LRED}${BOLD}Distro uses problematic 7-zip version ${Valid7Zip}${NC} -> http://tinyurl.com/ukcktbsm" # report significant mismatches between measured and 'advertised' clockspeeds from # 1st measurement prior to benchmarking (to rule out later possible throttling effects) if [ "X${USE_VCGENCMD}" = "Xtrue" ]; then ClockspeedMismatchBefore="$(grep -B1000 "^Executing ramlat" "${ResultLog}" | grep -A2 "^Checking cpufreq OPP " | awk -F"[()]" '/^Cpufreq OPP:/ {print $2}' | grep "^-" | sort -n | head -n1)" ClockspeedMismatchAfter="$(grep -A200 "^Testing maximum cpufreq again" "${ResultLog}" | grep -A2 "^Checking cpufreq OPP " | awk -F"[()]" '/^Cpufreq OPP:/ {print $2}' | grep "^-" | sort -n | head -n1)" else ClockspeedMismatchBefore="$(grep -B1000 "^Executing ramlat" "${ResultLog}" | grep -A2 "^Checking cpufreq OPP " | awk -F"[()]" '/^Cpufreq OPP:/ {print $4}' | grep "^-" | sort -n | head -n1)" ClockspeedMismatchAfter="$(grep -A200 "^Testing maximum cpufreq again" "${ResultLog}" | grep -A2 "^Checking cpufreq OPP " | awk -F"[()]" '/^Cpufreq OPP:/ {print $4}' | grep "^-" | sort -n | head -n1)" fi if [ "X${ClockspeedMismatchBefore}" != "X" ] && [ "X${ClockspeedMismatchAfter}" != "X" ]; then OneOrTwoDigitsBefore=$(wc -c <<<"${ClockspeedMismatchBefore}") OneOrTwoDigitsAfter=$(wc -c <<<"${OneOrTwoDigitsAfter}") if [ ${OneOrTwoDigitsBefore:-6} -gt 6 ] || [ ${OneOrTwoDigitsAfter:-6} -gt 6 ]; then # mismatch greater than 9%, print warning in red and bold echo -e "${LRED}${BOLD}Advertised vs. measured max CPU clockspeed: ${ClockspeedMismatchBefore} before, ${ClockspeedMismatchAfter} after${NC} -> https://tinyurl.com/32w9rr94" else echo -e "Advertised vs. measured max CPU clockspeed: ${ClockspeedMismatchBefore} before, ${ClockspeedMismatchAfter} after -> https://tinyurl.com/32w9rr94" fi elif [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ]; then # cpufreq scaling supported, check we've measured cpufreq before to report 'no mismatch' grep -q "^Checking cpufreq OPP" "${ResultLog}" && \ echo -e "${LGREEN}Measured clockspeed not lower than advertised max CPU clockspeed${NC}" fi # check for certain RPi specifics if [ -r "${ThreadXConfig}" ]; then # Check whether arm_boost=1 is missing with BCM2711 rev C0 or later. Armbian 'experts' # ship with such silly settings but set over_voltage=2 and arm_freq=1800 at the same # time as such RPi 4 with BCM2711 rev C0 or later running with Armbian will waste more # energy and starts to throttle earlier since the ARM cores are being fed unnecessarily # with higher supply voltages than necessary and all of this just due to the usual # amount of ignorance over at Armbian. # https://github.com/armbian/build/issues/3400#issuecomment-1011435796 grep -q "arm_boost=1" "${ThreadXConfig}" if [ $? -ne 0 ] && [ "${BCM2711}" = "C0 or later" ]; then grep -q "arm_freq=1800" "${ThreadXConfig}" case $? in 0) # Armbian settings echo -e "${LRED}${BOLD}Silly settings: \"arm_boost=1\" missing but \"arm_freq=1800\" set in ${ThreadXConfig}${NC} -> https://tinyurl.com/sbmvhwpf" ;; *) echo -e "${LRED}${BOLD}\"arm_boost=1\" missing in ${ThreadXConfig}${NC} -> https://tinyurl.com/sbmvhwpf" ;; esac fi # check whether temp_soft_limit is defined since otherwise on certain RPi models # the max clockspeed is silently lowered from 1400 to 1200 MHz once 60°C are hit case "${DeviceName}" in "Raspberry Pi 3 Model B Plus"*|"Raspberry Pi 3 Model A Plus"*|"Raspberry Pi Compute Module 3 Plus"*) grep -q "arm_freq=1200" <<<"${ActualThreadXsettings}" LimitedTo1200=$? grep -q "temp_soft_limit" "${ThreadXConfig}" TempSoftLimitSet=$? [ ${LimitedTo1200} -ne 0 ] && [ ${TempSoftLimitSet} -ne 0 ] && echo "\"temp_soft_limit\" not set in ${ThreadXConfig} -> https://tinyurl.com/4e3kpt6n" ;; esac fi # Swapping? Skip if there's no swap configured ProcSwapLines=$(wc -l https://tinyurl.com/3h222wnh" # check whether zswap sits on top of zram [ -r /sys/module/zswap/parameters/enabled ] && ZswapEnabled="$(sed 's/Y/1/' https://tinyurl.com/spkr8ytz" else echo -e "${LRED}${BOLD}Swapping occured${NC} -> https://tinyurl.com/3h222wnh" if [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ]; then # identify type of swap only when not running inside a VM SlowSwap="$(ListSwapDevices | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" | awk -F") on " '{print $2}' | sed '/^$/d' | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//')" [ -z "${SlowSwap}" ] || echo -e "${LRED}${BOLD}Swap configured on ${SlowSwap}${NC}" fi fi fi fi # Too high %system utilization (mostly caused by swapping on zram enabled systems)? UtilizationValues="$(grep -A2000 -E "^System health while running tinymembench|^System health while running ramlat" "${MonitorLog}" | sed '/^Time/,+1 d' | awk -F"MHz " '/^[0-9]/ {print $2}' | awk -F" " '{print $3}' | sed 's/%//')" PeakSysUtilization=$(sort -n -r <<<"${UtilizationValues}" | head -n1) LogLength=$(wc -l <<<"${UtilizationValues}") UtilizationSum=$(awk '{s+=$1} END {printf "%.0f", s}' <<<"${UtilizationValues}") AverageSysUtilization=$(( ${UtilizationSum} / ${LogLength} )) if [ ${PeakSysUtilization:-0} -gt 15 ] || [ ${AverageSysUtilization:-0} -gt 0 ]; then echo -e "${LRED}${BOLD}Too much background activity (%system): ${AverageSysUtilization}% avg, ${PeakSysUtilization}% max${NC} -> https://tinyurl.com/mr2wy5uv" else # only report background activity being ok when not running inside a VM/container [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ] && echo -e "${LGREEN}Background activity (%system) OK${NC}" fi # Too high overall CPU utilization with single threaded benchmarks? This is an indication # of background activities needing too much CPU ressources. Skip in MODE=extensive/gb/pts # Skip also if count of CPU cores is 1 or $1 is review if [ "X${MODE}" != "Xextensive" ] && [ "X${MODE}" != "Xpts" ] && [ "X${MODE}" != "Xgb" ] && [ ${CPUCores} -gt 1 ] && [ "X$1" != "Xreview" ]; then IdealUtilization=$(( 100 / ${CPUCores} )) UtilizationValues="$(grep -A2000 "^System health while running tinymembench" "${MonitorLog}" | grep -B2000 -E " 7-zip multi | 7-zip cluster " | sed '/^Time/,+1 d' | awk -F"MHz " '/^[0-9]/ {print $2}' | awk -F" " '{print $2}' | sed 's/%//' | while read ; do [ ${REPLY} -ge ${IdealUtilization} ] && echo ${REPLY} ; done)" PeakCPUUtilization=$(sort -n -r <<<"${UtilizationValues}" | head -n1) LogLength=$(wc -l <<<"${UtilizationValues}") UtilizationSum=$(awk '{s+=$1} END {printf "%.0f", s}' <<<"${UtilizationValues}") AverageCPUUtilization=$(( ${UtilizationSum} / ${LogLength} )) PeakDiff=$(( ${PeakCPUUtilization} - ${IdealUtilization} )) AverageDiff=$(( ${AverageCPUUtilization} - ${IdealUtilization} )) case ${CPUCores} in 2) # 8% peak and 2% average is acceptable [ ${PeakDiff} -gt 8 ] || [ ${AverageDiff} -gt 2 ] && echo -e "${LRED}${BOLD}Too much other background activity: ${AverageDiff}% avg, ${PeakDiff}% max${NC} -> https://tinyurl.com/mr2wy5uv" ;; 4) # 5% peak and 1% average is acceptable [ ${PeakDiff} -gt 5 ] || [ ${AverageDiff} -gt 1 ] && echo -e "${LRED}${BOLD}Too much other background activity: ${AverageDiff}% avg, ${PeakDiff}% max${NC} -> https://tinyurl.com/mr2wy5uv" ;; 5|6) # 3% peak and 1% average is acceptable [ ${PeakDiff} -gt 3 ] || [ ${AverageDiff} -gt 1 ] && echo -e "${LRED}${BOLD}Too much other background activity: ${AverageDiff}% avg, ${PeakDiff}% max${NC} -> https://tinyurl.com/mr2wy5uv" ;; 8) # 2% peak and 0% average is acceptable [ ${PeakDiff} -gt 2 ] || [ ${AverageDiff} -gt 0 ] && echo -e "${LRED}${BOLD}Too much other background activity: ${AverageDiff}% avg, ${PeakDiff}% max${NC} -> https://tinyurl.com/mr2wy5uv" ;; *) # With more CPU cores it's impossible to detect 'sane environment' so only # notify if average utilization is off by more than 0% [ ${AverageDiff} -gt 0 ] && echo -e "${LRED}${BOLD}Too much other background activity: ${AverageDiff}% avg, ${PeakDiff}% max${NC} -> https://tinyurl.com/mr2wy5uv" ;; esac fi # check for out of memory events (oom-killer invocations) if [ -s "${TempDir}/dmesg" ]; then OOMCount="$(grep -c "oom-killer" "${TempDir}/dmesg")" if [ ${OOMCount:-0} -gt 0 ]; then case ${ProcSwapLines} in 1) echo -e "${LRED}${BOLD}${OOMCount} oom-killer invocations (system too low on RAM and no ZRAM configured)${NC} -> https://tinyurl.com/ytma8u4e" ;; *) echo -e "${LRED}${BOLD}${OOMCount} oom-killer invocations (system too low on RAM and insufficient swap configured)${NC} -> https://tinyurl.com/ytma8u4e" ;; esac fi fi # powercap on Intel? if [ -d /sys/devices/virtual/powercap/intel-rapl ]; then grep -q -i GenuineIntel <<< "${ProcCPU}" && echo -e "Powercap detected. Details: \"sudo powercap-info -p intel-rapl\" -> https://tinyurl.com/4jh9nevj" fi # temporarily killed CPU cores due to overheating with Amlogic SoCs and BSP kernel if [ ! -f "${TempDir}/dmesg" ]; then # filter dmesg output to contain only contents from start of benchmarking TimeStamp="$(dmesg | tr -d '[' | tr -d ']' | awk -F" " '/sbc-bench started/ {print $1}' | tail -n1)" dmesg | sed "/${TimeStamp}/,\$!d" | grep -E -v 'sbc-bench started|started with executable stack' >"${TempDir}/dmesg" fi KilledCPUs="$(grep "CPU[0-7]: shutdown" "${TempDir}/dmesg" | awk -F" " '{print $2}' | sort | uniq | tr "\n" ":" | sed -e 's/::/, /g')" if [ "X${KilledCPUs}" != "X" ]; then ThrottlingWarning="${LRED}${BOLD} (throttled)${NC}" echo -e "${LRED}${BOLD}Overheating resulted in CPU cores temporarily being stopped ($(sed 's/, $//' <<<"${KilledCPUs,,}"))${NC} -> https://tinyurl.com/2jetn2eb" fi # Throttling and frequency capping on RPi? if [ "${USE_VCGENCMD}" = "true" ]; then case "${ThrottlingCheck}" in *"requency capping"*) [ "${ThrottlingWarning}" = "" ] && echo -e "${LRED}${BOLD}Frequency capping (under-voltage) occured${NC} -> https://tinyurl.com/3j2c66kd" || echo -e "${LRED}${BOLD}Throttling / frequency capping (under-voltage) occured${NC} -> https://tinyurl.com/4ky59sys / https://tinyurl.com/3j2c66kd" ;; *) [ "${ThrottlingWarning}" = "" ] && echo -e "${LGREEN}No throttling${NC}" || echo -e "${LRED}${BOLD}Throttling occured${NC} -> https://tinyurl.com/4ky59sys" ;; esac else # Throttling on all other systems? if [ "${ThrottlingWarning}" = "" ] && [ -f /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state ]; then # only report 'No throttling' when cpufreq statistics are available and not # running inside a VM/container [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ] && echo -e "${LGREEN}No throttling${NC}" elif [ "${ThrottlingWarning}" != "" ]; then # we need to check whether we're running in Geekbench or PTS mode since the # cluster tests on CPUs with different core types end up with the CPUs remaining # on lower clockspeeds for a fraction of time when bringing them back online # which then results in stats/time_in_state cpufreq statistics showing the cores # not all the time at maximum clockspeed. if [ "X${MODE}" != "Xpts" ] && [ "X${MODE}" != "Xgb" ]; then echo -e "${LRED}${BOLD}Throttling occured${NC} -> https://tinyurl.com/4ky59sys" else echo -e "Throttling might have occured -> https://tinyurl.com/4ky59sys" fi fi fi # ondemand governor used by distro but inappropriate settings on at least some cores if [ "X$1" = "Xreview" ] && [ "${OriginalCPUFreqGovernor}" = "ondemand" ] && [[ -n "${io_is_busy[@]}" ]]; then grep -q 0 <<<"${io_is_busy[@]}" && echo -e "${LRED}${BOLD}ondemand cpufreq governor used by distro but io_is_busy not set to 1 on all cores${NC} -> http://tinyurl.com/44pbmw79" elif [ "X$1" = "Xreview" ] && [ "${OriginalCPUFreqGovernor}" = "ondemand" ] && [[ -z "${io_is_busy[@]}" ]]; then echo -e "${LRED}${BOLD}ondemand cpufreq governor used by distro but io_is_busy not active${NC} -> http://tinyurl.com/44pbmw79" fi # check DT settings on big.LITTLE ARM designs for scheduler relevant stuff: if [ -d /proc/device-tree/cpus ] && [ ${#ClusterConfig[@]} -ne 1 ]; then capacitydmipsmhz=$(find /proc/device-tree/cpus -name capacity-dmips-mhz | wc -l) if [ ${capacitydmipsmhz} -eq 0 ]; then echo "${LRED}${BOLD}${#ClusterConfig[@]} different clusters but capacity-dmips-mhz property not set${NC}" elif [ ${capacitydmipsmhz} -ne ${CPUCores} ]; then echo "${LRED}${BOLD}${#ClusterConfig[@]} different clusters and ${CPUCores} cores but capacity-dmips-mhz property only set on ${capacitydmipsmhz}${NC}" fi if [ "${OriginalCPUFreqGovernor}" = "schedutil" ]; then dynamicpowercoefficient=$(find /proc/device-tree/cpus -name dynamic-power-coefficient | wc -l) schedenergycosts=$(find /proc/device-tree/cpus -name sched-energy-costs | wc -l) if [ ${dynamicpowercoefficient} -eq 0 ] && [ ${schedenergycosts} -eq 0 ]; then echo "${LRED}${BOLD}${OriginalCPUFreqGovernor} cpufreq governor configured but neither dynamic-power-coefficient nor sched-energy-costs defined${NC}" elif [ ${dynamicpowercoefficient} -ne ${CPUCores} ]; then echo "${OriginalCPUFreqGovernor} cpufreq governor configured: ${CPUCores} cores available vs. only ${dynamicpowercoefficient} dynamic-power-coefficient DT nodes" elif [ ${schedenergycosts} -ne ${CPUCores} ]; then echo "${OriginalCPUFreqGovernor} cpufreq governor configured: ${CPUCores} cores available vs. only ${schedenergycosts} sched-energy-costs DT nodes" fi fi fi } # ValidateResults ListSwapDevices() { # function to list swap devices grep -q "/dev/zram" /proc/swaps && ZRAMCtl="$(zramctl -n -o NAME,DISKSIZE,ALGORITHM,STREAMS,DATA,COMPR,TOTAL)" [ -r /sys/module/zswap/parameters/enabled ] && ZswapEnabled="$(sed 's/Y/1/' {"target": "/", "source": "/dev/mmcblk1p1", "fstype": "ext4", "options": "rw,noatime,nodiratime,errors=remount-ro,commit=600"} -> /sys/block/mmcblk1/device/type (SD or MMC) SwapDevicePartition="$(findmnt -U "$(stat --printf=%m ${SwapDevice})" | grep "^/" | awk -F" " '{print $2}')" if [ "X${SwapDevicePartition}" = "Xoverlay" ] && [ -r /proc/cmdline ]; then # FriendlyELEC images using overlayfs: https://wiki.friendlyelec.com/wiki/index.php/How_to_use_overlayfs_on_Linux RootPartition="$(tr ' ' '\n' /dev/null || \ sed '/^$/N;/^\n$/D' <"${ResultLog}" | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" | curl -s -F ${UploadScheme} ${UploadServer}) # Display benchmark results if not in PTS, GB or preview mode if [ "X${MODE}" != "Xpts" ] && [ "X${MODE}" != "Xgb" ] && [ "X${REVIEWMODE}" != "Xtrue" ]; then [ "X${IsValid}" != "X" ] && echo -e "${BOLD}Results validation:${NC}\n\n${IsValid}\n" MemoryScores="$(awk -F" " '/^ libc / {print $2$4" "$5" "$6}' <"${ResultLog}" | grep -E 'memcpy|memset' | awk -F'MB/s' '{print $1"MB/s"}')" CountOfMemoryScores=$(wc -l <<<"${MemoryScores}") if [ ${#ClusterConfig[@]} -eq 1 ] || [ $(( ${#ClusterConfig[@]} * 2 )) -ne ${CountOfMemoryScores} ]; then # if only 1 CPU cluster or mismatch between count of memory scores and cluster members # then report plain results echo -e "${BOLD}Memory performance${NC}\n${MemoryScores}" else # suffix memory scores with core types if possible ClusterInfo=" (all ${#ClusterConfig[@]} CPU clusters measured individually)" echo -e "${BOLD}Memory performance${NC}${ClusterInfo}:" for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo "$(grep "^memcpy:" <<<"${MemoryScores}" | sed -n $(( ${i} + 1 ))p)${CPUInfo}" echo "$(grep "^memset:" <<<"${MemoryScores}" | sed -n $(( ${i} + 1 ))p)${CPUInfo}" done fi if [ "${ExecuteCpuminer}" = "yes" ] && [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then echo -e "\n${BOLD}Cpuminer total scores${NC} (5 minutes execution): $(awk -F"Total Scores: " '/^Total Scores: / {print $2}' "${ResultLog}") kH/s" fi if [ "${ExecuteStockfish}" = "yes" ] && [ -x "${InstallLocation}/Stockfish-sf_15/src/stockfish" ]; then echo -e "\n${BOLD}Stockfish scores${NC} (${RunHowManyTimes} consecutive runs): ${StockfishScores} nodes/second" fi echo -e "\n${BOLD}7-zip total scores${NC} (${RunHowManyTimes} consecutive runs): $(awk -F" " '/^Total:/ {print $2}' "${ResultLog}"), single-threaded: ${ZipScoreSingleThreaded}" if [ -f "${OpenSSLLog}" ]; then echo -e "\n${BOLD}OpenSSL results${NC}${ClusterInfo}:\n$(grep '^type' "${OpenSSLLog}" | head -n1)" grep '^aes-...-cbc' "${OpenSSLLog}" fi elif [ "X${MODE}" = "Xgb" ]; then echo -e "First run:\n" case ${GBVersion} in 5.*) grep ' Score ' "${TempLog}" | sed '/Multi-Core*/i \ \ \ ' echo -e "\nSecond run:\n" grep ' Score ' "${TempLog2}" | sed '/Multi-Core*/i \ \ \ ' ;; 6.*) grep ' Score ' "${TempLog}" echo -e "\nSecond run:\n" grep ' Score ' "${TempLog2}" ;; esac echo -e "\n${CompareURL}\n" [ "X${IsValid}" != "X" ] && echo -e "${BOLD}Results validation:${NC}\n\n${IsValid}\n" elif [ "X${REVIEWMODE}" = "Xtrue" ] && [ "X${NOTUNING}" != "Xyes" ]; then [ "X${IsValid}" != "X" ] && echo -e "${BOLD}Results validation:${NC}\n\n${IsValid}\n" fi case ${UploadURL} in http*) # uploading results worked, check sanity of results and environment echo " ${UploadURL} |" | sed 's/http:/https:/' >>"${ResultLog}" echo -e "\nFull results uploaded to ${UploadURL}\c" | sed 's/http:/https:/' # check whether benchmark ran into a sane environment (no throttling and no swapping) if [ ${IOWaitAvg:-0} -le 2 ] && [ ${IOWaitMax:-0} -le 5 ] && [ ${SysMax:-0} -le 5 ] && [ ! -f "${TempDir}/throttling_info.txt" ]; then # in case it's not x86/x64 then also suggest adding results to official list case ${CPUArchitecture} in arm*|aarch*|riscv*|mips*|loongarch*) # not running on x86/x64, neither throttling nor relevant swapping occured. # Check whether SoC in question is already known since if true no more # submissions to official results are needed grep -q "^SoC guess:" "${ResultLog}" if [ $? -ne 0 ] && [ "X${MODE}" != "Xunattended" ]; then # not an already known SoC, so suggest submitting results echo -e "\n\nIn case this device ${BOLD}is not already represented${NC} in official sbc-bench results list then please" echo -e "consider submitting it at https://github.com/ThomasKaiser/sbc-bench/issues with this line:" tail -n 1 "${ResultLog}" fi echo ;; esac fi ;; *) if [ "X${MODE}" != "Xunattended" ]; then echo -e " |\n\n" >>"${ResultLog}" echo -e "\nUnable to upload full test results. Please copy&paste the below stuff to pastebin.com and\nprovide the URL. Check the output for throttling and swapping please.\n\n" >&2 sed '/^$/N;/^\n$/D' <"${ResultLog}" >&2 fi ;; esac # write continous log, see # Skip log writing if log is a symlink to somewhere else [ -h /var/log/sbc-bench.log ] && return 1 [ -s /var/log/sbc-bench.log ] && echo -e "\n\n\n" >>/var/log/sbc-bench.log sed '/^$/N;/^\n$/D' <"${ResultLog}" >>/var/log/sbc-bench.log echo "" # On systems running with Rockchip BSP kernel check DMC governor settings and if not # optimal, suggest retesting with performance DMC governor if [ -f "${DMCGovernor}" ]; then if [ "X${UsedDMCGovernor}" != "Xperformance" ]; then echo -e "\n${LRED}${BOLD}WARNING:${NC} ${LRED}The DMC governor settings of this device are not adjusted for performance${NC}" echo -e "${LRED}and as such DRAM bandwidth and latency might have been severly harmed. Your current\nsettings: ${DMCGovernorSettings}\n${NC}" echo -e "${LRED}For a discussion wrt settings see https://github.com/ThomasKaiser/Knowledge/issues/7${NC}\n" echo -e "${LRED}It is strongly advised to ${BOLD}switch to maximum performance and run sbc-bench again${NC} ${LRED}to be${NC}" echo -e "${LRED}able to compare real hardware performance with default settings of this distro/kernel.${NC}" echo -e "${LRED}You might want to execute this now and call sbc-bench again directly afterwards:\n${NC}" echo -e "${LRED}echo performance | sudo tee ${DMCGovernor}\n${NC}" echo -e "${LRED}Alternatively you can simply use review mode: sbc-bench -r\n${NC}" fi fi } # UploadResults CheckIOWait() { IOWait="$(awk -F" " '/^[0-9]/ {print $8}' <"${MonitorLog}" | sed 's/%//')" LogLength=$(wc -l <<<"${IOWait}") IOWaitSum="$(awk '{s+=$1} END {printf "%.0f", s}' <<<"${IOWait}")" echo $(( ${IOWaitSum} * 10 / ${LogLength} )) } # CheckIOWait CheckCPUUtilization() { # $1 is the CPU utilization w/o any disturbing background tasks, e.g. 50% on a dual # core system, 12% on an octa core, %25 on a quad core and so on IOWait="$(awk -F" " '/^[0-9]/ {print $8}' <"${MonitorLog}" | sed 's/%//')" LogLength=$(wc -l <<<"${IOWait}") IOWaitSum="$(awk '{s+=$1} END {printf "%.0f", s}' <<<"${IOWait}")" echo $(( ${IOWaitSum} * 10 / ${LogLength} )) } # CheckCPUUtilization CheckForThrottling() { # skip this check if $MaxKHz is set [ "X${MaxKHz}" != "X" ] && return # Check for throttling on normal ARM or RISC-V SBC (on x86 cpufreq statistics are # more or less irrelevant) case ${CPUArchitecture} in arm*|aarch*|riscv*) if [ -f /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state ]; then ls /sys/devices/system/cpu/cpufreq/policy?/stats/time_in_state | sort | while read ; do Number=$(echo ${REPLY} | tr -c -d '[:digit:]') diff "${TempDir}/time_in_state_after_${Number}" "${TempDir}/time_in_state_before_${Number}" >/dev/null 2>&1 if [ $? -ne 0 ]; then if [ ${#ClusterConfig[@]} -eq 1 ]; then # all CPU cores have same cpufreq policies, we report globally ReportCpufreqStatistics ${Number} echo -e "${LRED}${BOLD}ATTENTION: Throttling${SwapWarning} occured. Check the log for details.${NC}\n" else # separate swapping warning: [ "${SwapWarning}" = "" ] || echo -e "${LRED}${BOLD}ATTENTION: Swapping has occured invalidating benchmark scores.${NC}\n" # report affected cluster for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do if [ ${ClusterConfig[$i]} -eq ${Number} ]; then FirstCore=${Number} LastCore=$(GetLastClusterCore $(( ${i} + 1 ))) CPUInfo="$(GetCPUInfo ${Number})" ReportCpufreqStatistics ${Number} " for CPUs ${FirstCore}-${LastCore}${CPUInfo}" echo -e "${LRED}${BOLD}ATTENTION: Throttling occured on CPUs ${FirstCore}-${LastCore}${CPUInfo}. Check the log for details.${NC}\n" fi done fi fi done fi # Check for killed CPU cores. Some unfortunate users might still use Allwinner BSP kernels CPUCoresNow=$(lscpu | awk -F" " '/^CPU...:/ {print $2}') if [ ${CPUCoresNow} -lt ${CPUCores} ]; then echo -e "${LRED}${BOLD}ATTENTION: Due to overheating prevention $(( ${CPUCores} - ${CPUCoresNow} )) CPU cores have been killed. Check the log for details.${NC}\n" fi # Check for throttling/undervoltage on Raspberry Pi grep -q '1400/1200MHz' "${MonitorLog}" && Warning="ATTENTION: Silent throttling has occured. Check the log for details." if [ ${USE_VCGENCMD} = true ] ; then Health="$(LC_ALL=C perl -e "printf \"%19b\n\", $("${VCGENCMD}" get_throttled | cut -f2 -d=)" 2>/dev/null | tr -d '[:blank:]')" # https://forum.armbian.com/topic/7763-benchmarking-cpus/?do=findComment&comment=59042 HealthLength=$(wc -c <<<"${Health}") [ ${HealthLength} -eq 19 ] && Health="0${Health}" case ${Health} in 10*) Warning="ATTENTION: Frequency capping to 600 MHz${SwapWarning} has occured. Check the log for details." ReportRPiHealth ${Health} >>"${TempDir}/throttling_info.txt" ;; 01*) Warning="ATTENTION: Throttling${SwapWarning} has occured. Check the log for details." ReportRPiHealth ${Health} >>"${TempDir}/throttling_info.txt" ;; 11*) Warning="ATTENTION: Throttling and frequency capping${SwapWarning} has occured. Check the log for details." ReportRPiHealth ${Health} >>"${TempDir}/throttling_info.txt" ;; esac if [ "X${Warning}" = "X" ]; then echo -e "${LGREEN}No downclocking of CPU cores occured.${NC}\n" else echo -e "${LRED}${BOLD}${Warning}${NC}\n" fi fi ;; esac } # CheckForThrottling ReportCpufreqStatistics() { # Displays cpufreq driver statistics from before and after the benchmark as comparison if [ -f "${TempDir}/full_time_in_state_before_${1}" ]; then echo -e "\nThrottling statistics (time spent on each cpufreq OPP)${2}:\n" >>"${TempDir}/throttling_info.txt" awk -F" " '{print $1}' <${TempDir}/full_time_in_state_before_${1} | sort -r -n | while read ; do if [ ${REPLY} -le ${MaxKHz:-90000000} ]; then BeforeValue=$(awk -F" " "/^${REPLY} / {print \$2}" <${TempDir}/full_time_in_state_before_${1}) AfterValue=$(awk -F" " "/^${REPLY} / {print \$2}" <${TempDir}/full_time_in_state_after_${1}) if [ ${AfterValue} -eq ${BeforeValue} ]; then Duration=0 else Duration=$(awk '{printf ("%0.2f",$1/100); }' <<<$(( ${AfterValue} - ${BeforeValue} )) ) fi echo -e "$(printf "%4s" $(( ${REPLY} / 1000 )) ) MHz: $(printf "%7s" ${Duration}) sec" >>"${TempDir}/throttling_info.txt" fi done fi } # ReportCpufreqStatistics Parse_ADC_Readouts() { # this function tries to multiply all amperage and voltage values spitten out by the # RPi 5B PMIC ADCs. Querying ThreadX via 'vcgencmd pmic_read_adc' will result in # something like this: https://gist.github.com/ThomasKaiser/f98c6f97a2f82d85791cbaf6cebf10b8 grep current <<<"${1}" | while read ; do PowerRail="${REPLY%_A*}" Ampere=$(sed 's/A//' <<<"${REPLY##*=}") Voltage=$(grep "${PowerRail}_V" <<<"${1}") [ -n "${Voltage}" ] && echo "${Ampere} ${Voltage##*=}" | sed 's/V//' done } # Parse_ADC_Readouts ReportRPiHealth() { # Displaying the 'vcgencmd get_throttled' output in an understandable way echo -e "\nQuerying ThreadX on RPi for thermal or undervoltage issues:\n\n${1}" cat <<-EOF ||| |||_ under-voltage ||| ||_ currently throttled ||| |_ arm frequency capped |||_ under-voltage has occurred since last reboot ||_ throttling has occurred since last reboot |_ arm frequency capped has occurred since last reboot EOF } # ReportRPiHealth CacheAndDIMMDetails() { DIMMDetails="$(lshw -C memory 2>/dev/null | grep -E "^ |-bank:" | grep -vi "serial:")" if [ "X${DIMMDetails}" != "X" ]; then if [ "X${VirtWhat}" = "X" ] || [ "X${VirtWhat}" = "Xnone" ]; then # only report DIMM config when running bare metal echo "${DIMMDetails}" >"${TempDir}/dimms" # unfortunately lshw only reports max clockspeeds of DIMM modules so we try to # get configured speed via dmidecode as well DIMMFilter="$(echo -e "\tLocator:|\tConfigured Memory Speed:|\tConfigured Clock Speed:")" command -v dmidecode >/dev/null 2>&1 && dmidecode --type 17 | grep -E "${DIMMFilter}" | tr "\n" "|" | sed -e 's/|\tCon/:/g' -e 's/\ //g' | tr '|' '\n' | grep -vi "unknown$" | while read ; do Pattern="$(awk -F":" '{print $2}' <<<"${REPLY}")" Insertion="$(awk -F":" '{print $4}' <<<"${REPLY}" | sed 's/\//\\\//')" if [ "X${Insertion}" != "X" ]; then sed -i "s/^ slot: ${Pattern}$/ configured speed: ${Insertion}/g" "${TempDir}/dimms" fi done echo -e "\nDIMM configuration:" cat "${TempDir}/dimms" fi fi # in MODE=extensive check wether there's even more detailed DIMM info available via i2c if [ "X${MODE}" = "Xextensive" ]; then unset DIMMDetails modprobe eeprom >/dev/null 2>&1 command -v decode-dimms >/dev/null 2>&1 && \ DIMMDetails="$(decode-dimms 2>/dev/null | sed -ne '/Decoding EEPROM/,$ p' | sed '/^$/N;/^\n$/D' | grep -v Serial)" if [ "X${DIMMDetails}" != "X" ]; then echo -e "\n${DIMMDetails}" fi fi if [ ${#ClusterConfig[@]} -gt 1 ] || [ "${CPUArchitecture}" = "riscv64" ]; then # only output individual cache sizes from sysfs on RISC-V or if more than 1 CPU # cluster (since otherwise lscpu already reported the full picture) find /sys/devices/system/cpu -name "cache" -type d 2>/dev/null | sort -V | while read ; do find "${REPLY}" -name size -type f 2>/dev/null | while read ; do echo -e "\n${REPLY}: $(cat ${REPLY})\c" [ -f ${REPLY%/*}/level ] && echo -e ", level: $(cat ${REPLY%/*}/level)\c" [ -f ${REPLY%/*}/type ] && echo -e ", type: $(cat ${REPLY%/*}/type)\c" done done | sed -e 's|/sys/devices/system/cpu/||' -e 's|cache/||' -e 's|/size||' | sort -V fi echo "" } # CacheAndDIMMDetails GuessARMSoC() { # function that tries to guess ARM SoC names correctly # # ARM core types/steppings this function deals with as of early 2024: # https://gist.github.com/ThomasKaiser/1fef6a6a09b07d48632ca99ec075a1df # # For a rough performance estimate wrt different Cortex ARMv8 cores see: # https://www.cnx-software.com/2021/12/10/starfive-dubhe-64-bit-risc-v-core-12nm-2-ghz-processors/#comment-588823 # # As for the logic wrt Rockchip and Amlogic BSP kernel output parsing see here please: # https://gist.github.com/ThomasKaiser/f28027f182ff9ae7bfa60065c950be75 # # MIDR_EL1 related stuff: https://gist.github.com/ThomasKaiser/d99228ac986378c41f4f8e6bc3f5cb70 # # Allwinner SID related stuff: https://gist.github.com/ThomasKaiser/05a0ec1685ee19b3713640b06431d2b7 # If in simulation mode (-m and CPUINFOFILE set) skip all SoC vendor specific detection # mechanisms (dmesg, nvmem) if [ "X${ProcCPUFile}" != "X/proc/cpuinfo}" ]; then SoCGuess="$(GuessSoCbySignature)" if [ "X${SoCGuess}" != "X" ] && [ "X${MonitorMode}" = "XTRUE" ]; then echo "${SoCGuess}" return fi fi [ "X${AW_NVMEM}" != "X" ] && Allwinner_SID=" (SID: ${AW_NVMEM:19:2}${AW_NVMEM:16:2}${AW_NVMEM:13:2}${AW_NVMEM:10:2})" || Allwinner_SID="" HardwareInfo="$(awk -F': ' '/^Hardware/ {print $2}' <<< "${ProcCPU}" | tail -n1)" RockchipGuess="$(awk -F': ' '/rockchip-cpuinfo cpuinfo: SoC/ {print $3}' <<<"${DMESG}" | head -n1)" AmlogicGuess="Amlogic Meson$(grep -i " detected$" <<<"${DMESG}" | awk -F"Amlogic Meson" '/Amlogic Meson/ {print $2}' | head -n1)" AMLS4Guess="$(awk -F"= " '/cpu_version: chip version/ {print $2}' <<<"${DMESG}")" if [ "X${RockchipGuess}" != "X" ] && [ "X${RockchipGuess}" != "X0" ]; then # use Rockchip SoC info from dmesg output if [ "X${RK_NVMEM}" != "X" ] && [ "${RockchipGuess:0:4}" = "3588" ]; then case "${RK_NVMEM:28:2}" in 21) echo "Rockchip RK3588 (${RockchipGuess} / ${RK_NVMEM:16:42})" ;; 33) echo "Rockchip RK3588s (${RockchipGuess} / ${RK_NVMEM:16:42})" ;; *) echo "Rockchip RK3588/RK3588s (${RockchipGuess} / ${RK_NVMEM:16:42})" ;; esac elif [ "X${RK_NVMEM}" != "X" ]; then echo "Rockchip RK${RockchipGuess:0:4} (${RockchipGuess} / ${RK_NVMEM:16:42})" | sed 's| RK3588| RK3588/RK3588s|' else echo "Rockchip RK${RockchipGuess:0:4} (${RockchipGuess})" | sed 's| RK3588| RK3588/RK3588s|' fi elif [ "X${RK_NVMEM}" != "X" ]; then # use Rockchip NVMEM available below /sys/bus/nvmem/devices/rockchip* to parse SoC model from there case ${RK_NVMEM:16:5} in 03*|13*|23*|53*|81*) # reverse order: 52 4b 23 82 -> RK3228, also affects RK3288 # RK3228A and RK3229 share same '52 4b 23 82' signature echo "Rockchip RK${RK_NVMEM:17:1}${RK_NVMEM:16:1}${RK_NVMEM:20:1}${RK_NVMEM:19:1}" | sed 's|3228|3229/RK3228A|' ;; 33*) # unknown order, affects RK3306, RK3308, RK3318, RK3326, RK3328, RK3358 and in theory RK3399 case ${RK_NVMEM:19:2} in 80|81|62|82|85) # reverse order: 52 4b 33 81 -> RK3318, also affects RK3328 echo "Rockchip RK${RK_NVMEM:17:1}${RK_NVMEM:16:1}${RK_NVMEM:20:1}${RK_NVMEM:19:1}" ;; *) # normal order echo "Rockchip RK${RK_NVMEM:16:2}${RK_NVMEM:19:2}" ;; esac ;; 11*) # unknown order, affects RV1103, RV1106, RV1109 and RV1126 case ${RK_NVMEM:19:2} in 30|60|62|90) echo "Rockchip RV11${RK_NVMEM:20:1}${RK_NVMEM:19:1}" ;; *) echo "Rockchip RV11${RK_NVMEM:19:2}" ;; esac ;; "35 88") # RK3588/RK3588s, normal order: 52 4b 35 88 -> RK3588 case "${RK_NVMEM:28:2}" in 21) echo "Rockchip RK3588 / ${RK_NVMEM:16:42}" ;; 33) echo "Rockchip RK3588s / ${RK_NVMEM:16:42}" ;; *) echo "Rockchip RK3588/RK3588s / ${RK_NVMEM:16:42}" ;; esac ;; *) # normal order: 52 4b 35 28 -> RK3528, also affects RK3566, RK3568, RK3582, RK3583 echo "Rockchip RK${RK_NVMEM:16:2}${RK_NVMEM:19:2} / ${RK_NVMEM:16:42}" ;; esac elif [ "X${AmlogicGuess}" != "XAmlogic Meson" ]; then echo "${AmlogicGuess}" | sed -e 's/GXL (Unknown) Revision 21:b (2:2)/GXL (S905D) Revision 21:b (2:2)/' \ -e 's/GXL (Unknown) Revision 21:c (84:2)/GXL (S905X) Revision 21:c (84:2)/' \ -e 's/GXL (Unknown) Revision 21:c (c2:2)/GXL (S905L) Revision 21:c (c2:2)/' \ -e 's/GXL (Unknown) Revision 21:c (e2:2)/GXL (S905X) Revision 21:c (e2:2)/' \ -e 's/GXL (Unknown) Revision 21:d (4:2)/GXL (S905D) Revision 21:d (4:2)/' \ -e 's/GXL (Unknown) Revision 21:d (a4:2)/GXL (S905W) Revision 21:d (a4:2)/' \ -e 's/GXM (Unknown) Revision 22:a (82:2)/GXM (S912) Revision 22:a (82:2)/' \ -e 's/AXG (Unknown) Revision 25:b (43:2)/AXG (A113X) Revision 25:b (43:2)/' \ -e 's/AXG (Unknown) Revision 25:c (43:2)/AXG (A113X) Revision 25:c (43:2)/' \ -e 's/G12A (Unknown) Revision 28:b (30:2)/G12A (S905Y2) Revision 28:b (30:2)/' \ -e 's/G12A (Unknown) Revision 28:c (30:2)/G12A (S905Y2) Revision 28:c (30:2)/' \ -e 's/G12A (Unknown) Revision 28:b (70:2)/G12A (S905L3A) Revision 28:b (70:2)/' \ -e 's/G12A (Unknown) Revision 28:c (70:2)/G12A (S905L3A) Revision 28:c (70:2)/' \ -e 's/SM1 (Unknown) Revision 2b:b (1:2)/SM1 (S905D3) Revision 2b:b (1:2)/' \ -e 's/SM1 (Unknown) Revision 2b:c (10:2)/SM1 (S905X3) Revision 2b:c (10:2)/' \ -e 's/ Detected//' -e 's/ detected//' elif [ "X${AMLS4Guess}" != "X" ]; then case ${AMLS4Guess} in *" 3:4") echo "Amlogic Meson S4 (S905Y4) Revision ${AMLS4Guess}" ;; *) echo "Amlogic Meson S4 (S805X2 or S905Y4) Revision ${AMLS4Guess}" ;; esac else # Guessing by 'Hardware :' in /proc/cpuinfo is something that only reliably works # with vendor's BSP kernels. With mainline kernel it's impossible to rely on this case ${HardwareInfo} in Amlogic*) ModelName="$(awk -F': ' '/^model name/ {print $2}' <<< "${ProcCPU}" | tail -n1)" case "${ModelName}" in Amlogic*) echo "${ModelName}" ;; *) AmLogicSerial="$(awk -F': ' '/^AmLogic Serial/ {print $2}' <<< "${ProcCPU}" | tail -n1)" if [ "X${AmLogicSerial}" = "X" ]; then AmLogicSerial="$(awk -F': ' '/^Serial/ {print $2}' <<< "${ProcCPU}" | tail -n1)" fi case "${AmLogicSerial}" in 19*) # Meson8: S802: RevC (19 - 0:27ED) echo "Amlogic S802" ;; 1b*) # Meson8B: S805: RevA (1b - 0:B72) echo "Amlogic S805" ;; 1d*) # Meson8m2: S812: RevA (1d - 0:74E) echo "Amlogic S812" ;; 1f??0*|1f??1*) # GXBB: S905: 1f:b (0:1) / 1f:c (0:1) / 1f:c (13:1) ParseAmlSerial GXBB S905 ;; 1f??23*) # GXBB: S905H: 1f:c (23:1) ParseAmlSerial GXBB S905H ;; 1f??20*) # GXBB: S905M ParseAmlSerial GXBB S905M ;; 1f*) # GXBB: S905, S905H, S905M # - S905: 1f:b (0:1) / 1f:c (0:1) # - S905H: 1f:c (23:1) ParseAmlSerial GXBB S905/S905H/S905M ;; 20*) # GXTVBB ParseAmlSerial GXTVBB Unknown ;; 21??0*|21??4*) # GXL: S905D: 21:d (0:2), 21:d (4:2) ParseAmlSerial GXL S905D ;; 21??3*) # GXL: S805X: 21:d (34:2) ParseAmlSerial GXL S805X ;; 21??8*) # GXL: S905X: 21:a (82:2), 21:b (82:2), 21:c (84:2), 21:d (84:2) ParseAmlSerial GXL S905X ;; 21??a*) # GXL: S905W: 21:b (a2:2), 21:e (a5:2) ParseAmlSerial GXL S905W ;; 21??b*) # GXL: S805Y ParseAmlSerial GXL S805Y ;; 21??c*) # GXL: S905L: 21:b (c2:2), 21:c (c4:2), 21:d (c4:2), 21:e (c5:2) ParseAmlSerial GXL S905L ;; 21??e*) # GXL: S905M2: 21:b (e2:2), 21:d (e4:2) ParseAmlSerial GXL S905M2 ;; 21*) # GXL: S805X, S805Y, S905X, S905D, S905W, S905L, S905L2, S905M2 # - S905D: 21:d (0:2), 21:d (4:2) # - S805X: 21:d (34:2) # - S905X: 21:a (82:2), 21:b (82:2), 21:c (84:2), 21:d (84:2) # - S905W: 21:b (a2:2), 21:e (a5:2) # - S905L: 21:b (c2:2), 21:c (c4:2), 21:d (c4:2), 21:e (c5:2) # - S905M2: 21:b (e2:2), 21:d (e4:2) ParseAmlSerial GXL Unknown ;; 22*) # GXM: S912: 22:a (82:2), 22:b (82:2) ParseAmlSerial GXM S912 ;; 23*) # TXL ParseAmlSerial TXL Unknown ;; 24??10*) # TXLX: T962X ParseAmlSerial TXLX T962X ;; 24??20*) # TXLX: T962E ParseAmlSerial TXLX T962E ;; 24*) # TXLX: T962X, T962E ParseAmlSerial TXLX Unknown ;; 25??22*) # AXG: A113D ParseAmlSerial AXG A113D ;; 25??37*) # AXG: A113X ParseAmlSerial AXG A113X ;; 25*) # AXG: A113X, A113D # - Unknown: 25:b (43:2), 25:c (43:2) ParseAmlSerial AXG Unknown ;; 26*) # GXLX, seems to be compatible to GXL since one occurence of ID '26:e (c1:2)' # has been detected with 'Amlogic Meson GXL (S905X) P212 Development Board' # and same ID on IPBS9505 TV box ParseAmlSerial GXLX Unknown ;; 27*) # TXHD ParseAmlSerial TXHD Unknown ;; 28??1*) # G12A: S905D2: 28:b (10:2) ParseAmlSerial G12A S905D2 ;; 28??3*) # G12A: S905Y2: 28:b (30:2) ParseAmlSerial G12A S905Y2 ;; 28??4*) # G12A: S905X2: 28:b (40:2) ParseAmlSerial G12A S905X2 ;; 28??7*) # G12A: S905L3A: 28:b (70:2) ParseAmlSerial G12A S905L3A ;; 28*) # G12A: S905X2, S905D2, S905Y2 # - S905X2: 28:b (40:2) # - S905Y2: 28:b (30:2) # - S905L3A: 28:b (70:2) ParseAmlSerial G12A Unknown ;; 29??1*) # - G12B: A311D: 29:b (10:2) # - G12B: A311D: 29:c (10:0) ParseAmlSerial G12B A311D ;; 29??4*) # - G12B: S922X: 29:a (40:2), 29:b (40:2), 29:c (40:2) ParseAmlSerial G12B S922X ;; 29*) # G12B: A311D, S922X # - A311D: 29:b (10:2) # - S922X: 29:a (40:2), 29:b (40:2) # - 'S922X-B': 29:c (40:2) ParseAmlSerial G12B Unknown ;; 2a*) # GXLX2: some weird GXL variant, most probably for S905L2 only # https://www.cnx-software.com/2020/09/15/low-cost-amlogic-s905l2-tv-boxes-show-up-on-aliexpress-for-20/ # http://ix.io/45QA / http://ix.io/3RLI # Fake 2 GHz while in reality 1.2 GHz, 'Revision 2a:e (c5:2)' # while S905L always appears as 'Revision 21:X (cX:X)' ParseAmlSerial GXLX2 S905L2 ;; 2b??05*|2b??10*) # SM1: S905X3: 2b:c (10:2) ParseAmlSerial SM1 S905X3 ;; 2b??01*|2b??04*|2b??30*) # SM1: S905D3: 2b:b (1:2), 2b:c (4:2) ParseAmlSerial SM1 S905D3 ;; 2b*) # SM1: S905X3, S905D3, S905Y3: 2b:b (1:2), 2b:c (4:2), 2b:c (10:2), 2b:b (40:2) ParseAmlSerial SM1 Unknown ;; 2c*) # A1: A113L (dual A35: https://www.amlogic.com/#Products/408/index.html / meson-a1.dtsi) ParseAmlSerial A1 A113L ;; 2e*) # TL1: T962X2 ParseAmlSerial TL1 T962X2 ;; 2f*) # TM2: T962X3, T962E2 (quad-core A55) ParseAmlSerial TM2 T962X3/T962E2 ;; 30*) # C1 ParseAmlSerial C1 Unknown ;; 32??02*) # SC2: S905X4: 32:b (2:2) ParseAmlSerial SC2 S905X4 ;; 32*) # SC2: S905X4, S905C2 ParseAmlSerial SC2 S905X4/S905C2 ;; 33*) # C2 ParseAmlSerial C2 Unknown ;; 34*) # T5 ParseAmlSerial T5 Unknown ;; 35*) # T5D: T950D4, T950X4 (quad-core A53) ParseAmlSerial T5D Unknown ;; 360b*) # T7: A311D2, POP1-G: 36:b (1:3) ParseAmlSerial T7 A311D2 ;; 360c*) # T7C: A311D2-N0D (A311D2 with NPU and different ISP): 36:c (1:2) # https://docs.khadas.com/products/sbc/vim4/configurations/identify-version / https://archive.md/YUeWa ParseAmlSerial T7C A311D2-N0D ;; 370b*) # S4: S905Y4 ParseAmlSerial S4 S905Y4 ;; 37*) # S4: S905Y4, S805X2, S905W2 ParseAmlSerial S4 S905Y4/S805X2/S905W2 ;; 38*) # T3: T965D4, T963D4, T982 (quad-core A55) ParseAmlSerial T3 Unknown ;; 39*) # P1: (8 x A55) https://tinyurl.com/ye8m9uf4 ParseAmlSerial P1 Unknown ;; 3a*) # S4D: S805C3, S905C3, S905C3ENG (quad Cortex-A35): https://archive.md/4H6xM # but quad-core A55 according to Amlogic 5.4 BSP kernel: tinyurl.com/r598z7aa # and CoreElec device tree files # https://tinyurl.com/y85lsxsc and/vs. https://tinyurl.com/5n99muj6 ParseAmlSerial S4D Unknown ;; 3b*) # T5W: T962D4 (quad-core A55) ParseAmlSerial T5W Unknown ;; 3c*) # A5: A113X2 (quad-core A55) ParseAmlSerial A5 Unknown ;; 3d*) # C3 ParseAmlSerial C3 Unknown ;; 3e*) # S5: S928X (4 x A55 + 1 x A76) https://tinyurl.com/mpwhcsua ParseAmlSerial S5 Unknown ;; *) # https://tinyurl.com/wwuwdef7 ParseAmlSerial Unknown Unknown ;; esac ;; esac ;; sun3i*) # SoC ID: 0x1663 echo "Allwinner F1C100s/F1C100A/F1C200s/F1C500/F1C500s/F1C600/F1D100/R6${Allwinner_SID}" ;; sun4i*) # SoC ID: 0x1623 echo "Allwinner A10${Allwinner_SID}" ;; sun5i*) # SoC ID: 0x1625 echo "Allwinner A10s/A13/R8${Allwinner_SID}" ;; sun8iw10*) # SoC ID: 0x1699 echo "Allwinner B288/B100${Allwinner_SID}" ;; sun8iw11*) # SoC ID: 0x1701 echo "Allwinner R40/V40/T3/A40i${Allwinner_SID}" ;; sun8iw12*) # SoC ID: 0x1721 echo "Allwinner V5/V100${Allwinner_SID}" ;; sun8iw15*) # SoC ID: 0x1755 echo "Allwinner A50/MR133/R311${Allwinner_SID}" ;; sun8iw16*) # SoC ID: 0x1816 echo "Allwinner V313/V316/V526/V536/V5V200${Allwinner_SID}" ;; sun8iw17*) echo "Allwinner T7${Allwinner_SID}" ;; sun8iw18*) echo "Allwinner R328${Allwinner_SID}" ;; sun8iw19*) # SoC ID: 0x1817 echo "Allwinner V533/V833/V831${Allwinner_SID}" ;; sun6i*|sun8iw1*) # SoC ID: 0x1633 echo "Allwinner A31/A31s${Allwinner_SID}" ;; sun8iw20*) # SoC ID: 0x1859 echo "Allwinner F133/H133/R528/T113${Allwinner_SID}" ;; sun8iw21*) # SoC ID: 0x1886, https://v853.docs.aw-ol.com echo "Allwinner R853/V851S/V851SE/V853${Allwinner_SID}" ;; sun7iw2*|sun8iw2*) # SoC ID: 0x1651 echo "Allwinner A20${Allwinner_SID}" ;; sun8iw3*) # SoC ID: 0x1650 echo "Allwinner A23${Allwinner_SID}" ;; sun8iw5*) # SoC ID: 0x1667 echo "Allwinner A33/R16${Allwinner_SID}" ;; sun8iw6*) # SoC ID: 0x1673 echo "Allwinner A83T/H8/H80/V66/R58${Allwinner_SID}" ;; sun8iw7*) # SoC ID: 0x1680 echo "Allwinner H3/H2+${Allwinner_SID}" ;; sun8iw8*) # SoC ID: 0x1681 echo "Allwinner V3/S3/V3s${Allwinner_SID}" ;; sun9i*) # SoC ID: 0x1639 echo "Allwinner A80${Allwinner_SID}" ;; sun20iw1*) # https://d1.docs.aw-ol.com / https://d1s.docs.aw-ol.com echo "Allwinner D1${Allwinner_SID}" ;; sun20iw2*) # https://r128.docs.aw-ol.com echo "Allwinner R128${Allwinner_SID}" ;; sun50iw1p*) # SoC ID: 0x1689 # Since Armbian patched arch/arm64/kernel/cpuinfo.c since Aug 2016 every # other Allwinner ARMv8 SoC (H5/H6/H616) will identify itself as sun50iw1p1 # when kernel has been tampered with by Armbian. # https://github.com/armbian/build/issues/3400 / https://archive.md/VxK14 AllwinnerGuess="$(IdentifyAllwinnerARMv8 | head -n1)" case ${AllwinnerGuess} in *unidentified*) if [ -f /etc/armbian-release ]; then # use Armbian's release info since there we can rely on SoC family # naming unlike cpuinfo output that was fake for many years ArmbianReleaseFile=/etc/armbian-release elif [ -f /etc/orangepi-release ]; then # try to deal with Xunlong's lame Armbian rip-off over at # https://github.com/orangepi-xunlong/orangepi-build ArmbianReleaseFile=/etc/orangepi-release fi if [ -f "${ArmbianReleaseFile}" ]; then BoardFamily="$(awk -F'=' '/^BOARDFAMILY/ {print $2}' <"${ArmbianReleaseFile}")" case ${BoardFamily} in sun50iw1) echo "Allwinner A64${Allwinner_SID}" ;; sun50iw2) echo "Allwinner H5${Allwinner_SID}" ;; sun50iw6) echo "Allwinner H6${Allwinner_SID}" ;; sun50iw9) echo "Allwinner H616/H313/H618${Allwinner_SID}" ;; *) echo "Allwinner A64${Allwinner_SID} or https://tinyurl.com/yyf3d7fg" ;; esac else echo "Allwinner A64${Allwinner_SID} or https://tinyurl.com/yyf3d7fg" fi ;; *) echo "${AllwinnerGuess}${Allwinner_SID}" ;; esac ;; sun50iw2*) # SoC ID: 0x1718 echo "Allwinner H5${Allwinner_SID}" ;; sun50iw3*) # SoC ID: 0x1719 echo "Allwinner A63${Allwinner_SID}" ;; sun50iw6*) # SoC ID: 0x1728 echo "Allwinner H6${Allwinner_SID}" ;; sun50iw9*) # SoC ID: 0x1823 echo "Allwinner H313/H503/H513/H616/H618/H700/T507/T517${Allwinner_SID}" ;; sun50iw10*) # SoC ID: 0x1855 echo "Allwinner A100/A133/A53/B810/MR813/R818/T509${Allwinner_SID}" ;; sun50iw11*) # SoC ID: 0x1851, https://r329.docs.aw-ol.com echo "Allwinner R329${Allwinner_SID}" ;; sun50iw12*) # SoC ID: 0x1860 echo "Allwinner TV303${Allwinner_SID}" ;; sun55iw3*) # SoC ID: 0x1890 echo "Allwinner A523/A527/T523/T527/MR527${Allwinner_SID}" ;; sun55iw5*) echo "Allwinner TV301${Allwinner_SID}" ;; sun60iw1*) echo "Allwinner A736/T736${Allwinner_SID}" ;; sun60iw2*) echo "Allwinner A733${Allwinner_SID}" ;; sun*) SoCGuess="$(GuessSoCbySignature)" [ "X${SoCGuess}" = "X" ] && echo "unknown Allwinner SoC${Allwinner_SID}" || echo "${SoCGuess}${Allwinner_SID}" ;; Allwinner*) SoCGuess="$(GuessSoCbySignature)" [ "X${SoCGuess}" = "X" ] && sed -e 's/ board//' -e 's/ Family//' <<<"${HardwareInfo}" || echo "${SoCGuess}${Allwinner_SID}" ;; Hardkernel*) case ${HardwareInfo} in *XU3|*XU4|*HC1|*HC2|*MC1|*XU4Q) echo "Exynos 5422" ;; *C1*) echo "Amlogic S805" ;; *C2) echo "Amlogic S905" ;; *C4) echo "Amlogic S905X3" ;; *M1) echo "Rockchip RK3568" ;; *M1S) echo "Rockchip RK3566" ;; *N1) echo "Rockchip RK3399" ;; *N2*) echo "Amlogic S922X" ;; *) sed 's/Hardkernel //' <<<"${HardwareInfo}" ;; esac ;; *) # guess SoC based on CPU topology SoCGuess="$(GuessSoCbySignature)" if [ "X${SoCGuess}" != "X" ] && [ "X${VirtWhat}" != "X" ] && [ "X${VirtWhat}" != "Xnone" ]; then # add virtualization disclaimer echo "${SoCGuess}${Allwinner_SID} / guessing impossible since running in ${VirtWhat}" else echo "${SoCGuess:-n/a}${Allwinner_SID}" fi ;; esac fi } # GuessARMSoC ParseAmlSerial() { # gets SoC family and SoC guess as $1/$2 and then translates the Serial number's digits # 0-7 into Soc variant/revision details. Looks like this for example: # GXL S905X 210a820094e04a85134XXXXXXXXXXXXX -> Amlogic Meson GXL (S905X) Revision 21:a (82:0) # T7 A311D2 360b0103000000000e0XXXXXXXXXXXXX -> Amlogic Meson T7 (A311D2) Revision 36:b (1:3) # SC2 S905X4 320b020200000000190XXXXXXXXXXXXX -> Amlogic Meson SC2 (S905X4) Revision 32:b (2:2) echo "Amlogic Meson ${1} (${2}) Revision ${AmLogicSerial:0:2}:${AmLogicSerial:2:2} (${AmLogicSerial:4:2}:${AmLogicSerial:6:2})" | sed -e 's/:0/:/g' -e 's/(0/(/' } # ParseAmlSerial GuessSoCbySignature() { # Guess by CPU topology (core types and revision, clusters and cpufreq policies) and by # specific features/flags. Skip whole check if one or more cores are offline. if [ "X${OfflineCores}" != "X" ]; then echo "Not able to guess since these CPU cores are offline: ${OfflineCores}" return 1 fi case ${CPUSignature} in ??A8r1p3) # TI OMAP3530, Cortex-A8 / r1p3 / swp half thumb fastmult vfp edsp thumbee neon echo "TI Sitara OMAP3530" ;; ??A8r1p7) # TI Sitara AM35xx, Cortex-A8 / r1p7 echo "TI Sitara AM35xx" ;; ??A8r2p2) # Samsung Exynos 3110 (S5PC110): 1 x Cortex-A8 / r2p2 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 echo "Samsung Exynos 3110 (S5PC110)" ;; ??A8r2p5) # Freescale/NXP i.MX515: 1 x Cortex-A8 / r2p5 / https://bench.cr.yp.to/computers.html echo "Freescale/NXP i.MX515" ;; ??A8r3p2) # TI OMAP3530/DM3730/AM335x or Allwinner A10, 1 x Cortex-A8 / r3p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 case "${Allwinner_SID:7:8}" in 1623*) echo "Allwinner A10" ;; 1625*) echo "Allwinner A10s/A13/R8" ;; *) lsmod | grep -i -q sun4i case $? in 0) # Allwinner A10, 1 x Cortex-A8 / r3p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 echo "Allwinner A10" ;; *) case "${DTCompatible,,}" in *sun4i*) # Allwinner A10, 1 x Cortex-A8 / r3p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 echo "Allwinner A10" ;; *sun5i*) # Allwinner A10s/A13/R8, 1 x Cortex-A8 / r3p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 echo "Allwinner A10s/A13/R8" ;; *) # TI DM3730/AM335x, 1 x Cortex-A8 / r3p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 echo "TI DM3730/AM335x" ;; esac ;; esac ;; esac ;; 00A7r0p5) # Allwinner R853/S3/V3/V3s/V533/V831/V833/V851S/V851SE/V853, 1 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm # or maybe Microchip SAMA7G54, 1 x Cortex-A7 / r0p5 # or maybe Rockchip RV1108 | 1 x Cortex-A7 / r0p5 case "${DTCompatible,,}" in *rockchip*) # Rockchip RV1108 | 1 x Cortex-A7 / r0p5 echo "Rockchip RV1108" ;; *sun8iw8*) # Allwinner S3/V3/V3s, 1 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner S3/V3/V3s" ;; *sun8iw19*) # Allwinner V533/V831/V833, 1 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner V533/V831/V833" ;; *sun8iw21*) # Allwinner R853/V851S/V851SE/V853, 1 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner R853/V851S/V851SE/V853" ;; *allwinner*) # Allwinner R853/S3/V3/V3s/V533/V831/V833/V851S/V851SE/V853, 1 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner R853/S3/V3/V3s/V533/V831/V833/V851S/V851SE/V853" ;; *) # Microchip SAMA7G54, 1 x Cortex-A7 / r0p5 echo "Microchip SAMA7G54" ;; esac ;; ??A9r3p0??A9r3p0??A9r3p0??A9r3p0) # RK3188 or Exynos 4412 or Nexell S5P4418, 4 x Cortex-A9 / r3p0 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 case "${DTCompatible,,}" in *s5p4418*) echo "Nexell S5P4418" ;; *samsung*) echo "Exynos 4412" ;; *rk3188*) echo "Rockchip RK3188" ;; esac ;; ??A9r3p0??A9r3p0) # AML8726-MX, 2 x Cortex-A9 / r3p0 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 # or RK3066, 2 x Cortex-A9 / r3p0 / swp half thumb fastmult vfp edsp neon vfpv3 / https://lore.kernel.org/all/CAAFQd5CN_xvkdD+Bf9A+Mc+_jVxtdOKosrYH_8bNNHkGQw7eGA@mail.gmail.com/T/ # or ST Micro STiH415, 2 x Cortex-A9 / r3p0 / swp half thumb fastmult vfp edsp neon vfpv3 tls case "${DTCompatible,,}" in *amlogic*) echo "Amlogic AML8726-MX" ;; *rk3066*) echo "Rockchip RK3066" ;; *) echo "ST Micro STiH416" ;; esac ;; *A9r3p0) # Mediatek MT5880, 1 x Cortex-A9 / r3p0 / swp half thumb fastmult vfp edsp vfpv3 vfpv3d16 # or ST Micro STiH415: 1 x Cortex-A9 / r3p0 / swp half thumb fastmult vfp edsp neon vfpv3 tls # or Xilinx Zynq 7000S: 1 x Cortex-A9 / r3p0 / half thumb fastmult vfp edsp neon vfpv3 tls vfpd32 case "${DTCompatible,,}" in *mt5880*) echo "Mediatek MT5880" ;; *xilinx*) echo "Xilinx Zynq 7000S" ;; *) echo "ST Micro STiH415" ;; esac ;; 00A7r0p400A7r0p4) # Allwinner A20, 2 x Cortex-A7 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner A20" ;; 00A7r0p500A7r0p500A7r0p500A7r0p5) # Allwinner sun8i: could be Allwinner H3/H2+, R40/V40/T3/A40i or A33/R16 or A50/MR133/R311 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm # or Spreadtrum SC7731/SC8830 or Rockchip RV1126/RK3126/RK3126B/RK3126C/RK3128 case "${DTCompatible,,}" in *rv1126*) # Rockchip RV1126 | 4 x Cortex-A7 / r0p5 echo "Rockchip RV1126" ;; *rk3126b*) # Rockchip RK3126B | 4 x Cortex-A7 / r0p5 echo "Rockchip RK3126B" ;; *rk3126c*) # Rockchip RK3126C | 4 x Cortex-A7 / r0p5 echo "Rockchip RK3126C" ;; *rk3126*) # Rockchip RK3126 | 4 x Cortex-A7 / r0p5 echo "Rockchip RK3126" ;; *rk3128*) # Rockchip RK3128 | 4 x Cortex-A7 / r0p5 echo "Rockchip RK3128" ;; *rockchip*) # Rockchip RV1126 or RK3126/RK3126B/RK3126C or RK3128 | 4 x Cortex-A7 / r0p5 echo "Rockchip RV1126 or RK3126/RK3126B/RK3126C or RK3128" ;; *hi3796*) # HiSilicon Hi3796M-V100, 4 x Cortex-A7 / r0p5 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt echo "HiSilicon Hi3796M-V100" ;; *hi3798*) # HiSilicon Hi3798M-V100, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae echo "HiSilicon Hi3798M-V100" ;; *hisilicon*) # HiSilicon Hi3796M-V100 or Hi3798M-V100, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae echo "HiSilicon Hi3796M-V100 or Hi3798M-V100" ;; *sc7731*) # Spreadtrum SC7731: 4 x Cortex-A7 / r0p5 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 echo "Spreadtrum SC7731" ;; *sc8830*) # Spreadtrum SC8830: 4 x Cortex-A7 / r0p5 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 echo "Spreadtrum SC8830" ;; *sun8i|*) case "${Allwinner_SID:7:8}" in 02c00?81) echo "Allwinner H3" ;; 02c00?42) echo "Allwinner H2+" ;; 02c000*) echo "Allwinner H3/H2+" ;; 0461872a) echo "Allwinner A33/R16" ;; 12c00017|16554153) # checking sbc-bench submissions all BPI-M2 Berry/Ultra (R40/V40) show 16554153 # as first part of the serial number (which should be the relevant SID part with # mainline kernel). Could be a bug in Allwinner's BSP kernel but the versions # were as follows: 5.1, 5.4, 5.10, 5.15, 5.16, 5.19, 6.0, 6.1, 6.2 and 6.4 echo "Allwinner R40/V40" ;; *) case "${DTCompatible,,}" in *sun8i-r40*|*sun8i-v40*|*sun8i-a40i*|*sun8i-t3*) # Allwinner R40/V40/T3/A40i, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner R40/V40/T3/A40i" ;; *sun8i-h3*) # Allwinner H3, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner H3" ;; *sun8i-h2*) # Allwinner H2+, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner H2+" ;; *sun8i-a33*|*sun8i-r16*) # Allwinner A33/R16, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner A33/R16" ;; *sun8iw15p1*|*sun8i-a50*) # Allwinner A50/MR133/R311, 4 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner A50/MR133/R311" ;; *) echo "Unknown" esac ;; esac ;; esac ;; 00A7r0p5000000) # Allwinner sun8i running with 3.x BSP kernel: could be Allwinner H3/H2+, R40/V40 or A33/R16 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm case "${DTCompatible,,}" in *sun8i*) # kernel 3.10 with device-tree support echo "Allwinner R40/V40" ;; *) # kernel 3.4 w/o device-tree support echo "Allwinner H3/H2+ or A33/R16" ;; esac ;; *A7r0p5*A7r0p5*A7r0p5*A7r0p5*A15r4p0*A15r4p0*A15r4p0*A15r4p0) # Allwinner A80: 4 x Cortex-A7 / r0p5 + 4 x Cortex-A15 / r4p0 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # GB4 reports stepping of big core: https://browser.geekbench.com/v4/cpu/16436341.gb4, /proc/cpuinfo of little: https://nitter.net/miniNodes/status/533125394686562304 echo "Allwinner A80" ;; ??A7r0p5??A7r0p5??A7r0p5??A7r0p5??A7r0p5??A7r0p5??A7r0p5??A7r0p5) # Allwinner A83T, 8 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Allwinner A83T" ;; 20A5r0p120A5r0p120A5r0p120A5r0p1|2A5222|20A5r0p1202020) # Actions ATM8029, 4 x Cortex-A5 / r0p1 / ? # Amlogic S805/M805, 4 x Cortex-A5 / r0p1 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 vfpd32 (3.10: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4) # or Qualcomm MSM8625Q: 4 x Cortex-A5 / r0p1 / scp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 case "${DTCompatible,,}" in *amlogic*) echo "Amlogic S805" ;; *8625*) echo "Qualcomm MSM8625Q" ;; *) echo "Actions ATM8029" ;; esac ;; 20A9r4p120A9r4p120A9r4p120A9r4p1|2A9222) # Amlogic S802/S812: 4 x Cortex-A9 / r4p1 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 # or HiSilicon Hi6620: 4 x Cortex-A9 / r4p1 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 grep -q -i amlogic <<<"${DTCompatible,,}" && echo "Amlogic S802/S812" || echo "HiSilicon Hi6620" ;; *A57r0p0*A57r0p0*A53r0p0*A53r0p0*A53r0p0*A53r0p0) # ARM Juno: 2 x Cortex-A57 / r0p0 + 4 x Cortex-A53 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "ARM Juno r0" ;; *A53r0p0*A53r0p0*A53r0p0*A53r0p0) # Snapdragon 410 / MSM8916/APQ8016: 4 x Cortex-A53 / r0p0 / fp asimd evtstrm crc32 cpuid echo "Snapdragon 410" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A72r0p0*A72r0p0*A72r0p0*A72r0p0) # Snapdragon 652/653 / MSM8976/MSM8976PRO: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A72 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or HiSilicon 950/955: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A72 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 case "${DTCompatible,,}" in *hisilicon*|*950*|*955) echo "HiSilicon Kirin 950/955" ;; *snapdragon*|*652*|*653*) echo "Snapdragon 652/653" ;; esac ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A72r0p0*A72r0p0) # Snapdragon 650 / MSM8956: 4 x Cortex-A53 / r0p4 + 2 x Cortex-A72 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Snapdragon 650" ;; *A53r0p3*A53r0p3*A53r0p3*A53r0p3*A57r1p2*A57r1p2) # Snapdragon 808 / MSM8992: 4 x Cortex-A53 / r0p3 + 2 x Cortex-A57 / r1p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Snapdragon 808" ;; *A53r0p2*A53r0p2*A53r0p2*A53r0p2*A57r1p1*A57r1p1*A57r1p1*A57r1p1) # Snapdragon 810 / MSM8994/MSM8994V: 4 x Cortex-A53 / r0p2 + 4 x Cortex-A57 / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Snapdragon 810" ;; 00QualcommV2r10p400QualcommV2r10p400QualcommV2r10p400QualcommV2r10p404QualcommV1Kryor10p104QualcommV1Kryor10p104QualcommV1Kryor10p104QualcommV1Kryor10p1) # Snapdragon 835 / MSM8998: 4 x Qualcomm Kryo V2 / r10p4 + 4 x Qualcomm Falkor V1/Kryo / r10p1 / fp asimd aes pmull sha1 sha2 crc32 echo "Snapdragon 835" ;; *Kryor1p2*Kryor1p2*Kryor1p2*Kryor1p2) # Qualcomm MSM8996: 2 x Kryo r1p2 + 2 x Kryo r1p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Qualcomm MSM8996" ;; *Kryor2p1*Kryor2p1*Kryor2p1*Kryor2p1) # Qualcomm MSM8996pro: 2 x Kryo r2p1 + 2 x Kryo r2p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Qualcomm MSM8996pro" ;; 00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver06Qualcomm4XXGold06Qualcomm4XXGold) # Qualcomm Snapdragon 7c (SC7180): 6 x Kryo 468 Silver / r13p14 + 2 x Kryo 468 Gold / r15p15 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp echo "Qualcomm Snapdragon 7c" ;; *Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXGold*Qualcomm4XXGold*Qualcomm4XXGold*Qualcomm4XXGold) # Qualcomm SM8150 / Snapdragon 855: 4 x Qualcomm Kryo 4XX Silver / r13p14 + 4 x Qualcomm Kryo 4XX Gold / r13p14 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp echo "Qualcomm SM8150 (Snapdragon 855)" ;; *Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXSilver*Qualcomm4XXGold*Qualcomm4XXGold*Qualcomm4XXGold*Qualcomm4XXGold*) # Qualcomm Snapdragon 7c+ Gen 3 (SC7280) or QCM6490: 4 x Kryo 468 Silver + 3 x Kryo 468 Gold + 1 x Kryo 468 Gold Plus case "${DTCompatible,,}" in *qcm6490*) echo "Qualcomm QCM6490" ;; *sc7280*) echo "Qualcomm Snapdragon 7c+ Gen 3 (SC7280)" ;; esac ;; *QualcommOryon*) # Qualcomm Snapdragon X Elite (X1E80100): 8 x Oryon + 4 x Oryon or variants # https://browser.geekbench.com/v6/cpu/3327362.gb6 is in conflict with https://browser.geekbench.com/v6/cpu/3326512.gb6 # wrt stepping: "ARM implementer 81 architecture 8 variant 1 part 1 revision 1" vs. "ARMv8 (64-bit) Family 8 Model 1 Revision 201" # The former is r1p1, the latter r2p1. But maybe it's different steppings since it's also said the clusters would consist of two # different core types: https://lore.kernel.org/linux-arm-msm/b165d2cd-e8da-4f6d-9ecf-14df2b803614@linaro.org/ case ${CPUCores} in 12) echo "Qualcomm Snapdragon X Elite (X1E80100)" ;; 10) echo "Qualcomm SC8370/SC8370XP" ;; 8) echo "Qualcomm SC8350/SC8350X" ;; esac ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4) # Socionext SC2A11: 24 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid echo "Socionext SC2A11" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A73r0p2*A73r0p2*A73r0p2*A73r0p2) # Mediatek MT6771V: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Mediatek MT8183: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt lpae evtstrm aes pmull sha1 sha2 crc32 # or HiSilicon Kirin 710: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or HiSilicon Kirin 970: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Allwinner R923: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 case "${DTCompatible,,}" in *710*) echo "HiSilicon Kirin 710" ;; *970*) echo "HiSilicon Kirin 970" ;; *hisilicon*) echo "HiSilicon Kirin 710 or 970" ;; *mt6771*) echo "Mediatek MT6771V" ;; *mt8183*) echo "Mediatek MT8183" ;; *allwinner*|*sun*|*r923*) echo "Allwinner R923" ;; esac ;; 00A53r0p400A53r0p400A53r0p400A53r0p4|??A53r0p4??????) # The boring quad Cortex-A53 done by every SoC vendor: 4 x Cortex-A53 / r0p4 # Allwinner A100/A133/A53/A64/H5/H6/H313/H616/MR813/R818/T507/T509/B810, BCM2837/BCM2709, RK3318/RK3328/RK3528/RK3562, i.MX8 M, S905, S905X/S805X, S805Y, S905X/S905D/S905W/S905L/S905M2, S905X2/S905Y2/T962X2, Mediatek MT6762M/MT6765, RealTek RTD129x/RTD139x case "${DeviceName}" in "Raspberry Pi Compute Module 3 Plus"*) # 4 x Cortex-A53 / r0p4 / fp asimd evtstrm crc32 echo "BCM2837B0" ;; "Raspberry Pi 2"*|"Raspberry Pi Compute Module 3"*) # 4 x Cortex-A53 / r0p4 / fp asimd evtstrm crc32 echo "BCM2837 (BCM2709)" ;; "Raspberry Pi 3"*) # 4 x Cortex-A53 / r0p4 / fp asimd evtstrm crc32 echo "BCM2837 (BCM2710)" ;; "Raspberry Pi Zero 2"*) # 4 x Cortex-A53 / r0p4 / fp asimd evtstrm crc32 echo "RP3A0-AU (BCM2710A1)" ;; *) # No Raspberry, check for AES capabilities first grep 'Features' <<< "${ProcCPU}" | grep -q aes if [ $? -ne 0 ]; then # no ARMv8 Crypto Extensions licensed like RPi ARMv8 cores... then it's # S905: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm crc32 echo "Amlogic S905" else case "${DTCompatible,,}" in *allwinner*) # 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 case "${Allwinner_SID:7:8}" in 92c00?ba) echo "Allwinner A64" ;; 92c000bb) echo "Allwinner H64" ;; 828000*) echo "Allwinner H5" ;; 82c000*) echo "Allwinner H6" ;; 32c05c*) echo "Allwinner H313" ;; 32c050*) echo "Allwinner H616" ;; 33802*) echo "Allwinner H618" ;; 33806*) echo "Allwinner H700" ;; *) case "${DTCompatible,,}" in *h618*|*orangepi-zero3*|*orangepi-zero2w*) echo "Allwinner H618" ;; *orangepi-zero2*) echo "Allwinner H616" ;; *h313*) echo "Allwinner H313" ;; *h616*) echo "Allwinner H616/H313/H618" ;; *pine-h64*) echo "Allwinner H6" ;; *h64*) echo "Allwinner H64" ;; *h6*) echo "Allwinner H6" ;; *h5*) echo "Allwinner H5" ;; *a64*) echo "Allwinner A64" ;; *t507*) echo "Allwinner T507" ;; *t509*) echo "Allwinner T509" ;; *b810*) echo "Allwinner B810" ;; *a100*) echo "Allwinner A100" ;; *a133*) echo "Allwinner A133" ;; *a53*) echo "Allwinner A53" ;; *mr813*) echo "Allwinner MR813" ;; *r818*) echo "Allwinner R818" ;; *h700*) echo "Allwinner H700" ;; esac ;; esac ;; *amlogic*) case "${DTCompatible,,}" in *axg*) # AXG: A113X, A113D, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Amlogic A113X/A113D" ;; *g12a*) # G12A: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid echo "Amlogic S905X2/S905Y2/S905D2/S905L3A" ;; *tl1*) # TL1: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid echo "Amlogic T962X2" ;; *gxl*) # GXL: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Amlogic S805X, S805Y, S905X/S905D/S905W/S905L/S905L2/S905M2" ;; *) # GXL or G12A: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 IdentifyAXGGXLG12A ;; esac ;; *rk3318*) # RK3318, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Rockchip RK3318" ;; *rk3328*) # RK3328, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Rockchip RK3328" ;; *rk3528*) # RK3528, 4 x Cortex-A53 / r0p4 / https://github.com/LubanCat/u-boot/blob/b36e944afbe275808a3d88575991417bae5e569f/arch/arm/dts/rk3528.dtsi#L76-L114 echo "Rockchip RK3528" ;; *rk3562*) # RK3562, 4 x Cortex-A53 / r0p4 / https://github.com/JeffyCN/rockchip_mirrors/blob/30c488cd350f5f7b53c7f46f803a64929f619230/configs/rockchip/chips/rk3562.config / https://cdn.cnx-software.com/wp-content/uploads/2023/03/Rockchip-RK3528-RK3562.jpg echo "Rockchip RK3562" ;; *imx8*) # i.MX8M, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "NXP i.MX8M" ;; *rtd1395*) # RealTek RTD1395, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "RealTek RTD1395" ;; *realtek*) # RealTek RTD129x/RTD139x, 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "RealTek RTD129x/RTD139x" ;; *mt6762m*) # Mediatek MT6762M, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Mediatek MT6762M" ;; *mt6765*) # Mediatek MT6765, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Mediatek MT6765" ;; *mt6739wa*) # Mediatek MT6739WA, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Mediatek MT6739WA" ;; *mt7986a*|*bpi-r3*) # Mediatek MT7986A, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Mediatek MT7986A" ;; *mt8735*) # Mediatek MT8735, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Mediatek MT8735" ;; *xlnx*|*zynqmp*|*zcu102*) # Xilinx XCZU9EG, 4 x Cortex-A53 / r0p4 / fp asimd aes pmull sha1 sha2 crc32 echo "Xilinx XCZU9EG" ;; *hi3798*) # HiSilicon Hi3798C-V200, 4 x Cortex-A53 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm (booting 32-bit) echo "HiSilicon Hi3798C-V200" ;; *am623*) # Texas Instruments K3 AM623, 4 x Cortex-A53 / r0p4 echo "Texas Instruments K3 AM6234" ;; *am625*) # Texas Instruments K3 AM625, 4 x Cortex-A53 / r0p4 echo "Texas Instruments K3 AM6254" ;; *am62a7*) # Texas Instruments K3 AM62A, 4 x Cortex-A53 / r0p4 echo "Texas Instruments K3 AM62A7" ;; *am654*) # Texas Instruments K3 AM654, 4 x Cortex-A53 / r0p4 echo "Texas Instruments K3 AM654" ;; *ipq5332*) # Qualcomm IPQ5332, 4 x Cortex-A53 / r0p4 echo "Qualcomm IPQ5332" ;; *7570*) # Exynos 7570, 4 x Cortex-A53 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt lpae evtstrm aes pmull sha1 sha2 crc32 echo "Exynos 7570" ;; esac fi ;; esac ;; ??BroadcomB53r0p0??BroadcomB53r0p0??BroadcomB53r0p0??BroadcomB53r0p0) # technically another quad core A53 but in this case the cores are Broadcom's Brahma-B53. # Broadcom used these cores in a bunch of SoCs, usually with ARMv8 Crypto Extensions but # sometimes they're not active or licensed (applies to early VideoCore SoCs between 2017 # and 2018). Stepping was always r0p0: http://tinyurl.com/yt85tt78 case "${DTCompatible,,}" in *brcm,bcm4908*) # Broadcom BCM4908: 4 x Brahma B53 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid echo "Broadcom BCM4908" ;; esac ;; 00A53r0p400A53r0p4) # Allwinner R329, 2 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Armada 3720, 2 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or MediaTek MT7622B, 2 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Texas Instruments K3 AM62x, 2 x Cortex-A53 / r0p4 # or Texas Instruments K3 AM642, 2 x Cortex-A53 / r0p4 # or Texas Instruments K3 AM652, 2 x Cortex-A53 / r0p4 case "${DTCompatible,,}" in *armada37*) echo "Marvell Armada 3720" ;; *am623*) echo "Texas Instruments K3 AM6232" ;; *am625*) echo "Texas Instruments K3 AM6252" ;; *am642*) echo "Texas Instruments K3 AM642" ;; *am652*) echo "Texas Instruments K3 AM652" ;; *r329*) echo "Allwinner R329" ;; *mt7622b*) echo "MediaTek MT7622B" ;; esac ;; 00A53r0p4) # Armada 3710, 1 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Texas Instruments K3 AM62x, 1 x Cortex-A53 / r0p4 case "${DTCompatible,,}" in *armada37*) echo "Marvell Armada 3710" ;; *am623*) echo "Texas Instruments K3 AM6231" ;; *am625*) echo "Texas Instruments K3 AM6251" ;; esac ;; ??A53r0p4??A53r0p4??A53r0p4??A53r0p4??A53r0p4??A53r0p4??A53r0p4??A53r0p4) # Amlogic S912, 4 x Cortex-A53 / r0p4 + 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or NXP QorIQ LS1088: 8 x Cortex-A53 / r0p4 / https://bench.cr.yp.to/computers.html # or Samsung Exynos 7870: 8 x Cortex-A53 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt lpae evtstrm aes pmull sha1 sha2 crc32 # or HiSilicon Kirin 650: 8 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Qualcomm SDM439 / Snapdragon 439: 8 x Cortex-A53 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt lpae evtstrm aes pmull sha1 sha2 crc32 # or Qualcomm SDM450 / Snapdragon 450: 8 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Qualcomm MSM8937 / Snapdragon 430: 8 x Cortex-A53 / r0p4 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm aes pmull sha1 sha2 crc32 (booting 32-bit) # or Qualcomm MSM8952 / Snapdragon 617: 8 x Cortex-A53 / r0p4 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 evtstrm (booting 32-bit) # or Qualcomm MSM8953: 8 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Xiaomi Surge S1: 8 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Sophgo BM1684: 8 x Cortex-A53 / r0p4 # or Qualcomm APQ8053: 8 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 case "${DTCompatible,,}" in *amlogic*) echo "Amlogic S912" ;; *7870*) echo "Samsung Exynos 7870" ;; *hisilicon*|*hi6250*) echo "HiSilicon Kirin 650" ;; *msm8937*|*430*) echo "Qualcomm Snapdragon 430" ;; *sdm439*) echo "Qualcomm Snapdragon 439" ;; *sdm450*) echo "Qualcomm Snapdragon 450" ;; *msm8952*|*617*) echo "Qualcomm Snapdragon 617" ;; *msm8953*) echo "Qualcomm MSM8953" ;; *xiaomi*) echo "Xiaomi Surge S1" ;; *bm1684x*) echo "Sophgo BM1684X" ;; *bm1684*) echo "Sophgo BM1684" ;; *apq8053*) echo "Qualcomm APQ8053" ;; *ls1088*) echo "NXP QorIQ LS1088" ;; esac ;; 00A53r0p400A53r0p412A73r0p212A73r0p212A73r0p212A73r0p2) # Amlogic S922X/A311D, 2 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Amlogic S922X/A311D" ;; *A73r0p2*A73r0p2*A73r0p2*A73r0p2*A73r0p2*A73r0p2*A73r0p2*A73r0p2) # https://github.com/vmlemon/understand/wiki/Lenovo-ChromeBook-Duet suggests all 8 cores are # Cortex-A73 / r0p2 (cpu0 -> 'Booting Linux on physical CPU 0x0000000000 [0x410fd092]' and # all subsequently brought up cores 'Booted secondary processor 0x000000000x [0x410fd092]') # but this is in contrast to specs on the Internet # MediaTek Helio P60T, 8 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "MediaTek Helio P60T" ;; 00A73r0p200A73r0p200A73r0p200A73r0p214A53r0p414A53r0p414A53r0p414A53r0p4) # Amlogic A311D2: 4 x Cortex-A73 / r0p2 + 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Amlogic A311D2" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A73r0p1*A73r0p1*A73r0p1*A73r0p1) # HiSilicon Kirin 960: 4 x Cortex-A53 / r0p4 + 4 x Cortex-A73 / r0p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 grep -q hisilicon <<<"${DTCompatible,,}" && echo "HiSilicon Kirin 960" ;; *A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0) # HiSilicon Ascend 310, 8 x Cortex-A55 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp echo "HiSilicon Ascend 310" ;; 0?A55r2p00?A55r2p00?A55r2p00?A55r2p00?A55r2p00?A55r2p00?A55r2p00?A55r2p0) # Allwinner A523/A527/MR527/R828/T523/T527, 8 x Cortex-A55 / r2p0 / at least 'aes pmull sha1 sha2' (https://browser.geekbench.com/v5/cpu/21564626) # or Amlogic P1, 8 x Cortex-A55 / r2p0 case "${DTCompatible,,}" in *a523*) echo "Allwinner A523" ;; *a527*) echo "Allwinner A527" ;; *t523*) echo "Allwinner T523" ;; *mr527*) echo "Allwinner MR527" ;; *t527*) echo "Allwinner T527" ;; *r828*) echo "Allwinner R828" ;; *allwinner*|*sun55i*) echo "Allwinner A523/A527/MR527/R828/T523/T527" ;; esac ;; *A55r1p0*A55r1p0*A55r1p0*A55r1p0*A76r1p0*A76r1p0*A76r1p0*A76r1p0) # HiSilicon Kirin 980, 4 x Cortex-A55 / r1p0 + 4 x HiSilicon-A76 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp # The big cores are marketed/documented as Cortex-A76 by HiSilicon but instead of the usual ARM core ID 0x41/0xd0b they have 0x48/0xd40 echo "HiSilicon Kirin 980" ;; *A55r1p0*A55r1p0*A55r1p0*A55r1p0*A76r3p0*A76r3p0*A76r3p0*A76r3p0|*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A76r3p1*A76r3p1*A76r3p1*A76r3p1) # HiSilicon Kirin 990, 4 x Cortex-A55 / r1p0 + 4 x HiSilicon-A76 / r3p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp # The big cores are marketed/documented as Cortex-A76 by HiSilicon but instead of the usual ARM core ID 0x41/0xd0b they have 0x48/0xd40 # There also seems to be a newer Kirin 990 version with HiSilicon-A76 / r3p1 cores # https://browser.geekbench.com/v5/cpu/compare/21007796?baseline=18843090 echo "HiSilicon Kirin 990" ;; *A55*TaiShanv120*) # HiSilicon Kirin 990A, 4 x Cortex-A55 + 4 x TaiShan v120 Lite echo "HiSilicon Kirin 990A" ;; *A55r?p*A55r?p*A55r?p*A55r?p*A77r1p0*A77r1p0*A77r1p0*A77r1p0) # HiSilicon Kirin 9000, 4 x Cortex-A55 + 4 x Cortex-A77 / r1p0 / https://browser.geekbench.com/v6/cpu/2493425 echo "HiSilicon Kirin 9000" ;; *A510r1p1*A510r1p1*A510r1p1*A510r1p1*A710r2p2*A710r2p2*A710r2p2*TaiShanv120r2p2) # HiSilicon Kirin 9000s: 4 x Cortex-A510 / r1p1 + 3 x HiSilicon-A710 / r2p2 + 1 x TaiShan v120 / r2p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit ilrcpc flagm ssbs sb pacg dcpodp flagm2 frint svei8mm i8mm bti # https://youtu.be/SCRIFe0uaac?feature=shared&t=32 # The 'A710' like cores use HiSilicon's own 48/d42 ID and all the big cores are SMT capable echo "HiSilicon Kirin 9000s" ;; *A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A55r1p0*A76r3p0*A76r3p0) # HiSilicon Kirin 810, 6 x Cortex-A55 / r1p0 + 2 x HiSilicon-A76 / r3p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp # The big cores are marketed/documented as Cortex-A76 by HiSilicon but instead of the usual ARM core ID 0x41/0xd0b they have 0x48/0xd40 echo "HiSilicon Kirin 810" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A76r4p?*A76r4p?) # Allwinner A733/A736/T736, 6 x Cortex-A55 / r2p0 + 2 x Cortex-A76 / r4p? case "${DTCompatible,,}" in *sun60iw1*|*a736*|*t736*) echo "Allwinner A736/T736" ;; *sun60iw2*|*a733*) echo "Allwinner A733" ;; esac ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A78*A78) # Allwinner A737/T737, 6 x Cortex-A55 / r2p0 + 2 x Cortex-A78 grep -E -q 'allwinner|a737|t737|' <<<"${DTCompatible,,}" && echo "Allwinner A737/T737" ;; 0A9r4p10A9r4p1|0?A9r4p10?A9r4p1) # Armada 375/38x, 2 x Cortex-A9 / r4p1 / swp half thumb fastmult vfp edsp neon vfpv3 tls # or MStar Infinity2: 2 Cortex-A9 / r4p1 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 # or Triductor TR6560: 2 x Cortex-A9 / r4p1 case "${DTCompatible,,}" in *armada*) echo "Marvell Armada 375/38x" ;; *mstar*|*infinity*) echo "MStar Infinity2" ;; *) echo "Triductor TR6560" ;; esac ;; *A9r4p1) # HiSilicon Hi3520D-V300: 1 x Cortex-A9 / r4p1 / swp half fastmult edsp echo "HiSilicon Hi3520D-V300" ;; 0?A72r0p10?A72r0p10?A72r0p10?A72r0p1) # Armada 8040, 4 x Cortex-A72 / r0p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Marvell Armada 8040" ;; 0?A72r0p10?A72r0p1) # Armada 8020, 2 x Cortex-A72 / r0p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Marvell Armada 8020" ;; ??A53r0p4??A53r0p4) # Armada 3700, 2 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Marvell Armada 37x0" ;; 10ARM1176r0p7) # BCM2835, 1 x ARM1176 / r0p7 / half thumb fastmult vfp edsp java tls echo "BCM2835" ;; ??A73r?p???A73r?p???A73r?p???A73r?p?) # Qualcomm IPQ9574, 4 x Cortex-A73 # or Synaptics VS680 (also called SenaryTech SN3680), 4 x Cortex-A73 # or MediaTek MT7988A (Filogic 880), 4 x Cortex-A73 case "${DTCompatible,,}" in *qualcomm*|*9574*) echo "Qualcomm IPQ9574" ;; *synaptics*|*syna,*|*vs680*|*senarytech*|*sn3680*) echo "Synaptics VS680" ;; *880*|*mt7988*) echo "MediaTek MT7988A (Filogic 880)" ;; esac ;; 00A72r0p300A72r0p300A72r0p300A72r0p3) # BCM2711, 4 x Cortex-A72 / r0p3 / fp asimd evtstrm crc32 (running 32-bit: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32) # or Marvell Armada3900-A1, 4 x Cortex-A72 / r0p3 / https://community.cisco.com/t5/wireless/catalyst-9130ax-ap-booting-into-wnc-linux-instead-of-ios-xe/td-p/4460181 case "${DTCompatible,,}" in *raspberrypi*|*bcm283*|*bcm2711*) echo "BCM2711${BCM2711}" ;; *marvell*|*3900*) echo "Marvell Armada3900-A1" ;; esac ;; *A76r4p1*A76r4p1*A76r4p1*A76r4p1) # BCM2712, 4 x Cortex-A76 / r4p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "BCM2712" ;; ??A72r0p3??A72r0p3) # Xilinx Versal, 2 x Cortex-A72 / r0p3 / fp asimd aes pmull sha1 sha2 crc32 cpuid echo "Xilinx Versal" ;; 10A7r0p310A7r0p310A7r0p310A7r0p30?A15r2p30?A15r2p30?A15r2p30?A15r2p3) # Exynos 5422, 4 x Cortex-A7 / r0p3 + 4 x Cortex-A15 / r2p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae (with 5.x also evtstrm) echo "Exynos 5422" ;; 10A7r0p410A7r0p410A7r0p410A7r0p404A15r3p304A15r3p304A15r3p304A15r3p3) # Exynos 5430, 4 x Cortex-A7 / r0p4 + 4 x Cortex-A15 / r3p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae (with 5.x also evtstrm) echo "Exynos 5430" ;; *A7r0p4*A7r0p4*A7r0p4*A7r0p4*A7r0p4*A7r0p4*A7r0p4*A7r0p4|*A7r0p4*A7r0p4) # Mediatek MT6592: 8 x Cortex-A7 / r0p4 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # (with vendor kernel often only 2 cores show up in /proc/cpuinfo) echo "Mediatek MT6592" ;; 00A35r0p100A35r0p100A35r0p100A35r0p1) # Mediatek MT8167B: 4 x Cortex-A35 / r0p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Mediatek MT8167B" ;; *A35r0p1*A35r0p1*A35r0p1*A35r0p1*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A73r0p2*A73r0p2|*A35r0p1*A53r0p4*A73r0p2) # Mediatek MT6799: 4 x Cortex-A35 / r0p1 + 4 x Cortex-A53 / r0p4 + 2 x Cortex-A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Mediatek MT6799" ;; 00A35r0p200A35r0p2) # RK1808, 2 x Cortex-A35 / r0p2 / https://patchwork.kernel.org/project/linux-arm-kernel/patch/20210516230551.12469-6-afaerber@suse.de/#24199353 echo "Rockchip RK1808" ;; 00A35r0p200A35r0p200A35r0p200A35r0p2) # RK3308/RK3326/PX30/PX30S, 4 x Cortex-A35 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or i.MX8QXP, 4 x Cortex-A35 / r0p2 / https://gist.github.com/stravs/f82c8a0af276b2d1e6b57235d048f027 case "${DTCompatible,,}" in *rk3308*) echo "Rockchip RK3308" ;; *rk3326*) echo "Rockchip RK3326" ;; *px30s*) echo "Rockchip PX30S" ;; *px30*) echo "Rockchip PX30/PX30S" ;; *rockchip*) echo "Rockchip RK3308/RK3326/PX30/PX30S" ;; *) echo "NXP i.MX8QXP" ;; esac ;; 00A35r1p000A35r1p000A35r1p000A35r1p0) # S805X2/S905Y4/S905W2, 4 x Cortex-A35 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Amlogic S805X2/S905Y4/S905W2" ;; *A35r*A35r*A35r*A35r???) # RK quad-core A35 with yet unknown details (stepping/flags) case "${DTCompatible,,}" in *rk3308b*) echo "Rockchip RK3308B" ;; *rk3308h*) echo "Rockchip RK3308H" ;; *rk3358j*) echo "Rockchip RK3358J" ;; esac ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A72r1p0*A72r1p0*A72r1p0*A72r1p0) # RK3576, 4 x Cortex-A53 / r0p4 + 4 x Cortex-A72 / r1p0 https://archive.ph/7lkym # (most recent steppings are pure assumption since announced in 2023) # Why A53/A72 still in 2024? Since last ARM cores able to boot a 32-bit # ARMv8l kernel (32-bit userlands consume way less memory compared to 64-bit) # The RK3576J variant can run isolated operating systems on each CPU cluster: # https://twitter.com/BG5USN/status/1767000716709609521 echo "Rockchip RK3576" ;; 0?A55r2p00?A55r2p00?A55r2p00?A55r2p0??A76r4p0??A76r4p0??A76r4p0??A76r4p0) # RK3588, 4 x Cortex-A55 / r2p0 + 2 x Cortex-A76 / r4p0 / + 2 x Cortex-A76 / r4p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp # or RK3582/RK3583 which are chips not fully passing QA where one of the two A76 clusters is hidden by default even if fully functional and might have been brought back by a DT overlay # or Unisoc UMS9620, 4 x Cortex-A55 / r2p0 + 3 x Cortex-A76 / r4p0 / + 1 x Cortex-A76 / r4p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp case "${DTCompatible,,}" in *ums9620*) echo "Unisoc UMS9620" ;; *) if [ "X${RK_NVMEM}" != "X" ]; then if [ "${RK_NVMEM:16:2}${RK_NVMEM:19:2}" = "3588" ]; then case "${RK_NVMEM:28:2}" in 21) echo "Rockchip RK3588 / ${RK_NVMEM:16:42}" ;; 33) echo "Rockchip RK3588s / ${RK_NVMEM:16:42}" ;; *) echo "Rockchip RK3588/RK3588s / ${RK_NVMEM:16:42}" ;; esac else echo "Rockchip RK${RK_NVMEM:16:2}${RK_NVMEM:19:2} / ${RK_NVMEM:16:42}" fi elif [ -f /sys/devices/system/cpu/cpufreq/policy7/cpuinfo_max_freq ]; then # According to this site RK3588M is limited to 2.1 GHz # https://techacute.com/rockchip-launched-flagship-smart-vehicle-solution-rk3588m-with-360-panoramic-view-function/ read MaxRK3588Freq ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d) # or Allwinner F133/H133/R528/T113: 2 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm (https://forums.100ask.net/t/topic/2867 -> ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d) # or STMicroelectronics STM32MP153C: 2 x Cortex-A7 / r0p5 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm case "${DTCompatible,,}" in *rockchip*) echo "Rockchip RV1109" ;; *renesas*) echo "Renesas RZ/N1D" ;; *sstar*) echo "SigmaStar SSD201/SSD202D" ;; *sun8iw18*|*r328*) echo "Allwinner R328" ;; *sun8iw20*|*f133*|*h133*|*r528*|*t113*) echo "Allwinner F133/H133/R528/T113" ;; *stm32mp153*) echo "STMicroelectronics STM32MP153C" ;; esac ;; ??A9r1p0??A9r1p0) # Nvidia Tegra 2, 2 x Cortex-A9 / r1p0 / swp half thumb fastmult vfp edsp thumbee vfpv3 vfpv3d16 (no neon) echo "Nvidia Tegra 2" ;; ??A9r2p9??A9r2p9??A9r2p9??A9r2p9) # Nvidia Tegra 3, 4 x Cortex-A9 / r2p9 / swp half thumb fastmult vfp edsp neon vfpv3 tls echo "Nvidia Tegra 3" ;; *A7r0p5*A7r0p5*A7r0p5*A7r0p5*A15r3p3*A15r3p3*A15r3p3*A15r3p3) # HiSilicon Kirin 920/925/928: 4 x Cortex-A7 / r0p5 + 4 x Cortex-A15 / r3p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt echo "HiSilicon Kirin 920/925/928" ;; *A15r3p3*A15r3p3*A15r3p3*A15r3p3) # Nvidia Tegra K1 (124): 4 x Cortex-A15 / r3p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt echo "Nvidia Tegra K1 (124)" ;; 00A57r1p100A57r1p100A57r1p100A57r1p1) # Tegra X1/TX1, 4 x Cortex-A57 / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Nvidia Tegra X1/TX1" ;; *A57r1p3*A57r1p3*A57r1p3*A57r1p3*A57r1p3*A57r1p3*A57r1p3*A57r1p3) # Baikal-M, 8 x Cortex-A57 / r1p3 / fp asimd evtstrm crc32 cpuid echo "Baikal-M" ;; *A57r1p3*) # Jetson TX2, 1-4 x Cortex-A57 / r1p3 + 0-2 x Denver 2 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Renesas R-Car family: 2-4 x Cortex-A57 / r1p3 + 0-4 Cortex-A53 / r0p4 / https://jira.automotivelinux.org/browse/SPEC-2614?focusedCommentId=20987&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel case "${DTCompatible,,}" in *r8a7795*) echo "Renesas R8A7795/R-Car H3" ;; *r8a77965*) echo "Renesas R8A77965/R-Car M3N" ;; *r8a7796*) echo "Renesas R8A7796/R-Car M3" ;; *nvidia*) echo "Nvidia Jetson TX2" ;; esac ;; *NVidiar0p0*NVidiar0p0) # Nvidia Tegra K1 (Tegra132): 2 x Denver / r0p0 / fp asimd aes pmull sha1 sha2 crc32 echo "Nvidia Tegra K1 (Tegra132)" ;; *NVidiar0p0*) # Nvidia Tegra Xavier | 4-8 x NVidia Carmel / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp echo "Nvidia Tegra Xavier" ;; *A78AEr0p1*A78AEr0p1*A78AEr0p1*A78AEr0p1*A78AEr0p1*) # Nvidia Jetson Orin NX / AGX Orin: 6-12 Cortex-A78AE / r0p1 / https://forums.developer.nvidia.com/t/orin/212053/8 https://developer.nvidia.com/embedded/jetson-orin case ${CPUCores} in 6) echo "Nvidia Jetson Orin NX" ;; 8) echo "Nvidia Jetson Orin NX or AGX Orin" ;; *) echo "Nvidia Jetson AGX Orin" ;; esac ;; 50A17r0p150A17r0p150A17r0p150A17r0p1) # RK3288: 4 x Cortex-A17 / r0p1 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "Rockchip RK3288" ;; *A7r0p5*A7r0p5*A7r0p5*A7r0p5*A17r0p0*A17r0p0*A17r0p0*A17r0p0) # MediaTek MT6595: 4 x Cortex-A7 / r0p5 + 4 x Cortex-A17 / r0p0 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt echo "MediaTek MT6595" ;; *A17r0p0*A17r0p0) # MediaTek MT5890: 2 x Cortex-A17 / r0p0 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt # though has been advertised as quad core (2 x A7 + 2 x A17): https://www.avforums.com/attachments/safari-27-jul-2018-at-09-23-pdf.1043417/ echo "MediaTek MT5890" ;; *A15r2p1*A15r2p1*A7r0p1*A7r0p1*A7r0p1) # ARM Versatile V2P-CA15-CA7: 2 x Cortex-A15 / r2p1 + 3 x Cortex-A7 / r0p1 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm echo "ARM Versatile V2P-CA15-CA7" ;; ?0A7r0p2?0A7r0p2?0A7r0p2?0A7r0p2) # MT6589: 4 x Cortex-A7 / r0p2 / https://gist.github.com/MaTBeu4uk/3a1bea6bf8c658829622f3ecbcf4b7eb which is in conflict to other sources who claim Cortex-A7 / r0p3 echo "Mediatek MT6589" ;; *A7r0p3*A7r0p3*A15r3p2*A15r3p2) # MT8135: 2 x Cortex-A7 / r0p3 + 2 x Cortex-A15 / r3p2 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt echo "Mediatek MT8135" ;; *A7r0p3*A7r0p3*A7r0p3*A7r0p3) # Allwinner A31/A31s: 4 x Cortex-A7 / r0p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm # or MT7623: 4 x Cortex-A7 / r0p3 / half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm # or MT6580: 4 x Cortex-A7 / r0p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae # or MT6582: 4 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # or MT6589: 4 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # or MT8127: 4 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # or Qualcomm MSM8226/MSM8926 (Snapdragon 400): 4 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt case "${DTCompatible,,}" in *mt6580*) echo "Mediatek MT6580" ;; *mt6582*) echo "Mediatek MT6582" ;; *mt6589*) echo "Mediatek MT6589" ;; *mt8127*) echo "Mediatek MT8127" ;; *mt7623*) echo "Mediatek MT7623" ;; *msm8226*|*msm8926*|*400*) echo "Qualcomm MSM8226/MSM8926 (Snapdragon 400)" ;; *a31*|*sun6i*) echo "Allwinner A31/A31s" ;; esac ;; *A7r0p3*A7r0p3) # Mediatek MT6572: 2 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt # or Qualcomm MSM8610 (Snapdragon 200): 2 x Cortex-A7 / r0p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt case "${DTCompatible,,}" in *mt6572*) echo "Mediatek MT6572" ;; *) echo "Qualcomm MSM8610 (Snapdragon 200)" ;; esac ;; *A53r0p1*A53r0p1*A53r0p1*A53r0p1*A53r0p1*A53r0p1*A53r0p1*A53r0p1) # Qualcomm MSM8939: 8 x Cortex-A53 / r0p1 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 evtstrm (booting 32-bit obviously) echo "Qualcomm MSM8939" ;; ??A53r0p3??A53r0p3??A53r0p3??A53r0p3??A53r0p3??A53r0p3??A53r0p3??A53r0p3|??A53r0p3??A53r0p3??A53r0p3??A53r0p3) # Nexell S5P6818, 8 x Cortex-A53 / r0p3 / fp asimd aes pmull sha1 sha2 crc32 # or HiSilicon Kirin 620/930, 8 x Cortex-A53 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or Samsung Exynos 7580, 4 or 8 x Cortex-A53 / r0p3 / fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt case "${DTCompatible,,}" in *hi6220*|*hi6210*) echo "HiSilicon Kirin 620" ;; *hisilicon*) echo "HiSilicon Kirin 620/930" ;; *nexell*) echo "Nexell S5P6818" ;; *7580*) echo "Samsung Exynos 7580" ;; esac ;; *A53r0p3*A53r0p3*A53r0p3*A53r0p3) # Mediatek MT6735: 4 x Cortex-A53 / r0p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm sha2 (booting 32-bit) # or Mediatek MT8163: 4 x Cortex-A53 / r0p3 / half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt lpae evtstrm aes pmull sha1 sha2 crc32 # or HiSilicon Hi3751: 4 x Cortex-A53 / r0p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt (booting 32-bit kernel) case "${DTCompatible,,}" in *mt6735*) echo "Mediatek MT6735" ;; *mt8163*) echo "Mediatek MT8163" ;; *hi3751*) echo "HiSilicon Hi3751" ;; esac ;; *A53r0p3*A53r0p3) # HiSilicon Hi3751: 2 x Cortex-A53 / r0p3 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt (booting 32-bit kernel) echo "HiSilicon Hi3751" ;; 00Cavium88XXr1p1*) # ThunderX CN8890, 48 x ThunderX 88XX / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "$(( ${CPUCores} / 48 )) x ThunderX CN8890" ;; ??A9r2p10??A9r2p10??A9r2p10??A9r2p10) # NXP i.MX6 Quad | 4 x Cortex-A9 / r2p10 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 echo "NXP i.MX6 Quad" ;; *A9r2p10*A9r2p10) # NXP i.MX6 Dual | 2 x Cortex-A9 / r2p10 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 # or TI OMAP4470: 2 x Cortex-A9 / r2p10 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 grep -q "imx6" <<<"${DTCompatible,,}" && echo "NXP i.MX6 Dual" || echo "TI OMAP4470" ;; ??A9r2p1??A9r2p1) # FreeScale/NXP QorIQ LS1024A | 2 x Cortex-A9 / r2p1 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls # or Samsung Exynos 4210 | 2 x Cortex-A9 / r2p1 / swp half thumb fastmult vfp edsp neon vfpv3 tls echo "NXP QorIQ LS1024A or Samsung Exynos 4210" ;; ??A9r1p2??A9r1p2) # TI OMAP 4460 | 2 x Cortex-A9 / r1p2 / https://e2e.ti.com/support/processors-group/processors/f/processors-forum/243190/booting-problem-of-omap4460-pandaboard echo "TI OMAP 4460" ;; ??A9r1p3??A9r1p3) # TI OMAP 4430 | 2 x Cortex-A9 / r1p3 / swp half thumb fastmult vfp edsp thumbee neon vfpv3 echo "TI OMAP 4430" ;; ??A15r0p4??A15r0p4) # Samsung Exynos 5 Dual 5250: 2 x Cortex-A15 / r0p4 / https://bench.cr.yp.to/computers.html echo "Samsung Exynos 5 Dual 5250" ;; ??A15r2p4??A15r2p4) # AnnapurnaLabs Alpine | 2 x Cortex-A15 / r2p4 / swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt echo "AnnapurnaLabs Alpine" ;; ??Marvellr1p1) # Marvell Armada 370/XP | 1 x Marvell PJ4 / r1p1 / half thumb fastmult vfp edsp vfpv3 vfpv3d16 tls idivt echo "Marvell Armada 370/XP" ;; ??Marvellr0p5) # Marvell Armada 510, 1 x Marvell PJ4 / r0p5 / swp half thumb fastmult vfp edsp iwmmxt thumbee echo "Marvell Armada 510" ;; ??Marvell88FR131r2p1) # Marvell Kirkwood 88F6281: 1 x Marvell Feroceon 88FR131 / r2p1 / swp half thumb fastmult edsp echo "Marvell Kirkwood 88F6281 / Armada 300/310" ;; ??Marvellr2p2??Marvellr2p2??Marvellr2p2??Marvellr2p2) # Marvell PJ4Bv7 | 4 x Marvell PJ4B-MP / r2p2 / swp half thumb fastmult vfp edsp vfpv3 tls echo "Marvell PJ4Bv7" ;; ??ARM11r0p5??ARM11r0p5) # PLX NAS7820: 2 x ARM11 MPCore / r0p5 / swp half thumb fastmult edsp java echo "PLX NAS7820" ;; *FTC660r1p1*FTC660r1p1*FTC660r1p1*FTC660r1p1) # Phytium FT-1500A: 4 x Phytium FTC660 / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 / https://openbenchmarking.org/s/Phytium+FT1500A echo "Phytium FT-1500A" ;; *FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*FTC662r1p2*) # Phytium FT-2000+/64: 64 x Phytium FTC662 / r1p2 / fp asimd evtstrm crc32 / https://github.com/util-linux/util-linux/issues/1036 echo "Phytium FT-2000+/64" ;; *FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*) # Phytium FT-2500: 64 x Phytium FTC663 / r1p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "$(( ${CPUCores} / 64 )) x Phytium FT-2500" ;; *FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3) # Phytium D2000/8: 8 x Phytium FTC663 / r1p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Phytium D2000/8" ;; *FTC663r1p3*FTC663r1p3*FTC663r1p3*FTC663r1p3) # Phytium FT-2000/4: 4 x Phytium FTC663 / r1p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 / https://www.phytium.com.cn/en/article/97 echo "Phytium FT-2000/4" ;; *FTC310r0p4*FTC310r0p4*FTC664r1p3*FTC664r1p3) # Phytium E2000Q: 2 x FTC310 / r0p4 + 2 x FTC664 / r1p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid sha3 sha512 echo "Phytium E2000Q" ;; *FTC310r0p4*FTC310r0p4) # Phytium E2000D: 2 x FTC310 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid sha3 sha512 echo "Phytium E2000D" ;; *FTC310r0p4) # Phytium E2000S: 1 x FTC310 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid sha3 sha512 echo "Phytium E2000S" ;; *FTC862r0p0*FTC862r0p0*FTC862r0p0*FTC862r0p0*FTC862r0p0*FTC862r0p0*FTC862r0p0*FTC862r0p0) # Phytium D3000/8: 8 x Phytium FTC862 / r0p0 / https://browser.geekbench.com/v6/cpu/3447903.gb6 echo "Phytium D3000" ;; *A57r1p2*A57r1p2*A57r1p2*A57r1p2*A57r1p2*A57r1p2*A57r1p2*A57r1p2) # AMD Opteron A1100: 8 x Cortex-A57 / r1p2 / https://bugzilla-attachments.redhat.com/attachment.cgi?id=1475897 echo "AMD Opteron A1100" ;; *Cavium99xxr0p1*) # Cavium ThunderX2 CN9980: 32 x Cavium ThunderX2 99xx / r0p1 echo "$(( ${CPUCores} / 32 )) x Cavium ThunderX2 CN9980" ;; *XGener3p2*) # APM Emag 8180: 32 x Ampere X-Gene / r3p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid # Earlier X-Gene revisions than r3 lack CRC and crypto flags/features echo "$(( ${CPUCores} / 32 )) x APM Emag 8180" ;; *Xgener0p0*Xgener0p0*Xgener0p0*Xgener0p0*Xgener0p0*Xgener0p0*Xgener0p0*Xgener0p0*) # APM 883208-X1: 8 x APM X-Gene / r0p0 / fp asimd evtstrm echo "$(( ${CPUCores} / 8 )) x APM 883208-X1" ;; *A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*) # HiSilicon Kunpeng 916 in Huawei Taishan 100 2280 server: 2 x 32 x Cortex-A72 / r0p2 / https://gist.github.com/expipiplus1/bd48761b119e867d3c9ddabc2f677374 echo "$(( ${CPUCores} / 32 )) x HiSilicon Kunpeng 916" ;; *TaiShanv110r1p0*) # HiSilicon Kunpeng 920-6426 in Huawei Taishan 200 2280 V2 server: 2 x 64 x TaiShan v110 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma dcpop asimddp asimdfhm ssbs # https://www.spec.org/cpu2017/results/res2020q2/cpu2017-20200529-22564.html / https://en.wikichip.org/wiki/hisilicon/microarchitectures/taishan_v110 case $(awk -F":" '/ per socket/ {print $2}' <<<"${LSCPU}" | tr -c -d '[:digit:]') in 32) echo "$(( ${CPUCores} / 32 )) x HiSilicon Kunpeng 920-3226" ;; 48) echo "$(( ${CPUCores} / 48 )) x HiSilicon Kunpeng 920-4826" ;; 64) echo "$(( ${CPUCores} / 64 )) x HiSilicon Kunpeng 920-6426" ;; 24) echo "HiSilicon Kunpeng 920 3211K" ;; 8) echo "HiSilicon Kunpeng 920 2249K" ;; 4) echo "HiSilicon Kunpeng 920 quad core" ;; esac ;; *TaiShanv120*) # HiSilicon HiSilicon Kunpeng 930 echo "HiSilicon Kunpeng 930" ;; *A72r0p2*A72r0p2*A53r0p4*A53r0p4) # Socionext UniPhier LD20: 2 x Cortex-A72 / r0p2 + 2 x Cortex-A53 / r0p4 / https://lore.kernel.org/all/CAM-ziR6N36F-2C7wHLEa4rUD1BpN+pAyMtnjCS9NWJWACZnwQA@mail.gmail.com/T/ echo "Socionext UniPhier LD20" ;; *A53r0p1*A53r0p1*A53r0p1*A53r0p1*A57r1p0*A57r1p0*A57r1p0*A57r1p0) # Samsung Exynos 5433: 4 x Cortex A53 / r0p1 + 4 x Cortex A57 / r1p0 / ? echo "Samsung Exynos 5433" ;; *A53r0p2*A53r0p2*A53r0p2*A53r0p2*A57r1p0*A57r1p0*A57r1p0*A57r1p0) # Samsung Exynos 7420: 4 x Cortex A53 / r0p2 + 4 x Cortex A57 / r1p0 / fp asimd aes pmull sha1 sha2 crc32 echo "Samsung Exynos 7420" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A53r0p4*A73r0p2*A73r0p2) # Samsung Exynos 7885: 6 x Cortex A53 / r0p4 + 2 Cortex A73 / r0p2 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Samsung Exynos 7885" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*Samsungr1p1*Samsungr1p1*Samsungr1p1*Samsungr1p1) # Samsung Exynos 8890: 4 x Cortex A53 / r0p4 + 2 or 4 x Exynos-m1 / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "Samsung Exynos 8890" ;; *A53r0p4*A53r0p4*A53r0p4*A53r0p4*Samsungr4p0*Samsungr4p0*Samsungr4p0*Samsungr4p0) # Samsung Exynos 8895: 4 x Cortex A53 / r0p4 + 4 x Exynos-m1 / r4p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # The cores are called M2 (Mongoose 2) but share same core ID with M1 and only stepping differs echo "Samsung Exynos 8895" ;; *A55r0p1*A55r0p1*A55r0p1*A55r0p1*Samsungr1p0*Samsungr1p0*Samsungr1p0*Samsungr1p0) # Samsung Exynos 9810: 4 x Cortex A55 / r0p1 + 4 x Exynos-m3 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp echo "Samsung Exynos 9810" ;; *A55r1p0*A55r1p0*A55r1p0*A55r1p0*A75r2p1*A75r2p1*Samsungr1p0*Samsungr1p0) # Samsung Exynos 9820: 4 x Cortex A55 / r1p0 + 2 x Cortex-A75 / r2p1 + 2 x Exynos-m4 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm dcpop asimddp echo "Samsung Exynos 9820" ;; *A76r3p0*A76r3p0*A76r3p0*A76r3p0*A76r3p0*A76r3p0*A76r3p0*A76r3p0) # Samsung Exynos Auto V9: 8 x Cortex-A76 / r3p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop echo "Samsung Exynos Auto V9" ;; *A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2*A72r0p2) # NXP LS2088A: 8 x Cortex-A72 / r0p2 / https://community.nxp.com/t5/QorIQ/LS2088-ETH1-connection/td-p/1024323 echo "NXP LS2088A" ;; *A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3*A72r0p3) # NXP LX2160A: 16 x Cortex-A72 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 # or AWS Graviton, 16 x Cortex-A72 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 / L1d: 32K, L1i: 48K, L2: 2048K grep -q -E "nxp|lx2160" <<<"${DTCompatible,,}" && echo "NXP LX2160A" || echo "AWS Graviton" ;; 36?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p3) # NXP LX2080A: 8 x Cortex-A72 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "NXP LX2080A" ;; 36?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p336?A72r0p3) # NXP LX2120A: 12 x Cortex-A72 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "NXP LX2120A" ;; *A72r0p3*A72r0p3) # NXP LS1028A: 2 x Cortex-A72 / r0p3 / fp asimd evtstrm aes pmull sha1 sha2 crc32 echo "NXP LS1028A" ;; *A75r3p1*) # Unisoc T610/T618/T700/T740: 4-6 x Cortex A55 + 2/4 x Cortex-A75 / r3p1 case "${DTCompatible,,}" in *t610*) echo "Unisoc T610" ;; *t618*) echo "Unisoc T618" ;; *t700*) echo "Unisoc T700" ;; *t740*) echo "Unisoc T740" ;; *unisoc*) echo "Unisoc T610/T618/T700/T740" ;; esac ;; *NeoverseN1r3p1*) # Ampere Altra / Altra Max: 32/48/64/72/80/96/128 x Neoverse-N1 / r3p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp ssbs # or AWS Graviton2: 1/2/4/8/16/32/48/64 vCPU Neoverse-N1 / r3p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp ssbs / L1d 128K, L1i 128K, L2 2M, L3 32M # https://lore.kernel.org/all/alpine.DEB.2.22.394.2204131354190.3066615@ubuntu-linux-20-04-desktop/ # https://www.spec.org/cpu2017/results/res2021q3/cpu2017-20210702-27694.pdf # https://www.youtube.com/watch?v=4jImmuMqnwc&t=1111s # https://www.anandtech.com/show/15578/cloud-clash-amazon-graviton2-arm-against-intel-and-amd # https://github.com/coreos/fedora-coreos-tracker/issues/920#issuecomment-904981854 # https://github.com/xianyi/OpenBLAS/issues/2696#issuecomment-652627716 # https://www.anandtech.com/show/16979/the-ampere-altra-max-review-pushing-it-to-128-cores-per-socket # https://blog.cloudflare.com/arms-race-ampere-altra-takes-on-aws-graviton2/ # # Distinguishing between Graviton2 and Ampere Altra (QuickSilver) isn't easy since they # share same core types, stepping, CPU flags and even cache sizes though this site here # https://adam.younglogic.com/2022/05/building-linux-tip-of-tree-on-an-ampere-based-system/ # reports smaller cache sizes for Ampere Altra. Measured clockspeeds should differ (2.5 GHz # for AWS vs. 3/3+ GHz for Altra while reviews mentioned little less). Altra Max (Mystique) # could be identified by its smaller L3 cache. if [ -r "${ResultLog}" ]; then grep -q 'No cpufreq support available. Measured on cpu' "${ResultLog}" && \ MeasuredClockspeed=$(awk -F": " '/No cpufreq support available. Measured on cpu/ {print $2}' <"${ResultLog}" | cut -f1 -d' ' | head -n 1) || \ MeasuredClockspeed=$(grep -A2 "^Checking cpufreq OPP" "${ResultLog}" | awk -F" " '/Measured/ {print $5}' | sort -r | head -n1) fi ProcessorInfo="$(dmidecode -t processor 2>/dev/null | grep -E -v -i "^#|^Handle|Serial|O.E.M.|1234567|SMBIOS|: Not |Unknown|OUT OF SPEC|00 00 00 00 00 00 00 00|: None")" case ${ProcessorInfo} in *Ampere*|*Altra*) # Try to get Part Number via DMI PartNumber="$(awk -F": " '/Part Number: / {print $2}' <<<"${ProcessorInfo}" | tr '\n' '/' | sed -e 's/NotSet/Unpopulated/' -e 's/\/$//')" if [ -n "${PartNumber}" ]; then # output part number: echo "Ampere Altra ${PartNumber}" else # empty string, so check count of CPU cores per socket and in total and report that case $(awk -F":" '/ per socket/ {print $2}' <<<"${LSCPU}" | tr -c -d '[:digit:]') in 32) echo "$(( ${CPUCores} / 32 )) x Ampere Altra AADP-32" ;; 48) echo "$(( ${CPUCores} / 48 )) x Ampere Altra AADP-48" ;; 64) echo "$(( ${CPUCores} / 64 )) x Ampere Altra AADP-64" ;; 72) echo "$(( ${CPUCores} / 72 )) x Ampere Altra AADP-72" ;; 80) echo "$(( ${CPUCores} / 80 )) x Ampere Altra AADP-80" ;; 96) echo "$(( ${CPUCores} / 96 )) x Ampere Altra AADP-96" ;; 128) echo "$(( ${CPUCores} / 128 )) x Ampere Altra Max" ;; *) echo "Ampere Altra / Altra Max" ;; esac fi ;; *) # No DMI information hinting at Ampere/Altra so we check clockspeeds if [ ${MeasuredClockspeed:-0} -gt 2550 ]; then # Lame assumption that cpufreq above 2.5GHz identifies Ampere Altra echo "Ampere Altra" else echo "AWS Graviton2" fi ;; esac ;; *NeoverseV1r1p1*) # AWS Graviton3: 1/2/4/8/16/32/48/64 vCPU Neoverse-V1 / r1p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs paca pacg dcpodp svei8mm svebf16 i8mm bf16 dgh rng echo "AWS Graviton3" ;; *NeoverseN2r0p0*) # Marvell Octeon 10: 24 x Neoverse-N2 / r0p0 / fp asimd aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint rng / L1d 1.5M, L1i 1.5M, L2 24M, L3 48M # or Alibaba g8y/c8y/r8y VMs hosted on YiTian 710 CPUs: 1/2/4/8/16/32/64/128 x Neoverse-N2 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh bti if [ ${CPUCores} -eq 24 ]; then echo "Marvell Octeon 10" else echo "YiTian 710" fi ;; *NeoverseV2r0p0*) # Nvidia Grace: 72 or 144 Neoverse-V2 / r0p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh case ${CPUCores} in 72) echo "Nvidia Grace Hopper" ;; 144) echo "Nvidia Grace" ;; esac ;; *AppleM1r0p0*AppleM1r0p0*AppleM1r0p0*AppleM1r0p0*AppleM1*AppleM1*AppleM1*AppleM1*|*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1*AppleM1r1p1) # Apple M1: 4 x Apple Icestorm / r1p1 + 4 x Apple Firestorm / r1p1 / https://gist.github.com/z4yx/13520bd2beef49019b1b7436e3b95ddd # or 4 x Apple Icestorm / r0p0 + 4 x Apple Firestorm / ? / https://bench.cr.yp.to/computers.html echo "Apple M1" ;; *AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0*AppleM1Pror2p0) # Apple M1 Pro: 2 x Apple Icestorm / r2p0 + 4 x Apple Firestorm / r2p0 + 4 x Apple Firestorm / r2p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint echo "Apple M1 Pro" ;; *AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max) # Apple M1 Ultra: 2 M1 Max combined on an Interposer echo "Apple M1 Ultra" ;; *AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max*AppleM1Max) # Apple M1 Max: 2 x Apple Icestorm + 8 x Apple Firestorm echo "Apple M1 Max" ;; *AppleM2r1p0*AppleM2r1p0*AppleM2r1p0*AppleM2r1p0*AppleM2r1p0*AppleM2r1p0*AppleM2r1p0*AppleM2r1p0) # Apple M2: 4 x Apple Blizzard / r1p0 + 4 x Apple Avalanche / r1p0 / https://github.com/hrw/arm-socs-table/blob/main/cpuinfo-data/apple-m2 echo "Apple M2" ;; *AppleM2Pro*) # Apple M2 Pro: 4 x Apple Blizzard + 6/8 x Apple Avalanche / https://github.com/AsahiLinux/m1n1/commit/7904ed43dc0c2760e728f611efa0b26895ebcd10 echo "Apple M2 Pro" ;; *AppleM2Max*) # Apple M2 Max: 4 x Apple Blizzard + 8 x Apple Avalanche / https://github.com/AsahiLinux/m1n1/commit/8d0474f57b1fb45588d412dcbe7e229dbe6d43cb echo "Apple M2 Max" ;; *AppleM3Pro*) # Apple M3 Pro: 6 x Apple Sawtooth + 6 x Apple Everest echo "Apple M3 Pro" ;; *AppleM3Max*) # Apple M3 Max: 4 x Apple Sawtooth + 12 x Apple Everest / https://github.com/AsahiLinux/m1n1/pull/357 echo "Apple M3 Max" ;; *VirtualizedSiliconr0p0*) # When running bare metal M1 and M2 SoCs differ by flags/features: # M1: 'fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint' # M2: 'fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 bti ecv' # M3: Based on comparing 'sysctl -A | grep "hw.optional.arm.FEAT_"' output in macOS CPU features/flag seem to be identical to M2 # Inside Hypervisor.Framework guests on M1/M2 hosts all ID values are set to 0 (0x61/0x0 r0p0 on M3) and on M2/M3 SoCs VMs only see M1 CPU flags/features. grep -q -E 'fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint|fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp flagm2 frint' <<< "${ProcCPU}" && echo "Apple Silicon" ;; *thead,c906|10) # Allwinner D1: single T-Head C906 core echo "Allwinner D1" ;; *rv64i2p0m2p0a2p0f2p0d2p0c2p0xv50p0*rv64i2p0m2p0a2p0f2p0d2p0c2p0xv50p0) # Kendryte K510: Dual-core 64-bit RISC-V https://canaan.io/product/kendryte-k510 grep -q k510 <<<"${DTCompatible,,}" && echo "Kendryte K510" ;; *thead,c908*thead,c908|*rv64imafdcvxthead*) # Kendryte K230: Dual-core 64-bit RISC-V https://canaan.io/product/kendryte-k230 echo "Kendryte K230" ;; *thead,c910*thead,c910|1?1?|10rv64imafdcsu10rv64imafdcsu) # T-Head C910: Dual-core XuanTieISA (compatible with RISC-V 64GC) https://www.t-head.cn/product/c910?lang=en grep -q c910 <<<"${DTCompatible,,}" && echo "T-Head C910" ;; ??rv64imafdcvsu??rv64imafdcvsu??rv64imafdcvsu??rv64imafdcvsu) # T-Head TH1520: quad-core T-Head C910 https://occ.t-head.cn/wujian600?id=4080405462988689408 echo "T-Head TH1520" ;; *sifive,u54-mc*sifive,u54-mc*sifive,u54-mc*sifive,u54-mc) # SiFive "Freedom" U540: 4 x U54-MC https://www.sifive.com/cores/u54-mc echo "SiFive U540" ;; 10sifive,u74mc10sifive,u74mc10sifive,u74mc10sifive,u74mc|*sifive,u74mc*sifive,u74mc*sifive,u74mc*sifive,u74mc) # StarFive JH7110: 4 x U74-MC https://raw.githubusercontent.com/ThomasKaiser/sbc-bench/master/results/cpuinfo/StarFive-JH7110-6.5.0.cpuinfo echo "StarFive JH7110" ;; *sifive,u74mc*sifive,u74mc) # StarFive JH7100: 2 x U74-MC https://doc-en.rvspace.org/Doc_Center/datasheet_7100.html echo "StarFive JH7100" ;; *00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv00rv64imafdcv) # Sophgo Mango / Sophon SG2042, 64 x T-Head C910 https://servernews.ru/1081875 if [ ${CPUCores} -gt 64 ]; then echo "$(( ${CPUCores} / 64 )) x Sophon SG2042" else echo "Sophon SG2042" fi ;; *rv64imafdc) # Renesas RZ/Five R9A07G043: single Andes AX45MP core if [ ${CPUCores} -eq 1 ]; then echo "Renesas RZ/Five R9A07G043" fi ;; 0?Qualcomm3XXSilver0?Qualcomm3XXSilver0?Qualcomm3XXSilver0?Qualcomm3XXSilver0?Qualcomm3XXGold0?Qualcomm3XXGold0?Qualcomm3XXGold0?Qualcomm3XXGold) # Qualcomm Snapdragon 845 / SDM845: 4 x Qualcomm Kryo 3XX Silver / r7p12 + 4 x Qualcomm Kryo 3XX Gold / r6p13 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop echo "Qualcomm Snapdragon 845" ;; 00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver00Qualcomm4XXSilver14A77r1p014A77r1p014A77r1p027A77r1p0) # Qualcomm Snapdragon 865 or QRB5165: 4 x Qualcomm Kryo 4XX Silver / r13p14 + 3 x Cortex-A77 / r1p0 + 1 x Cortex-A77 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp asimdrdm lrcpc dcpop asimddp echo "Qualcomm Snapdragon 865 / QRB5165" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A78r1p0*A78r1p0*A78r1p0*X1r1p0) # Qualcomm SM8350 (Snapdragon 888): 4 x Cortex-A55 / r2p0 + 3 x Cortex-A78 / r1p0 + 1 x Cortex-X1 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "Qualcomm SM8350 (Snapdragon 888)" ;; *A78Cr0p0*A78Cr0p0*A78Cr0p0*A78Cr0p0*X1Cr0p0*X1Cr0p0*X1Cr0p0*X1Cr0p0|360X1Cr0p0360X1Cr0p0360X1Cr0p0360X1Cr0p0360X1Cr0p0360X1Cr0p0360X1Cr0p0360X1Cr0p0) # Qualcomm Snapdragon 8cx Gen 3 : 4 x Cortex-A78C / r0p0 + 4 x Cortex-X1C / r0p0 / fp asimd aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp ilrcpc flagm # When running Linux on this thing in WSL2 (Windows Subsystem for Linux 2) all 8 cores are presented as Cortex-X1C and from within Linux it's impossible # to assign single-threaded tasks to an A78C since they end up on an X1C anyway: https://github.com/ThomasKaiser/sbc-bench/issues/58#issuecomment-1374900303 echo "Qualcomm Snapdragon 8cx Gen 3" ;; *A510r0p2*A510r0p2*A510r0p2*A510r0p2*A710r2p0*A710r2p0*A710r2p0*X2r2p0) # Qualcomm Snapdragon 8 Gen1: 4 x Cortex-A510 / r0p2 + 3 Cortex-A710 / r2p0 + 1 x Cortex-X2 / r2p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 bti echo "Qualcomm Snapdragon 8 Gen1" ;; *A510r0p3*A510r0p3*A510r0p3*A510r0p3*A710r2p0*A710r2p0*A710r2p0*X2r2p0) # Qualcomm Snapdragon 8+ Gen1: 4 x Cortex-A510 / r0p3 + 3 Cortex-A710 / r2p0 + 1 x Cortex-X2 / r2p0 echo "Qualcomm Snapdragon 8+ Gen1" ;; *A510r1p1*A510r1p1*A510r1p1*A715r1p0*A715r1p0*A710r2p0*A710r2p0*X3r1p0) # Qualcomm Snapdragon 8 Gen 2 (SM8550): 3 x Cortex-A510 / r1p1 + 2 x Cortex-A715 / r1p0 + 2 x Cortex-A710 / r2p0 + 1 x Cortex-X3 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 bti echo "Qualcomm Snapdragon 8 Gen 2" ;; *A520r0p1*A520r0p1*A720r0p1*A720r0p1*A720r0p1*A720r0p1*A720r0p1*X4r0p1) # Qualcomm Snapdragon 8 Gen 3: 2 x Cortex-A520 / r0p1 + 5 x Cortex-A720 / r0p1 + 1 x Cortex-X4 / r0p1 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bf16 dgh bti ecv afp echo "Qualcomm Snapdragon 8 Gen 3" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A76r4p0*A76r4p0*X1r1p0*X1r1p0) # Google Tensor G1: 4 x Cortex-A55 / r2p0 + 2 x Cortex-A76 / r4p0 + 2 x Cortex-X1 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "Google Tensor G1" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A78r1p1*A78r1p1*X1r1p0*X1r1p0) # Google Tensor G2: 4 x Cortex-A55 / r2p0 + 2 x Cortex-A78 / r1p1 + 2 x Cortex-X1 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "Google Tensor G2" ;; *A510r1p1*A510r1p1*A510r1p1*A510r1p1*A715r1p0*A715r1p0*A715r1p0*A715r1p0) # Google Tensor G3: 4 x Cortex-A510 / r1p1 + 4 x Cortex-A715 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bti mte mte3 echo "Google Tensor G3" ;; 0?Loongson3A10000?Loongson3A10000?Loongson3A10000?Loongson3A1000) # Loongson 3A1000: 4 x Loongson-3 V0.5 FPU V0.1 https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A1000.cpuinfo echo "Loongson 3A1000" ;; 0?Loongson3A30000?Loongson3A30000?Loongson3A30000?Loongson3A3000) # Loongson 3A3000: 4 x Loongson-3 V0.9 FPU V0.1 https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A3000-5.4.211-aosc-lemote.cpuinfo echo "Loongson 3A3000" ;; 0?Loongson3A5000H0?Loongson3A5000H0?Loongson3A5000H0?Loongson3A5000H) # Loongson-3A5000-H: 4 x LoongArch / loongarch32, loongarch64 / cpucfg lam ual fpu lsx lasx complex crypto lvz lbt_x86 lbt_arm lbt_mips https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A5000-H-4.19.0-17-loongson-3.cpuinfo echo "Loongson-3A5000-H" ;; 0?Loongson3A5000HV0?Loongson3A5000HV0?Loongson3A5000HV0?Loongson3A5000HV) # Loongson-3A5000-HV: 4 x LoongArch / loongarch32, loongarch64 / cpucfg lam ual fpu lsx lasx complex crypto lvz lbt_x86 lbt_arm lbt_mips https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A5000-HV-4.19.0-loongson-3.cpuinfo echo "Loongson-3A5000-HV" ;; 0?Loongson3A5000M0?Loongson3A5000M0?Loongson3A5000M0?Loongson3A5000M) # Loongson-3A5000M: 4 x LoongArch / loongarch32, loongarch64 / cpucfg lam ual fpu lsx lasx complex crypto lvz lbt_x86 lbt_arm lbt_mips https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A5000M-4.19.0.cpuinfo echo "Loongson-3A5000M" ;; *Loongson3A6000*Loongson3A6000*Loongson3A6000*Loongson3A6000) # Loongson-3A6000: 4 x LoongArch / loongarch32, loongarch64 / cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips https://github.com/ThomasKaiser/sbc-bench/blob/master/results/cpuinfo/Loongson-3A6000-4.19.0-loongson-3.cpuinfo # With SMT support: with SCHED_SMT enabled CPU appears as 4c/8t, without as 8c/8t? https://lkml.org/lkml/2023/6/14/397 echo "Loongson-3A6000" ;; *Loongson3?6000*Loongson3?6000*Loongson3?6000*Loongson3?6000) # Loongson-3A6000 variants with more cores [ "X${ModelName}" != "X" ] && echo "${ModelName}" ;; *A55*A55*A55*A55*A76r4p1|*A76r4p1*A55*A55*A55*A55r?p?) # Amlogic S928X, 4 x Cortex-A55 + 1 x Cortex-A76 / r4p1: https://browser.geekbench.com/v5/cpu/compare/19788026?baseline=20656779 echo "Amlogic S928X" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A55r2p0*A78r1p0*A78r1p0) # MediaTek MT8188JV/A: 6 x Cortex-A55 / r2p0 + 2 x Cortex-A78 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "MediaTek MT8188JV/A" ;; *A55r2p0*A55r2p0*A55r2p0*A55r2p0*A78r1p0*A78r1p0*A78r1p0*A78r1p0) # MediaTek Genio 1200 (MT8395): 4 x Cortex-A55 / r2p0 + 4 x Cortex-A78 / r1p0 / fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp echo "MediaTek Genio 1200 (MT8395)" ;; 0?IngenicV4.15FPUV0.00?IngenicV4.15FPUV0.0) # Ingenic JZ4780: dual-core MIPS32 echo "Ingenic JZ4780" ;; *Azure100r?p?*) # Microsoft Azure Cobalt 100, based on Neoverse-N2 r0p0 # https://lore.kernel.org/linux-arm-kernel/b99a7196-011e-4f08-83ec-e63a690ab919@linux.microsoft.com/T/ echo "Azure Cobalt 100" ;; esac } # GuessSoCbySignature GetCPUSignature() { case ${CPUArchitecture} in arm*|aarch*|riscv*|mips*|loongarch*) sed -e '1,/^ CPU/ d' -e 's/Cortex-//' -e 's/HiSilicon-//' -e 's/Phytium //' <<<"${CPUTopology}" | while read ; do echo -e "$(awk -F" " '{print $2$3$6$8$9$10}' <<<"${REPLY}" | sed -e 's/-//g' -e 's/\///g')\c" done ;; esac } # GetCPUSignature IdentifyAXGGXLG12A() { # Amlogic AXG, GXL and G12A SoC families share same cluster details (quad A53 / r0p4). # They differ in speed though: GXL usually fakes 1.5 GHz while being limited to 1.4 GHz # (LibreComputer's La Frite and Jethub devices being the exceptions reporting the true # 1416 MHz) but G12A (S905X2, S905D2, S905Y2) clocks higher and doesn't fake clockspeeds MaxSpeed="$(sed -e '1,/^ CPU/ d' <<<"${CPUTopology}" | tail -n1 | awk -F" " '{print $5}')" case ${MaxSpeed} in 15??|14??) # GXL or AXG: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid (running 32-bit: fp asimd evtstrm aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt) echo "Amlogic A113X/A113D, S805X, S805Y, S905X/S905D/S905L/S905L2/S905M2" ;; 12??) # S905W being limited to 1.2 GHz echo "Amlogic S905W" ;; *) # G12A/TL1: 4 x Cortex-A53 / r0p4 / fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid echo "Amlogic S905X2/S905Y2/S905D2/S905L3A/T962X2" ;; esac } # IdentifyAXGGXLG12A IdentifyAllwinnerARMv8() { # Allwinner sun50iw* except sun50iw11 are all quad A53 / r0p4. Today sun50iw1p # (A64/H64), sun50iw2 (H5), sun50iw6 (H6) and sun50iw9 (H616/H313) seem to exist # in the wild. # # Allwinner SoCs feature a Security/Chip ID (SID): https://linux-sunxi.org/SID_Register_Guide # # Allwinner H5, 5.10.60-sunxi64 # /sys/devices/platform/soc/1c14000.eeprom/sunxi-sid0/nvmem # 00000000 01 00 80 82 04 47 00 64 04 c3 36 50 0e 09 37 64 # 00000010 42 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 00000030 00 00 00 00 cd 07 ed 07 00 00 00 00 00 00 00 00 # 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # # Allwinner H6, 5.4.20-sunxi64 # /sys/devices/platform/soc/3006000.efuse/sunxi-sid0/nvmem # 00000000 01 00 c0 82 08 47 00 0c 3e 04 41 01 4b 22 74 54 # 00000010 00 00 00 00 f7 10 21 09 29 09 01 00 40 00 00 00 # 00000020 00 00 00 00 79 00 00 00 47 8f 00 00 01 00 e0 02 # 00000030 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # # H616/H313 is usually combined with AXP806 PMIC, lacks USB3 but should be # equipped with 2 GMAC according to http://ix.io/2cGy and http://ix.io/2MBN # According to https://irclog.whitequark.org/linux-sunxi/2021-01-19#28890526; # AXP1530 and AXP305 (compatible to AXP805) are also mentioned but won't show # up in /proc/interrupts # # Since no access to A64 and H616/H313 right now, try to identify the different # quad core SoCs based on presence of PMIC, USB3 and maybe clockspeeds # check device-tree compatible strings grep -q h616 <<<"${DTCompatible,,}" && echo "Allwinner H616/H313" grep -q t507 <<<"${DTCompatible,,}" && echo "Allwinner T507" grep -q h313 <<<"${DTCompatible,,}" && echo "Allwinner H313" grep -q pine-h64 <<<"${DTCompatible,,}" && echo "Allwinner H6" grep -q h64 <<<"${DTCompatible,,}" && echo "Allwinner H64" grep -q h6 <<<"${DTCompatible,,}" && echo "Allwinner H6" grep -q h5 <<<"${DTCompatible,,}" && echo "Allwinner H5" grep -q a64 <<<"${DTCompatible,,}" && echo "Allwinner A64" # Check for USB3 first, mainline kernel: grep -q "xhci" /proc/interrupts && echo "Allwinner H6" # No idea about H6 BSP kernel but we simply play safe lsusb -t | grep -q xhci && echo "Allwinner H6" # Now check for PMIC. Both H616 and H6 use AXP806 but the latter has USB3. # Checking with mainline seemed to work for axp806: http://ix.io/2GH3 # but with later kernels it's gone: http://ix.io/3woR. Reasons why: # https://github.com/jernejsk/linux-1/commit/50b9a4612a8cc6e2131b83f498d7c5d4cb6ea74f grep -q axp806 /proc/interrupts && echo "Allwinner H616/H313" # Maybe there's something in kernel ring buffer identifying the SoC grep -E -q "sun50i-h616|sun50iw9" <<<"${DMESG}" && echo "Allwinner H616/H313" grep -q sun50i-a64 <<<"${DMESG}" && echo "Allwinner A64" grep -q sun50i-h5 <<<"${DMESG}" && echo "Allwinner H5" grep -q sun50i-h6- <<<"${DMESG}" && echo "Allwinner H6" # A64 is accompanied by AXP803 PMIC: grep -q axp803 /proc/interrupts && echo "Allwinner A64" # With A64 BSP kernel we can try to rely on a sysfs node [ -d /sys/devices/platform/axp81x_board/axp81x-supplyer.47 ] \ && echo "Allwinner A64" # H5 is usually combined with an I2C attached Silergy SY8106A voltage regulator lsmod | grep -i -q sy8106a && echo "Allwinner H5" # Maybe there's something in kernel ring buffer identifying the SoC grep -E -q "sun50i-h5|sun50iw2" <<<"${DMESG}" && echo "Allwinner H5" # if we end up here then print some generic BS echo "Allwinner unidentified SoC" } # IdentifyAllwinnerARMv8 ShowZswapStats() { # https://www.kernel.org/doc/Documentation/vm/zswap.txt [ -r /sys/module/zswap/parameters/enabled ] && ZswapEnabled="$(sed 's/Y/1/' /dev/null read compressor /dev/null read zpool /dev/null if [ -n "${max_pool_percent}" ] && [ -n "${compressor}" ] && [ -n "${zpool}" ]; then echo -e "Zswap active using ${compressor}/${zpool}, max pool occupation: ${max_pool_percent}%\c" elif [ -n "${compressor}" ] && [ -n "${zpool}" ]; then echo -e "Zswap active using ${compressor}/${zpool}\c" else echo -e "Zswap active\c" fi if [ -d /sys/kernel/debug/zswap ]; then echo ", details:" read stored_pages /dev/null | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g")" case ${CPUFetchOutput} in *SoC:*) FirstLine="$(grep "SoC:" <<<"${CPUFetchOutput}")" LogoArea=${FirstLine%%SoC:*} ;; *Name:*) FirstLine="$(grep "Name:" <<<"${CPUFetchOutput}")" LogoArea=${FirstLine%%Name:*} ;; *"Part Number:"*) FirstLine="$(grep "Part Number:" <<<"${CPUFetchOutput}")" LogoArea=${FirstLine%%Part*} ;; esac cut -c$((${#LogoArea} + 1))- <<<"${CPUFetchOutput}" | sed -e '/^$/d' | grep -v "Peak Performance:" else echo "Unknown" fi } # GrabCPUFetchInfo ProvideReviewInfo() { # This function tries to collect as much performance relevant information # possible based on sbc-bench being now 5 years used on lots of devices. # # Once it's ready collecting data (which includes some measurements of e.g. # CPU clockspeeds and memory bandwidth/latency) it switches all performance # relevant governors to performance, then remains in the background to # report throttling while the reviewer conducts tests with other benchmarks # # TODO: # # * warning if uneven count of DIMMs # * ThreadX version, UEFI/BIOS version # * coherent_pool size echo "Starting to examine hardware/software for review purposes..." # ensure other sbc-bench instances are terminated for PID in $( (pgrep -f "${PathToMe}" ; jobs -p) | sort | uniq -u) ; do if [ ${PID} -ne $$ ]; then kill ${PID} 2>/dev/null fi done # prerequisits: create temp dir, ensure CPU utilization and/or average load is low # enough to continue, collect information, set governors to performance [ -z "${DMESG}" ] && DMESG="$(dmesg | grep -E "Linux|pvtm|rockchip-dmc|rockchip-cpuinfo|Amlogic Meson|sun50i|pcie|mmc|leakage")" CreateTempDir [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && \ read OriginalCPUFreqGovernor /dev/null [ "${OriginalCPUFreqGovernor}" = "ondemand" ] && \ io_is_busy=( $(find /sys/devices/system/cpu/cpufreq -name io_is_busy | while read ; do cat "${REPLY}"; done) ) GovernorState="$(HandleGovernors)" [ "X${RunBenchmarks}" = "XTRUE" ] && CheckLoadAndDmesg || echo "" # Allow for a NOTUNING=yes operation mode to compare default with performance settings if [ "X${NOTUNING}" != "Xyes" ] && [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ]; then HandleGovernors powersave if [ -f /sys/kernel/debug/clk/clk_summary ]; then snore 1 cat /sys/kernel/debug/clk/clk_summary >"${TempDir}/clk_summary.powersave" fi HandleGovernors performance OriginalCPUInfo="$(PrintCPUInfo)" TunedGovernorState="$(HandleGovernors | awk -F" " '{print $1" "$2" "$3" "$4" "$5}')" BasicSetup performance >/dev/null 2>&1 [ -f /sys/kernel/debug/clk/clk_summary ] && cat /sys/kernel/debug/clk/clk_summary >"${TempDir}/clk_summary.tuned" else BasicSetup nochange >/dev/null 2>&1 OriginalCPUInfo="$(PrintCPUInfo)" fi GetTempSensor OperatingSystem="$(GetOSRelease)" InstallPrerequisits [ "X${RunBenchmarks}" = "XTRUE" ] && InstallCpuminer InitialMonitoring CheckNetio unset SPACING if [ "X${RunBenchmarks}" = "XTRUE" ]; then CheckClockspeedsAndSensors # repeat SoC guessing after measuring CPU clockspeeds (some guesses depend on clockspeed) [ "${CPUArchitecture}" = "aarch64" ] && GuessedSoC="$(GuessARMSoC)" ClockspeedsBefore="$(cat "${TempDir}/cpufreq" | sed -e 's/^/ /')" [ "X${NOTUNING}" != "Xyes" ] && [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && CheckTimeInState before RunTinyMemBench RunRamlat RunOpenSSLBenchmark 256 Run7ZipBenchmark if [ -x "${InstallLocation}/Stockfish-sf_15/src/stockfish" ]; then ExecuteStockfish=yes RunStockfishBenchmark elif [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then ExecuteCpuminer=yes RunCpuminerBenchmark else RunStressNG fi [ -z ${InitialTemp} ] || TempNow=$(ReadSoCTemp) [ "X${NOTUNING}" != "Xyes" ] && [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && CheckTimeInState after CheckClockspeedsAndSensors ClockspeedsAfter="$(cat "${TempDir}/cpufreq" | sed -e 's/^/ /')" SummarizeResults else echo -e "\x08\x08 Done.\nNow quickly examining OS, settings and hardware...\c" echo -e "\n##########################################################################\n" >>"${ResultLog}" echo -e "$(iostat | grep -E -v "^loop|boot0|boot1|mtdblock")\n\n$(free -h)\n\n$(swapon -s)\n" | sed '/^$/N;/^\n$/D' >>"${ResultLog}" ShowZswapStats 2>/dev/null >>"${ResultLog}" echo >>"${ResultLog}" cat "${TempDir}/cpu-topology.log" >>"${ResultLog}" echo "${LSCPU}" >>"${ResultLog}" LogEnvironment >>"${ResultLog}" fi if [ -f "${TempDir}/clk_summary.tuned" ]; then # add clk_summary diff to results output if something has changed ClockDiff="$(diff "${TempDir}"/clk_summary.*)" if [ $? -ne 0 ]; then echo -e "\n##########################################################################\n\n/sys/kernel/debug/clk/clk_summary diff between all governors set to powersave and performance:\n" >>"${ResultLog}" head -n3 "${TempDir}/clk_summary.tuned" | sed -e 's/^/ /' >>"${ResultLog}" echo -e "${ClockDiff}" >>"${ResultLog}" fi fi # Prepare device listing in Markdown friendly format echo -e "# ${DeviceName:-$HostName}" >"${TempDir}/review" echo -e "\nTested with sbc-bench v${Version} on $(date -R).\n\n### General information:\n" >>"${TempDir}/review" # include cpufetch output if available CPUFetchInfo="$(GrabCPUFetchInfo)" if [[ ${CPUFetchInfo} != *Unknown* ]]; then echo "${CPUFetchInfo}" | sed -e 's/^/ /' >>"${TempDir}/review" echo " " >>"${TempDir}/review" fi # add info about CPU cores, if more than 2 CPU clusters add info about them and core types if [ ${#ClusterConfig[@]} -gt 1 ] && [ ${#ClusterConfigByCoreType[@]} -eq 1 ]; then echo -e "The CPU features ${#ClusterConfig[@]} clusters of same core type:\n" >>"${TempDir}/review" elif [ ${#ClusterConfig[@]} -gt 1 ] && [ ${#ClusterConfig[@]} -eq ${#ClusterConfigByCoreType[@]} ]; then echo -e "The CPU features ${#ClusterConfig[@]} clusters of different core types:\n" >>"${TempDir}/review" elif [ ${#ClusterConfig[@]} -gt 1 ]; then echo -e "The CPU features ${#ClusterConfig[@]} clusters consisting of ${#ClusterConfigByCoreType[@]} different core types:\n" >>"${TempDir}/review" fi sed -e 's/^/ /' <<<"${OriginalCPUInfo}" >>"${TempDir}/review" AvailableMem=$(free | awk -F" " '/^Mem: / {print $2}' | tail -n1) echo -e "\n$(( ${AvailableMem} / 1024 )) KB available RAM" >>"${TempDir}/review" # report probably performance relevant governors (not on x86_64) and policies PolicyStateNow="$(HandlePolicies)" if [ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && [ "${CPUArchitecture}" != "x86_64" ]; then echo -e "\n### Governors/policies (performance vs. idle consumption):\n\nOriginal governor settings:\n" >>"${TempDir}/review" sed -e 's/^/ /' <<<"${GovernorState}" >>"${TempDir}/review" if [ "X${TunedGovernorState}" != "X" ]; then # when running with NOTUNING=yes no before/after check will be performed echo -e "\nTuned governor settings:\n" >>"${TempDir}/review" sed -e 's/^/ /' <<<"${TunedGovernorState}" >>"${TempDir}/review" fi else [ "X${PolicyStateNow}" != "X" ] && echo -e "\n### Policies (performance vs. idle consumption):" >>"${TempDir}/review" fi if [ "X${PolicyStateNow}" != "X" ]; then echo -e "\nStatus of performance related policies found below /sys:\n" >>"${TempDir}/review" sed -e 's/^/ /' <<<"${PolicyStateNow}" >>"${TempDir}/review" fi if [ "X${RunBenchmarks}" = "XTRUE" ]; then # measured clockspeeds if [ -z ${InitialTemp} ]; then # no thermal readouts possible echo -e "\n### Clockspeeds (idle vs. heated up):\n\nBefore:\n\n${ClockspeedsBefore}\n\nAfter:\n\n${ClockspeedsAfter}" >>"${TempDir}/review" else echo -e "\n### Clockspeeds (idle vs. heated up):\n\nBefore at ${InitialTemp}°C:\n\n${ClockspeedsBefore}\n\nAfter at ${TempNow}°C${ThrottlingWarning}:\n\n${ClockspeedsAfter}" >>"${TempDir}/review" fi # Performance baseline: MemoryScores="$(awk -F" " '/^ libc / {print $2$4" "$5" "$6}' <"${ResultLog}" | grep -E 'memcpy|memchr|memset' | awk -F'MB/s' '{print $1"MB/s"}')" CountOfMemoryScores=$(wc -l <<<"${MemoryScores}") if [ $(( ${#ClusterConfig[@]} * 3 )) -eq ${CountOfMemoryScores} ]; then # only report if all measurements finished successfully, add warning for # NOTUNING operation mode. [ "X${NOTUNING}" = "Xyes" ] && PerfWarning=" (NOTUNING=yes was set)" echo -e "\n### Performance baseline${PerfWarning}\n" >>"${TempDir}/review" if [ ${#ClusterConfig[@]} -eq 1 ]; then # only 1 CPU cluster, no differentiation between clusters/core types echo -e " * $(grep "^memcpy:" <<<"${MemoryScores}"), $(grep "^memchr:" <<<"${MemoryScores}"), $(grep "^memset:" <<<"${MemoryScores}")" >>"${TempDir}/review" grep "^ 16384k:" "${ResultLog}" | sed 's/ 16384k:/ * 16M latency:/' >>"${TempDir}/review" grep "^ 131072k:" "${ResultLog}" | sed 's/ 131072k:/ * 128M latency:/' >>"${TempDir}/review" else # list individual CPUs and suffix memory scores with core types if possible for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e " * cpu${ClusterConfig[$i]}${CPUInfo}: $(grep "^memcpy:" <<<"${MemoryScores}" | sed -n $(( ${i} + 1 ))p), $(grep "^memchr:" <<<"${MemoryScores}" | sed -n $(( ${i} + 1 ))p), $(grep "^memset:" <<<"${MemoryScores}" | sed -n $(( ${i} + 1 ))p)" >>"${TempDir}/review" done for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e " * cpu${ClusterConfig[$i]}${CPUInfo} 16M latency: $(grep "^ 16384k:" "${ResultLog}" | sed -n $(( ${i} + 1 ))p | sed 's/ 16384k: //')" >>"${TempDir}/review" done for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do CPUInfo="$(GetCPUInfo ${ClusterConfig[$i]})" echo -e " * cpu${ClusterConfig[$i]}${CPUInfo} 128M latency: $(grep "^ 131072k:" "${ResultLog}" | sed -n $(( ${i} + 1 ))p | sed 's/ 131072k: //')" >>"${TempDir}/review" done fi ZIPResults="$(awk -F" " '/^Total:/ {print $2}' "${ResultLog}" | sed -e 's/,/, /g' -e 's/, $//')" [ "X${ZIPResults}" != "X" ] && echo -e " * 7-zip MIPS (${RunHowManyTimes} consecutive runs): ${ZIPResults} (${ZipScore} avg), single-threaded: ${ZipScoreSingleThreaded}" >>"${TempDir}/review" OpenSSLResults="$(grep '^aes-256-cbc' "${OpenSSLLog}")" [ "X${OpenSSLResults}" != "X" ] && echo -e "${OpenSSLResults}" | sed -e 's/^/ * `/' -e 's/$/`/' >>"${TempDir}/review" fi fi # PCIe and storage devices, important stuff like downgraded PCIe link width/speed, # SMART errors and suspectible values, counterfeit SD card and speed negotiation # issues, (almost) worn out SSDs, unhealthy drive temps (most SSDs start to throttle # above a certain thermal treshold), negotiated USB and SATA speeds relating to e.g. # underpowering (https://github.com/raspberrypi/linux/issues/4130#issuecomment-787826273) # and so on... PCIeStatus="$(CheckPCIe | sed 's/Vendor\ Device\ 0001/Raspberry Pi Ltd. RP1/')" StorageStatus="$(CheckStorage update-smart-drivedb | sed 's/000Mbps/Gbps/')" if [ "X${PCIeStatus}" != "X" ] && [ "X${StorageStatus}" != "X" ]; then echo -e "\n### PCIe and storage devices:\n\n${PCIeStatus}\n${StorageStatus}" >>"${TempDir}/review" elif [ "X${StorageStatus}" = "X" ]; then echo -e "\n### PCIe devices:\n\n${PCIeStatus}" >>"${TempDir}/review" elif [ "X${PCIeStatus}" = "X" ]; then echo -e "\n### Storage devices:\n\n${StorageStatus}" >>"${TempDir}/review" fi # In preparation of before/after diff remove sane drive temperatures to keep only "unhealthy drive temp" StorageStatus="$(echo "${StorageStatus}" | awk -F", drive temp: " '{print $1"X"}' | sed -e 's/X$//' -e '/^$/d' | grep -v smartctl)" # check whether NTFS filesystems are attached (do not need to be mounted yet) [ -z "${LSBLK}" ] && LSBLK="$(LC_ALL="C" lsblk -l -o SIZE,NAME,FSTYPE,LABEL,MOUNTPOINT 2>&1)" NTFSdevices="$(awk -F" " '/ ntfs / {print $2}' <<< "${LSBLK}" | tr '\n' ',' | sed 's/,$//')" if [ "X${NTFSdevices}" != "X" ]; then echo -e "\n### Challenging filesystems:\n\nThe following partitions are NTFS: ${NTFSdevices} -> https://tinyurl.com/mv7wvzct" >>"${TempDir}/review" fi # check swap devices/files/partitions SwapDevices="$(ListSwapDevices)" if [ "X${SwapDevices}" != "X" ]; then echo -e "\n### Swap configuration:\n\n${SwapDevices}" >>"${TempDir}/review" fi # software versions echo -e "\n### Software versions:\n" >>"${TempDir}/review" case "${OperatingSystem}" in Armbian*|"Orange Pi"*) # Armbian went full megalomania in the meantime. They try to trick their users # into thinking to run an 'Armbian 23.02.0-trunk.0072 Jammy' while in reality it's # 'Ubuntu 22.04 Jammy' debootstraped with a certain version of Armbian's build # scripts. The Xunlong copycats do something similarly stupid and report for # example 'Orange Pi 1.0.6 Jammy' as distro. Let's fix this nonsense. RealDistro="$(awk -F":\t" '/^Description/ {print $2}' <"${ResultLog}")" OSCodename="$(awk -F":\t" '/^Codename/ {print $2}' <"${ResultLog}")" grep -q "${OSCodename}" <<<"${RealDistro}" && echo " * ${RealDistro} ${ARCH}" >>"${TempDir}/review" \ || echo " * ${RealDistro} (${OSCodename}) ${ARCH}" >>"${TempDir}/review" ;; *) echo " * ${OperatingSystem}" >>"${TempDir}/review" ;; esac BuildInfo="$(grep "^Build system: " "${ResultLog}" | sed 's/Build system: / * Build scripts: /')" [ -z "${BuildInfo}" ] || echo "${BuildInfo}" >>"${TempDir}/review" grep -i "^ Compiler:" "${ResultLog}" | sed -e 's/^/ */' >>"${TempDir}/review" openssl version | awk -F" " '{print " * "$1" "$2", built on "$3" "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13" "$14" "$15}' >>"${TempDir}/review" [ -z "${ThreadXVersion}" ] || echo -e " * ThreadX: $(tail -n1 <<<"${ThreadXVersion}" | cut -d' ' -f2) / $(head -n1 <<<"${ThreadXVersion}")" >>"${TempDir}/review" # Kernel relevant settings / versions echo -e "\n### Kernel info:\n" >>"${TempDir}/review" [ -r /proc/cmdline ] && echo -e " * \`/proc/cmdline: $(>"${TempDir}/review" grep "^Vulnerability" <<<"${LSCPU}" | grep -v 'Not affected' | sed -e 's/^/ * /' >>"${TempDir}/review" CONFIGHZ="$(awk -F" " '/CONFIG_HZ=/ {print $1}' <"${ResultLog}")" [ -z "${CONFIGHZ}" ] && echo " * Kernel ${KernelVersion}" >>"${TempDir}/review" || echo " * Kernel ${KernelVersion} / ${CONFIGHZ}" >>"${TempDir}/review" [ -z "${KernelInfo}" ] || echo -e "\n${KernelInfo}" >>"${TempDir}/review" # if in NetIO mode then measure idle consumption walking through available governors and PCIe ASPM if [ "X${OutputCurrents[*]}" != "X" ]; then CountOfGovernors=$(wc -l <<<"${GovernorState}") [ "X${RunBenchmarks}" = "XTRUE" ] || echo -e "\x08\x08 Done." echo -e "Now measuring idle consumption ($(( 3 + ${CountOfGovernors} * 3 )) more minutes)...\c" NetioConsumptionFile="${TempDir}/netio.current" echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${OutputCurrent[$(( ${NetioSocket} - 1 ))]}" ) * 10 )) >"${NetioConsumptionFile}" export NetioConsumptionFile HandleGovernors powersave >/dev/null 2>&1 HandlePolicies powersave >/dev/null 2>&1 /bin/bash "${PathToMe}" -N ${NetioDevice} ${NetioSocket} ${NetioConsumptionFile} "1.8" "55" >/dev/null 2>&1 & NetioMonitoringPID=$! sleep 180 read IdleConsumption <${NetioConsumptionFile} echo -e "\n### Idle consumption (measured with Netio ${NetioModel}, FW v${Firmware}):\n" >>"${TempDir}/review" echo -e " * everything set to powersave: ${IdleConsumption} mW" >>"${TempDir}/review" if [ ${CountOfGovernors} -gt 1 ]; then find /sys -name "*governor" 2>/dev/null | grep -E -v '/sys/module|cpuidle|watchdog' | sort -n | while read ; do if [ -w "${REPLY}" ]; then echo performance >"${REPLY}" sleep 180 read IdleConsumption <${NetioConsumptionFile} echo -e " * ${REPLY%/*} set to performance: ${IdleConsumption} mW" >>"${TempDir}/review" else echo -e " * Not able to set ${REPLY%/*} to performance" >>"${TempDir}/review" fi done fi if [ -w /sys/module/pcie_aspm/parameters/policy ] && [ -d /sys/bus/pci_express ]; then echo performance >/sys/module/pcie_aspm/parameters/policy sleep 180 read IdleConsumption <${NetioConsumptionFile} echo -e " * /sys/module/pcie_aspm/parameters/policy set to performance: ${IdleConsumption} mW" >>"${TempDir}/review" fi kill ${NetioMonitoringPID} 2>/dev/null echo -e "\x08\x08 Done." fi # add review info to full info echo -e "\n##########################################################################\n" >>"${ResultLog}" sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${TempDir}/review" >>"${ResultLog}" [ "X${RunBenchmarks}" = "XTRUE" ] && UploadResults 2>/dev/null || UploadResults >/dev/null 2>&1 case "${UploadURL}" in http*) echo -e "\n\n\n\n# ${DeviceName:-$HostName}\n\nTested with sbc-bench v${Version} on $(date -R). Full info: [${UploadURL}](${UploadURL})" | sed 's/http:/https:/' tail -n +4 "${TempDir}/review" ;; *) echo -e "\n\n\n" cat "${TempDir}/review" ;; esac if [ "X${NOTUNING}" != "Xyes" ] && [ "X${RunBenchmarks}" = "XTRUE" ]; then # throttling check and routine waiting for the board to cool down since otherwise the # next monitoring step will report throttling even if none happens from now on. [ "X${ThrottlingWarning}" != "X" ] && snore 5 # temporary switch to performance when in Netio mode since otherwise the prior idle # measurements will result in the following check never finishing [ "X${OutputCurrents[*]}" != "X" ] && HandleGovernors performance >/dev/null 2>&1 if [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq ]; then CpuFreqToQuery=cpuinfo_cur_freq elif [ -r /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ]; then CpuFreqToQuery=scaling_cur_freq fi if [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]; then cpuinfo_max_freq=$(cat /sys/devices/system/cpu/cpu?/cpufreq/cpuinfo_max_freq) cpuinfo_cur_freq=$(cat /sys/devices/system/cpu/cpu?/cpufreq/${CpuFreqToQuery}) if [ "X${cpuinfo_max_freq}" != "X${cpuinfo_cur_freq}" ]; then echo -e "\nWaiting for the device to cool down \c" while [ "X${cpuinfo_max_freq}" != "X${cpuinfo_cur_freq}" ]; do TempNow=$(ReadSoCTemp) echo -e "\x08\x08\x08\x08\x08\x08\x08. ${TempNow}°C\c" snore 2 cpuinfo_max_freq=$(cat /sys/devices/system/cpu/cpu?/cpufreq/cpuinfo_max_freq) cpuinfo_cur_freq=$(cat /sys/devices/system/cpu/cpu?/cpufreq/${CpuFreqToQuery}) done # wait 20 more seconds for temperatures to stabilize for i in {1..10} ; do TempNow=$(ReadSoCTemp) echo -e "\x08\x08\x08\x08\x08\x08\x08. ${TempNow}°C\c" snore 2 done echo "" fi fi [ "X${OutputCurrents[*]}" != "X" ] && HandlePolicies performance >/dev/null 2>&1 fi # drop caches echo 3 >/proc/sys/vm/drop_caches # device now ready for benchmarking cat <<- EOF All known settings adjusted for performance. Device now ready for benchmarking. Once finished stop with [ctrl]-[c] to get info about throttling, frequency cap and too high background activity all potentially invalidating benchmark scores. All changes with storage and PCIe devices as well as suspicious dmesg contents will be reported too. EOF # Now switch to monitoring mode, report consumption if Netio powermeter is available if [ "X${OutputCurrents[*]}" != "X" ]; then # We are in Netio monitoring mode as such provide consumption info as well /bin/bash "${PathToMe}" -N ${NetioDevice} ${NetioSocket} ${NetioConsumptionFile} "4.8" "30" >/dev/null 2>&1 & NetioMonitoringPID=$! fi echo "sbc-bench review mode started" >/dev/kmsg trap 'FinalReporting ; exit 0' 0 1 2 3 15 unset ThrottlingWarning ThrottlingCheck SwapWarning rm "${TempDir}"/*time_in_state* "${TempDir}/throttling_info.txt" 2>/dev/null SwapBefore="$(awk -F" " '{print $4}' "${MonitorLog}" & MonitoringPID=$! echo "" snore 1 tail -f "${MonitorLog}" } # ProvideReviewInfo FinalReporting() { trap 'rm -rf "${TempDir}" ; exit 0' 0 echo -e "\n\nCleaning up...\c" kill ${NetioMonitoringPID} ${MonitoringPID} 2>/dev/null SwapNow="$(awk -F" " '{print $4}' "${TempDir}/dmesg" # check PCIe and storage devices again to spot disconnects or link degradation and # stuff like this. Strip off sane drive temps so we only compare unhealthy ones PCIeStatusNow="$(CheckPCIe)" StorageStatusNow="$(CheckStorage | sed 's/000Mbps/Gbps/' | awk -F", drive temp: " '{print $1"X"}' | sed -e 's/X$//' -e '/^$/d' | grep -v smartctl)" if [ "X${RunBenchmarks}" = "XTRUE" ]; then ClockspeedsNow="$(cat "${TempDir}/cpufreq" | sed -e 's/^/ /')" if [ -z ${InitialTemp} ]; then # no thermal readouts possible echo -e "\x08\x08 Done.\n\nClockspeeds now:\n\n${ClockspeedsNow}\n" else echo -e "\x08\x08 Done.\n\nClockspeeds now at $(ReadSoCTemp)°C:\n\n${ClockspeedsNow}\n" fi fi # check for throttling ThrottlingCheck="$(CheckForThrottling | sed -e 's/ Check the log for details.//' -e '/^[[:space:]]*$/d')" if [ -f "${TempDir}/throttling_info.txt" ]; then ThrottlingWarning="${LRED}${BOLD} (throttled)${NC}" ThrottlingDetails=$(<"${TempDir}/throttling_info.txt") fi # Print warnings if count or details of attached PCIe or storage devices have changed. # Possible reasons: cable/connector problems, overheating, other transmission errors and so on... StorageDiff="$(diff <(echo "${StorageStatus}") <(echo "${StorageStatusNow}") )" PCIeDiff="$(diff <(echo "${PCIeStatus}") <(echo "${PCIeStatusNow}") )" if [ "X${StorageDiff}" != "X" ] && [ "X${PCIeDiff}" != "X" ]; then echo -e "\n${LRED}${BOLD}ATTENTION:${NC} status of PCIe and storage devices has changed:${NC}\n" echo -e "${PCIeDiff}\n${StorageDiff}\n" elif [ "X${StorageDiff}" != "X" ]; then echo -e "\n${LRED}${BOLD}ATTENTION:${NC} status of storage devices has changed:${NC}\n" echo -e "${StorageDiff}\n" elif [ "X${PCIeDiff}" != "X" ]; then echo -e "\n${LRED}${BOLD}ATTENTION:${NC} status of PCIe devices has changed:${NC}\n" echo -e "${PCIeDiff}\n" fi # print warnings if kernel ring buffer contains messages since start of monitoring: if [ ${DMESGLines:-0} -gt 20 ]; then echo -e "${LRED}${BOLD}ATTENTION:${NC} ${LRED}lots of noise in kernel ring buffer since start of monitoring:${NC}\n" echo -e "${DMESGSinceStart}\n" elif [ ${DMESGLines:-0} -gt 1 ]; then echo -e "${LRED}${BOLD}ATTENTION:${NC} some noise in kernel ring buffer since start of monitoring:\n" echo -e "${DMESGSinceStart}\n" fi # output execution validation, fake ClusterConfig to get definitive throttling warning ClusterConfig=(0) IsValid="$(ValidateResults review | sed -e 's/^/ * /')" [ "X${IsValid}" != "X" ] && echo -e "${BOLD}Results validation:${NC}\n\n${IsValid}\n" echo -e "${ThrottlingDetails}" CheckAgeOfScript # revert at least cpufreq and PCIe ASPM settings BasicSetup ${OriginalCPUFreqGovernor} >/dev/null 2>&1 [ -w /sys/module/pcie_aspm/parameters/policy ] && echo "${OriginalASPMSetting}" >/sys/module/pcie_aspm/parameters/policy >/dev/null 2>&1 } # FinalReporting CheckKernelVersion() { # checks device's kernel version and compares with https://endoflife.date/linux # Major challenge: identify those smelly vendor BSP kernels that show a version # number similar to an official LTS kernel but are in reality forward ported since # ages and can't be trusted AT ALL. # check whether endoflife data has been downloaded, if not return [ -r "${TempDir}/linuxkernel.md" ] || return # check whether kernel version contains "-rc" and if true simply return to not # bother devs developing/testing release candidate kernels case $1 in *-rc*) return ;; esac # check kernel version number string for distro kernel schemes and return if true # Debian/Ubuntu kernel versions look like 6.2.0-32-generic, 5.10.0-23-amd64 or # 5.15.0-1035-raspi for example grep -v -E 'amlogic|librem5|rockchip' <<<"$1" | grep -q -E '[3-9]\.[0-9]{1,3}\.[0-9]{1,2}-[0-9]{1,4}-raspi|[3-9]\.[0-9]{1,3}\.[0-9]{1,2}-[0-9]{1,3}-[a-z]{1,3}' && return # RPi Bookworm images implement a new naming scheme not directly exposing real kernel # version, e.g. an RPi 4 runs 6.1.0-rpi4-rpi-v8, RPi 5 runs 6.1.0-rpi4-rpi-2712 or # 6.1.0-rpi6-rpi-v8 grep -q -E 'rpi.-rpi' <<<"$1" && return # Fedora/Rockylinux kernels also follow an own release scheme and not mainline's grep -q -E '\.fc3|\.fc4|\.el9' <<<"$1" && return # skip this whole check on x86 and in aarch64 VMs where usually distro kernels are # used that follow an own release schedule case "${CPUArchitecture}" in x86_64) return ;; aarch64) case ${VirtWhat} in kvm|oracle|parallels|vmware|wsl|xen|qemu) return ;; esac ;; esac # Avoid annoying users of latest bleeding edge kernels by ignoring anything with # higher version number than 1st entry on endoflife.date LastReleaseCycle="$(grep -A1 "^releases:$" "${TempDir}/linuxkernel.md" | awk -F'"' '/releaseCycle:/ {print $2}')" CheckRelease=$(echo -e "${LastReleaseCycle}\n${ShortKernelVersion}" | sort -n | head -n1) [ "${CheckRelease}" = "${LastReleaseCycle}" ] && return KernelVersionDigitsOnly=$(cut -f1 -d- <<<"$1") # parse LTS kernel info, in Nov 2023 this looks like this for example with 5.10: # releaseCycle: "5.10" # lts: true # releaseDate: 2020-12-13 # eol: 2026-12-31 # Projected EOL from https://www.kernel.org/category/releases.html # latest: "5.10.201" # latestReleaseDate: 2023-11-20 KernelStatus="$(grep -A5 "releaseCycle: \"${ShortKernelVersion}\"" "${TempDir}/linuxkernel.md" | tail -n5)" LatestKernelVersion="$(awk -F'"' '/latest:/ {print $2}' <<<"${KernelStatus}")" LatestKernelDate="$(awk -F": " '/latestReleaseDate:/ {print $2}' <<<"${KernelStatus}")" IsLTS="$(awk -F": " '/lts:/ {print $2}' <<<"${KernelStatus}")" [ "X${IsLTS}" = "Xtrue" ] && KernelSuffix=" LTS" EOLDate="$(awk -F": " '/eol:/ {print $2}' <<<"${KernelStatus}" | awk -F" # " '{print $1}')" Today="$(date "+%Y-%m-%d")" # check EOL date vs. today CheckEOL=$(echo -e "${Today}\n${EOLDate}" | sort -n | head -n1) if [ -z "${KernelStatus}" ]; then # some old kernel version neither being an LTS kernel nor any actively developed variant echo -e "${LRED}${BOLD}Kernel version ${KernelVersionDigitsOnly} is not covered by any active release cycle any more.${NC}\n" echo -e "${LRED}${BOLD}See https://endoflife.date/linux for details. It is highly likely countless${NC}" echo -e "${LRED}${BOLD}exploitable vulnerabilities do exist for this kernel as well as tons of bugs.${NC}" elif [ "X${KernelVersionDigitsOnly}" != "X${LatestKernelVersion}" ]; then # kernel version at least matches a supported kernel but is not most recent one BSPDisclaimer="\n${BOLD}But this version string doesn't matter since this is not an official${KernelSuffix} Linux${NC}\n${BOLD}from kernel.org.${NC} \c" UsedKernelRevision=$(cut -f3 -d. <<<"${KernelVersionDigitsOnly}" | tr -c -d '[:digit:]') LatestKernelRevision=$(cut -f3 -d. <<<"${LatestKernelVersion}" | tr -c -d '[:digit:]') RevisionDifference=$(( ${LatestKernelRevision:-0} - ${UsedKernelRevision:-0} )) if [ "X${CheckEOL}" = "X${EOLDate}" ] && [ "X${EOLDate}" != "Xfalse" ]; then # EOL date is in the past echo -e "${LRED}${BOLD}${ShortKernelVersion}${KernelSuffix} has reached end-of-life on ${EOLDate} with version ${LatestKernelVersion}.${NC}" echo -e "${LRED}${BOLD}This ${KernelVersionDigitsOnly} and all other ${ShortKernelVersion}${KernelSuffix} revisions are unsupported since then.${NC}" elif [ ${RevisionDifference} -gt 5 ]; then # report version mismatch only if kernel revision difference is greater than 5 echo -e "${BOLD}Kernel ${KernelVersionDigitsOnly} is not latest ${LatestKernelVersion}${KernelSuffix} that was released on ${LatestKernelDate}.${NC}\n" else BSPDisclaimer="\n${BOLD}The ${KernelVersionDigitsOnly} kernel version string doesn't matter since this is not an official${NC}\n${BOLD}${KernelSuffix} Linux from kernel.org.${NC} \c" fi if [ "X${IsLTS}" = "Xtrue" ]; then # warn about vulnerabilities only on LTS kernels since users of actively # developed kernel branches should know what they're doing. if [ ${RevisionDifference} -ge 50 ]; then echo -e "${LRED}${BOLD}See https://endoflife.date/linux for details. It is somewhat likely that${NC}" echo -e "${LRED}${BOLD}a lot of exploitable vulnerabilities exist for this kernel as well as many${NC}" echo -e "${LRED}${BOLD}unfixed bugs.${NC}" elif [ ${RevisionDifference} -ge 20 ]; then echo -e "${LRED}${BOLD}See https://endoflife.date/linux for details. It is somewhat likely that some${NC}" echo -e "${LRED}${BOLD}exploitable vulnerabilities exist for this kernel as well as many unfixed bugs.${NC}" elif [ ${RevisionDifference} -gt 5 ]; then echo -e "${LRED}${BOLD}See https://endoflife.date/linux for details. Perhaps some kernel bugs have${NC}" echo -e "${LRED}${BOLD}been fixed in the meantime and maybe vulnerabilities as well.${NC}" else # Avoid annoying warnings if kernel revision difference is lower than 5. # Happened eg. with 6.1.9 shortly after 6.1 became most recent LTS kernel as # 'Kernel 6.1.9 is not latest 6.1.10 LTS that was released on 2023-02-06' : fi fi else # kernel version seems to match most recent upstream kernel. BSPDisclaimer="\n${BOLD}But this version string doesn't matter since this is not an official${KernelSuffix} Linux${NC}\n${BOLD}from kernel.org.${NC} \c" if [ "X${CheckEOL}" = "X${EOLDate}" ] && [ "X${EOLDate}" != "Xfalse" ]; then # EOL date is in the past echo -e "${LRED}${BOLD}${ShortKernelVersion}${KernelSuffix} has reached end-of-life on ${EOLDate}. ${KernelVersionDigitsOnly} is unsupported since then.${NC}" else echo -e "${LGREEN}According to https://endoflife.date/linux this kernel version is up to date.${NC}" if [ "X${IsLTS}" = "Xtrue" ]; then echo "The predicted end-of-life date for the ${ShortKernelVersion} LTS kernel is ${EOLDate}" fi fi fi case ${KernelVersionDigitsOnly} in # EOL notifications and BSP kernel warnings "3.10.33"|"4.16.1"|"4.18.7"|"5.0.2"|"5.1.0"|"5.3.0"|"5.3.11"|"5.5.0"|"5.6.0"|"5.7."*|"5.8."*|"5.9."*|"5.10.0"|"5.14.0") # Popular kernels for all sorts of Amlogic SoCs from https://github.com/150balbes # Unfortunately lots of devices still run with these ancient kernels lacking any fixes : ;; 3.4.*) # some SDKs/BSPs based on this version: Allwinner A10, A20, A83T, H2+/H3 case ${GuessedSoC} in *Allwinner*) PrintBSPWarning Allwinner ;; esac echo -e "\n${LRED}${BOLD}The 3.4 series has reached end-of-life on 2016-10-26 with version 3.4.113.${NC}" ;; 3.10.*) # some SDKs/BSPs based on this version: Allwinner A64, H5, R40/V40, Amlogic S805 (Meson8b), Exynos 5422 case ${GuessedSoC} in *Allwinner*) PrintBSPWarning Allwinner ;; *Amlogic*) PrintBSPWarning Amlogic ;; *5422*) PrintBSPWarning Samsung ;; esac echo -e "\n${LRED}${BOLD}The 3.10 series has reached end-of-life on 2017-11-05 with version 3.10.108.${NC}" ;; 3.14.*) case ${GuessedSoC} in *Amlogic*) PrintBSPWarning Amlogic ;; esac echo -e "\n${LRED}${BOLD}The 3.14 series has reached end-of-life on 2016-09-11 with version 3.14.79.${NC}" ;; 3.16.*) # some SDKs/BSPs based on this version: Amlogic S905 (ODROID C2) case ${GuessedSoC} in *Amlogic*) PrintBSPWarning Amlogic ;; esac echo -e "\n${LRED}${BOLD}The 3.16 series has reached end-of-life on 2020-06-11 with version 3.16.85.${NC}" ;; 3.18.*) # some SDKs/BSPs based on this version: Ingenic JZ4780, Qualcomm APQ8053 case ${GuessedSoC} in *Ingenic*) PrintBSPWarning Ingenic ;; *Qualcomm*) PrintBSPWarning Qualcomm ;; esac echo -e "\n${LRED}${BOLD}The 3.18 series has reached end-of-life on 2017-02-08 with version 3.18.48.${NC}" ;; 4.4.*) # some SDKs/BSPs based on this version: Rockchip RK32xx/RK33xx, Nexell S5P4418/S5P6818 case ${GuessedSoC} in *RK3*|*Rockchip*) PrintBSPWarning Rockchip ;; *Nexell*) PrintBSPWarning Nexell ;; esac echo -e "\n${LRED}${BOLD}The 4.4 series has reached end-of-life on 2022-02-03 with version 4.4.302.${NC}" ;; 4.9.*) # some SDKs/BSPs based on this version: Allwinner A50/MR133/R311/H6/H616/H313/H618, Amlogic S905X3 (SM1) / S922X/A311D (G12B), Exynos 5422, Nvidia AGX Xavier / Nvidia Jetson Nano / Nvidia Tegra X1 / Nvidia Tegra Xavier, RealTek RTD129x/RTD139x case ${GuessedSoC} in *5422*) PrintBSPWarning Samsung ;; *Allwinner*) PrintBSPWarning Allwinner ;; *Amlogic*) PrintBSPWarning Amlogic ;; "RealTek RTD"*) PrintBSPWarning RealTek ;; Nvidia*) PrintBSPWarning Nvidia ;; esac # prepare 4.9 not being listed any more on https://endoflife.date/linux grep -q "releaseCycle: \"4.9\"" "${TempDir}/linuxkernel.md" || \ echo -e "\n${LRED}${BOLD}The 4.9 series has reached end-of-life on 2023-01-07 with version 4.9.337.${NC}" ;; 4.14.*) # some SDKs/BSPs based on this version: Exynos 5422/9820, NXP i.MX8x, Nexell S5P4418/S5P6818 case ${GuessedSoC} in *Nexell*) PrintBSPWarning Nexell ;; *5422*|"Samsung Exynos 9820") PrintBSPWarning Samsung ;; "NXP i.MX8"*) PrintBSPWarning NXP ;; esac ;; 4.19.*) # some SDKs/BSPs based on this version: Rockchip RK356x/RK3399 case ${GuessedSoC} in *RK3566*|*RK3568*) PrintBSPWarning Rockchip ;; Phytium*) PrintBSPWarning Phytium ;; esac ;; "5.4.125"|"5.4.180") # New Amlogic SDK initially released with 5.4.125 and after some version string cosmetics # stuck at 5.4.180 case ${GuessedSoC} in Allwinner*) # 5.4.125 is also used in Allwinner Android builds PrintBSPWarning Allwinner ;; *Amlogic*) PrintBSPWarning Amlogic ;; esac ;; 5.4.*) # some SDKs/BSPs based on this version: Allwinner A133/A64/D1/R528/T113/H5/H6/H616/H618, Amlogic A311D2 (T7), S805X2/S905Y4/S905W2 (S4), S928X (S5), Exynos 5422 case ${GuessedSoC} in Allwinner*) # highly unlikely that there's any Allwinner A64/H5/H6 out in the wild still running a _mainline_ 5.4 version PrintBSPWarning Allwinner ;; *A311D2*|*S805X2*|*S905Y4*|*S905W2*|*S928X*) PrintBSPWarning Amlogic ;; *5422*) PrintBSPWarning Samsung ;; Phytium*) PrintBSPWarning Phytium ;; *Unisoc*) PrintBSPWarning Unisoc ;; *Synaptics*) PrintBSPWarning Synaptics ;; esac ;; 5.10.4) case ${GuessedSoC} in *K230*) # Kendryte K230 PrintBSPWarning Kendryte ;; esac ;; 5.10.66|5.10.72|5.10.110) case ${GuessedSoC} in *Rockchip*) # RK 5.10 BSP in different flavours PrintBSPWarning RockchipGKI ;; esac ;; 5.10.*) # some SDKs/BSPs based on this version: Nvidia Jetson AGX Orin, Rockchip RK3399/RK3528/RK356x/RK3588/ case ${GuessedSoC} in "Nvidia Jetson AGX Orin") PrintBSPWarning Nvidia ;; Phytium*) PrintBSPWarning Phytium ;; *RK3528*|*RK3588*) PrintBSPWarning RockchipGKI ;; *RK3399*) # With RK3399 we need to differentiate between mainline and BSP kernel, for # example CONFIG_HZ (not reliable once someone gets the idea to switch BSP # settings from 300 to 250) or microvolts entries below /sys/devices/platform/ dmesg | grep -q pvtm-volt-sel && PrintBSPWarning RockchipGKI ;; *RK3566*|*RK3568*) # With RK3566/RK3568 same problem: how to differentiate between latest # RK BSP based on 5.10.66/5.10.110 and former mainlining efforts? Check # dmesg output for PVTM for example dmesg | grep -q 'cpu cpu0: pvtm' && PrintBSPWarning RockchipGKI ;; "Qualcomm Snapdragon 8 Gen1"|"Qualcomm Snapdragon 8+ Gen1") PrintBSPWarning Qualcomm ;; *S928X*) PrintBSPWarning Amlogic ;; *T-Head*) PrintBSPWarning T-Head ;; esac ;; "5.15.78"|"5.15.119") # New Amlogic SDK released with 5.15.78, supports at least T7, S4, SM1 and G12B. # Kernel version 5.15.119 some images were using is the result of version string # cosmetics over at ophub/flippy/unifreq # Allwinner's Android 13 BSP also uses both 5.15.78 and 5.15.119 case ${GuessedSoC} in Allwinner*) # though there's a small chance users of flippy/unifreq kernels on older Allwinner SoCs are affected print BSP warning PrintBSPWarning Allwinner ;; *Amlogic*) PrintBSPWarning Amlogic ;; esac ;; "5.15.41"|"5.15.94"|"5.15.123"|"5.15.151") # At least used with Android 13 and OpenWRT builds for A133/T527: https://browser.geekbench.com/v5/cpu/21947495.gb5 / https://browser.geekbench.com/v6/cpu/2610471.gb6 / # https://bbs.aw-ol.com/topic/5060/syterkit-启动-t527-失败 / https://github.com/chainsx/linux-t527/blob/main/Makefile case ${GuessedSoC} in Allwinner*) # though there's a small chance users of flippy/unifreq kernels on older Allwinner SoCs are affected PrintBSPWarning Allwinner ;; esac ;; 5.15.*) case ${GuessedSoC} in StarFive*) PrintBSPWarning StarFive ;; *MT7986A*|*"Genio 1200"*) PrintBSPWarning MediaTek ;; *S928X*) # Android 14 BSP relies on 5.15 PrintBSPWarning Amlogic ;; esac ;; *) case ${GuessedSoC} in *S928X*) PrintBSPWarning Amlogic ;; esac ;; esac } # CheckKernelVersion PrintBSPWarning() { echo -e "${BSPDisclaimer}" case $1 in Allwinner) echo -e "${BOLD}This device runs an $1 vendor/BSP kernel.${NC}\n" echo -e "${BOLD}This kernel has been forward ported since ages based on unknown sources. While${NC}" echo -e "${BOLD}the version string suggests being a ${ShortKernelVersion} LTS release the code base differs way${NC}" echo -e "${BOLD}too much. Better expect tons of unfixed bugs and vulnerabilities hiding in this${NC}" echo -e "${BOLD}vendor kernel.${NC}" ;; Amlogic) echo -e "${BOLD}This device runs an $1 vendor/BSP kernel.${NC}\n" echo -e "${BOLD}This kernel has been forward ported since ages based on unknown sources. While${NC}" echo -e "${BOLD}the version string suggests being a ${ShortKernelVersion} LTS release the code base differs way${NC}" echo -e "${BOLD}too much. Better expect tons of unfixed bugs and vulnerabilities hiding in this${NC}" echo -e "${BOLD}vendor kernel. See https://tinyurl.com/y8k3af73 and https://tinyurl.com/ywtfec7n${NC}" echo -e "${BOLD}for details.${NC}" ;; Ingenic|Kendryte|MediaTek|Nexell|Nvidia|NXP|Phytium|Qualcomm|RealTek|Samsung|StarFive|Synaptics|T-Head|Unisoc) echo -e "${BOLD}This device runs a $1 vendor/BSP kernel.${NC}" ;; Rockchip) echo -e "${BOLD}This device runs a forward ported $1 vendor/BSP kernel.${NC}" ;; RockchipGKI) echo -e "${BOLD}This device runs a Rockchip vendor/BSP kernel.${NC}\n" echo -e "${BOLD}This kernel is based on a mixture of Android GKI and other sources. Also some${NC}" echo -e "${BOLD}community attempts to do version string cosmetics might have happened, see${NC}" echo -e "${BOLD}https://tinyurl.com/2p8fuubd for example. To examine how far away this ${KernelVersionDigitsOnly}${NC}" echo -e "${BOLD}is from an official LTS of same version someone would have to reapply Rockchip's${NC}" echo -e "${BOLD}thousands of patches to a clean ${KernelVersionDigitsOnly} LTS.${NC}" ;; *) echo -e "${BOLD}This device runs an unknown vendor/BSP/Android kernel.${NC}" ;; esac } # PrintBSPWarning PrintKernelInfo() { # Kernel info: version number and vendor/BSP check CreateTempDir trap 'rm -rf "${TempDir}" ; exit 0' 0 curl -s -q --connect-timeout 10 https://raw.githubusercontent.com/endoflife-date/endoflife.date/master/products/linuxkernel.md \ >"${TempDir}/linuxkernel.md" BasicSetup nochange >/dev/null 2>&1 KernelVersion="$(uname -r)" ShortKernelVersion="$(awk -F"." '{print $1"."$2}' <<<"${KernelVersion}")" [ -z "${DTCompatible}" ] && [ -r /proc/device-tree/compatible ] && DTCompatible="$(tr "\000" "\n" /dev/null)" [ -z "${LSCPU}" ] && LSCPU="$(lscpu)" [ -z "${CPUArchitecture}" ] && CPUArchitecture="$(awk -F" " '/^Architecture/ {print $2}' <<<"${LSCPU}")" [ -z "${VoltageSensor}" ] && VoltageSensor="$(GetVoltageSensor)" [ -z "${CPUTopology}" ] && CPUTopology="$(PrintCPUTopology)" [ -z "${CPUSignature}" ] && CPUSignature="$(GetCPUSignature)" [ -z "${X86CPUName}" ] && X86CPUName="$(sed 's/ \{1,\}/ /g' <<<"${LSCPU}" | awk -F": " '/^Model name/ {print $2}' | sed -e 's/1.th Gen //' -e 's/.th Gen //' -e 's/Core(TM) //' -e 's/ Processor//' -e 's/Intel(R) Xeon(R) CPU //' -e 's/Intel(R) //' -e 's/(R)//' -e 's/CPU //' -e 's/ 0 @/ @/' -e 's/AMD //' -e 's/Authentic //' -e 's/ with .*//')" [ "${CPUArchitecture}" = "x86_64" ] || GuessedSoC="$(GuessARMSoC)" KernelInfo="$(CheckKernelVersion "${KernelVersion}")" if [ -z "${KernelInfo}" ]; then if [ "${CPUArchitecture}" = "x86_64" ]; then echo -e "This tool does not yet implement distro kernel checks" else uname -a fi else echo -e "$(uname -a)\n\n${KernelInfo}" fi echo "" } # PrintKernelInfo GetASPMSettings() { case "${1}" in *"ASPM L1 Enabled"*) ASPMSettings="$(awk -F':\t' '/LnkCtl:/ {print $2}' <<<"${PCIeDetails}")" case "${1}" in *L1SubCap*) ASPMSettings="${ASPMSettings}$(grep -A1 L1SubCap <<<"${PCIeDetails}" | grep -v "L1SubCtl1" | sed -e 's/L1SubCap://' -e 's/\t//g' | tr '\n' ' ' | sed 's/ \{1,\}/ /g')" ASPMSettings="${ASPMSettings}$(grep -A1 L1SubCtl1 <<<"${PCIeDetails}" | grep -v "L1SubCtl2" | sed -e 's/L1SubCtl1://' -e 's/\t//g' | tr '\n' ' ' | sed 's/ \{1,\}/ /g')" ASPMSettings="${ASPMSettings}$(grep L1SubCtl2 <<<"${PCIeDetails}" | sed -e 's/L1SubCtl2://' -e 's/\t//g' | tr '\n' ' ' | sed 's/ \{1,\}/ /g')" ;; esac ;; *) ASPMSettings="ASPM Disabled" ;; esac echo "${ASPMSettings}" } # GetASPMSettings CheckPCIe() { # Examine devices on the PCI buses. Report link width/speed and whether those are # downgraded or not. With NVMe devices try to query SMART data to report drive health, # with other PCIe devices report driver (for example to spot the 'famous' RealTek NIC # performance issues that go away once the appropriate driver is loaded) # # TODO: print RTL8111 revision based on lspci output since everything prior to G sucks: # http://tinyurl.com/3jcashvt # # rev 01 -> RTL8111C # rev 03 -> RTL8111D # rev 06 -> RTL8111E # rev 07 -> RTL8111F # rev 0a -> ? # rev 0c -> RTL8111G # rev 15 -> RTL8111H # # If $1 is "nvme" then try to update smartmontools device database if needed and report # also NVMe devices found on the PCIe buses # use colours and bold when outputting to a terminal [ -z "${BOLD}" ] && CheckTerminal [ -b /dev/nvme0 ] && [ "$1" = "nvme" ] && { update-smart-drivedb >/dev/null 2>&1 ; [ -z "${TempDir}" ] && CreateTempDir ; } # grab info about block devices [ -z "${LSBLK}" ] && LSBLK="$(LC_ALL="C" lsblk -l -o SIZE,NAME,FSTYPE,LABEL,MOUNTPOINT 2>&1)" lspci -Q -mm 2>/dev/null | grep controller | while read ; do unset DeviceWarning DevizeSize AdditionalInfo AdditionalSMARTInfo RawDiskTemp DriveTemp PercentageUsed BusAddress="$(awk -F" " '{print $1}' <<<"${REPLY}")" ControllerType="$(awk -F'"' '{print $2}' <<<"${REPLY}")" case "${ControllerType}" in "Encryption"*|"VGA compatible"*|"Serial bus"*|"Communication"*|"Signal processing"*|"Memory"*) # ignore since internal mainboard components : ;; *) # check device DeviceName="$(sed 's/Advanced Micro Devices,/AMD/' <<<"${REPLY}" | awk -F'"' '{print $4}' | cut -f1 -d' ') $(awk -F'"' '{print $6}' <<<"${REPLY}" | sed -e 's/ SSD//' -e 's/ Controller//')" PCIeDetails="$(lspci -vv -s "${BusAddress}" 2>/dev/null)" LnkSta="$(awk -F"\t" '/LnkSta:/ {print $4}' <<<"${PCIeDetails}" | awk -F", " '{print $1", "$2}' | head -n1)" if [ "X${LnkSta}" != "X" ]; then # only report devices for which a link state can be determined if [ "X${ControllerType}" = "XNon-Volatile memory controller" ]; then # only report NVMe devices when $1 is nvme ASPMSettings="$(GetASPMSettings "${PCIeDetails}")" if [ "$1" = "nvme" ]; then # omit driver information since it's nvme anyway unset AdditionalInfo # try to get nvme device node to query drive model by SMART PathGuess="$(ls /dev/disk/by-path/*${BusAddress}-nvme-1)" if [ -h "${PathGuess}" ]; then # try to query via SMART NVMeDevice="$(readlink "${PathGuess}")" CheckSMARTData "/dev/${NVMeDevice##*/}" nvme DevizeSize="$(GetDiskSize "/dev/${NVMeDevice##*/}" "${DeviceName}")" if [ "X${DeviceWarning}" = "XTRUE" ]; then echo -e "smartctl -x /dev/${NVMeDevice##*/} ; \c" >>"${TempDir}/check-smart" echo -e " * ${LRED}${DevizeSize}${DeviceName}: ${LnkSta}${AdditionalSMARTInfo}${AdditionalInfo}${DriveTemp}, ${ASPMSettings}${NC}" else echo -e " * ${DevizeSize}${DeviceName}: ${LnkSta}${AdditionalSMARTInfo}${AdditionalInfo}${DriveTemp}, ${ASPMSettings}" fi fi fi else AdditionalInfo=", driver in use: $(awk -F": " '/Kernel driver in use:/ {print $2}' <<<"${PCIeDetails}" | head -n1)" echo -e " * ${DeviceName}: ${LnkSta}${AdditionalInfo}, ${ASPMSettings}" fi fi ;; esac done } # CheckPCIe CheckStorage() { # examine storage devices of these patterns: /dev/sd?, /dev/mmcblk? and /dev/mtd? # With the more sophisticated ones (USB and SATA) try to report as much connection details # as possible (e.g. USB/SATA speed, whether speed downgrades have happened, usb-storage # vs. uas and so on) # # TODO: # * maybe overtake CheckSMARTModes code from armbianmonitor to query picky/old USB # bridges in all possible ways. # use colours and bold when outputting to a terminal [ -z "${BOLD}" ] && CheckTerminal [ -z "${TempDir}" ] && CreateTempDir # try to update SMART drive database if SMART capable devices exist and $1 is set to # update-smart-drivedb [ -b /dev/nvme0 ] || [ -b /dev/sda ] && [ "$1" = "update-smart-drivedb" ] && update-smart-drivedb >/dev/null 2>&1 # try to restrict SMART queries to 15 sec duration due to buggy USB-to-SATA bridges SmartCtl="$(command -v smartctl 2>/dev/null)" command -v timeout >/dev/null 2>&1 && SmartCtl="timeout 15 ${SmartCtl}" # grab info about block devices udevadm settle [ -z "${LSBLK}" ] && LSBLK="$(LC_ALL="C" lsblk -l -o SIZE,NAME,FSTYPE,LABEL,MOUNTPOINT 2>&1)" for StorageDevice in $(ls /dev/sd? /dev/nvme? /dev/vd? 2>/dev/null | sort) ; do unset DeviceName DeviceInfo DeviceWarning DevizeSize AdditionalInfo AdditionalSMARTInfo ProductName VendorName SpeedInfo SupportedSpeeds RawDiskTemp DriveTemp LnkSta PercentageUsed ASPMSettings UdevInfo="$(udevadm info -a -n ${StorageDevice} 2>/dev/null)" Driver="$(awk -F'"' '/DRIVERS==/ {print $2}' <<<"${UdevInfo}" | grep -E 'uas|usb-storage|ahci|nvme|virtio-')" case "${Driver}" in ahci|ahci-*|*-ahci|sata_*|sata-*|*-sata|*pata|pata*) # (S)ATA attached, we need to also take care about other driver names like # ahci-mvebu, ahci-sunxi, sata_promise, tegra-ahci, xgene-ahci and so on CheckSMARTData "${StorageDevice}" ahci ;; nvme) # NVMe, we need to determine bus address to check PCIe link width/speed BusAddress="$(awk -F'"' '/ATTR{address}/ {print $2}' <<<"${UdevInfo}" | head -n1)" PCIeDetails="$(lspci -vv -s "${BusAddress}" 2>/dev/null)" LnkSta="$(awk -F"\t" '/LnkSta:/ {print $4}' <<<"${PCIeDetails}" | awk -F", " '{print $1", "$2}' | head -n1)" ASPMSettings="$(GetASPMSettings "${PCIeDetails}")" [[ -n "${ASPMSettings}" ]] && ASPMSettings=", ${ASPMSettings}" CheckSMARTData "${StorageDevice}" "${Driver}" ;; usb-storage|uas) # USB attached CheckSMARTData "${StorageDevice}" "${Driver}" # try to lookup device ID in usbutils' database idProduct="$(awk -F'"' '/ATTRS{idProduct}/ {print $2}' <<<"${UdevInfo}" | head -n1)" idVendor="$(awk -F'"' '/ATTRS{idVendor}/ {print $2}' <<<"${UdevInfo}" | head -n1)" USBBus="$(awk -F'"' '/ATTRS{busnum}/ {print $2}' <<<"${UdevInfo}" | head -n1)" USBDevice="$(awk -F'"' '/ATTRS{devnum}/ {print $2}' <<<"${UdevInfo}" | head -n1)" LsusbInfo="$(lsusb -v -s ${USBBus}:${USBDevice} 2>/dev/null)" # Check device capabilities and speeds that could be negotiated to spot downgrading # With SuperSpeed it looks like this: # Device can operate at Full Speed (12Mbps) # Device can operate at High Speed (480Mbps) # Device can operate at SuperSpeed (5Gbps) # With SuperSpeed Plus like this: # Speed Attribute ID: 0 10Gb/s Symmetric RX SuperSpeedPlus # Speed Attribute ID: 0 10Gb/s Symmetric TX SuperSpeedPlus SupportedSpeeds="$(awk -F"[()]" '/Device can operate at/ {print $2}' <<<"${LsusbInfo}" | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//')" SupportedSuperSpeeds="$(awk -F": " '/Speed Attribute ID/ {print $2}' <<<"${LsusbInfo}" | cut -c3- | sort | uniq | grep -E "^10G|^20G|^40G" | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//')" if [ "X${SupportedSpeeds}" != "X" ] && [ "X${SupportedSuperSpeeds}" != "X" ]; then SpeedInfo=" (capable of ${SupportedSpeeds}, ${SupportedSuperSpeeds})" elif [ "X${SupportedSpeeds}" != "X" ]; then SpeedInfo=" (capable of ${SupportedSpeeds})" fi ProductName="$(awk -F"${idProduct} " "/0x${idProduct} / {print \$2}" <<<"${LsusbInfo}")" VendorName="$(awk -F"${idVendor} " "/0x${idVendor} / {print \$2}" <<<"${LsusbInfo}" | cut -f1 -d',')" if [ "X${ProductName}" != "X" ] && [ "X${VendorName}" != "X" ]; then LsusbGuess="${VendorName} ${ProductName}" else LsusbGuess="$(lsusb -s ${USBBus}:${USBDevice} 2>/dev/null | awk -F"${idVendor}:${idProduct} " "/${idVendor}:${idProduct} / {print \$2}")" fi NegotiatedSpeed="$(awk -F'"' '/ATTRS{speed}/ {print $2}' <<<"${UdevInfo}" | head -n1)" if [ "X${DeviceName}" = "X${DeviceToCheck}" ]; then # no SMART support or SMART query failed, we need to find a fallback name if [ "X${LsusbGuess}" != "X" ]; then # Seems like a legit string, so use usbutils database but doublecheck # against our exception list first to avoid lengthy or wrong names BetterNameAvailable="$(GetUSBSataBridgeName "${idVendor}" "${idProduct}" "${LsusbGuess}")" if [ "X${BetterNameAvailable}" = "X" ]; then DeviceName="\"${LsusbGuess}\" as ${StorageDevice}" else DeviceName="\"${BetterNameAvailable}\" as ${StorageDevice}" fi else # try to construct device name from ATTRS{vendor}+ATTRS{model} udev info DeviceVendor="$(awk -F'"' '/ATTRS{vendor}/ {print $2}' <<<"${UdevInfo}" | awk '{$1=$1};1')" if [ "X${DeviceVendor}" != "X" ]; then DeviceName="\"${DeviceVendor} $(awk -F'"' '/ATTRS{model}/ {print $2}' <<<"${UdevInfo}" | awk '{$1=$1};1')\" as ${StorageDevice}" else DeviceName="[unknown device] as ${StorageDevice}" fi fi DeviceInfo="USB" else # SMART data has been found, now try to determine bridge chip in between # disk and USB host controller case "${idVendor}:${idProduct}" in :) # no IDs found, generic report DeviceInfo="behind USB" ;; 152d*|174c*|2109*|0bda*|1058:0a10) # USB-to-SATA bridge vendors: JMicron, ASMedia, VIA Labs, Realtek BetterNameAvailable="$(GetUSBSataBridgeName "${idVendor}" "${idProduct}" "${LsusbGuess}")" if [ "X${BetterNameAvailable}" = "X" ]; then DeviceInfo="behind \"${LsusbGuess}\" (${idVendor}:${idProduct})" else DeviceInfo="behind ${BetterNameAvailable} (${idVendor}:${idProduct})" fi ;; 0bc2*|1058*|04e8*|0781*|0930*|0fce*|054c*|0411*|17ef*|07ab*|0718*|2009*|18a5*) # vendor IDs of disk manufacturers that use USB SATA bridges with # branded firmwares like Seagate, WD, Samsung, SanDisk, Toshiba, # Sony, Buffalo, Lenovo, Freecom, Imation, iStorage, Verbatim DeviceInfo="in \"${LsusbGuess}\" (${idVendor}:${idProduct})" ;; *) DeviceInfo="behind \"${LsusbGuess}\" (${idVendor}:${idProduct})" ;; esac fi AdditionalInfo=", Driver=${Driver}, ${NegotiatedSpeed}Mbps${SpeedInfo}${AdditionalInfo}" ;; virtio-*) # virtual disk DeviceName="Virtual disk" AdditionalInfo="${StorageDevice}, Driver=${Driver}" ;; esac DevizeSize="$(GetDiskSize "${StorageDevice}" "${DeviceName}")" if [ "X${DeviceWarning}" = "XTRUE" ]; then echo -e "smartctl -x ${StorageDevice} ; \c" >>"${TempDir}/check-smart" echo -e " * ${LRED}${DevizeSize}${DeviceName%%*( )}: ${DeviceInfo}${LnkSta}${AdditionalSMARTInfo}${AdditionalInfo}${DriveTemp}${ASPMSettings}${NC}" else echo -e " * ${DevizeSize}${DeviceName%%*( )}: ${DeviceInfo}${LnkSta}${AdditionalSMARTInfo}${AdditionalInfo}${DriveTemp}${ASPMSettings}" fi [ -n "${LetsBenchmark}" ] && snore 0.1 && echo -e " \c" >&2 && GetBenchmarkPartition "${StorageDevice}" 600000 >&2 done # MMC devices for StorageDevice in /dev/mmcblk? ; do [[ -e "${StorageDevice}" ]] || break unset DeviceName DeviceInfo DeviceWarning DevizeSize DeviceType DmesgInfo AdditionalInfo CountOfProblems TuningProblems Manufacturer if [ -x /sys/block/${StorageDevice##*/}/device ]; then cd /sys/block/${StorageDevice##*/}/device || return 1 # read in variables from sysfs in q&d style and prefix them all with "mmc_". # We rely here on the kernel version being recent enough to decode CIS and CSD # correctly. Should be double checked against https://tinyurl.com/29c8393f or # https://gurumeditation.org/1342/sd-memory-card-register-decoder/ # # Looks like this for example: https://gist.github.com/ThomasKaiser/97770ef60a94e19df3c270506209e70f declare mmc_serial mmc_manfid mmc_oemid mmc_date mmc_type mmc_hwrev mmc_fwrev eval $(grep . * 2>/dev/null | grep -v uevent | sed -e 's/:/=/' -e 's/^/mmc_/' -e 's/\ //g') # Rely on human readable real storage sizes and not marketing 'sizes': # e.g. show 29.7GB for a '32GB' card: mmc_size=$(awk -F" " "/ ${StorageDevice##*/} / {print \$1}" <<<"${LSBLK}") [ "X${mmc_size}" != "X" ] && FlashSize="${mmc_size}B " # check serial number since low serial numbers are suspicious, might be overriden # one step later since some OEM cards (AData, Phison) seem to use low serial numbers. # https://github.com/ThomasKaiser/sbc-bench/blob/master/results/SD-cards-with-low-serial-numbers.md case "${mmc_serial}" in 0x0000*) # probable fake card Manufacturer="Probable counterfeit " ;; esac # Only add manufacturer info if really unique. Partially misleading since counterfeit # cards were and still are an issue: https://www.bunniestudios.com/blog/?page_id=1022 # Also newer manfids (based on JEDEC vendor IDs) are broken by design: https://archive.ph/x2kpx case "${mmc_manfid}/${mmc_oemid}" in 0x0000df/0x0118) # Despite the weird 0x0000df manufacturer ID this seems to be a legit soldered # 128GB eMMC used in e.g. GoWin R86S and MeLE Quieter3C : ;; 0x000000*|0x0000ff/0x0000|0x00006f/0x0013|0x000025/0x1708|0x0000df*|0x0000fe/0x3456) # counterfeit card Manufacturer="Definite counterfeit " DeviceWarning=TRUE ;; 0x000001*) Manufacturer="${Manufacturer}Panasonic " ;; 0x000002/0x544d) # 0x544d -> "TM" Manufacturer="${Manufacturer}Toshiba " ;; 0x000003/0x5344|0x000003/0x5744) # 0x5344 -> "SD", 0x5744 -> "WD" (Western Digital owning the SanDisk brand since 2016) Manufacturer="${Manufacturer}SanDisk " ;; 0x000008*) Manufacturer="${Manufacturer}Silicon Power " ;; 0x000012/0x3456) # 0x3456 -> "4V", used by at least the following brands: EssentielB, Extrememory, # fake Kingston: https://www.bunniestudios.com/blog/?page_id=1022 # show up with very low serial numbers as "ASTC", "SD" or "F0F0", fake Manufacturer="Definite counterfeit " DeviceWarning=TRUE ;; 0x000012/0x5678) # 0x5678 -> "Vx", show up with very low serial numbers as "ASTC", "MS", fake Manufacturer="Definite counterfeit " DeviceWarning=TRUE ;; 0x000012*) # fake Samsung PRO Endurance: https://tinyurl.com/ytd8hmka # this manufacturer ID is definitely a counterfeit indicator Manufacturer="Definite counterfeit " DeviceWarning=TRUE ;; 0x000018*) Manufacturer="${Manufacturer}Infineon " ;; 0x000015/0x0100|0x00001b/0x534d|0x0000ce*) # 0x534d -> "SM" if [ "X${mmc_name}" = "X00000" ]; then # erase mmc_name if 00000 since Samsung SD cards all use this non-descriptive # string that's also used by scammers (Samsung eMMC though uses descriptive names) Manufacturer="${Manufacturer}Samsung" mmc_name="" else Manufacturer="${Manufacturer}Samsung " fi ;; 0x00001d/0x4144) # 0x4144 -> "AD" Manufacturer="AData " ;; 0x00001d*) Manufacturer="${Manufacturer}Corsair " ;; 0x000027/0x5048) # 0x5048 -> "PH", used by at least the following brands: AgfaPhoto, Delkin, Intenso, Integral, Lexar, Patriot, PNY, Polaroid, Sony, Verbatim Manufacturer="Phison " ;; 0x000028/0x4245) # 0x4245 -> "BE", used by at least the following brands: Lexar, PNY, ProGrade Manufacturer="${Manufacturer}Lexar " ;; 0x000041/0x3432|0x000070/0x0100) # 0x3432 -> "42" Manufacturer="${Manufacturer}Kingston " ;; */0x3432) # 0x3432 -> "42" but differing manfid from Kingston's Manufacturer="Definite counterfeit " DeviceWarning=TRUE ;; 0x000045*) Manufacturer="${Manufacturer}SanDisk/Toshiba " ;; 0x000073/0x4247) # 0x4247 -> "BG", used by at least the following brands: Fugi, Hama, V-Gen : ;; 0x000074/0x4a45) # 0x4a45 -> "JE" case ${mmc_date} in *200*|*2010|*2011|*2012) # low serial number is fine dating back then Manufacturer="Transcend " ;; *) Manufacturer="${Manufacturer}Transcend " ;; esac ;; 0x000074/0x4a60) # 0x4a60 -> "J`", combination used with some cards showing low serial numbers, # bogus names like 00000/ASTC or name/capacity mismatch: SD16G with 7.44 GiB Manufacturer="${Manufacturer}Transcend " ;; 0x000082/0x4a54) # 0x4a54 -> "JT" Manufacturer="${Manufacturer}Sony " ;; 0x000084/0x5446) # 0x5446 -> "TF", http://strontium.biz/products/memory-cards/mobile-memory-cards/#seven : ;; 0x000088/0x0103|0x0000d6/0x0103|0x0000d6/0x2903) Manufacturer="${Manufacturer}Foresee " ;; 0x00009b/0x0100) # https://en.wikipedia.org/wiki/Yangtze_Memory_Technologies Manufacturer="${Manufacturer}YMTC " ;; 0x00009f/0x5449) # 0x5449 -> "TI", found with maybe counterfeit Kingston SDC10G2 (name:SD16G): https://wiki.kobol.io/helios4/sdcard/#kingston-mobile-card-microsdhc-16gb # or as "Kingston Canvas Select Plus, SDCS2/32GB" "SD32G", date 10/2020, hw/fw rev: 0x6/0x1 Manufacturer="${Manufacturer}Texas Instruments " ;; 0x0000ad*|0x000090/0x014a) Manufacturer="${Manufacturer}SK Hynix " ;; 0x0000ea/0x010e) Manufacturer="${Manufacturer}SiliconGo " ;; 0x0000fe*) Manufacturer="${Manufacturer}Micron-Numonyx " ;; esac # check certain fraud criteria again to switch from 'Probable counterfeit' (low # serial number) to 'Definite counterfeit' if [ "${mmc_serial}" = "0x00000000" ] || [ "X${mmc_name}" = "X00000" ] || [ "X${mmc_name}" = "XSMI" ] || [ "X${mmc_name}" = "XASTC" ] || [ "X${mmc_name}" = "XAS" ]; then Manufacturer="Definite counterfeit " DeviceWarning=TRUE fi # check dmesg output to gather further info like negotiation problems and errors. MMCNode="$(grep "] ${StorageDevice##*/}: " <<<"${DMESG}" | grep "iB" | awk -F":" "/ ${mmc_name} / {print \$2}" | head -n1)" if [ "X${MMCNode}" != "X" ]; then DmesgInfo="$(awk -F' new ' "/]${MMCNode}: new/ {print \$2}" <<<"${DMESG}" | awk -F' at ' '{print $1}' | tail -n1)" CountOfProblems=$(grep "]${MMCNode}: " <<<"${DMESG}" | grep -c error) TuningProblems=$(grep -c "]${MMCNode}: tuning execution failed" <<<"${DMESG}") if [ ${CountOfProblems} -gt 0 ] && [ ${TuningProblems} -gt 0 ]; then AdditionalInfo=" (speed negotiation problems and errors, check dmesg)" DeviceWarning=TRUE elif [ ${CountOfProblems} -gt 0 ] && [ ${TuningProblems} -eq 0 ]; then AdditionalInfo=" (various errors occured, check dmesg)" DeviceWarning=TRUE elif [ ${CountOfProblems} -eq 0 ] && [ ${TuningProblems} -gt 0 ]; then AdditionalInfo=" (speed negotiation problems, check dmesg)" DeviceWarning=TRUE fi fi case "${mmc_type}" in SD) DeviceType="$(sed -e 's/ultra /U/' -e 's/high speed/HS/' -e 's/SDHC /SD /' <<<"${DmesgInfo:-SDHC card}")" ;; MMC) # try to query additional info via mmc-utils (for now only MMC version) ExtendedInfo="$(mmc extcsd read ${StorageDevice} 2>/dev/null)" grep -q "Extended CSD rev" <<<"${ExtendedInfo}" && MMCVersion="$(awk -F"[()]" '/Extended CSD rev/ {print $2}' <<<"${ExtendedInfo}")" DeviceType="$(sed "s/MMC/e${MMCVersion:-MMC}/" <<<"${DmesgInfo:-MMC}")" ;; esac if [ "X${DeviceWarning}" = "XTRUE" ]; then echo -e " * ${LRED}${FlashSize}\"${Manufacturer}${mmc_name}\" ${DeviceType}${AdditionalInfo} as ${StorageDevice}: date ${mmc_date}, manfid/oemid: ${mmc_manfid}/${mmc_oemid}, hw/fw rev: ${mmc_hwrev}/${mmc_fwrev}${NC}" else echo -e " * ${FlashSize}\"${Manufacturer}${mmc_name}\" ${DeviceType}${AdditionalInfo} as ${StorageDevice}: date ${mmc_date}, manfid/oemid: ${mmc_manfid}/${mmc_oemid}, hw/fw rev: ${mmc_hwrev}/${mmc_fwrev}" fi fi [ -n "${LetsBenchmark}" ] && snore 0.1 && echo -e " \c" >&2 && GetBenchmarkPartition "${StorageDevice}" 120000 >&2 done # MTD devices: partitions appear as single mtd devices so different logic is needed # to deal with devices MTDDevices="$(CheckMTD)" echo -n "${MTDDevices}" | cut -f1 -d'|' | sort | uniq | sed '/^$/d' | while read ; do unset RawSize DeviceName Driver FlashManufacturer FlashPartName DeviceInfo="$(grep "^${REPLY}" <<<"${MTDDevices}")" PartitionCount=$(wc -l <<<"${DeviceInfo}") case ${PartitionCount} in 1) # device seems not to be partitioned, report raw size RawSize=$(cut -f6 -d'|' <<<"${DeviceInfo}") [ "X${RawSize}" != "X" ] && FlashSize="$(( ${RawSize} / 1024 ))MB " PartitionInfo="" ;; *) # partitions, we report the individual partitions (label/size) PartitionInfo=" (${PartitionCount} partitions: $(awk -F"|" '{print $7": "$6"KB"}' <<<"${DeviceInfo}" | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//'))" FlashSize="" ;; esac Driver=$(cut -f2 -d'|' <<<"${DeviceInfo}" | head -n1) FlashManufacturer=$(cut -f4 -d'|' <<<"${DeviceInfo}" | head -n1) FlashPartName=$(cut -f5 -d'|' <<<"${DeviceInfo}" | head -n1) [ "X${FlashManufacturer}" != "X" ] && [ "X${FlashPartName}" != "X" ] && DeviceName="${FlashManufacturer} ${FlashPartName} " # Maybe TODO: parse lists like https://github.com/Ruimusume/DiM_BACKUP/blob/main/chiplist.xml to show device size JedecID=$(cut -f3 -d'|' <<<"${DeviceInfo}" | head -n1) case "${Driver}" in *spi-nor*) echo -e " * ${DeviceName}${FlashSize}SPI NOR flash${PartitionInfo}, drivers in use: ${Driver}" ;; *nand*) grep -q "spi" <<<"${Driver}" \ && echo -e " * ${DeviceName}${FlashSize}SPI NAND flash${PartitionInfo}, drivers in use: ${Driver}" \ || echo -e " * ${DeviceName}${FlashSize}NAND flash${PartitionInfo}, drivers in use: ${Driver}" \ ;; *) echo -e " * ${DeviceName}${FlashSize}MTD device${PartitionInfo}, drivers in use: ${Driver}" ;; esac done if [ -f "${TempDir}/check-smart" ]; then echo -e "\n\"$(sed 's/ ; $//' <"${TempDir}/check-smart")\" could be used to get further information about the reported issues." rm "${TempDir}/check-smart" fi } # CheckStorage GetBenchmarkPartition() { # function that gets a block device as $1 and minimum free blocks as $2 and returns # whether benchmarking in a reasonable way is possible or not and if possible prints # the best suited partition (e.g. on spinning rust due to ZBR outer partitions are # faster than inner) echo lalala $2 } # GetBenchmarkPartition BenchmarkDrives() { # function that takes a list of suited partitions generated in review mode and # tests sequential and in case it's worth to check (not on spinning rust) also random # I/O # TODO: check SMART attribute 199 before/after to spot cabling problems : } # BenchmarkDrives CheckMTD() { for StorageDevice in /dev/mtd? ; do [[ -e "${StorageDevice}" ]] || break UdevInfo="$(udevadm info -a -n ${StorageDevice} 2>/dev/null)" DevicePath="$(awk -F"'" '/looking at device/ {print $2}' <<<"${UdevInfo}")" Driver="$(awk -F'"' '/DRIVERS==/ {print $2}' <<<"${UdevInfo}" | sed '/^$/d' | tr '\n' '/' | sed 's/\/$//')" JedecID="$(awk -F'"' '/\/jedec_id}==/ {print $2}' <<<"${UdevInfo}" | head -n1)" FlashManufacturer="$(awk -F'"' '/\/manufacturer}==/ {print $2}' <<<"${UdevInfo}" | head -n1)" FlashPartName="$(awk -F'"' '/\/partname}==/ {print $2}' <<<"${UdevInfo}" | head -n1)" RawSize=$(awk -F'"' '/ATTR{size}/ {print $2}' <<<"${UdevInfo}" | head -n1) [ "X${RawSize}" != "X" ] && FlashSize="$(( ${RawSize} / 1024 ))" DeviceLabel="$(awk -F'"' '/ATTR{name}/ {print $2}' <<<"${UdevInfo}" | head -n1)" echo -e "${DevicePath%/*}|${Driver}|${JedecID}|$(tr '[:lower:]' '[:upper:]' <<<"${FlashManufacturer:0:1}")${FlashManufacturer:1}|${FlashPartName^^}|${FlashSize}|${DeviceLabel}" done } # CheckMTD GetUSBSataBridgeName() { # function wants USB vendor and product ID as $1 and $2 and the name guessed by # usbutils as $3. Returns nicer and/or more correct name for the USB-to-SATA bridge case "${1}:${2}" in 1058:0a10) # JMicron JMS56x USB-to-SATA bridge with integrated port multiplier # that has been flashed with Western Digital branded firmware DeviceInfo="JMicron JMS56x dual SATA 6Gb/s bridge" ;; 152d:0561) # Listed as "JMS551 - Sharkoon SATA QuickPort Duo" DeviceInfo="JMicron JMS561 dual SATA 6Gb/s bridge" ;; 152d:0576) # Listed as "JMicron Technology Corp. / JMicron USA Technology Corp. Gen1 SATA 6Gb/s Bridge" DeviceInfo="JMicron JMS576 SATA 6Gb/s bridge" ;; 152d:0578) # JMS578 wrongly listed as JMS567 in usbutils database, though there are some JMS567 that # can be flashed with a firmware that then results in them identifying as product ID 0578 DeviceInfo="JMicron JMS578 SATA 6Gb/s bridge" ;; 152d:0580) DeviceInfo="JMicron JMS580 SATA bridge (SuperSpeed Plus / 6Gb/s)" ;; 152d:0581) # SuperSpeed Plus bridge, one of the following: # JMS581LT NVMe/SATA/SD7.1 bridge (SuperSpeed Plus / Gen3 x2 / 6Gb/s) # JMS581D NVMe/SATA bridge (SuperSpeed Plus / Gen3 x2 / 6Gb/s) # JMS581SD SD7.1/SD8.0 bridge (SuperSpeed Plus) # # To properly differentiate maybe bcdDevice can be used but at least to get # negotiated device type ATTRS{model} can be used. Reads for example 'Generic # SD70' with a ADATA Premier Extreme SD7.0 Express card: https://archive.md/h51hS unset BridgeModel BridgeModel="$(awk -F'"' '/ATTRS{model}/ {print $2}' <<<"${UdevInfo}" | head -n1 | tr -s ' ' | sed -e 's/\ $//' -e 's/^ //')" DeviceInfo="JMicron JMS581 ${BridgeModel} bridge (SuperSpeed Plus)" ;; 152d:0583) DeviceInfo="JMicron JMS583 NVMe bridge (SuperSpeed Plus / Gen3 x2)" ;; 152d:0586) DeviceInfo="JMicron JMS586 NVMe bridge (SuperSpeed Plus 20Gbps / Gen3 x2)" ;; 152d:0901) DeviceInfo="JMicron JMS901 UFS 2.1/UHS-I bridge" ;; 152d*) # JMicron bridge, let's replace the monstrous vendor string with JMicron DeviceInfo="$(sed 's|JMicron Technology Corp. / JMicron USA Technology Corp.|JMicron|' <<<"${3}")" ;; 174c:1352|174c:1356) DeviceInfo="ASMedia ASM1352R SATA bridge (SuperSpeed Plus / 6Gb/s)" ;; 174c:225c) DeviceInfo="ASMedia ASM225CM SATA 6Gb/s bridge" ;; 174c:2362) DeviceInfo="ASMedia ASM2362 NVMe bridge (SuperSpeed Plus / Gen3 x2)" ;; 174c:2364) DeviceInfo="ASMedia ASM2362 NVMe bridge (SuperSpeed Plus 20Gbps / Gen3 x4)" ;; 174c:55aa) # The product ID has been used by ASMedia for a bunch of different bridges and the usbutils name reads # 'ASMedia Technology Inc. Name: ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge' # ASM1153 is fine in general while the older ASM105x thingies are known to be buggy. Connected via # USB3 it would be possible to identify the different bridges but if they're behind USB2 then there's # no chance. So we live with a generic string (that isn't that long/useless as lsusb output). # ASM1153 is also used in many USB disks (e.g. Seagate) but with different firmware and then appears # not with 174c vendor ID. Same ID is also used with ASM1156 and ASM235CM so it needs to query product string unset ProductString ProductString="$(awk -F'"' '/ATTRS{product}/ {print $2}' <<<"${UdevInfo}" | head -n1 | tr -s ' ' | sed -e 's/\ $//' -e 's/^ //')" case "${ProductString}" in ASM235*) DeviceInfo="ASMedia ${ProductString} SATA bridge (SuperSpeed Plus / 6Gb/s)" ;; ASM105*|ASM115*) DeviceInfo="ASMedia ${ProductString} SATA 6Gb/s bridge" ;; *) DeviceInfo="ASMedia SATA 6Gb/s bridge" ;; esac ;; 174c*) # ASMedia bridges, let's replace the monstrous vendor string with ASMedia DeviceInfo="$(sed 's|ASMedia Technology Inc.|ASMedia|' <<<"${3}")" ;; 2109:0715) # VIA VL715/VL716 USB to SATA bridges, appear in usbutils as # "VIA Labs, Inc. VLI Product String" or "VL817 SATA Adaptor" # while VL817 is a hub! VL715/VL716 differ by PHY (VL716 USB-C) # but share same product ID since otherwise identical DeviceInfo="VIA Labs VL715/VL716 SATA 6Gb/s bridge" ;; 2109:070*) # SATA 3Gb/s bridges: VL700/VL701 DeviceInfo="VIA Labs VL${idProduct:1} SATA 3Gb/s bridge" ;; 2109:071*) # any of the other VIA Labs VL71* SATA 6Gb/s bridges (that may follow) DeviceInfo="VIA Labs VL${idProduct:1} SATA bridge (SuperSpeed Plus / 6Gb/s)" ;; 0bda:9210) # Listed as 'RTL9210 M.2 NVME Adapter' in usbutils database but the RTL9210B-CG # variant supports both NVMe and SATA with auto detection DeviceInfo="Realtek RTL9210 NVMe/SATA bridge (SuperSpeed Plus / Gen3 x2 / 6Gb/s)" ;; 0bda*) # RealTek bridges, let's replace the vendor string with Realtek DeviceInfo="$(sed 's|Realtek Semiconductor Corp.|Realtek|' <<<"${3}")" ;; esac echo "${DeviceInfo}" } # GetUSBSataBridgeName GetDiskSize() { # $1 device node # $2 Device name obtained using SMART or usbutils # # returns an empty string if device name seems to consist of the capacity already # (e.g. "Model Number: KXG50ZNV256G NVMe TOSHIBA 256GB") or the capacity reported # by lsblk if lsblk lists the device local disksize grep -q " ${1##*/}" <<<"${LSBLK}" || return disksize=$(awk -F" " "/ ${1##*/}/ {print \$1}" <<<"${LSBLK}" | head -n1) # return disk size only if not '0B' [ "X${disksize}" != "X0B" ] && echo "${disksize}B " } # GetDiskSize CheckSMARTData() { DeviceToCheck="$1" LinuxDriver="$2" SMARTInfo="$(${SmartCtl} -i ${DeviceToCheck} 2>/dev/null)" unset PercentageUsed DeviceWarning # do nothing if $SMARTInfo either contains 'No Information Found' or no disk name grep -q "No Information Found" <<<"${SMARTInfo}" && return grep -E -q "Device Model:|Model Number:" <<<"${SMARTInfo}" if [ $? -eq 0 ]; then # use SMART data, we need to differentiate by Linux driver (ahci, nvme, uas, usb-storage) case "${LinuxDriver}" in ahci) # native SATA Protocol="ATA" DeviceAddition="" ;; nvme) # directly PCIe accessible NVMe Protocol="NVMe" DeviceAddition="" ;; usb-storage|uas) # USB attached, so we need to see what's behind the bridge chip grep -E -q "Device Model:" <<<"${SMARTInfo}" case $? in 0) # SATA behind an USB-to-SATA bridge Protocol="ATA" DeviceAddition=" [SATA]" ;; *) # USB-to-NVMe bridge remains then Protocol="NVMe" DeviceAddition=" [NVMe]" ;; esac ;; esac case ${Protocol} in NVMe) # we can use the standardized NVMe SMART attributes, use old text based # output since JSON works only since smartmontools 7.x SMARTData="$(${SmartCtl} -a ${DeviceToCheck} 2>/dev/null)" DeviceName="\"$(awk -F": " "/^Model Number:/ {print \$2}" <<<"${SMARTData}" | sed 's/^ *//g')\" SSD as ${DeviceToCheck}${DeviceAddition}" PercentageUsed="$(awk -F": " "/^Percentage Used:/ {print \$2}" <<<"${SMARTData}" | sed -e 's/^ *//g' -e 's/%//')" if [ ${PercentageUsed:-0} -gt 75 ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${PercentageUsed}% worn out${NC}${LRED}" else AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${PercentageUsed}% worn out" fi MediaErrors=$(awk -F": " "/^Media and Data Integrity Errors:/ {print \$2}" <<<"${SMARTData}" | sed 's/^ *//g') [ ${MediaErrors:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${MediaErrors} media errors${NC}${LRED}" ErrorLogEntries=$(awk -F": " "/^Error Information Log Entries:/ {print \$2}" <<<"${SMARTData}" | sed 's/^ *//g') if [ ${ErrorLogEntries:-0} -gt 0 ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${ErrorLogEntries} error log entries${NC}${LRED}" grep -q "/dev/nvme" <<<"${DeviceToCheck}" && echo -e "nvme error-log ${DeviceToCheck} ; \c" >>"${TempDir}/check-smart" fi RawDiskTemp="$(awk -F": " "/^Temperature:/ {print \$2}" <<<"${SMARTData}" | sed 's/^ *//g' | cut -f1 -d' ')" TempTreshold=60 if [ "X${RawDiskTemp}" != "X" ]; then if [ ${RawDiskTemp} -gt ${TempTreshold} ]; then DeviceWarning=TRUE DriveTemp=", ${BOLD}unhealthy drive temp: ${RawDiskTemp}°C${NC}" else DriveTemp=", drive temp: ${RawDiskTemp}°C" fi fi Health="$(awk -F": " '/overall-health self-assessment test result/ {print $2}' <<<"${SMARTData}")" if [ "X${Health}" != "XPASSED" ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}SMART health: **FAILED**${NC}${LRED}" DeviceWarning=TRUE fi if [ ${PercentageUsed:-0} -gt 75 ] || [ ${MediaErrors:-0} -gt 0 ] || [ ${ErrorLogEntries:-0} -gt 0 ]; then DeviceWarning=TRUE fi ;; ATA) # it gets complicated since we need to deal with vendor specific attributes # and need to differentiate between spinning rust and SSDs :( SMARTData="$(${SmartCtl} -a ${DeviceToCheck})" # Check for available firmware upgrades grep -q -A5 "A firmware update for this drive may be available" <<<"${SMARTData}" if [ $? -eq 0 ]; then FWUpdateURLs="$(grep -A5 "A firmware update for this drive may be available" <<<"${SMARTData}" | grep http | tr '\n' ' ' | sed 's/\ $//')" FWVersion="$(awk -F": " '/Firmware Version:/ {print $2}' <<<"${SMARTData}" | sed 's/^ *//g')" AdditionalSMARTInfo="${AdditionalSMARTInfo}, firmware version ${FWVersion}, updates may be available: ${FWUpdateURLs}" fi # Check for CRC errors. Usually caused by bad cables or jack contacts, # goes along with retransmits and as such harms performance. Beware that # this attribute records these issues over the drive's lifetime as such # it's only an issue when the CRC error counter increases while accessing # the drive CRCErrors="$(awk -F": " '/^199 / {print $10}' <<<"${SMARTData}")" [ ${CRCErrors:-0} -gt 0 ] && AdditionalSMARTInfo=", ${BOLD}${CRCErrors} CRC errors${NC}${LRED}" SATAVersion="$(awk -F": " '/^SATA Version is/ {print $2": "$3}' <<<"${SMARTData}" | sed -e 's/^ *//g' -e 's/: $//')" if [ "X${SATAVersion}" = "X: " ]; then DeviceInfo="SATA" else DeviceInfo="${SATAVersion}" [ "X${DeviceAddition}" != "X" ] && DeviceAddition="$(echo "${DeviceAddition}" | sed "s|SATA|${SATAVersion}|")" fi # differentiate between HDDs and SSDs RotationRate="$(awk -F": " '/^Rotation Rate/ {print $2}' <<<"${SMARTData}")" case "${RotationRate}" in *"Solid State Device"*) # SSD DriveType="SSD" TempTreshold=60 # Dealing with custom vendor attributes is unreliable by definition, # so let's try ATA Device Statistics first SMARTDevstat="$(${SmartCtl} -l devstat ${DeviceToCheck})" PercentageUsed="$(awk -F" " '/Percentage Used Endurance Indicator/ {print $4}' <<<"${SMARTDevstat}")" if [ "X${PercentageUsed}" != "X" ]; then # use 'Percentage Used Endurance Indicator' if [ ${PercentageUsed:-0} -gt 75 ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${PercentageUsed}% worn out${NC}${LRED}" else AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${PercentageUsed}% worn out" fi else # not compliant with ATA Device Statistics so try to deal with the vendor # attributes if in good mood sometimes in the future, for now only caring # about 169, 177 and 202 # Silicon Motion based SSDs / CT500BX100SSD1: 169, raw value decreasing from 100 to 0 # 169 Remaining_Lifetime_Perc 0x0000 100 100 000 Old_age Offline - 78 [ -z "${SMARTValue}" ] && SMARTValue="$(awk -F" " '/^169 / {print $10}' <<<"${SMARTData}" | sed 's/^0*//')" # Samsung SSDs that don't support ATA Device Statistics, normalized value decreasing from 100 to 0 # 177 Wear_Leveling_Count 0x0013 095 095 000 Pre-fail Always - 57 [ -z "${SMARTValue}" ] && SMARTValue="$(awk -F" " '/^177 / {print $4}' <<<"${SMARTData}" | sed 's/^0*//')" # Crucial SSDs not relying on Silicon Motion controllers, normalized value decreasing from 100 to 0 # 202 Percent_Lifetime_Remain 0x0030 092 092 001 Old_age Offline - 8 [ -z "${SMARTValue}" ] && SMARTValue="$(awk -F" " '/^202 / {print $4}' <<<"${SMARTData}" | sed 's/^0*//')" # WD/SanDisk use 230 # 230 Media_Wearout_Indicator 0x0032 017 017 --- Old_age Always - 0x11570e001157 # depends on drive's firmware version how values have to be interpreted, # as such unreliable by definition: # https://listi.jpberlin.de/pipermail/smartmontools-support/2021-February/000580.html if [ ${SMARTValue:-101} -lt 101 ]; then PercentageUsed=$(( 100 - ${SMARTValue} )) if [ ${PercentageUsed:-0} -gt 75 ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${PercentageUsed}% worn out${NC}${LRED}" else AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${PercentageUsed}% worn out" fi fi fi ;; *) # HDD, check for the most common fail indicators DriveType="HDD" TempTreshold=45 # 5 Reallocated_Sector_Ct 0x0033 252 252 010 Pre-fail Always - 0 Reallocated_Sector_Ct="$(awk -F": " '/^ 5 / {print $10}' <<<"${SMARTData}")" [ ${Reallocated_Sector_Ct:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${Reallocated_Sector_Ct} Reallocated Sector Count${NC}${LRED}" # 195 Hardware_ECC_Recovered 0x003a 100 100 000 Old_age Always - 0 Hardware_ECC_Recovered="$(awk -F": " '/^195 / {print $10}' <<<"${SMARTData}")" [ ${Hardware_ECC_Recovered:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${Hardware_ECC_Recovered} Hardware ECC Recovered${NC}${LRED}" # 196 Reallocated_Event_Count 0x0032 252 252 000 Old_age Always - 0 Reallocated_Event_Count="$(awk -F": " '/^196 / {print $10}' <<<"${SMARTData}")" [ ${Reallocated_Event_Count:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${Reallocated_Event_Count} Reallocated Event Count${NC}${LRED}" # 197 Current_Pending_Sector 0x0032 252 252 000 Old_age Always - 0 Current_Pending_Sector="$(awk -F": " '/^197 / {print $10}' <<<"${SMARTData}")" [ ${Current_Pending_Sector:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${Current_Pending_Sector} Current Pending Sector${NC}${LRED}" # 198 Offline_Uncorrectable 0x0030 252 252 000 Old_age Offline - 0 Offline_Uncorrectable="$(awk -F": " '/^197 / {print $10}' <<<"${SMARTData}")" [ ${Offline_Uncorrectable:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${Offline_Uncorrectable} Offline Uncorrectable${NC}${LRED}" ;; esac RawDiskTemp="$(awk -F" " '/Temperature/ {print $10" "$2}' <<<"${SMARTData}" | head -n1 | sed 's/_/ /g' | sed -e 's/ Airflow//' -e 's/ Temperature//' | tr -d -c '[:digit:]')" if [ "X${RawDiskTemp}" != "X" ]; then if [ ${RawDiskTemp} -gt ${TempTreshold} ]; then DeviceWarning=TRUE DriveTemp=", ${BOLD}unhealthy drive temp: ${RawDiskTemp}°C${NC}" else DriveTemp=", drive temp: ${RawDiskTemp}°C" fi fi # 184 End-to-End_Error 0x0032 100 100 --- Old_age Always - 0 EndtoEnd_Error="$(awk -F": " '/^184 / {print $10}' <<<"${SMARTData}")" [ ${EndtoEnd_Error:-0} -gt 0 ] && AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}${EndtoEnd_Error} End-to-End errors${NC}${LRED}" # Check whether drive model name already contains vendor and if not try to add it DeviceVendor="$(awk -F": " "/^Model Family:/ {print \$2}" <<<"${SMARTData}" | grep -E -v "Silicon Motion|Phison|SandForce|SK hynix|Marvell|JMicron|Apacer SSDs|Indilinx" | sed 's/^ *//g' | cut -f1 -d' ')" DeviceModel="$(awk -F": " "/^Device Model:/ {print \$2}" <<<"${SMARTData}" | sed 's/^ *//g')" grep -q "${DeviceVendor}" <<<"${DeviceModel}" case $? in 0) # drive vendor already part of device model string DeviceName="\"${DeviceModel}\" ${DriveType} as ${DeviceToCheck}${DeviceAddition}" ;; *) # we add vendor to device name DeviceName="\"${DeviceVendor} ${DeviceModel}\" ${DriveType} as ${DeviceToCheck}${DeviceAddition}" ;; esac Health="$(awk -F": " '/overall-health self-assessment test result/ {print $2}' <<<"${SMARTData}")" if [ "X${Health}" != "XPASSED" ]; then AdditionalSMARTInfo="${AdditionalSMARTInfo}, ${BOLD}SMART health: **FAILED**${NC}${LRED}" DeviceWarning=TRUE fi if [ ${PercentageUsed:-0} -gt 75 ] || [ ${Reallocated_Sector_Ct:-0} -gt 1 ] || [ ${Hardware_ECC_Recovered:-0} -gt 1 ] || [ ${Reallocated_Event_Count:-0} -gt 1 ] || [ ${Current_Pending_Sector:-0} -gt 1 ] || [ ${Offline_Uncorrectable:-0} -gt 1 ] || [ ${EndtoEnd_Error:-0} -gt 1 ]; then DeviceWarning=TRUE fi ;; esac # Add vendor name where missing from model string case "${DeviceName}" in "\"CT"*) DeviceName="\"Crucial ${DeviceName:1}" ;; "\"TS"*) DeviceName="\"Transcend ${DeviceName:1}" ;; esac else DeviceName="${DeviceToCheck}" fi } # CheckSMARTData DisplayUsage() { echo -e "\nUsage: ${BOLD}${0##*/} [-c] [-g] [-G] [-h] [-m] [-P] [-r] [-R] [-s] [-t \$degree] [-T \$degree] [-u]${NC}\n" echo -e "############################################################################" echo -e "\n Use ${BOLD}${0##*/}${NC} for the following tasks:\n" echo -e " ${0##*/} invoked without arguments runs a standard benchmark" echo -e " ${0##*/} ${BOLD}-c${NC} also executes cpuminer stress tester (NEON/SSE/AVX)" echo -e " ${0##*/} ${BOLD}-g${NC} graphs 7-zip MIPS for every cpufreq OPP" echo -e " ${0##*/} ${BOLD}-G${NC} Geekbench mode, ensures benchmark is properly monitored" echo -e " ${0##*/} ${BOLD}-h${NC} displays this help text" echo -e " ${0##*/} ${BOLD}-j${NC} Jeff Geerling mode. Don't use if you're not him" echo -e " ${0##*/} ${BOLD}-k${NC} Kernel info: version number and vendor/BSP check" echo -e " ${0##*/} ${BOLD}-m${NC} [\$seconds] provides CLI monitoring (5 sec default interval)" echo -e " ${0##*/} ${BOLD}-P${NC} Phoronix mode, ensures benchmark is properly monitored" echo -e " ${0##*/} ${BOLD}-r${NC} Review mode: generate insights via benchmarking" echo -e " ${0##*/} ${BOLD}-R${NC} Review mode w/o baseline benchmarks for further testing" echo -e " ${0##*/} ${BOLD}-s${NC} also executes stockfish stress tester (NEON/SSE/AVX/RAM)" echo -e " ${0##*/} ${BOLD}-t${NC} [\$degree] runs thermal test waiting to cool down to this value" echo -e " ${0##*/} ${BOLD}-T${NC} [\$degree] runs thermal test heating up to this value" echo -e " ${0##*/} ${BOLD}-u${NC} update sbc-bench to latest version from Github\n" echo -e " The environment variable ${BOLD}MODE${NC} can be set to either ${BOLD}extensive${NC} or ${BOLD}unattended${NC}" echo -e " prior to benchmark execution. Exporting ${BOLD}MaxKHz${NC} will also be honored, see here" echo -e " for details: https://github.com/ThomasKaiser/sbc-bench#unattended-execution\n" echo -e " With a Netio powermeter accessible you can export ${BOLD}Netio=address/socket${NC}" to echo -e " sbc-bench defining address and socket this device is plugged into. Requires" echo -e " XML API enabled and read-only access w/o password. Use this ${BOLD}only${NC} with ${BOLD}-g${NC} to" echo -e " draw efficiency graphs since results will be slightly tampered by this mode." echo -e "\n############################################################################\n" } # DisplayUsage # Allows the script to be sourced [[ "${BASH_SOURCE}" = "$0" ]] && Main "$@"