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  }