github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/pci/pci.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 import ( 8 "encoding/binary" 9 "fmt" 10 "math" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 ) 16 17 // PCI is a PCI device. We will fill this in as we add options. 18 // For now it just holds two uint16 per the PCI spec. 19 type PCI struct { 20 Addr string 21 Vendor uint16 22 Device uint16 23 Class uint32 24 25 VendorName string 26 DeviceName string 27 ClassName string 28 29 Latency byte 30 IRQPin byte 31 IRQLine uint 32 Bridge bool 33 FullPath string 34 ExtraInfo []string 35 Config []byte 36 // The rest only gets filled in config space is read. 37 // Type 0 38 Control Control 39 Status Status 40 Resource string `pci:"resource"` 41 BARS []BAR `json:"omitempty"` 42 43 // Type 1 44 Primary uint8 45 Secondary uint8 46 Subordinate uint8 47 SecLatency string 48 IO BAR 49 Mem BAR 50 PrefMem BAR 51 } 52 53 // String concatenates PCI address, Vendor, and Device and other information 54 // to make a useful display for the user. 55 func (p *PCI) String() string { 56 return strings.Join(append([]string{fmt.Sprintf("%s: %v: %v %v", p.Addr, p.ClassName, p.VendorName, p.DeviceName)}, p.ExtraInfo...), "\n") 57 } 58 59 // SetVendorDeviceName changes VendorName and DeviceName from a name to a number, 60 // if possible. 61 func (p *PCI) SetVendorDeviceName() { 62 ids = newIDs() 63 p.VendorName, p.DeviceName = Lookup(ids, p.Vendor, p.Device) 64 } 65 66 // ReadConfig reads the config space. 67 func (p *PCI) ReadConfig() error { 68 dev := filepath.Join(p.FullPath, "config") 69 c, err := os.ReadFile(dev) 70 if err != nil { 71 return err 72 } 73 p.Config = c 74 p.Control = Control(binary.LittleEndian.Uint16(c[4:6])) 75 p.Status = Status(binary.LittleEndian.Uint16(c[6:8])) 76 return nil 77 } 78 79 type barreg struct { 80 offset int64 81 *os.File 82 } 83 84 func (r *barreg) Read(b []byte) (int, error) { 85 return r.ReadAt(b, r.offset) 86 } 87 88 func (r *barreg) Write(b []byte) (int, error) { 89 return r.WriteAt(b, r.offset) 90 } 91 92 // ReadConfigRegister reads a configuration register of size 8, 16, 32, or 64. 93 // It will only work on little-endian machines. 94 func (p *PCI) ReadConfigRegister(offset, size int64) (uint64, error) { 95 dev := filepath.Join(p.FullPath, "config") 96 f, err := os.Open(dev) 97 if err != nil { 98 return 0, err 99 } 100 defer f.Close() 101 var reg uint64 102 r := &barreg{offset: offset, File: f} 103 switch size { 104 default: 105 return 0, fmt.Errorf("ReadConfigRegister@%#x width of %d: only options are 8, 16, 32, 64", offset, size) 106 case 64: 107 err = binary.Read(r, binary.LittleEndian, ®) 108 case 32: 109 var val uint32 110 err = binary.Read(r, binary.LittleEndian, &val) 111 reg = uint64(val) 112 case 16: 113 var val uint16 114 err = binary.Read(r, binary.LittleEndian, &val) 115 reg = uint64(val) 116 case 8: 117 var val uint8 118 err = binary.Read(r, binary.LittleEndian, &val) 119 reg = uint64(val) 120 } 121 return reg, err 122 } 123 124 // WriteConfigRegister writes a configuration register of size 8, 16, 32, or 64. 125 // It will only work on little-endian machines. 126 func (p *PCI) WriteConfigRegister(offset, size int64, val uint64) error { 127 f, err := os.OpenFile(filepath.Join(p.FullPath, "config"), os.O_WRONLY, 0) 128 if err != nil { 129 return err 130 } 131 defer f.Close() 132 w := &barreg{offset: offset, File: f} 133 switch size { 134 default: 135 return fmt.Errorf("WriteConfigRegister@%#x width of %d: only options are 8, 16, 32, 64", offset, size) 136 case 64: 137 err = binary.Write(w, binary.LittleEndian, &val) 138 case 32: 139 if val > math.MaxUint32 { 140 return fmt.Errorf("%x:%w", val, strconv.ErrRange) 141 } 142 v := uint32(val) 143 err = binary.Write(w, binary.LittleEndian, &v) 144 case 16: 145 if val > math.MaxUint16 { 146 return fmt.Errorf("%x:%w", val, strconv.ErrRange) 147 } 148 v := uint16(val) 149 err = binary.Write(w, binary.LittleEndian, &v) 150 case 8: 151 if val > math.MaxUint8 { 152 return fmt.Errorf("%x:%w", val, strconv.ErrRange) 153 } 154 v := uint8(val) 155 err = binary.Write(w, binary.LittleEndian, &v) 156 } 157 return err 158 } 159 160 // Read implements the BusReader interface for type bus. Iterating over each 161 // PCI bus device, and applying optional Filters to it. 162 func (bus *bus) Read(filters ...Filter) (Devices, error) { 163 devices := make(Devices, 0, len(bus.Devices)) 164 iter: 165 for _, d := range bus.Devices { 166 p, err := OnePCI(d) 167 if err != nil { 168 return nil, err 169 } 170 for _, f := range filters { 171 if !f(p) { 172 continue iter 173 } 174 } 175 // In the older versions of this package, reading was conditional. 176 // There is no harm done, and little performance lost, in just reading it. 177 // It's less than a millisecond. 178 // In all cases, the first 64 bits are visible, so setting vendor 179 // and device names is also no problem. If we can't read any bytes 180 // at all, that indicates a problem and it's worth passing that problem 181 // up to higher levels. 182 if err := p.ReadConfig(); err != nil { 183 return nil, err 184 } 185 p.SetVendorDeviceName() 186 187 c := p.Config 188 // Fill in whatever random stuff we can, from the base config. 189 p.Latency = c[LatencyTimer] 190 if c[HeaderType]&HeaderTypeMask == HeaderTypeBridge { 191 p.Bridge = true 192 } 193 p.IRQPin = c[IRQPin] 194 p.Primary = c[Primary] 195 p.Secondary = c[Secondary] 196 p.Subordinate = c[Subordinate] 197 p.SecLatency = fmt.Sprintf("%02x", c[SecondaryLatency]) 198 199 devices = append(devices, p) 200 } 201 return devices, nil 202 }