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