xref: /spdk/scripts/common.sh (revision c41c4c2babbc68d778e7f8bd055cfd18ca5c7209)
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