1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright (C) 2018 Intel Corporation 3# All rights reserved. 4# 5 6# Common shell utility functions 7 8# Check if PCI device is in PCI_ALLOWED and not in PCI_BLOCKED 9# Env: 10# if PCI_ALLOWED is empty assume device is allowed 11# if PCI_BLOCKED is empty assume device is NOT blocked 12# Params: 13# $1 - PCI BDF 14function pci_can_use() { 15 local i 16 17 # The '\ ' part is important 18 if [[ " $PCI_BLOCKED " =~ \ $1\ ]]; then 19 return 1 20 fi 21 22 if [[ -z "$PCI_ALLOWED" ]]; then 23 #no allow list specified, bind all devices 24 return 0 25 fi 26 27 for i in $PCI_ALLOWED; do 28 if [ "$i" == "$1" ]; then 29 return 0 30 fi 31 done 32 33 return 1 34} 35 36resolve_mod() { 37 local mod=$1 aliases=() 38 39 if aliases=($(modprobe -R "$mod")); then 40 echo "${aliases[0]}" 41 else 42 echo "unknown" 43 fi 2> /dev/null 44} 45 46cache_pci_init() { 47 local -gA pci_bus_cache 48 local -gA pci_ids_vendor 49 local -gA pci_ids_device 50 local -gA pci_bus_driver 51 local -gA pci_mod_driver 52 local -gA pci_mod_resolved 53 54 [[ -z ${pci_bus_cache[*]} || $CMD == reset ]] || return 1 55 56 pci_bus_cache=() 57 pci_bus_ids_vendor=() 58 pci_bus_ids_device=() 59 pci_bus_driver=() 60 pci_mod_driver=() 61 pci_mod_resolved=() 62} 63 64cache_pci() { 65 local pci=$1 class=$2 vendor=$3 device=$4 driver=$5 mod=$6 66 67 if [[ -n $class ]]; then 68 class=0x${class/0x/} 69 pci_bus_cache["$class"]="${pci_bus_cache["$class"]:+${pci_bus_cache["$class"]} }$pci" 70 fi 71 if [[ -n $vendor && -n $device ]]; then 72 vendor=0x${vendor/0x/} device=0x${device/0x/} 73 pci_bus_cache["$vendor:$device"]="${pci_bus_cache["$vendor:$device"]:+${pci_bus_cache["$vendor:$device"]} }$pci" 74 75 pci_ids_vendor["$pci"]=$vendor 76 pci_ids_device["$pci"]=$device 77 fi 78 if [[ -n $driver ]]; then 79 pci_bus_driver["$pci"]=$driver 80 fi 81 if [[ -n $mod ]]; then 82 pci_mod_driver["$pci"]=$mod 83 pci_mod_resolved["$pci"]=$(resolve_mod "$mod") 84 fi 85} 86 87cache_pci_bus_sysfs() { 88 [[ -e /sys/bus/pci/devices ]] || return 1 89 90 cache_pci_init || return 0 91 92 local pci 93 local class vendor device driver mod 94 95 for pci in /sys/bus/pci/devices/*; do 96 class=$(< "$pci/class") vendor=$(< "$pci/vendor") device=$(< "$pci/device") driver="" mod="" 97 driver=$(get_pci_driver_sysfs "${pci##*/}") 98 if [[ -e $pci/modalias ]]; then 99 mod=$(< "$pci/modalias") 100 fi 101 cache_pci "${pci##*/}" "$class" "$vendor" "$device" "$driver" "$mod" 102 done 103} 104 105cache_pci_bus_lspci() { 106 hash lspci 2> /dev/null || return 1 107 108 cache_pci_init || return 0 109 110 local dev 111 while read -ra dev; do 112 dev=("${dev[@]//\"/}") 113 # lspci splits ls byte of the class (prog. interface) into a separate 114 # field if it's != 0. Look for it and normalize the value to fit with 115 # what kernel exposes under sysfs. 116 if [[ ${dev[*]} =~ -p([0-9]+) ]]; then 117 dev[1]+=${BASH_REMATCH[1]} 118 else 119 dev[1]+=00 120 fi 121 # pci class vendor device driver 122 # lspci supports driver listing only under Linux, however, it's not 123 # included when specific display mode (i.e. -mm) is in use, even if 124 # extra -k is slapped on the cmdline. So with that in mind, just 125 # get that info from sysfs. 126 cache_pci "${dev[@]::4}" "$(get_pci_driver_sysfs "${dev[0]}")" 127 done < <(lspci -Dnmm) 128} 129 130cache_pci_bus_pciconf() { 131 hash pciconf 2> /dev/null || return 1 132 133 cache_pci_init || return 0 134 135 local class vendor device 136 local pci pci_info 137 local chip driver 138 139 while read -r pci pci_info; do 140 driver=${pci%@*} 141 pci=${pci##*pci} pci=${pci%:} 142 source <(echo "$pci_info") 143 # pciconf under FreeBSD 13.1 provides vendor and device IDs in its 144 # output under separate, dedicated fields. For 12.x they need to 145 # be extracted from the chip field. 146 if [[ -n $chip ]]; then 147 vendor=$(printf '0x%04x' $((chip & 0xffff))) 148 device=$(printf '0x%04x' $(((chip >> 16) & 0xffff))) 149 fi 150 cache_pci "$pci" "$class" "$vendor" "$device" "$driver" 151 done < <(pciconf -l) 152} 153 154get_pci_driver_sysfs() { 155 local pci=/sys/bus/pci/devices/$1 driver 156 157 if [[ -e $pci/driver ]]; then 158 driver=$(readlink -f "$pci/driver") driver=${driver##*/} 159 else 160 driver=unbound 161 fi 162 echo "$driver" 163} 164 165cache_pci_bus() { 166 case "$(uname -s)" in 167 Linux) cache_pci_bus_lspci || cache_pci_bus_sysfs ;; 168 FreeBSD) cache_pci_bus_pciconf ;; 169 esac 170} 171 172iter_all_pci_sysfs() { 173 cache_pci_bus_sysfs || return 1 174 175 # default to class of the nvme devices 176 local find=${1:-0x010802} findx=$2 177 local pci pcis 178 179 [[ -n ${pci_bus_cache["$find"]} ]] || return 0 180 read -ra pcis <<< "${pci_bus_cache["$find"]}" 181 182 if ((findx)); then 183 printf '%s\n' "${pcis[@]::findx}" 184 else 185 printf '%s\n' "${pcis[@]}" 186 fi 187} 188 189# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 190function iter_all_pci_class_code() { 191 local class 192 local subclass 193 local progif 194 class="$(printf %02x $((0x$1)))" 195 subclass="$(printf %02x $((0x$2)))" 196 progif="$(printf %02x $((0x$3)))" 197 198 if hash lspci &> /dev/null; then 199 if [ "$progif" != "00" ]; then 200 lspci -mm -n -D \ 201 | grep -i -- "-p${progif}" \ 202 | awk -v cc="\"${class}${subclass}\"" -F " " \ 203 '{if (cc ~ $2) print $1}' | tr -d '"' 204 else 205 lspci -mm -n -D \ 206 | awk -v cc="\"${class}${subclass}\"" -F " " \ 207 '{if (cc ~ $2) print $1}' | tr -d '"' 208 fi 209 elif hash pciconf &> /dev/null; then 210 local addr=($(pciconf -l | grep -i "class=0x${class}${subclass}${progif}" \ 211 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 212 echo "${addr[0]}:${addr[1]}:${addr[2]}:${addr[3]}" 213 elif iter_all_pci_sysfs "$(printf '0x%06x' $((0x$progif | 0x$subclass << 8 | 0x$class << 16)))"; then 214 : 215 else 216 echo "Missing PCI enumeration utility" >&2 217 exit 1 218 fi 219} 220 221# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 222function iter_all_pci_dev_id() { 223 local ven_id 224 local dev_id 225 ven_id="$(printf %04x $((0x$1)))" 226 dev_id="$(printf %04x $((0x$2)))" 227 228 if hash lspci &> /dev/null; then 229 lspci -mm -n -D | awk -v ven="\"$ven_id\"" -v dev="\"${dev_id}\"" -F " " \ 230 '{if (ven ~ $3 && dev ~ $4) print $1}' | tr -d '"' 231 elif hash pciconf &> /dev/null; then 232 local addr=($(pciconf -l | grep -iE "chip=0x${dev_id}${ven_id}|vendor=0x$ven_id device=0x$dev_id" \ 233 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 234 echo "${addr[0]}:${addr[1]}:${addr[2]}:${addr[3]}" 235 elif iter_all_pci_sysfs "0x$ven_id:0x$dev_id"; then 236 : 237 else 238 echo "Missing PCI enumeration utility" >&2 239 exit 1 240 fi 241} 242 243function iter_pci_dev_id() { 244 local bdf="" 245 246 for bdf in $(iter_all_pci_dev_id "$@"); do 247 if pci_can_use "$bdf"; then 248 echo "$bdf" 249 fi 250 done 251} 252 253# This function will filter out PCI devices using PCI_ALLOWED and PCI_BLOCKED 254# See function pci_can_use() 255function iter_pci_class_code() { 256 local bdf="" 257 258 for bdf in $(iter_all_pci_class_code "$@"); do 259 if pci_can_use "$bdf"; then 260 echo "$bdf" 261 fi 262 done 263} 264 265function nvme_in_userspace() { 266 # Check used drivers. If it's not vfio-pci or uio-pci-generic 267 # then most likely PCI_ALLOWED option was used for setup.sh 268 # and we do not want to use that disk. 269 270 local bdf bdfs 271 local nvmes 272 273 if [[ -n ${pci_bus_cache["0x010802"]} ]]; then 274 nvmes=(${pci_bus_cache["0x010802"]}) 275 else 276 nvmes=($(iter_pci_class_code 01 08 02)) 277 fi 278 279 for bdf in "${nvmes[@]}"; do 280 if [[ -e /sys/bus/pci/drivers/nvme/$bdf ]] \ 281 || [[ $(uname -s) == FreeBSD && $(pciconf -l "pci${bdf/./:}") == nvme* ]]; then 282 continue 283 fi 284 bdfs+=("$bdf") 285 done 286 ((${#bdfs[@]})) || return 1 287 printf '%s\n' "${bdfs[@]}" 288} 289 290cmp_versions() { 291 local ver1 ver1_l 292 local ver2 ver2_l 293 294 IFS=".-:" read -ra ver1 <<< "$1" 295 IFS=".-:" read -ra ver2 <<< "$3" 296 local op=$2 297 298 ver1_l=${#ver1[@]} 299 ver2_l=${#ver2[@]} 300 301 local lt=0 gt=0 eq=0 v 302 case "$op" in 303 "<") : $((eq = gt = 1)) ;; 304 ">") : $((eq = lt = 1)) ;; 305 "<=") : $((gt = 1)) ;; 306 ">=") : $((lt = 1)) ;; 307 "==") : $((lt = gt = 1)) ;; 308 esac 309 310 decimal() ( 311 local d=${1,,} 312 if [[ $d =~ ^[0-9]+$ ]]; then 313 echo $((10#$d)) 314 elif [[ $d =~ ^0x || $d =~ ^[a-f0-9]+$ ]]; then 315 d=${d/0x/} 316 echo $((0x$d)) 317 else 318 echo 0 319 fi 320 ) 321 322 for ((v = 0; v < (ver1_l > ver2_l ? ver1_l : ver2_l); v++)); do 323 ver1[v]=$(decimal "${ver1[v]}") 324 ver2[v]=$(decimal "${ver2[v]}") 325 ((ver1[v] > ver2[v])) && return "$gt" 326 ((ver1[v] < ver2[v])) && return "$lt" 327 done 328 [[ ${ver1[*]} == "${ver2[*]}" ]] && return "$eq" 329} 330 331lt() { cmp_versions "$1" "<" "$2"; } 332gt() { cmp_versions "$1" ">" "$2"; } 333le() { cmp_versions "$1" "<=" "$2"; } 334ge() { cmp_versions "$1" ">=" "$2"; } 335eq() { cmp_versions "$1" "==" "$2"; } 336neq() { ! eq "$1" "$2"; } 337 338block_in_use() { 339 local block=$1 pt 340 # Skip devices that are in use - simple blkid it to see if 341 # there's any metadata (pt, fs, etc.) present on the drive. 342 # FIXME: Special case to ignore atari as a potential false 343 # positive: 344 # https://github.com/spdk/spdk/issues/2079 345 # Devices with SPDK's GPT part type are not considered to 346 # be in use. 347 348 if "$rootdir/scripts/spdk-gpt.py" "$block"; then 349 return 1 350 fi 351 352 if ! pt=$(blkid -s PTTYPE -o value "/dev/${block##*/}"); then 353 return 1 354 elif [[ $pt == atari ]]; then 355 return 1 356 fi 357 358 # Devices used in SPDK tests always create GPT partitions 359 # with label containing SPDK_TEST string. Such devices were 360 # part of the tests before, so are not considered in use. 361 if [[ $pt == gpt ]] && parted "/dev/${block##*/}" -ms print | grep -q "SPDK_TEST"; then 362 return 1 363 fi 364 365 return 0 366} 367 368get_spdk_gpt_old() { 369 local spdk_guid 370 371 [[ -e $rootdir/module/bdev/gpt/gpt.h ]] || return 1 372 373 GPT_H="$rootdir/module/bdev/gpt/gpt.h" 374 IFS="()" read -r _ spdk_guid _ < <(grep -w SPDK_GPT_PART_TYPE_GUID_OLD "$GPT_H") 375 spdk_guid=${spdk_guid//, /-} spdk_guid=${spdk_guid//0x/} 376 377 echo "$spdk_guid" 378} 379 380get_spdk_gpt() { 381 local spdk_guid 382 383 [[ -e $rootdir/module/bdev/gpt/gpt.h ]] || return 1 384 385 GPT_H="$rootdir/module/bdev/gpt/gpt.h" 386 IFS="()" read -r _ spdk_guid _ < <(grep -w SPDK_GPT_PART_TYPE_GUID "$GPT_H") 387 spdk_guid=${spdk_guid//, /-} spdk_guid=${spdk_guid//0x/} 388 389 echo "$spdk_guid" 390} 391 392if [[ -e "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" ]]; then 393 # Adjust uname to report the operating system as WSL, Msys or Cygwin 394 # and the kernel name as Windows. Define kill() to invoke the SIGTERM 395 # handler before causing a hard stop with TerminateProcess. 396 source "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" 397fi 398 399# Make sure we have access to proper binaries installed in pkgdep/common.sh 400if [[ -e /etc/opt/spdk-pkgdep/paths/export.sh ]]; then 401 source /etc/opt/spdk-pkgdep/paths/export.sh 402fi > /dev/null 403