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, &reg)
   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  }