Skip to content

Commit cdfd01a

Browse files
authored
Merge pull request #2 from hertg/domain
change pci domain to be 32bit, closes #1
2 parents 7c5c717 + f5f073a commit cdfd01a

File tree

3 files changed

+65
-25
lines changed

3 files changed

+65
-25
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ assist in getting even more custom information.
2323

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

2828
## Usage
2929

@@ -71,9 +71,9 @@ goarch: amd64
7171
pkg: github.com/hertg/gopci/pkg/pci
7272
cpu: AMD Ryzen 9 5950X 16-Core Processor
7373
BenchmarkGoPci
74-
BenchmarkGoPci-32 499 2426047 ns/op 267106 B/op 4355 allocs/op
74+
BenchmarkGoPci-32 518 2554827 ns/op 297709 B/op 5178 allocs/op
7575
BenchmarkGhw
76-
BenchmarkGhw-32 38 33902755 ns/op 15745488 B/op 200981 allocs/op
76+
BenchmarkGhw-32 34 33286659 ns/op 15745188 B/op 201189 allocs/op
7777
PASS
7878
ok github.com/hertg/gopci/pkg/pci 2.802s
7979
```

pkg/addr/address.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,24 @@ import (
88
// Address Represents a PCI Address including
99
// domain, bus, device, and function.
1010
type Address struct {
11-
// Number The 32-bit PCI hardware address.
12-
// This contains the domain (16 bits), bus (8 bits),
11+
// Number The 32bit/48bit PCI hardware address.
12+
// This contains the domain (16/32 bits), bus (8 bits),
1313
// device (5 bits), and function (3 bits).
14-
Number uint32
14+
Number uint64
1515
}
1616

17-
// Domain Get the domain as uint16
18-
func (s *Address) Domain() uint16 {
19-
return uint16(s.Number >> 16)
17+
// Domain Get the domain as uint32
18+
func (s *Address) Domain() uint32 {
19+
return uint32(s.Number >> 16)
2020
}
2121

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

@@ -59,47 +63,59 @@ func (s *Address) Function() uint8 {
5963
// FunctionHex Get the function as hexadecimal string.
6064
// Example: '1'
6165
func (s *Address) FunctionHex() string {
62-
return fmt.Sprintf("%x", s.Device())
66+
return fmt.Sprintf("%x", s.Function())
6367
}
6468

6569
// Hex Get the PCI Address in full human readable form.
6670
// This includes the domain, bus, device, and function.
67-
// Example: 0000:2f:00.1
71+
// Example: 0000:2f:00.1, or 10000:2f:00.1
6872
func (s *Address) Hex() string {
73+
if s.Domain() > 65535 {
74+
return fmt.Sprintf("%x:%02x:%02x.%x", s.Domain(), s.Bus(), s.Device(), s.Function())
75+
}
6976
return fmt.Sprintf("%04x:%02x:%02x.%x", s.Domain(), s.Bus(), s.Device(), s.Function())
7077
}
7178

7279
// AddrFromHex Parse a human readable PCI address
7380
// into an Address struct. Omitting the domain
7481
// defaults to '0000'.
75-
// Expected formats: '0000:2f:00.1' or '2f:00.1'
82+
//
83+
// Expected formats: '10000:2f:00.1', '0000:2f:00.1', or '2f:00.1'
84+
// -> The domain is expected to be omitted OR 4-8 chars long
7685
func AddrFromHex(addr string) (*Address, error) {
7786
if len(addr) == 7 {
7887
addr = "0000:" + addr
7988
}
80-
if len(addr) != 12 {
89+
domainLength := len(addr) - 8
90+
if domainLength < 4 || domainLength > 8 {
8191
return nil, fmt.Errorf("unable to parse '%s' as pci address", addr)
8292
}
83-
domain, err := strconv.ParseUint(addr[:4], 16, 16)
93+
domain, err := strconv.ParseUint(addr[:domainLength], 16, 32)
8494
if err != nil {
8595
return nil, fmt.Errorf("unable to parse pci domain address")
8696
}
87-
bus, err := strconv.ParseUint(addr[5:7], 16, 8)
97+
addr = addr[domainLength+1:]
98+
bus, err := strconv.ParseUint(addr[:2], 16, 8)
8899
if err != nil {
89-
return nil, fmt.Errorf("unable to parse pci bus address")
100+
return nil, fmt.Errorf("unable to parse pci bus address '%s'", addr[:2])
90101
}
91-
device, err := strconv.ParseUint(addr[8:10], 16, 8)
102+
addr = addr[3:]
103+
device, err := strconv.ParseUint(addr[:2], 16, 8)
92104
if err != nil {
93-
return nil, fmt.Errorf("unable to parse pci device address")
105+
return nil, fmt.Errorf("unable to parse pci device address '%s'", addr[:2])
94106
}
95107
if device > 31 {
96108
return nil, fmt.Errorf("the device can not be a number larger than 0x1f")
97109
}
98-
function, _ := strconv.ParseUint(addr[11:12], 16, 8)
110+
addr = addr[3:]
111+
function, err := strconv.ParseUint(addr[:1], 16, 8)
112+
if err != nil {
113+
return nil, fmt.Errorf("unable to parse pci bus function '%s'", addr[3:])
114+
}
99115
if function > 7 {
100116
return nil, fmt.Errorf("the function can not be a number larger than 0x7")
101117
}
102118
return &Address{
103-
Number: uint32((domain << 16) | (bus << 8) | (device << 3) | function),
119+
Number: uint64((domain << 16) | (bus << 8) | (device << 3) | function),
104120
}, nil
105121
}

pkg/addr/address_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99

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

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

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

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

71-
// the max function num is 7, there this address is invalid
71+
// the max function num is 7, this address is invalid
7272
_, err = addr.AddrFromHex("aaaa:ab:1f.8")
7373
assert.Error(t, err)
7474
}
75+
76+
func TestAddressParse32bitDomain(t *testing.T) {
77+
a, err := addr.AddrFromHex("10000:2f:00.0")
78+
assert.NoError(t, err)
79+
assert.NotNil(t, a)
80+
assert.Equal(t, "10000", a.DomainHex())
81+
82+
a, err = addr.AddrFromHex("deadbeef:2f:00.0")
83+
assert.NoError(t, err)
84+
assert.NotNil(t, a)
85+
assert.Equal(t, "deadbeef", a.DomainHex())
86+
}
87+
88+
func TestAddressExactlyFull16bitDomain(t *testing.T) {
89+
a, err := addr.AddrFromHex("ffff:2f:00.0")
90+
assert.NoError(t, err)
91+
assert.NotNil(t, a)
92+
assert.Equal(t, "ffff", a.DomainHex())
93+
}
94+
95+
func TestAddressParseTooLongDomain(t *testing.T) {
96+
_, err := addr.AddrFromHex("1ffffffff:2f:00.0")
97+
assert.Error(t, err)
98+
}

0 commit comments

Comments
 (0)