mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-03 22:33:02 +02:00
board/common: basic support for smbios/dmi based systems in probe
This allows us to gather better product information in /run/system.json also for x86 based systems. For Qemu x86_64 systems the 'product-name' changes from 'VM' to 'Standard PC (i440FX + PIIX, 1996)'. Also, add new 'product-version' field for, e.g., board revisions, and add missing 'serial-number' value for systems like Rasperry Pi that inject it in the device tree. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -146,6 +146,87 @@ class DTSystem:
|
||||
flat_devices = [device for sublist in self.infix_devices("vpds") for device in sublist]
|
||||
return [self.into_vpd(device) for device in flat_devices]
|
||||
|
||||
def vendor_name(self):
|
||||
"""Extract vendor name from devicetree compatible property"""
|
||||
compatible = self.base.str_array("compatible")
|
||||
if not compatible or len(compatible) == 0:
|
||||
return None
|
||||
|
||||
# Map of common devicetree vendor prefixes to proper names
|
||||
vendor_map = {
|
||||
"raspberrypi": "Raspberry Pi Foundation",
|
||||
"brcm": "Broadcom Inc.",
|
||||
"marvell": "Marvell Technology, Inc.",
|
||||
"fsl": "NXP Semiconductors N.V.",
|
||||
"nxp": "NXP Semiconductors N.V.",
|
||||
"ti": "Texas Instruments Inc.",
|
||||
"qcom": "Qualcomm Inc.",
|
||||
"rockchip": "Rockchip Electronics Co., Ltd.",
|
||||
"amlogic": "Amlogic Inc.",
|
||||
"allwinner": "Allwinner Technology Co., Ltd.",
|
||||
"mediatek": "MediaTek Inc.",
|
||||
"st": "STMicroelectronics N.V.",
|
||||
"xlnx": "Xilinx, Inc.",
|
||||
"intel": "Intel Corporation",
|
||||
"amd": "Advanced Micro Devices, Inc.",
|
||||
"nvidia": "NVIDIA Corporation",
|
||||
"bananapi": "SinoVoip Co., Ltd.",
|
||||
"sinovoip": "SinoVoip Co., Ltd.",
|
||||
"friendlyarm": "FriendlyElec",
|
||||
"friendlyelec": "FriendlyElec",
|
||||
"microchip": "Microchip Technology Inc.",
|
||||
"atmel": "Microchip Technology Inc.",
|
||||
}
|
||||
|
||||
# Get the first (most specific) compatible string
|
||||
compat = compatible[0]
|
||||
|
||||
# Extract vendor prefix (part before comma)
|
||||
if ',' in compat:
|
||||
prefix = compat.split(',')[0].lower()
|
||||
# Return mapped name or capitalized prefix
|
||||
return vendor_map.get(prefix, prefix.capitalize())
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class DMISystem:
|
||||
BASE = "/sys/class/dmi/id"
|
||||
|
||||
def read_dmi(self, attr):
|
||||
"""Read DMI attribute from /sys/class/dmi/id/"""
|
||||
path = os.path.join(DMISystem.BASE, attr)
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
value = f.read().strip()
|
||||
return value if value else None
|
||||
except:
|
||||
return None
|
||||
|
||||
def populate(self, out):
|
||||
"""Read DMI/SMBIOS data and populate output dictionary"""
|
||||
vendor = self.read_dmi("sys_vendor")
|
||||
if vendor:
|
||||
out["vendor"] = vendor
|
||||
|
||||
product_name = self.read_dmi("product_name")
|
||||
if product_name:
|
||||
out["product-name"] = product_name
|
||||
|
||||
serial = self.read_dmi("product_serial")
|
||||
if serial:
|
||||
out["serial-number"] = serial
|
||||
|
||||
version = self.read_dmi("product_version")
|
||||
if version:
|
||||
out["product-version"] = version
|
||||
|
||||
def vpds(self):
|
||||
"""DMI systems don't have VPD in the traditional sense"""
|
||||
return []
|
||||
|
||||
|
||||
class QEMUSystem:
|
||||
BASE = "/sys/firmware/qemu_fw_cfg"
|
||||
@@ -355,6 +436,9 @@ def probe_qemusystem(out):
|
||||
if not out[attr]:
|
||||
out[attr] = default
|
||||
|
||||
if os.path.exists(DMISystem.BASE):
|
||||
DMISystem().populate(out)
|
||||
|
||||
if not out["factory-password-hash"] and \
|
||||
not out["vpd"]["product"]["available"]:
|
||||
# Virtual instance without VPD emulation, fallback to
|
||||
@@ -397,6 +481,21 @@ def generic_usb_ports(out):
|
||||
out["usb-ports"] = [{"name": f"USB{p['num']}", "path": p["path"]} for p in ports]
|
||||
|
||||
|
||||
def probe_dmisystem(out):
|
||||
"""Probe DMI/SMBIOS based system (x86/AMD64)"""
|
||||
dmisys = DMISystem()
|
||||
|
||||
dmisys.populate(out)
|
||||
generic_usb_ports(out)
|
||||
|
||||
if not out["mac-address"]:
|
||||
out["mac-address"] = fallback_base_mac()
|
||||
|
||||
vpd_inject(out, dmisys.vpds())
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def probe_dtsystem(out):
|
||||
"""Probe DTS based system, expects a VPD in ONIE PROM format."""
|
||||
dtsys = DTSystem()
|
||||
@@ -413,12 +512,24 @@ def probe_dtsystem(out):
|
||||
|
||||
out["compatible"] = dtsys.base.str_array("compatible")
|
||||
|
||||
# Extract vendor from compatible string if not already set
|
||||
if not out["vendor"]:
|
||||
vendor = dtsys.vendor_name()
|
||||
if vendor:
|
||||
out["vendor"] = vendor
|
||||
|
||||
staticpw = dtsys.infix.str("factory-password-hash")
|
||||
if not out["factory-password-hash"]:
|
||||
out["factory-password-hash"] = staticpw
|
||||
|
||||
vpd_inject(out, vpds)
|
||||
|
||||
# Fallback to devicetree serial-number if VPD doesn't provide one
|
||||
if not out["serial-number"]:
|
||||
serial = dtsys.base.str("serial-number")
|
||||
if serial:
|
||||
out["serial-number"] = serial
|
||||
|
||||
# Fallback to interface MAC if VPD doesn't provide one (e.g., SBCs)
|
||||
if not out["mac-address"]:
|
||||
out["mac-address"] = fallback_base_mac()
|
||||
@@ -430,6 +541,7 @@ def main():
|
||||
out = {
|
||||
"vendor": None,
|
||||
"product-name": None,
|
||||
"product-version": None,
|
||||
"part-number": None,
|
||||
"serial-number": None,
|
||||
"mac-address": None,
|
||||
@@ -440,6 +552,8 @@ def main():
|
||||
|
||||
if os.path.exists(QEMUSystem.REV):
|
||||
err = probe_qemusystem(out)
|
||||
elif os.path.exists(DMISystem.BASE):
|
||||
err = probe_dmisystem(out)
|
||||
elif os.path.exists(DTSystem.BASE):
|
||||
err = probe_dtsystem(out)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user