1#!/usr/bin/env bash 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (C) 2023 Intel Corporation 4# All rights reserved. 5# 6set +e 7 8yn() { 9 local -A yn=() 10 local _yn 11 12 yn["y"]=0 yn["n"]=1 13 14 while read -rp "$* (y|N)> " _yn || true; do 15 _yn=${_yn::1} _yn=${_yn,,} _yn=${_yn:-n} 16 [[ -n ${yn["$_yn"]} ]] && return "${yn["$_yn"]}" 17 done 18} 19 20elevate() { 21 ((UID != 0)) || return 0 22 23 if yn "You ($UID) need to be root to commit any changes. Elevate privileges?"; then 24 exec sudo -E "$rootdir/scripts/setup.sh" interactive "$@" 25 fi 26} 27 28stdin() { 29 [[ ! -t 0 ]] || return 0 30 31 echo "Requested interactive mode but stdin is not attached to a terminal, bailing" >&2 32 return 1 33} 34 35main_menu() { 36 local type=all answer quick_mode=$1 37 38 stdin || return 1 39 elevate "$quick_mode" 40 41 case "${quick_mode,,}" in 42 config | reset) 43 editor odevices quick && mode=$quick_mode 44 return 45 ;; 46 esac 47 48 while ((1)); do 49 cat <<- MENU 50 51 1) List PCI Devices [Currently Listing: "$type"] 52 2) Change Devices To List 53 3) Mark Device As Blocked (${PCI_BLOCKED:-none}) 54 4) Mark Device As Allowed (${PCI_ALLOWED:-none}) 55 5) Override Device In-Use Status 56 $([[ $os == Linux ]] && echo "6) Bind Device") 57 58 c) configure 59 s) status 60 r) reset 61 $([[ $os == Linux ]] && echo "hp) hugepages") 62 63 Q) Quit 64 U) Update Devices View 65 66 MENU 67 68 read -rp "> " answer || answer=q 69 70 case "${answer,,}" in 71 1) pdevices ;; 72 2) ctype && pdevices ;; 73 3) fdevices 0 ;; 74 4) fdevices 1 ;; 75 5) odevices ;; 76 5e) editor odevices ;; 77 6) bdevices ;;& 78 q) yn "Are you sure you want to quit?" && return 1 ;; 79 c | commit | config) 80 yn "Are you sure you want jump to config mode?" || continue 81 mode=config 82 return 83 ;; 84 hp) hugepages ;; 85 s | status) status ;; 86 r | reset) 87 yn "Are you sure you want jump to reset mode?" || continue 88 mode=reset 89 return 90 ;; 91 6 | u | update) update_status ;; 92 esac 93 done 94} 95 96gdevices() { 97 if [[ $type == all ]]; then 98 local -gn dev_ref=all_devices_d 99 else 100 local -gn dev_ref=${type}_d 101 fi 102} 103 104pdevices() { 105 gdevices 106 107 local set_marker=$1 108 local use_map=() 109 local -A markers=() 110 111 use_map[0]="not used" use_map[1]="used" 112 markers["not used"]=pick markers["used"]=skip 113 114 if ((${#dev_ref[@]} == 0)); then 115 echo "No devices found" 116 else 117 for dev in "${!dev_ref[@]}"; do 118 printf '%s- %s [%s, %s] (%s@%s:%s)%s\n' \ 119 "${set_marker:+${markers["${use_map[all_devices_d["$dev"]]}"]} }" \ 120 "$dev" "${use_map[all_devices_d["$dev"]]}" "${pci_bus_driver["$dev"]:-none}" \ 121 "${all_devices_type_d["$dev"]}" \ 122 "${pci_ids_vendor["$dev"]}" \ 123 "${pci_ids_device["$dev"]}" \ 124 "${nvme_vmd_d["$dev"]:+"@(VMD -> ${nvme_vmd_d["$dev"]})"}" 125 done 126 fi 127} 128 129ctype() { 130 local type_to_set 131 local -n types_ref=types_d 132 133 while read -rp "(${!types_ref[*]} all)> " type_to_set; do 134 type_to_set=${type_to_set,,} 135 if [[ -z $type_to_set ]]; then 136 return 137 elif [[ -n ${types_ref["$type_to_set"]} || $type_to_set == all ]]; then 138 type=$type_to_set 139 return 140 fi 141 done 142} 143 144fdevices() { 145 local action=${1:-0} bdf 146 local am=() 147 local -gA action_0 action_1 148 149 am[0]=PCI_BLOCKED am[1]=PCI_ALLOWED 150 151 gdevices 152 local -n action_ref=action_${action} 153 local -n action_ref_rev=action_$((!action)) 154 155 while read -rp "(${!am[action]:-BDF})> " bdf; do 156 bdf=${bdf,,} 157 if [[ -z $bdf ]]; then 158 return 159 elif [[ -n ${dev_ref["$bdf"]} ]]; then 160 if [[ -n ${action_ref["$bdf"]} ]]; then 161 unset -v "action_ref[$bdf]" 162 else 163 action_ref["$bdf"]=1 164 unset -v "action_ref_rev[$bdf]" 165 fi 166 eval "${am[action]}='${!action_ref[*]}'" 167 eval "${am[!action]}='${!action_ref_rev[*]}'" 168 elif [[ -z ${dev_ref["$bdf"]} ]]; then 169 unset -v "action_ref[$bdf]" 170 eval "${am[action]}='${!action_ref[*]}'" 171 fi 172 done 173} 174 175editor() { 176 local devs_list=() devs_picked=() devs_skipped=() 177 local editor=${VISUAL:-${EDITOR:-vim}} 178 local tmp_file 179 180 type -P "$editor" > /dev/null || return 181 182 mapfile -t devs_list < <(pdevices markers) 183 184 tmp_file=$(mktemp -u) 185 cat <<- ODEVICES > "$tmp_file" || return 186 # Listing '$type' devices 187 # Devices marked as "used" (i.e. contains any data) will be skipped by default 188 # Devices marked as "not used" (i.e. does not contain data) will be picked by default 189 190 $(printf '%s\n' "${devs_list[@]}") 191 192 # p, pick devices 193 # s, skip devices 194 195 $([[ $editor == vi?(m) ]] && echo "# :cq[!] to not save any changes") 196 ODEVICES 197 198 "$editor" "$tmp_file" || return 199 [[ -s $tmp_file ]] || return 200 201 local action dev _type 202 while read -r action _ dev _type; do 203 case "${action,,}" in 204 s | skip) 205 [[ $_type != *"not used"* ]] && continue 206 devs_skipped+=("$dev") 207 ;; 208 p | pick) 209 [[ $_type == *"not used"* ]] && continue 210 devs_picked+=("$dev") 211 ;; 212 esac 213 done < "$tmp_file" 214 rm "$tmp_file" 215 216 if [[ $2 == quick ]] && ((${#devs_picked[@]} > 0)); then 217 if ! yn "Detected data on some of the devices (${devs_picked[*]}). Continue?"; then 218 return 1 219 fi 220 fi 221 222 "$1" < <(printf '%s\n' "${devs_skipped[@]}" "${devs_picked[@]}") 223 224} 225 226odevices() { 227 local bdf 228 229 type=all gdevices 230 231 while read -rp "(BDF)> " bdf; do 232 bdf=${bdf,,} 233 if [[ -z $bdf ]]; then 234 return 235 elif [[ -n ${dev_ref["$bdf"]} ]]; then 236 dev_ref["$bdf"]=$((!dev_ref["$bdf"])) 237 fi 238 done 239} 240 241bdevices() { 242 [[ $os == Linux ]] || return 0 243 244 local bdf driver 245 246 gdevices 247 248 while read -rp "(BDF)> " bdf; do 249 bdf=${bdf,,} 250 if [[ -z $bdf ]]; then 251 return 252 fi 253 254 [[ -n ${dev_ref["$bdf"]} ]] || continue 255 256 pdriver "$bdf" 257 258 while read -rp "Select driver ($bdf)> " driver; do 259 driver=${driver,,} 260 if [[ -z $driver ]]; then 261 continue 2 262 fi 263 if [[ $driver == "${pci_bus_driver["$bdf"]}" ]]; then 264 echo "$bdf already bound to $driver" 265 continue 266 fi 267 break 268 done 269 270 # Try to be nice and silently attempt to load the driver just in case 271 modprobe -q "$driver" || true 272 273 if yn "$bdf currently bound to ${pci_bus_driver["$bdf"]:-none}. Bind to $driver?"; then 274 linux_bind_driver "$bdf" "$driver" 275 return 276 fi 277 done 278} 279 280pdriver() { 281 local bdf=$1 282 283 cat <<- DRIVER 284 285 $bdf: 286 main driver: $(collect_driver "$bdf") 287 current driver: ${pci_bus_driver["$bdf"]:-none} 288 289 DRIVER 290} 291 292status() { 293 local _os=${os,,} 294 295 if [[ $(type -t "status_${_os}") == function ]]; then 296 "status_${_os}" 297 fi 2> /dev/null 298} 299 300hugepages() { 301 [[ $os == Linux ]] || return 0 302 local hp 303 304 while read -rp "('clear' 'even' 'commit' HUGEMEM[=$HUGEMEM MB])> " hp; do 305 hp=${hp,,} 306 if [[ -z $hp ]]; then 307 return 308 elif [[ $hp == clear ]]; then 309 clear_hugepages 310 return 311 elif [[ $hp =~ ^[1-9][0-9]*$ ]]; then 312 NRHUGE="" 313 HUGEMEM=$hp 314 elif [[ $hp == commit ]]; then 315 set_hp 316 configure_linux_hugepages 317 return 318 fi 319 done 320} 321 322update_status() { 323 CMD=reset cache_pci_bus 324 collect_devices 325} 326