diff --git a/audit/internal/collector/pcie.go b/audit/internal/collector/pcie.go index c1473c8..1e0b324 100644 --- a/audit/internal/collector/pcie.go +++ b/audit/internal/collector/pcie.go @@ -4,7 +4,9 @@ import ( "bee/audit/internal/schema" "fmt" "log/slog" + "os" "os/exec" + "path/filepath" "strconv" "strings" ) @@ -140,6 +142,9 @@ func parseLspciDevice(fields map[string]string) schema.HardwarePCIeDevice { } else if numaNode, ok := parsePCINumaNode(fields["NUMANode"]); ok { dev.NUMANode = &numaNode } + if group, ok := readPCIIOMMUGroup(bdf); ok { + dev.IOMMUGroup = &group + } if width, ok := readPCIIntAttribute(bdf, "current_link_width"); ok { dev.LinkWidth = &width } @@ -179,6 +184,21 @@ func parseLspciDevice(fields map[string]string) schema.HardwarePCIeDevice { return dev } +// readPCIIOMMUGroup resolves the IOMMU group number for a BDF via the +// iommu_group symlink in sysfs: .../devices//iommu_group -> .../kernel/iommu_groups/ +func readPCIIOMMUGroup(bdf string) (int, bool) { + link := "/sys/bus/pci/devices/" + bdf + "/iommu_group" + target, err := os.Readlink(link) + if err != nil { + return 0, false + } + n, err := strconv.Atoi(filepath.Base(target)) + if err != nil { + return 0, false + } + return n, true +} + // readPCIIDs reads vendor and device IDs from sysfs for a given BDF. func readPCIIDs(bdf string) (vendorID, deviceID int) { base := "/sys/bus/pci/devices/" + bdf diff --git a/audit/internal/schema/hardware.go b/audit/internal/schema/hardware.go index fa88f5e..18b80f7 100644 --- a/audit/internal/schema/hardware.go +++ b/audit/internal/schema/hardware.go @@ -211,6 +211,7 @@ type HardwarePCIeDevice struct { Firmware *string `json:"firmware,omitempty"` MacAddresses []string `json:"mac_addresses,omitempty"` Present *bool `json:"present,omitempty"` + IOMMUGroup *int `json:"iommu_group,omitempty"` Telemetry map[string]any `json:"-"` }