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 if [[ -e $pci/driver ]]; then 98 driver=$(readlink -f "$pci/driver") 99 driver=${driver##*/} 100 else 101 driver=unbound 102 fi 103 if [[ -e $pci/modalias ]]; then 104 mod=$(< "$pci/modalias") 105 fi 106 cache_pci "${pci##*/}" "$class" "$vendor" "$device" "$driver" "$mod" 107 done 108} 109 110cache_pci_bus_lspci() { 111 hash lspci 2> /dev/null || return 1 112 113 cache_pci_init || return 0 114 115 local dev 116 while read -ra dev; do 117 dev=("${dev[@]//\"/}") 118 # lspci splits ls byte of the class (prog. interface) into a separate 119 # field if it's != 0. Look for it and normalize the value to fit with 120 # what kernel exposes under sysfs. 121 if [[ ${dev[*]} =~ -p([0-9]+) ]]; then 122 dev[1]+=${BASH_REMATCH[1]} 123 else 124 dev[1]+=00 125 fi 126 # pci class vendor device 127 cache_pci "${dev[@]::4}" 128 done < <(lspci -Dnmm) 129} 130 131cache_pci_bus_pciconf() { 132 hash pciconf 2> /dev/null || return 1 133 134 cache_pci_init || return 0 135 136 local class vendor device 137 local pci pci_info 138 local chip driver 139 140 while read -r pci pci_info; do 141 driver=${pci%@*} 142 pci=${pci##*pci} pci=${pci%:} 143 source <(echo "$pci_info") 144 # pciconf under FreeBSD 13.1 provides vendor and device IDs in its 145 # output under separate, dedicated fields. For 12.x they need to 146 # be extracted from the chip field. 147 if [[ -n $chip ]]; then 148 vendor=$(printf '0x%04x' $((chip & 0xffff))) 149 device=$(printf '0x%04x' $(((chip >> 16) & 0xffff))) 150 fi 151 cache_pci "$pci" "$class" "$vendor" "$device" "$driver" 152 done < <(pciconf -l) 153} 154 155cache_pci_bus() { 156 case "$(uname -s)" in 157 Linux) cache_pci_bus_lspci || cache_pci_bus_sysfs ;; 158 FreeBSD) cache_pci_bus_pciconf ;; 159 esac 160} 161 162iter_all_pci_sysfs() { 163 cache_pci_bus_sysfs || return 1 164 165 # default to class of the nvme devices 166 local find=${1:-0x010802} findx=$2 167 local pci pcis 168 169 [[ -n ${pci_bus_cache["$find"]} ]] || return 0 170 read -ra pcis <<< "${pci_bus_cache["$find"]}" 171 172 if ((findx)); then 173 printf '%s\n' "${pcis[@]::findx}" 174 else 175 printf '%s\n' "${pcis[@]}" 176 fi 177} 178 179# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 180function iter_all_pci_class_code() { 181 local class 182 local subclass 183 local progif 184 class="$(printf %02x $((0x$1)))" 185 subclass="$(printf %02x $((0x$2)))" 186 progif="$(printf %02x $((0x$3)))" 187 188 if hash lspci &> /dev/null; then 189 if [ "$progif" != "00" ]; then 190 lspci -mm -n -D \ 191 | grep -i -- "-p${progif}" \ 192 | awk -v cc="\"${class}${subclass}\"" -F " " \ 193 '{if (cc ~ $2) print $1}' | tr -d '"' 194 else 195 lspci -mm -n -D \ 196 | awk -v cc="\"${class}${subclass}\"" -F " " \ 197 '{if (cc ~ $2) print $1}' | tr -d '"' 198 fi 199 elif hash pciconf &> /dev/null; then 200 local addr=($(pciconf -l | grep -i "class=0x${class}${subclass}${progif}" \ 201 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 202 echo "${addr[0]}:${addr[1]}:${addr[2]}:${addr[3]}" 203 elif iter_all_pci_sysfs "$(printf '0x%06x' $((0x$progif | 0x$subclass << 8 | 0x$class << 16)))"; then 204 : 205 else 206 echo "Missing PCI enumeration utility" >&2 207 exit 1 208 fi 209} 210 211# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 212function iter_all_pci_dev_id() { 213 local ven_id 214 local dev_id 215 ven_id="$(printf %04x $((0x$1)))" 216 dev_id="$(printf %04x $((0x$2)))" 217 218 if hash lspci &> /dev/null; then 219 lspci -mm -n -D | awk -v ven="\"$ven_id\"" -v dev="\"${dev_id}\"" -F " " \ 220 '{if (ven ~ $3 && dev ~ $4) print $1}' | tr -d '"' 221 elif hash pciconf &> /dev/null; then 222 local addr=($(pciconf -l | grep -iE "chip=0x${dev_id}${ven_id}|vendor=0x$ven_id device=0x$dev_id" \ 223 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 224 echo "${addr[0]}:${addr[1]}:${addr[2]}:${addr[3]}" 225 elif iter_all_pci_sysfs "0x$ven_id:0x$dev_id"; then 226 : 227 else 228 echo "Missing PCI enumeration utility" >&2 229 exit 1 230 fi 231} 232 233function iter_pci_dev_id() { 234 local bdf="" 235 236 for bdf in $(iter_all_pci_dev_id "$@"); do 237 if pci_can_use "$bdf"; then 238 echo "$bdf" 239 fi 240 done 241} 242 243# This function will filter out PCI devices using PCI_ALLOWED and PCI_BLOCKED 244# See function pci_can_use() 245function iter_pci_class_code() { 246 local bdf="" 247 248 for bdf in $(iter_all_pci_class_code "$@"); do 249 if pci_can_use "$bdf"; then 250 echo "$bdf" 251 fi 252 done 253} 254 255function nvme_in_userspace() { 256 # Check used drivers. If it's not vfio-pci or uio-pci-generic 257 # then most likely PCI_ALLOWED option was used for setup.sh 258 # and we do not want to use that disk. 259 260 local bdf bdfs 261 local nvmes 262 263 if [[ -n ${pci_bus_cache["0x010802"]} ]]; then 264 nvmes=(${pci_bus_cache["0x010802"]}) 265 else 266 nvmes=($(iter_pci_class_code 01 08 02)) 267 fi 268 269 for bdf in "${nvmes[@]}"; do 270 if [[ -e /sys/bus/pci/drivers/nvme/$bdf ]] \ 271 || [[ $(uname -s) == FreeBSD && $(pciconf -l "pci${bdf/./:}") == nvme* ]]; then 272 continue 273 fi 274 bdfs+=("$bdf") 275 done 276 ((${#bdfs[@]})) || return 1 277 printf '%s\n' "${bdfs[@]}" 278} 279 280cmp_versions() { 281 local ver1 ver1_l 282 local ver2 ver2_l 283 284 IFS=".-:" read -ra ver1 <<< "$1" 285 IFS=".-:" read -ra ver2 <<< "$3" 286 local op=$2 287 288 ver1_l=${#ver1[@]} 289 ver2_l=${#ver2[@]} 290 291 local lt=0 gt=0 eq=0 v 292 case "$op" in 293 "<") : $((eq = gt = 1)) ;; 294 ">") : $((eq = lt = 1)) ;; 295 "<=") : $((gt = 1)) ;; 296 ">=") : $((lt = 1)) ;; 297 "==") : $((lt = gt = 1)) ;; 298 esac 299 300 decimal() ( 301 local d=${1,,} 302 if [[ $d =~ ^[0-9]+$ ]]; then 303 echo $((10#$d)) 304 elif [[ $d =~ ^0x || $d =~ ^[a-f0-9]+$ ]]; then 305 d=${d/0x/} 306 echo $((0x$d)) 307 else 308 echo 0 309 fi 310 ) 311 312 for ((v = 0; v < (ver1_l > ver2_l ? ver1_l : ver2_l); v++)); do 313 ver1[v]=$(decimal "${ver1[v]}") 314 ver2[v]=$(decimal "${ver2[v]}") 315 ((ver1[v] > ver2[v])) && return "$gt" 316 ((ver1[v] < ver2[v])) && return "$lt" 317 done 318 [[ ${ver1[*]} == "${ver2[*]}" ]] && return "$eq" 319} 320 321lt() { cmp_versions "$1" "<" "$2"; } 322gt() { cmp_versions "$1" ">" "$2"; } 323le() { cmp_versions "$1" "<=" "$2"; } 324ge() { cmp_versions "$1" ">=" "$2"; } 325eq() { cmp_versions "$1" "==" "$2"; } 326neq() { ! eq "$1" "$2"; } 327 328block_in_use() { 329 local block=$1 data pt 330 # Skip devices that are in use - simple blkid it to see if 331 # there's any metadata (pt, fs, etc.) present on the drive. 332 # FIXME: Special case to ignore atari as a potential false 333 # positive: 334 # https://github.com/spdk/spdk/issues/2079 335 # Devices with SPDK's GPT part type are not considered to 336 # be in use. 337 338 if "$rootdir/scripts/spdk-gpt.py" "$block"; then 339 return 1 340 fi 341 342 data=$(blkid "/dev/${block##*/}") || data=none 343 344 if [[ $data == none ]]; then 345 return 1 346 fi 347 348 pt=$(blkid -s PTTYPE -o value "/dev/${block##*/}") || pt=none 349 350 if [[ $pt == none || $pt == atari ]]; then 351 return 1 352 fi 353 354 # Devices used in SPDK tests always create GPT partitions 355 # with label containing SPDK_TEST string. Such devices were 356 # part of the tests before, so are not considered in use. 357 if [[ $pt == gpt ]] && parted "/dev/${block##*/}" -ms print | grep -q "SPDK_TEST"; then 358 return 1 359 fi 360 361 return 0 362} 363 364get_spdk_gpt() { 365 local spdk_guid 366 367 [[ -e $rootdir/module/bdev/gpt/gpt.h ]] || return 1 368 369 IFS="()" read -r _ spdk_guid _ < <(grep SPDK_GPT_PART_TYPE_GUID "$rootdir/module/bdev/gpt/gpt.h") 370 spdk_guid=${spdk_guid//, /-} spdk_guid=${spdk_guid//0x/} 371 372 echo "$spdk_guid" 373} 374 375if [[ -e "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" ]]; then 376 # Adjust uname to report the operating system as WSL, Msys or Cygwin 377 # and the kernel name as Windows. Define kill() to invoke the SIGTERM 378 # handler before causing a hard stop with TerminateProcess. 379 source "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" 380fi 381