1#!/usr/bin/env bash 2# We simply check if BAR2 is present as that's where PMR or CMB is 3# meant to be located under qemu. If found, print some stats then exit. 4shopt -s nullglob 5 6[[ $(uname -s) == Linux ]] || exit 0 7# Use MSR instead? 8[[ $(< /sys/class/dmi/id/chassis_vendor) == QEMU ]] || exit 0 9 10get_bar() { 11 echo "0x$(setpci -s "$1" "$2.L")" 12} 13 14get_size() { 15 local addr=$1 16 local start end type 17 18 while IFS="- " read -r start end type; do 19 start=0x$start end=0x$end 20 if ((start == addr)) && [[ $type == *"$pci"* ]]; then 21 printf '0x%08x:0x%08x:0x%08x\n' \ 22 "$start" "$end" $((end - start + 1)) 23 return 0 24 fi 25 done < /proc/iomem 26 echo "unknown/unassigned" 27} 28 29info() { 30 local dev=$1 31 32 local pref loc 33 34 local base_addr2 35 local base_addr4 36 37 local bar local bar2 bar3 bar4 bar5 38 local bar_type2 39 40 pref[0]=non-prefetchable 41 pref[1]=prefetchable 42 43 print_info() { 44 local bar=$1 base_addr=$2 bar_type=$3 45 46 printf '%s:%s:%s:%s:%s:%s\n' \ 47 "${nvme##*/}" \ 48 "$dev" \ 49 "64-bit" \ 50 "${pref[bar & 1 << 3 ? 1 : 0]}" \ 51 "$(get_size "$base_addr")" \ 52 "$bar_type" 53 } 54 55 bar2=$(get_bar "$dev" 0x18) 56 bar3=$(get_bar "$dev" 0x1c) 57 bar4=$(get_bar "$dev" 0x20) 58 bar5=$(get_bar "$dev" 0x24) 59 60 # QEMU uses 64-bit BARs 61 if ((bar2 & 1 << 2)); then 62 bar_type2=pmr 63 if [[ -e $nvme/cmb ]]; then 64 bar_type2=cmb 65 fi 66 base_addr2=$(((bar2 & ~0xf) + (bar3 << 32))) 67 print_info "$bar2" "$base_addr2" "$bar_type2" 68 fi 69 # QEMU uses 64-bit BARs 70 if ((bar4 & 1 << 2)); then 71 base_addr4=$(((bar4 & ~0xf) + (bar5 << 32))) 72 print_info "$bar4" "$base_addr4" pmr 73 fi 74} 75 76for nvme in /sys/class/nvme/nvme*; do 77 pci=$(readlink -f "$nvme/device") pci=${pci##*/} 78 info "$pci" 79done 80