Skip to content

Commit

Permalink
Merge pull request #2 from hertg/domain
Browse files Browse the repository at this point in the history
change pci domain to be 32bit, closes #1
  • Loading branch information
hertg authored Sep 24, 2022
2 parents 7c5c717 + f5f073a commit cdfd01a
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ assist in getting even more custom information.

If you are looking for a library to parse specifically PCI Devices,
you will get a **significant performance benefit** by using this library (see [comparison](#Comparison)). If you need
to parse more than just PCI information, consider using [jaypipes/ghw](https://github.com/jaypipes/ghw).
to parse more than just PCI information, you might also consider using [jaypipes/ghw](https://github.com/jaypipes/ghw).

## Usage

Expand Down Expand Up @@ -71,9 +71,9 @@ goarch: amd64
pkg: github.com/hertg/gopci/pkg/pci
cpu: AMD Ryzen 9 5950X 16-Core Processor
BenchmarkGoPci
BenchmarkGoPci-32 499 2426047 ns/op 267106 B/op 4355 allocs/op
BenchmarkGoPci-32 518 2554827 ns/op 297709 B/op 5178 allocs/op
BenchmarkGhw
BenchmarkGhw-32 38 33902755 ns/op 15745488 B/op 200981 allocs/op
BenchmarkGhw-32 34 33286659 ns/op 15745188 B/op 201189 allocs/op
PASS
ok github.com/hertg/gopci/pkg/pci 2.802s
```
Expand Down
52 changes: 34 additions & 18 deletions pkg/addr/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ import (
// Address Represents a PCI Address including
// domain, bus, device, and function.
type Address struct {
// Number The 32-bit PCI hardware address.
// This contains the domain (16 bits), bus (8 bits),
// Number The 32bit/48bit PCI hardware address.
// This contains the domain (16/32 bits), bus (8 bits),
// device (5 bits), and function (3 bits).
Number uint32
Number uint64
}

// Domain Get the domain as uint16
func (s *Address) Domain() uint16 {
return uint16(s.Number >> 16)
// Domain Get the domain as uint32
func (s *Address) Domain() uint32 {
return uint32(s.Number >> 16)
}

// DomainHex Get the domain as hexadecimal string.
// Example: '0000'
// Example: '0000', '10000', or 'deadbeef'
func (s *Address) DomainHex() string {
if s.Domain() > 65535 {
// if domain is larger than 0xffff, don't cap output at 4 hex digits
return fmt.Sprintf("%x", s.Domain())
}
return fmt.Sprintf("%04x", s.Domain())
}

Expand Down Expand Up @@ -59,47 +63,59 @@ func (s *Address) Function() uint8 {
// FunctionHex Get the function as hexadecimal string.
// Example: '1'
func (s *Address) FunctionHex() string {
return fmt.Sprintf("%x", s.Device())
return fmt.Sprintf("%x", s.Function())
}

// Hex Get the PCI Address in full human readable form.
// This includes the domain, bus, device, and function.
// Example: 0000:2f:00.1
// Example: 0000:2f:00.1, or 10000:2f:00.1
func (s *Address) Hex() string {
if s.Domain() > 65535 {
return fmt.Sprintf("%x:%02x:%02x.%x", s.Domain(), s.Bus(), s.Device(), s.Function())
}
return fmt.Sprintf("%04x:%02x:%02x.%x", s.Domain(), s.Bus(), s.Device(), s.Function())
}

// AddrFromHex Parse a human readable PCI address
// into an Address struct. Omitting the domain
// defaults to '0000'.
// Expected formats: '0000:2f:00.1' or '2f:00.1'
//
// Expected formats: '10000:2f:00.1', '0000:2f:00.1', or '2f:00.1'
// -> The domain is expected to be omitted OR 4-8 chars long
func AddrFromHex(addr string) (*Address, error) {
if len(addr) == 7 {
addr = "0000:" + addr
}
if len(addr) != 12 {
domainLength := len(addr) - 8
if domainLength < 4 || domainLength > 8 {
return nil, fmt.Errorf("unable to parse '%s' as pci address", addr)
}
domain, err := strconv.ParseUint(addr[:4], 16, 16)
domain, err := strconv.ParseUint(addr[:domainLength], 16, 32)
if err != nil {
return nil, fmt.Errorf("unable to parse pci domain address")
}
bus, err := strconv.ParseUint(addr[5:7], 16, 8)
addr = addr[domainLength+1:]
bus, err := strconv.ParseUint(addr[:2], 16, 8)
if err != nil {
return nil, fmt.Errorf("unable to parse pci bus address")
return nil, fmt.Errorf("unable to parse pci bus address '%s'", addr[:2])
}
device, err := strconv.ParseUint(addr[8:10], 16, 8)
addr = addr[3:]
device, err := strconv.ParseUint(addr[:2], 16, 8)
if err != nil {
return nil, fmt.Errorf("unable to parse pci device address")
return nil, fmt.Errorf("unable to parse pci device address '%s'", addr[:2])
}
if device > 31 {
return nil, fmt.Errorf("the device can not be a number larger than 0x1f")
}
function, _ := strconv.ParseUint(addr[11:12], 16, 8)
addr = addr[3:]
function, err := strconv.ParseUint(addr[:1], 16, 8)
if err != nil {
return nil, fmt.Errorf("unable to parse pci bus function '%s'", addr[3:])
}
if function > 7 {
return nil, fmt.Errorf("the function can not be a number larger than 0x7")
}
return &Address{
Number: uint32((domain << 16) | (bus << 8) | (device << 3) | function),
Number: uint64((domain << 16) | (bus << 8) | (device << 3) | function),
}, nil
}
32 changes: 28 additions & 4 deletions pkg/addr/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (

func TestAddressParseDomain(t *testing.T) {
a, _ := addr.AddrFromHex("0000:2f:00.0")
assert.Equal(t, uint16(0), a.Domain())
assert.Equal(t, uint32(0), a.Domain())

a, _ = addr.AddrFromHex("00ab:2f:00.0")
assert.Equal(t, uint16(171), a.Domain())
assert.Equal(t, uint32(171), a.Domain())
}

func TestAddressParseBus(t *testing.T) {
Expand Down Expand Up @@ -44,7 +44,7 @@ func TestAddressParseFunction(t *testing.T) {
a, _ = addr.AddrFromHex("0000:2f:00.3")
assert.Equal(t, uint8(3), a.Function())

// the max number for 3-bit is 0x7 (7)
// the max number for 3-bit is 0x7 (7)
// any number higher than that is impossible
// and the parser is expected to return an error
a, err := addr.AddrFromHex("0000:2f:00.8")
Expand All @@ -68,7 +68,31 @@ func TestAddressParseBogusString(t *testing.T) {
_, err = addr.AddrFromHex("xyzx:ab:ab.0")
assert.Error(t, err)

// the max function num is 7, there this address is invalid
// the max function num is 7, this address is invalid
_, err = addr.AddrFromHex("aaaa:ab:1f.8")
assert.Error(t, err)
}

func TestAddressParse32bitDomain(t *testing.T) {
a, err := addr.AddrFromHex("10000:2f:00.0")
assert.NoError(t, err)
assert.NotNil(t, a)
assert.Equal(t, "10000", a.DomainHex())

a, err = addr.AddrFromHex("deadbeef:2f:00.0")
assert.NoError(t, err)
assert.NotNil(t, a)
assert.Equal(t, "deadbeef", a.DomainHex())
}

func TestAddressExactlyFull16bitDomain(t *testing.T) {
a, err := addr.AddrFromHex("ffff:2f:00.0")
assert.NoError(t, err)
assert.NotNil(t, a)
assert.Equal(t, "ffff", a.DomainHex())
}

func TestAddressParseTooLongDomain(t *testing.T) {
_, err := addr.AddrFromHex("1ffffffff:2f:00.0")
assert.Error(t, err)
}

0 comments on commit cdfd01a

Please sign in to comment.