github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/pci/pci_linux.go (about) 1 // Copyright 2012-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pci 6 7 //go:generate go run gen.go 8 9 import ( 10 "fmt" 11 "os" 12 "path/filepath" 13 "sort" 14 "strconv" 15 "strings" 16 ) 17 18 const ( 19 pciPath = "/sys/bus/pci/devices" 20 ) 21 22 type bus struct { 23 Devices []string 24 } 25 26 func readString(dir, file string) (string, error) { 27 s, err := os.ReadFile(filepath.Join(dir, file)) 28 if err != nil { 29 return "", err 30 } 31 return strings.TrimSuffix(string(s), "\n"), nil 32 } 33 34 func readUint(dir, file string, base, bits int) (uint64, error) { 35 s, err := readString(dir, file) 36 if err != nil { 37 return 0, err 38 } 39 s = strings.TrimPrefix(s, "0x") 40 return strconv.ParseUint(s, base, bits) 41 } 42 43 // OnePCI takes the name of a directory containing linux-style 44 // PCI files and returns a filled-in *PCI. 45 func OnePCI(dir string) (*PCI, error) { 46 pci := PCI{ 47 Addr: filepath.Base(dir), 48 FullPath: dir, 49 } 50 var err error 51 var n uint64 52 53 if n, err = readUint(dir, "vendor", 16, 16); err != nil { 54 return nil, err 55 } 56 pci.Vendor = uint16(n) 57 if n, err = readUint(dir, "device", 16, 16); err != nil { 58 return nil, err 59 } 60 pci.Device = uint16(n) 61 if n, err = readUint(dir, "class", 16, 24); err != nil { 62 return nil, err 63 } 64 pci.Class = uint32(n) 65 if n, err = readUint(dir, "irq", 0, 0); err != nil { 66 return nil, err 67 } 68 pci.IRQLine = uint(n) 69 if pci.Resource, err = readString(dir, "resource"); err != nil { 70 return nil, err 71 } 72 pci.VendorName, pci.DeviceName = fmt.Sprintf("%04x", pci.Vendor), fmt.Sprintf("%04x", pci.Device) 73 pci.ClassName = "ClassUnknown" 74 if nm, ok := ClassNames[pci.Class]; ok { 75 pci.ClassName = nm 76 } 77 78 for i, r := range strings.Split(pci.Resource, "\n") { 79 b, l, a, err := BaseLimType(r) 80 // It's not clear how this can happen, if ever; could someone 81 // hotunplug a device while we are scanning? 82 if err != nil { 83 return nil, fmt.Errorf("Scanning resource %d(%s): %v", i, dir, err) 84 } 85 if b == 0 { 86 continue 87 } 88 nb := BAR{ 89 Index: i, 90 Base: b, 91 Lim: l, 92 Attr: a, 93 } 94 switch i { 95 case 13: 96 pci.IO = nb 97 case 14: 98 pci.Mem = nb 99 case 15: 100 pci.PrefMem = nb 101 default: 102 pci.BARS = append(pci.BARS, nb) 103 } 104 } 105 return &pci, nil 106 } 107 108 // BaseLimType parses a Linux resource string into base, limit, and attributes. 109 // The string must have three hex fields. 110 // Gaul was divided into three parts. 111 // So are the BARs. 112 func BaseLimType(bar string) (uint64, uint64, uint64, error) { 113 f := strings.Fields(bar) 114 if len(f) != 3 { 115 return 0, 0, 0, fmt.Errorf("bar %q should have 3 fields", bar) 116 } 117 // They must all be parseable hex numbers. 118 var vals [3]uint64 119 for i, ff := range f { 120 var err error 121 if vals[i], err = strconv.ParseUint(ff, 0, 0); err != nil { 122 return 0, 0, 0, err 123 } 124 } 125 return vals[0], vals[1], vals[2], nil 126 } 127 128 // NewBusReader returns a BusReader, given a ...glob to match PCI devices against. 129 // If it can't glob in pciPath/g then it returns an error. 130 // For convenience, we use * as the glob if none are supplied. 131 func NewBusReader(globs ...string) (BusReader, error) { 132 if len(globs) == 0 { 133 globs = []string{"*"} 134 } 135 var exp []string 136 for _, g := range globs { 137 gg, err := filepath.Glob(filepath.Join(pciPath, g)) 138 if err != nil { 139 return nil, err 140 } 141 exp = append(exp, gg...) 142 } 143 // uniq 144 u := map[string]struct{}{} 145 for _, e := range exp { 146 u[e] = struct{}{} 147 } 148 exp = []string{} 149 for v := range u { 150 exp = append(exp, v) 151 } 152 // sort. This might even sort like a shell would do it. 153 sort.Strings(exp) 154 return &bus{Devices: exp}, nil 155 }