This repository has been archived by the owner on May 12, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 374
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
device: Introduce PciSlot and PciPath types
This is a dedicated data type for representing PCI paths, that is, PCI devices described by the slot numbers of the bridges we need to reach them. There are a number of places that uses strings with that structure for things. The plan is to use this data type to consolidate their handling. Signed-off-by: David Gibson <[email protected]>
- Loading branch information
Showing
2 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright (c) 2020 Red Hat | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
package types | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const ( | ||
pciSlotBits = 5 | ||
maxPciSlot = (1 << pciSlotBits) - 1 | ||
) | ||
|
||
// A PciSlot describes where a PCI device sits on a single bus | ||
// | ||
// This encapsulates the PCI slot number a.k.a device number, which is | ||
// limited to a 5 bit value [0x00..0x1f] by the PCI specification | ||
// | ||
// XXX In order to support multifunction device's we'll need to extend | ||
// this to include the PCI 3-bit function number as well. | ||
type PciSlot struct{ slot uint8 } | ||
|
||
func PciSlotFromString(s string) (PciSlot, error) { | ||
v, err := strconv.ParseUint(s, 16, pciSlotBits) | ||
if err != nil { | ||
return PciSlot{}, err | ||
} | ||
// The 5 bit width passed to ParseUint ensures the value is <= | ||
// maxPciSlot | ||
return PciSlot{slot: uint8(v)}, nil | ||
} | ||
|
||
func PciSlotFromInt(v int) (PciSlot, error) { | ||
if v < 0 || v > maxPciSlot { | ||
return PciSlot{}, fmt.Errorf("PCI slot value 0x%x out of range", v) | ||
} | ||
return PciSlot{slot: uint8(v)}, nil | ||
} | ||
|
||
func (slot PciSlot) String() string { | ||
return fmt.Sprintf("%02x", slot.slot) | ||
} | ||
|
||
// A PciPath describes where a PCI sits in a PCI hierarchy. | ||
// | ||
// Consists of a list of PCI slots, giving the slot of each bridge | ||
// that must be traversed from the PCI root to reach the device, | ||
// followed by the slot of the device itself | ||
// | ||
// When formatted into a string is written as "xx/.../yy/zz" Here, zz | ||
// is the slot of the device on its PCI bridge, yy is the slot of the | ||
// bridge on its parent bridge and so forth until xx is the slot of | ||
// the "most upstream" bridge on the root bus. If a device is | ||
// connected directly to the root bus, its PciPath is just "zz" | ||
type PciPath struct { | ||
slots []PciSlot | ||
} | ||
|
||
func (p PciPath) String() string { | ||
tokens := make([]string, len(p.slots)) | ||
for i, slot := range p.slots { | ||
tokens[i] = slot.String() | ||
} | ||
return strings.Join(tokens, "/") | ||
} | ||
|
||
func (p PciPath) IsNil() bool { | ||
return p.slots == nil | ||
} | ||
|
||
func PciPathFromString(s string) (PciPath, error) { | ||
if s == "" { | ||
return PciPath{}, nil | ||
} | ||
|
||
tokens := strings.Split(s, "/") | ||
slots := make([]PciSlot, len(tokens)) | ||
for i, t := range tokens { | ||
var err error | ||
slots[i], err = PciSlotFromString(t) | ||
if err != nil { | ||
return PciPath{}, err | ||
} | ||
} | ||
return PciPath{slots: slots}, nil | ||
} | ||
|
||
func PciPathFromSlots(slots ...PciSlot) (PciPath, error) { | ||
if len(slots) == 0 { | ||
return PciPath{}, fmt.Errorf("PCI path needs at least one component") | ||
} | ||
return PciPath{slots: slots}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright (c) 2020 Red Hat | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
package types | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPciSlot(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
// Valid slots | ||
slot, err := PciSlotFromInt(0x00) | ||
assert.NoError(err) | ||
assert.Equal(slot, PciSlot{}) | ||
assert.Equal(slot.String(), "00") | ||
|
||
slot, err = PciSlotFromString("00") | ||
assert.NoError(err) | ||
assert.Equal(slot, PciSlot{}) | ||
|
||
slot, err = PciSlotFromInt(31) | ||
assert.NoError(err) | ||
slot2, err := PciSlotFromString("1f") | ||
assert.NoError(err) | ||
assert.Equal(slot, slot2) | ||
|
||
// Bad slots | ||
_, err = PciSlotFromInt(-1) | ||
assert.Error(err) | ||
|
||
_, err = PciSlotFromInt(32) | ||
assert.Error(err) | ||
|
||
_, err = PciSlotFromString("20") | ||
assert.Error(err) | ||
|
||
_, err = PciSlotFromString("xy") | ||
assert.Error(err) | ||
|
||
_, err = PciSlotFromString("00/") | ||
assert.Error(err) | ||
|
||
_, err = PciSlotFromString("") | ||
assert.Error(err) | ||
} | ||
|
||
func TestPciPath(t *testing.T) { | ||
assert := assert.New(t) | ||
|
||
slot3, err := PciSlotFromInt(0x03) | ||
assert.NoError(err) | ||
slot4, err := PciSlotFromInt(0x04) | ||
assert.NoError(err) | ||
slot5, err := PciSlotFromInt(0x05) | ||
assert.NoError(err) | ||
|
||
// Empty/nil paths | ||
pcipath := PciPath{} | ||
assert.True(pcipath.IsNil()) | ||
|
||
pcipath, err = PciPathFromString("") | ||
assert.NoError(err) | ||
assert.True(pcipath.IsNil()) | ||
assert.Equal(pcipath, PciPath{}) | ||
|
||
// Valid paths | ||
pcipath, err = PciPathFromSlots(slot3) | ||
assert.NoError(err) | ||
assert.False(pcipath.IsNil()) | ||
assert.Equal(pcipath.String(), "03") | ||
pcipath2, err := PciPathFromString("03") | ||
assert.NoError(err) | ||
assert.Equal(pcipath, pcipath2) | ||
|
||
pcipath, err = PciPathFromSlots(slot3, slot4) | ||
assert.NoError(err) | ||
assert.False(pcipath.IsNil()) | ||
assert.Equal(pcipath.String(), "03/04") | ||
pcipath2, err = PciPathFromString("03/04") | ||
assert.NoError(err) | ||
assert.Equal(pcipath, pcipath2) | ||
|
||
pcipath, err = PciPathFromSlots(slot3, slot4, slot5) | ||
assert.NoError(err) | ||
assert.False(pcipath.IsNil()) | ||
assert.Equal(pcipath.String(), "03/04/05") | ||
pcipath2, err = PciPathFromString("03/04/05") | ||
assert.NoError(err) | ||
assert.Equal(pcipath, pcipath2) | ||
|
||
// Bad paths | ||
_, err = PciPathFromSlots() | ||
assert.Error(err) | ||
|
||
_, err = PciPathFromString("20") | ||
assert.Error(err) | ||
|
||
_, err = PciPathFromString("//") | ||
assert.Error(err) | ||
|
||
_, err = PciPathFromString("xyz") | ||
assert.Error(err) | ||
|
||
} |