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