1# Common shell utility functions 2 3# Check if PCI device is on PCI_WHITELIST and not on PCI_BLACKLIST 4# Env: 5# if PCI_WHITELIST is empty assume device is whitelistened 6# if PCI_BLACKLIST is empty assume device is NOT blacklistened 7# Params: 8# $1 - PCI BDF 9function pci_can_use() { 10 local i 11 12 # The '\ ' part is important 13 if [[ " $PCI_BLACKLIST " =~ \ $1\ ]]; then 14 return 1 15 fi 16 17 if [[ -z "$PCI_WHITELIST" ]]; then 18 #no whitelist specified, bind all devices 19 return 0 20 fi 21 22 for i in $PCI_WHITELIST; do 23 if [ "$i" == "$1" ]; then 24 return 0 25 fi 26 done 27 28 return 1 29} 30 31cache_pci_init() { 32 local -gA pci_bus_cache 33 34 [[ -z ${pci_bus_cache[*]} || $CMD == reset ]] || return 1 35 36 pci_bus_cache=() 37} 38 39cache_pci() { 40 local pci=$1 class=$2 vendor=$3 device=$4 41 42 if [[ -n $class ]]; then 43 class=0x${class/0x/} 44 pci_bus_cache["$class"]="${pci_bus_cache["$class"]:+${pci_bus_cache["$class"]} }$pci" 45 fi 46 if [[ -n $vendor && -n $device ]]; then 47 vendor=0x${vendor/0x/} device=0x${device/0x/} 48 pci_bus_cache["$vendor"]="${pci_bus_cache["$vendor"]:+${pci_bus_cache["$vendor"]} }$pci" 49 pci_bus_cache["$device"]="${pci_bus_cache["$device"]:+${pci_bus_cache["$device"]} }$pci" 50 pci_bus_cache["$vendor:$device"]="${pci_bus_cache["$vendor:$device"]:+${pci_bus_cache["$vendor:$device"]} }$pci" 51 fi 52} 53 54cache_pci_bus_sysfs() { 55 [[ -e /sys/bus/pci/devices ]] || return 1 56 57 cache_pci_init || return 0 58 59 local pci 60 local class vendor device 61 62 for pci in /sys/bus/pci/devices/*; do 63 class=$(< "$pci/class") vendor=$(< "$pci/vendor") device=$(< "$pci/device") 64 cache_pci "${pci##*/}" "$class" "$vendor" "$device" 65 done 66} 67 68cache_pci_bus_lspci() { 69 hash lspci 2> /dev/null || return 1 70 71 cache_pci_init || return 0 72 73 local dev 74 while read -ra dev; do 75 dev=("${dev[@]//\"/}") 76 # lspci splits ls byte of the class (prog. interface) into a separate 77 # field if it's != 0. Look for it and normalize the value to fit with 78 # what kernel exposes under sysfs. 79 if [[ ${dev[*]} =~ -p([0-9]+) ]]; then 80 dev[1]+=${BASH_REMATCH[1]} 81 else 82 dev[1]+=00 83 fi 84 # pci class vendor device 85 cache_pci "${dev[@]::4}" 86 done < <(lspci -Dnmm) 87} 88 89cache_pci_bus_pciconf() { 90 hash pciconf 2> /dev/null || return 1 91 92 cache_pci_init || return 0 93 94 local class vd vendor device 95 local pci domain bus device function 96 97 while read -r pci class _ vd _; do 98 IFS=":" read -r domain bus device function _ <<< "${pci##*pci}" 99 pci=$(printf '%04x:%02x:%02x:%x' \ 100 "$domain" "$bus" "$device" "$function") 101 class=$(printf '0x%06x' $((class))) 102 vendor=$(printf '0x%04x' $((vd & 0xffff))) 103 device=$(printf '0x%04x' $(((vd >> 16) & 0xffff))) 104 105 cache_pci "$pci" "$class" "$vendor" "$device" 106 done < <(pciconf -l) 107} 108 109cache_pci_bus() { 110 case "$(uname -s)" in 111 Linux) cache_pci_bus_lspci || cache_pci_bus_sysfs ;; 112 FreeBSD) cache_pci_bus_pciconf ;; 113 esac 114} 115 116iter_all_pci_sysfs() { 117 cache_pci_bus_sysfs || return 1 118 119 # default to class of the nvme devices 120 local find=${1:-0x010802} findx=$2 121 local pci pcis 122 123 [[ -n ${pci_bus_cache["$find"]} ]] || return 0 124 read -ra pcis <<< "${pci_bus_cache["$find"]}" 125 126 if ((findx)); then 127 printf '%s\n' "${pcis[@]::findx}" 128 else 129 printf '%s\n' "${pcis[@]}" 130 fi 131} 132 133# This function will ignore PCI PCI_WHITELIST and PCI_BLACKLIST 134function iter_all_pci_class_code() { 135 local class 136 local subclass 137 local progif 138 class="$(printf %02x $((0x$1)))" 139 subclass="$(printf %02x $((0x$2)))" 140 progif="$(printf %02x $((0x$3)))" 141 142 if hash lspci &> /dev/null; then 143 if [ "$progif" != "00" ]; then 144 lspci -mm -n -D \ 145 | grep -i -- "-p${progif}" \ 146 | awk -v cc="\"${class}${subclass}\"" -F " " \ 147 '{if (cc ~ $2) print $1}' | tr -d '"' 148 else 149 lspci -mm -n -D \ 150 | awk -v cc="\"${class}${subclass}\"" -F " " \ 151 '{if (cc ~ $2) print $1}' | tr -d '"' 152 fi 153 elif hash pciconf &> /dev/null; then 154 local addr=($(pciconf -l | grep -i "class=0x${class}${subclass}${progif}" \ 155 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 156 printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]} 157 elif iter_all_pci_sysfs "$(printf '0x%06x' $((0x$progif | 0x$subclass << 8 | 0x$class << 16)))"; then 158 : 159 else 160 echo "Missing PCI enumeration utility" >&2 161 exit 1 162 fi 163} 164 165# This function will ignore PCI PCI_WHITELIST and PCI_BLACKLIST 166function iter_all_pci_dev_id() { 167 local ven_id 168 local dev_id 169 ven_id="$(printf %04x $((0x$1)))" 170 dev_id="$(printf %04x $((0x$2)))" 171 172 if hash lspci &> /dev/null; then 173 lspci -mm -n -D | awk -v ven="\"$ven_id\"" -v dev="\"${dev_id}\"" -F " " \ 174 '{if (ven ~ $3 && dev ~ $4) print $1}' | tr -d '"' 175 elif hash pciconf &> /dev/null; then 176 local addr=($(pciconf -l | grep -i "chip=0x${dev_id}${ven_id}" \ 177 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 178 printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]} 179 elif iter_all_pci_sysfs "0x$ven_id:0x$dev_id"; then 180 : 181 else 182 echo "Missing PCI enumeration utility" >&2 183 exit 1 184 fi 185} 186 187function iter_pci_dev_id() { 188 local bdf="" 189 190 for bdf in $(iter_all_pci_dev_id "$@"); do 191 if pci_can_use "$bdf"; then 192 echo "$bdf" 193 fi 194 done 195} 196 197# This function will filter out PCI devices using PCI_WHITELIST and PCI_BLACKLIST 198# See function pci_can_use() 199function iter_pci_class_code() { 200 local bdf="" 201 202 for bdf in $(iter_all_pci_class_code "$@"); do 203 if pci_can_use "$bdf"; then 204 echo "$bdf" 205 fi 206 done 207} 208