Skip to content

Instantly share code, notes, and snippets.

@jborean93
Last active December 11, 2022 22:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jborean93/b8b7086213114da81183267119165ba6 to your computer and use it in GitHub Desktop.
Save jborean93/b8b7086213114da81183267119165ba6 to your computer and use it in GitHub Desktop.
Add-Type -TypeDefinition @'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace SMBIOS
{
public enum FirmwareProvider : uint
{
ACPI = 0x41435049,
FIRM = 0x4649524D,
RSMB = 0x52534D42
}
public static class Native
{
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern Int32 GetSystemFirmwareTable(
FirmwareProvider FirmwareTableProviderSignature,
int FirmwareTableID,
IntPtr pFirmwareTableBuffer,
int BufferSize);
public static byte[] GetSystemFirmwareTable(FirmwareProvider provider, int tableId)
{
int size = GetSystemFirmwareTable(provider, tableId, IntPtr.Zero, 0);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
GetSystemFirmwareTable(provider, tableId, buffer, size);
int res = Marshal.GetLastWin32Error();
if (res != 0)
{
throw new Win32Exception(res);
}
byte[] data = new byte[size];
Marshal.Copy(buffer, data, 0, data.Length);
return data;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
}
}
'@
$data = [SMBIOS.Native]::GetSystemFirmwareTable("RSMB", 0)
[System.Convert]::ToBase64String($data)
import base64
import dataclasses
import struct
import sys
from ruamel.yaml import YAML, yaml_object
def bytes_to_base64(self, data: bytes) -> str:
return self.represent_scalar("!b64", base64.b64encode(data).decode())
yaml = YAML()
yaml.representer.add_representer(bytes, bytes_to_base64)
@yaml_object(yaml)
@dataclasses.dataclass
class SMBIOSEntry:
entry_type: int
length: int
handle: int
data: bytes = dataclasses.field(repr=False)
string_table: list[str] = dataclasses.field(repr=False)
@yaml_object(yaml)
@dataclasses.dataclass
class SMBIOSProcessorInfo(SMBIOSEntry):
socket_designation: str | None
processor_type: int
family: int
manufacturer: str | None
processor_id: int
version: str | None
voltage: int
external_clock: int
max_speed: int
current_speed: int
status: int
processor_upgrade: int
l1_cache_handle: int
l2_cache_handle: int
l3_cache_handle: int
serial_number: str | None
asset_tag: str | None
part_number: str | None
core_count: int
core_enabled: int
thread_count: int
characteristics: int
family2: int
core_count2: int
core_enabled2: int
thread_count2: int
@yaml_object(yaml)
@dataclasses.dataclass
class RawSMBIOSData:
calling_method: int
major_version: int
minor_version: int
dmi_revision: int
length: int
data: list[SMBIOSEntry]
def extract_string_data(data: memoryview, string_table: list[str]) -> str | None:
idx = struct.unpack("B", data[:1])[0]
if idx == 0 or idx > len(string_table):
return None
return string_table[idx - 1]
def extract_raw_smbios_data(data: bytes) -> RawSMBIOSData:
view = memoryview(data)
major_version = struct.unpack("B", view[1:2])[0]
minor_version = struct.unpack("B", view[2:3])[0]
smbios_version = (major_version, minor_version)
length = struct.unpack("<I", view[4:8])[0]
data = view[8 : 8 + length]
if (data_len := len(data)) != length:
raise Exception(f"Expected data of length {length} but got {data_len}")
entries: list[SMBIOSEntry] = []
while data:
header_type = struct.unpack("B", data[:1])[0]
header_length = struct.unpack("B", data[1:2])[0]
header_handle = struct.unpack("<H", data[2:4])[0]
header_data = data[4:header_length]
raw_header_strings = data[header_length:]
end_header_strings = bytes(raw_header_strings).index(b"\x00\x00")
raw_header_strings = raw_header_strings[:end_header_strings]
header_strings = [b.decode("ascii") for b in bytes(raw_header_strings).split(b"\x00")]
data = data[header_length + end_header_strings + 2 :]
entry_kwargs = {
"entry_type": header_type,
"length": header_length,
"handle": header_handle,
"data": bytes(header_data),
"string_table": header_strings,
}
entry: SMBIOSEntry
if header_type == 4:
socket_designation = extract_string_data(header_data[:1], header_strings)
processor_type = struct.unpack("B", header_data[1:2])[0]
family = struct.unpack("B", header_data[2:3])[0]
manufacturer = extract_string_data(header_data[3:4], header_strings)
processor_id = struct.unpack("<Q", header_data[4:12])[0]
version = extract_string_data(header_data[12:13], header_strings)
voltage = struct.unpack("B", header_data[13:14])[0]
external_clock = struct.unpack("<H", header_data[14:16])[0]
max_speed = struct.unpack("<H", header_data[16:18])[0]
current_speed = struct.unpack("<H", header_data[18:20])[0]
status = struct.unpack("B", header_data[20:21])[0]
processor_upgrade = struct.unpack("B", header_data[21:22])[0]
l1_cache_handle = 0
l2_cache_handle = 0
l3_cache_handle = 0
if smbios_version >= (2, 1) and len(header_data) >= 28:
l1_cache_handle = struct.unpack("<H", header_data[22:24])[0]
l2_cache_handle = struct.unpack("<H", header_data[24:26])[0]
l3_cache_handle = struct.unpack("<H", header_data[26:28])[0]
serial_number = None
asset_tag = None
part_number = None
if smbios_version >= (2, 3) and len(header_data) >= 31:
serial_number = extract_string_data(header_data[28:29], header_strings)
asset_tag = extract_string_data(header_data[29:30], header_strings)
part_number = extract_string_data(header_data[30:31], header_strings)
core_count = 0
core_enabled = 0
thread_count = 0
characteristics = 0
if smbios_version >= (2, 5) and len(header_data) >= 36:
core_count = struct.unpack("B", header_data[31:32])[0]
core_enabled = struct.unpack("B", header_data[32:33])[0]
thread_count = struct.unpack("B", header_data[33:34])[0]
characteristics = struct.unpack("<H", header_data[34:36])[0]
family2 = 0
if smbios_version >= (2, 6) and len(header_data) >= 38:
family2 = struct.unpack("<H", header_data[36:38])[0]
core_count2 = 0
core_enabled2 = 0
thread_count2 = 0
if smbios_version >= (3, 0) and len(header_data) >= 44:
core_count2 = struct.unpack("<H", header_data[38:40])[0]
core_enabled2 = struct.unpack("<H", header_data[40:42])[0]
thread_count2 = struct.unpack("<H", header_data[42:44])[0]
entry = SMBIOSProcessorInfo(
socket_designation=socket_designation,
processor_type=processor_type,
family=family,
manufacturer=manufacturer,
processor_id=processor_id,
version=version,
voltage=voltage,
external_clock=external_clock,
max_speed=max_speed,
current_speed=current_speed,
status=status,
processor_upgrade=processor_upgrade,
l1_cache_handle=l1_cache_handle,
l2_cache_handle=l2_cache_handle,
l3_cache_handle=l3_cache_handle,
serial_number=serial_number,
asset_tag=asset_tag,
part_number=part_number,
core_count=core_count,
core_enabled=core_enabled,
thread_count=thread_count,
characteristics=characteristics,
family2=family2,
core_count2=core_count2,
core_enabled2=core_enabled2,
thread_count2=thread_count2,
**entry_kwargs,
)
else:
entry = SMBIOSEntry(**entry_kwargs)
entries.append(entry)
return RawSMBIOSData(
calling_method=struct.unpack("B", view[:1])[0],
major_version=major_version,
minor_version=minor_version,
dmi_revision=struct.unpack("B", view[3:4])[0],
length=length,
data=entries,
)
def main() -> None:
with open(sys.argv[1], mode="r") as fd:
data = base64.b64decode(fd.read())
smbios_data = extract_raw_smbios_data(data)
yaml.dump(smbios_data, sys.stdout)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment