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 s | status) status ;; 85 r | reset) 86 yn "Are you sure you want jump to reset mode?" || continue 87 mode=reset 88 return 89 ;; 90 u | update) 91 CMD=reset cache_pci_bus 92 collect_devices 93 ;; 94 hp) hugepages ;; 95 esac 96 done 97} 98 99gdevices() { 100 if [[ $type == all ]]; then 101 local -gn dev_ref=all_devices_d 102 else 103 local -gn dev_ref=${type}_d 104 fi 105} 106 107pdevices() { 108 gdevices 109 110 local set_marker=$1 111 local use_map=() 112 local -A markers=() 113 114 use_map[0]="not used" use_map[1]="used" 115 markers["not used"]=pick markers["used"]=skip 116 117 if ((${#dev_ref[@]} == 0)); then 118 echo "No devices found" 119 else 120 for dev in "${!dev_ref[@]}"; do 121 printf '%s- %s [%s, %s] (%s@%s:%s)\n' \ 122 "${set_marker:+${markers["${use_map[all_devices_d["$dev"]]}"]} }" \ 123 "$dev" "${use_map[all_devices_d["$dev"]]}" "${drivers_d["$dev"]:-none}" \ 124 "${all_devices_type_d["$dev"]}" \ 125 "${pci_ids_vendor["$dev"]}" \ 126 "${pci_ids_device["$dev"]}" 127 done 128 fi 129} 130 131ctype() { 132 local type_to_set 133 local -n types_ref=types_d 134 135 while read -rp "(${!types_ref[*]} all)> " type_to_set; do 136 type_to_set=${type_to_set,,} 137 if [[ -z $type_to_set ]]; then 138 return 139 elif [[ -n ${types_ref["$type_to_set"]} || $type_to_set == all ]]; then 140 type=$type_to_set 141 return 142 fi 143 done 144} 145 146fdevices() { 147 local action=${1:-0} bdf 148 local am=() 149 local -gA action_0 action_1 150 151 am[0]=PCI_BLOCKED am[1]=PCI_ALLOWED 152 153 gdevices 154 local -n action_ref=action_${action} 155 local -n action_ref_rev=action_$((!action)) 156 157 while read -rp "(${!am[action]:-BDF})> " bdf; do 158 bdf=${bdf,,} 159 if [[ -z $bdf ]]; then 160 return 161 elif [[ -n ${dev_ref["$bdf"]} ]]; then 162 if [[ -n ${action_ref["$bdf"]} ]]; then 163 unset -v "action_ref[$bdf]" 164 else 165 action_ref["$bdf"]=1 166 unset -v "action_ref_rev[$bdf]" 167 fi 168 eval "${am[action]}='${!action_ref[*]}'" 169 eval "${am[!action]}='${!action_ref_rev[*]}'" 170 elif [[ -z ${dev_ref["$bdf"]} ]]; then 171 unset -v "action_ref[$bdf]" 172 eval "${am[action]}='${!action_ref[*]}'" 173 fi 174 done 175} 176 177editor() { 178 local devs_list=() devs_picked=() devs_skipped=() 179 local editor=${VISUAL:-${EDITOR:-vim}} 180 local tmp_file 181 182 type -P "$editor" > /dev/null || return 183 184 mapfile -t devs_list < <(pdevices markers) 185 186 tmp_file=$(mktemp -u) 187 cat <<- ODEVICES > "$tmp_file" || return 188 # Listing '$type' devices 189 # Devices marked as "used" (i.e. contains any data) will be skipped by default 190 # Devices marked as "not used" (i.e. does not contain data) will be picked by default 191 192 $(printf '%s\n' "${devs_list[@]}") 193 194 # p, pick devices 195 # s, skip devices 196 197 $([[ $editor == vi?(m) ]] && echo "# :cq[!] to not save any changes") 198 ODEVICES 199 200 "$editor" "$tmp_file" || return 201 [[ -s $tmp_file ]] || return 202 203 local action dev _type 204 while read -r action _ dev _type; do 205 case "${action,,}" in 206 s | skip) 207 [[ $_type != *"not used"* ]] && continue 208 devs_skipped+=("$dev") 209 ;; 210 p | pick) 211 [[ $_type == *"not used"* ]] && continue 212 devs_picked+=("$dev") 213 ;; 214 esac 215 done < "$tmp_file" 216 rm "$tmp_file" 217 218 if [[ $2 == quick ]] && ((${#devs_picked[@]} > 0)); then 219 if ! yn "Detected data on some of the devices (${devs_picked[*]}). Continue?"; then 220 return 1 221 fi 222 fi 223 224 "$1" < <(printf '%s\n' "${devs_skipped[@]}" "${devs_picked[@]}") 225 226} 227 228odevices() { 229 local bdf 230 231 type=all gdevices 232 233 while read -rp "(BDF)> " bdf; do 234 bdf=${bdf,,} 235 if [[ -z $bdf ]]; then 236 return 237 elif [[ -n ${dev_ref["$bdf"]} ]]; then 238 dev_ref["$bdf"]=$((!dev_ref["$bdf"])) 239 fi 240 done 241} 242 243bdevices() { 244 [[ $os == Linux ]] || return 0 245 246 local bdfd bdf driver 247 248 gdevices 249 250 while read -rp "(BDF->driver)> " bdfd; do 251 bdfd=${bdfd,,} 252 if [[ -z $bdfd ]]; then 253 return 254 fi 255 256 bdf=${bdfd/->*/} driver=${bdfd/*->/} 257 258 if [[ $driver == "${drivers_d["$bdf"]}" ]]; then 259 echo "$bdf already bound to $driver" 260 continue 261 fi 262 263 if [[ -n ${dev_ref["$bdf"]} && -n $driver ]]; then 264 if yn "$bdf currently bound to ${drivers_d["$bdf"]:-none}. Bind to $driver?"; then 265 linux_bind_driver "$bdf" "$driver" 266 fi 267 fi 268 done 269} 270 271status() { 272 local _os=${os,,} 273 274 if [[ $(type -t "status_${_os}") == function ]]; then 275 "status_${_os}" 276 fi 277} 278 279hugepages() { 280 [[ $os == Linux ]] || return 0 281 local hp 282 283 HUGE_EVEN_ALLOC=no 284 while read -rp "('clear' 'even' 'commit' HUGEMEM[=$HUGEMEM MB])> " hp; do 285 hp=${hp,,} 286 if [[ -z $hp ]]; then 287 return 288 elif [[ $hp == clear ]]; then 289 clear_hugepages 290 return 291 elif [[ $hp == even ]]; then 292 HUGE_EVEN_ALLOC=yes 293 elif [[ $hp =~ ^[1-9][0-9]*$ ]]; then 294 NRHUGE="" 295 HUGEMEM=$hp 296 elif [[ $hp == commit ]]; then 297 set_hp 298 configure_linux_hugepages 299 return 300 fi 301 done 302} 303