/* * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nvpci import ( "fmt" "os" "github.com/NVIDIA/go-nvlib/pkg/nvpci/bytes" ) const ( // PCICfgSpaceStandardSize represents the size in bytes of the standard config space. PCICfgSpaceStandardSize = 256 // PCICfgSpaceExtendedSize represents the size in bytes of the extended config space. PCICfgSpaceExtendedSize = 4096 // PCICapabilityListPointer represents offset for the capability list pointer. PCICapabilityListPointer = 0x34 // PCIStatusCapabilityList represents the status register bit which indicates capability list support. PCIStatusCapabilityList = 0x10 // PCIStatusBytePosition represents the position of the status register. PCIStatusBytePosition = 0x06 ) // ConfigSpace PCI configuration space (standard extended) file path. type ConfigSpace struct { Path string } // ConfigSpaceIO Interface for reading and writing raw and preconfigured values. type ConfigSpaceIO interface { bytes.Bytes GetVendorID() uint16 GetDeviceID() uint16 GetPCICapabilities() (*PCICapabilities, error) } type configSpaceIO struct { bytes.Bytes } // PCIStandardCapability standard PCI config space. type PCIStandardCapability struct { bytes.Bytes } // PCIExtendedCapability extended PCI config space. type PCIExtendedCapability struct { bytes.Bytes Version uint8 } // PCICapabilities combines the standard and extended config space. type PCICapabilities struct { Standard map[uint8]*PCIStandardCapability Extended map[uint16]*PCIExtendedCapability } func (cs *ConfigSpace) Read() (ConfigSpaceIO, error) { config, err := os.ReadFile(cs.Path) if err != nil { return nil, fmt.Errorf("failed to open file: %v", err) } return &configSpaceIO{bytes.New(&config)}, nil } func (cs *configSpaceIO) GetVendorID() uint16 { return cs.Read16(0) } func (cs *configSpaceIO) GetDeviceID() uint16 { return cs.Read16(2) } func (cs *configSpaceIO) GetPCICapabilities() (*PCICapabilities, error) { caps := &PCICapabilities{ make(map[uint8]*PCIStandardCapability), make(map[uint16]*PCIExtendedCapability), } support := cs.Read8(PCIStatusBytePosition) & PCIStatusCapabilityList if support == 0 { return nil, fmt.Errorf("pci device does not support capability list") } soffset := cs.Read8(PCICapabilityListPointer) if int(soffset) >= cs.Len() { return nil, fmt.Errorf("capability list pointer out of bounds") } for soffset != 0 { if soffset == 0xff { return nil, fmt.Errorf("config space broken") } if int(soffset) >= PCICfgSpaceStandardSize { return nil, fmt.Errorf("standard capability list pointer out of bounds") } data := cs.Read32(int(soffset)) id := uint8(data & 0xff) caps.Standard[id] = &PCIStandardCapability{ cs.Slice(int(soffset), cs.Len()-int(soffset)), } soffset = uint8((data >> 8) & 0xff) } if cs.Len() <= PCICfgSpaceStandardSize { return caps, nil } eoffset := uint16(PCICfgSpaceStandardSize) for eoffset != 0 { if eoffset == 0xffff { return nil, fmt.Errorf("config space broken") } if int(eoffset) >= PCICfgSpaceExtendedSize { return nil, fmt.Errorf("extended capability list pointer out of bounds") } // |31 20|19 16|15 0| // |--------------------|------|-------------------------| // | Next Cap Offset |Vers. |PCI Express Ext. Cap ID | data := cs.Read32(int(eoffset)) id := uint16(data & 0xffff) version := uint8((data >> 16) & 0xf) caps.Extended[id] = &PCIExtendedCapability{ cs.Slice(int(eoffset), cs.Len()-int(eoffset)), version, } eoffset = uint16((data >> 20) & 0xfff) } return caps, nil }