294 lines
7.7 KiB
Go
294 lines
7.7 KiB
Go
// Use and distribution licensed under the Apache license version 2.
|
|
//
|
|
// See the COPYING file in the root project directory for full text.
|
|
//
|
|
|
|
package ghw
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/jaypipes/pcidb"
|
|
)
|
|
|
|
func (ctx *context) pciFillInfo(info *PCIInfo) error {
|
|
db, err := pcidb.New(pcidb.WithChroot(ctx.chroot))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
info.Classes = db.Classes
|
|
info.Vendors = db.Vendors
|
|
info.Products = db.Products
|
|
info.ctx = ctx
|
|
return nil
|
|
}
|
|
|
|
func (ctx *context) getPCIDeviceModaliasPath(address string) string {
|
|
pciAddr := PCIAddressFromString(address)
|
|
if pciAddr == nil {
|
|
return ""
|
|
}
|
|
return filepath.Join(
|
|
ctx.pathSysBusPciDevices(),
|
|
pciAddr.Domain+":"+pciAddr.Bus+":"+pciAddr.Slot+"."+pciAddr.Function,
|
|
"modalias",
|
|
)
|
|
}
|
|
|
|
type deviceModaliasInfo struct {
|
|
vendorID string
|
|
productID string
|
|
subproductID string
|
|
subvendorID string
|
|
classID string
|
|
subclassID string
|
|
progIfaceID string
|
|
}
|
|
|
|
func parseModaliasFile(fp string) *deviceModaliasInfo {
|
|
if _, err := os.Stat(fp); err != nil {
|
|
return nil
|
|
}
|
|
data, err := ioutil.ReadFile(fp)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
// The modalias file is an encoded file that looks like this:
|
|
//
|
|
// $ cat /sys/devices/pci0000\:00/0000\:00\:03.0/0000\:03\:00.0/modalias
|
|
// pci:v000010DEd00001C82sv00001043sd00008613bc03sc00i00
|
|
//
|
|
// It is interpreted like so:
|
|
//
|
|
// pci: -- ignore
|
|
// v000010DE -- PCI vendor ID
|
|
// d00001C82 -- PCI device ID (the product/model ID)
|
|
// sv00001043 -- PCI subsystem vendor ID
|
|
// sd00008613 -- PCI subsystem device ID (subdevice product/model ID)
|
|
// bc03 -- PCI base class
|
|
// sc00 -- PCI subclass
|
|
// i00 -- programming interface
|
|
vendorID := strings.ToLower(string(data[9:13]))
|
|
productID := strings.ToLower(string(data[18:22]))
|
|
subvendorID := strings.ToLower(string(data[28:32]))
|
|
subproductID := strings.ToLower(string(data[38:42]))
|
|
classID := string(data[44:46])
|
|
subclassID := string(data[48:50])
|
|
progIfaceID := string(data[51:53])
|
|
return &deviceModaliasInfo{
|
|
vendorID: vendorID,
|
|
productID: productID,
|
|
subproductID: subproductID,
|
|
subvendorID: subvendorID,
|
|
classID: classID,
|
|
subclassID: subclassID,
|
|
progIfaceID: progIfaceID,
|
|
}
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.Vendor struct matching the supplied vendor
|
|
// ID string. If no such vendor ID string could be found, returns the
|
|
// pcidb.Vendor struct populated with "unknown" vendor Name attribute and
|
|
// empty Products attribute.
|
|
func findPCIVendor(info *PCIInfo, vendorID string) *pcidb.Vendor {
|
|
vendor := info.Vendors[vendorID]
|
|
if vendor == nil {
|
|
return &pcidb.Vendor{
|
|
ID: vendorID,
|
|
Name: UNKNOWN,
|
|
Products: []*pcidb.Product{},
|
|
}
|
|
}
|
|
return vendor
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.Product struct matching the supplied vendor
|
|
// and product ID strings. If no such product could be found, returns the
|
|
// pcidb.Product struct populated with "unknown" product Name attribute and
|
|
// empty Subsystems attribute.
|
|
func findPCIProduct(
|
|
info *PCIInfo,
|
|
vendorID string,
|
|
productID string,
|
|
) *pcidb.Product {
|
|
product := info.Products[vendorID+productID]
|
|
if product == nil {
|
|
return &pcidb.Product{
|
|
ID: productID,
|
|
Name: UNKNOWN,
|
|
Subsystems: []*pcidb.Product{},
|
|
}
|
|
}
|
|
return product
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.Product struct matching the supplied vendor,
|
|
// product, subvendor and subproduct ID strings. If no such product could be
|
|
// found, returns the pcidb.Product struct populated with "unknown" product
|
|
// Name attribute and empty Subsystems attribute.
|
|
func findPCISubsystem(
|
|
info *PCIInfo,
|
|
vendorID string,
|
|
productID string,
|
|
subvendorID string,
|
|
subproductID string,
|
|
) *pcidb.Product {
|
|
product := info.Products[vendorID+productID]
|
|
subvendor := info.Vendors[subvendorID]
|
|
if subvendor != nil && product != nil {
|
|
for _, p := range product.Subsystems {
|
|
if p.ID == subproductID {
|
|
return p
|
|
}
|
|
}
|
|
}
|
|
return &pcidb.Product{
|
|
VendorID: subvendorID,
|
|
ID: subproductID,
|
|
Name: UNKNOWN,
|
|
}
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.Class struct matching the supplied class ID
|
|
// string. If no such class ID string could be found, returns the
|
|
// pcidb.Class struct populated with "unknown" class Name attribute and
|
|
// empty Subclasses attribute.
|
|
func findPCIClass(info *PCIInfo, classID string) *pcidb.Class {
|
|
class := info.Classes[classID]
|
|
if class == nil {
|
|
return &pcidb.Class{
|
|
ID: classID,
|
|
Name: UNKNOWN,
|
|
Subclasses: []*pcidb.Subclass{},
|
|
}
|
|
}
|
|
return class
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.Subclass struct matching the supplied class
|
|
// and subclass ID strings. If no such subclass could be found, returns the
|
|
// pcidb.Subclass struct populated with "unknown" subclass Name attribute
|
|
// and empty ProgrammingInterfaces attribute.
|
|
func findPCISubclass(
|
|
info *PCIInfo,
|
|
classID string,
|
|
subclassID string,
|
|
) *pcidb.Subclass {
|
|
class := info.Classes[classID]
|
|
if class != nil {
|
|
for _, sc := range class.Subclasses {
|
|
if sc.ID == subclassID {
|
|
return sc
|
|
}
|
|
}
|
|
}
|
|
return &pcidb.Subclass{
|
|
ID: subclassID,
|
|
Name: UNKNOWN,
|
|
ProgrammingInterfaces: []*pcidb.ProgrammingInterface{},
|
|
}
|
|
}
|
|
|
|
// Returns a pointer to a pcidb.ProgrammingInterface struct matching the
|
|
// supplied class, subclass and programming interface ID strings. If no such
|
|
// programming interface could be found, returns the
|
|
// pcidb.ProgrammingInterface struct populated with "unknown" Name attribute
|
|
func findPCIProgrammingInterface(
|
|
info *PCIInfo,
|
|
classID string,
|
|
subclassID string,
|
|
progIfaceID string,
|
|
) *pcidb.ProgrammingInterface {
|
|
subclass := findPCISubclass(info, classID, subclassID)
|
|
for _, pi := range subclass.ProgrammingInterfaces {
|
|
if pi.ID == progIfaceID {
|
|
return pi
|
|
}
|
|
}
|
|
return &pcidb.ProgrammingInterface{
|
|
ID: progIfaceID,
|
|
Name: UNKNOWN,
|
|
}
|
|
}
|
|
|
|
// GetDevice returns a pointer to a PCIDevice struct that describes the PCI
|
|
// device at the requested address. If no such device could be found, returns
|
|
// nil
|
|
func (info *PCIInfo) GetDevice(address string) *PCIDevice {
|
|
fp := info.ctx.getPCIDeviceModaliasPath(address)
|
|
if fp == "" {
|
|
return nil
|
|
}
|
|
|
|
modaliasInfo := parseModaliasFile(fp)
|
|
if modaliasInfo == nil {
|
|
return nil
|
|
}
|
|
|
|
vendor := findPCIVendor(info, modaliasInfo.vendorID)
|
|
product := findPCIProduct(
|
|
info,
|
|
modaliasInfo.vendorID,
|
|
modaliasInfo.productID,
|
|
)
|
|
subsystem := findPCISubsystem(
|
|
info,
|
|
modaliasInfo.vendorID,
|
|
modaliasInfo.productID,
|
|
modaliasInfo.subvendorID,
|
|
modaliasInfo.subproductID,
|
|
)
|
|
class := findPCIClass(info, modaliasInfo.classID)
|
|
subclass := findPCISubclass(
|
|
info,
|
|
modaliasInfo.classID,
|
|
modaliasInfo.subclassID,
|
|
)
|
|
progIface := findPCIProgrammingInterface(
|
|
info,
|
|
modaliasInfo.classID,
|
|
modaliasInfo.subclassID,
|
|
modaliasInfo.progIfaceID,
|
|
)
|
|
|
|
return &PCIDevice{
|
|
Address: address,
|
|
Vendor: vendor,
|
|
Subsystem: subsystem,
|
|
Product: product,
|
|
Class: class,
|
|
Subclass: subclass,
|
|
ProgrammingInterface: progIface,
|
|
}
|
|
}
|
|
|
|
// ListDevices returns a list of pointers to PCIDevice structs present on the
|
|
// host system
|
|
func (info *PCIInfo) ListDevices() []*PCIDevice {
|
|
devs := make([]*PCIDevice, 0)
|
|
// We scan the /sys/bus/pci/devices directory which contains a collection
|
|
// of symlinks. The names of the symlinks are all the known PCI addresses
|
|
// for the host. For each address, we grab a *PCIDevice matching the
|
|
// address and append to the returned array.
|
|
links, err := ioutil.ReadDir(info.ctx.pathSysBusPciDevices())
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "error: failed to read /sys/bus/pci/devices")
|
|
return nil
|
|
}
|
|
var dev *PCIDevice
|
|
for _, link := range links {
|
|
addr := link.Name()
|
|
dev = info.GetDevice(addr)
|
|
if dev == nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "error: failed to get device information for PCI address %s\n", addr)
|
|
} else {
|
|
devs = append(devs, dev)
|
|
}
|
|
}
|
|
return devs
|
|
}
|