pkg/smbios: add SMBIOS package
This adds a new SMBIOS package which contains common structures from
SMBIOS as well as corresponding parsers.
I originally explored an approach where I manually designed optimized Go
types for each structure, but that would have led to a huge amount of
code that reading a structure of this type would cause if done
literally. I also considered code generation, but if the generated types
are to be close to the manually-designed ones it would be an incredibly
complex piece of code as well.
Finally I went with a design based on reflection which is much more
compact than the first two and consists of plain Go code at the expense
some niceness in the types.
I called the current types SomeTypeRaw in case I want to come back later
introduce a small layer mapping the current structures into nicer ones.
But for our current purposes the raw ones are good enough already.
This has been tested against our deployment targets, but as the SMBIOS
data contains uniquely identifying information these small tests are not
part of this CL. Sadly I haven't found any public SMBIOS test-cases.
Change-Id: I55d746ada0801de456f2a0eb961821abd9d58fa2
Reviewed-on: https://review.monogon.dev/c/monogon/+/983
Tested-by: Jenkins CI
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/pkg/smbios/structures.go b/metropolis/pkg/smbios/structures.go
new file mode 100644
index 0000000..64ef523
--- /dev/null
+++ b/metropolis/pkg/smbios/structures.go
@@ -0,0 +1,177 @@
+package smbios
+
+import (
+ "time"
+)
+
+const (
+ structTypeBIOSInformation = 0
+ structTypeSystemInformation = 1
+ structTypeBaseboardInformation = 2
+ structTypeSystemSlot = 9
+ structTypeMemoryDevice = 17
+)
+
+// BIOSInformationRaw contains decoded data from the BIOS Information structure
+// (SMBIOS Type 0). See Table 6 in the specification for detailed documentation
+// about the individual fields. Note that structure versions 2.1 and 2.2 are
+// "invented" here as both characteristics extensions bytes were optional
+// between 2.0 and 2.4.
+type BIOSInformationRaw struct {
+ Handle uint16
+ StructureVersion Version
+ Vendor string
+ BIOSVersion string
+ BIOSStartingAddressSegment uint16
+ BIOSReleaseDate string
+ BIOSROMSize uint8
+ BIOSCharacteristics uint64
+ BIOSCharacteristicsExtensionByte1 uint8 `smbios_min_ver:"2.1"`
+ BIOSCharacteristicsExtensionByte2 uint8 `smbios_min_ver:"2.2"`
+ SystemBIOSMajorRelease uint8 `smbios_min_ver:"2.4"`
+ SystemBIOSMinorRelease uint8
+ EmbeddedControllerFirmwareMajorRelease uint8
+ EmbeddedControllerFirmwareMinorRelease uint8
+ ExtendedBIOSROMSize uint16 `smbios_min_ver:"3.1"`
+}
+
+// ROMSizeBytes returns the ROM size in bytes
+func (rb *BIOSInformationRaw) ROMSizeBytes() uint64 {
+ if rb.StructureVersion.AtLeast(3, 1) && rb.BIOSROMSize == 0xFF {
+ // Top 2 bits are SI prefix (starting at mega, i.e. 1024^2), lower 14
+ // are value. x*1024^n => x << log2(1024)*n => x << 10*n
+ return uint64(rb.ExtendedBIOSROMSize&0x3fff) << 10 * uint64(rb.ExtendedBIOSROMSize&0xc00+2)
+ } else {
+ // (n+1) * 64KiB
+ return (uint64(rb.BIOSROMSize) + 1) * (64 * 1024)
+ }
+}
+
+// ReleaseDate returns the release date of the BIOS as a time.Time value.
+func (rb *BIOSInformationRaw) ReleaseDate() (time.Time, error) {
+ return time.Parse("01/02/2006", rb.BIOSReleaseDate)
+}
+
+// SystemInformationRaw contains decoded data from the System Information
+// structure (SMBIOS Type 1). See Table 10 in the specification for detailed
+// documentation about the individual fields.
+type SystemInformationRaw struct {
+ Handle uint16
+ StructureVersion Version
+ Manufacturer string
+ ProductName string
+ Version string
+ SerialNumber string
+ UUID [16]byte `smbios_min_ver:"2.1"`
+ WakeupType uint8
+ SKUNumber string `smbios_min_ver:"2.4"`
+ Family string
+}
+
+// BaseboardInformationRaw contains decoded data from the BIOS Information
+// structure (SMBIOS Type 3). See Table 13 in the specification for detailed
+// documentation about the individual fields.
+type BaseboardInformationRaw struct {
+ Handle uint16
+ StructureVersion Version
+ Manufacturer string
+ Product string
+ Version string
+ SerialNumber string
+ AssetTag string `smbios_min_ver:"2.1"`
+ FeatureFlags uint8
+ LocationInChassis string
+ ChassisHandle uint16
+ BoardType uint8
+ NumberOfContainedObjectHandles uint8
+ ContainedObjectHandles []uint16 `smbios_repeat:"NumberOfContainedObjectHandles"`
+}
+
+// SystemSlotRaw contains decoded data from the System Slot structure
+// (SMBIOS Type 9). See Table 44 in the specification for detailed documentation
+// about the individual fields.
+type SystemSlotRaw struct {
+ Handle uint16
+ StructureVersion Version
+ SlotDesignation string
+ SlotType uint8
+ SlotDataBusWidth uint8
+ CurrentUsage uint8
+ SlotLength uint8
+ SlotID uint16
+ SlotCharacteristics1 uint8
+ SlotCharacteristics2 uint8 `smbios_min_ver:"2.1"`
+ SegmentGroupNumber uint16 `smbios_min_ver:"2.6"`
+ BusNumber uint8
+ DeviceFunctionNumber uint8
+ DataBusWidth uint8 `smbios_min_ver:"3.2"`
+ PeerGroupingCount uint8
+ PeerGroups []SystemSlotPeerRaw `smbios_repeat:"PeerGroupingCount"`
+ SlotInformation uint8 `smbios_min_ver:"3.4"`
+ SlotPhysicalWidth uint8
+ SlotPitch uint16
+ SlotHeight uint8 `smbios_min_ver:"3.5"`
+}
+
+type SystemSlotPeerRaw struct {
+ SegmentGroupNumber uint16
+ BusNumber uint8
+ DeviceFunctionNumber uint8
+ DataBusWidth uint8
+}
+
+// MemoryDeviceRaw contains decoded data from the BIOS Information structure
+// (SMBIOS Type 17). See Table 76 in the specification for detailed
+// documentation about the individual fields.
+type MemoryDeviceRaw struct {
+ Handle uint16
+ StructureVersion Version
+ PhysicalMemoryArrayHandle uint16 `smbios_min_ver:"2.1"`
+ MemoryErrorInformationHandle uint16
+ TotalWidth uint16
+ DataWidth uint16
+ Size uint16
+ FormFactor uint8
+ DeviceSet uint8
+ DeviceLocator string
+ BankLocator string
+ MemoryType uint8
+ TypeDetail uint16
+ Speed uint16 `smbios_min_ver:"2.3"`
+ Manufacturer string
+ SerialNumber string
+ AssetTag string
+ PartNumber string
+ Attributes uint8 `smbios_min_ver:"2.6"`
+ ExtendedSize uint32 `smbios_min_ver:"2.7"`
+ ConfiguredMemorySpeed uint16
+ MinimumVoltage uint16 `smbios_min_ver:"2.8"`
+ MaximumVoltage uint16
+ ConfiguredVoltage uint16
+ MemoryTechnology uint8 `smbios_min_ver:"3.2"`
+ MemoryOperatingModeCapability uint16
+ FirmwareVersion uint8
+ ModuleManufacturerID uint16
+ ModuleProductID uint16
+ MemorySubsystemControllerManufacturerID uint16
+ MemorySubsystemControllerProductID uint16
+ NonVolatileSize uint64
+ VolatileSize uint64
+ CacheSize uint64
+ LogicalSize uint64
+ ExtendedSpeed uint32 `smbios_min_ver:"3.3"`
+ ExtendedConfiguredMemorySpeed uint32
+}
+
+func (md *MemoryDeviceRaw) SizeBytes() (uint64, bool) {
+ if md.Size == 0 || md.Size == 0xFFFF {
+ // Device unpopulated / unknown memory, return ok false
+ return 0, false
+ }
+ if md.Size == 0x7FFF && md.StructureVersion.AtLeast(2, 7) {
+ // Bit 31 is reserved, rest is memory size in MiB
+ return uint64(md.ExtendedSize&0x7FFFFFFF) * (1024 * 1024), true
+ }
+ // Bit 15 flips between KiB and MiB, rest is size
+ return uint64(md.Size&0x7FFF) << 10 * uint64(md.Size&0x8000+1), true
+}