SMBIOS定制开发 - 第4节

4.1 Type 0 结构总览与字段布局

SMBIOS Type 0 是系统固件最核心的结构化描述块,它向操作系统和系统管理软件报告 BIOS 的基本身份、版本、发布日期以及一组标准化的特性位图。Type 0 结构在内存中的布局遵循 SMBIOS 2.x/3.x 规范,其头部固定为 4 字节的 Type/Length/Handle,随后是变长的字段区域。

偏移量字段名称长度(字节)说明
00hType1固定为 0x00
01hLength1结构总长度(含头部)
02hHandle2唯一标识符
04hVendor1指向字符串表中厂商名称的索引
05hBIOS Version1指向字符串表中版本号的索引
06hBIOS Starting Address Segment2BIOS 入口段地址(通常为 0xF000)
08hBIOS Release Date1指向字符串表中发布日期的索引
09hBIOS ROM Size1以 64KB 为单位的 ROM 大小
0AhBIOS Characteristics864 位特性位图
12hBIOS Characteristics Extension Bytes2扩展特性字节(可选)
14hSystem BIOS Major Release1主版本号(可选)
15hSystem BIOS Minor Release1次版本号(可选)
16hEmbedded Controller Firmware Major Release1EC 固件主版本(可选)
17hEmbedded Controller Firmware Minor Release1EC 固件次版本(可选)
定制提示: 在定制固件时,Vendor、Version、Release Date 三个字符串索引必须指向字符串表中正确的位置。如果索引为 0,表示该字段为空字符串,部分操作系统管理工具会因此无法正确识别 BIOS 信息。

4.2 核心字段详解

4.2.1 Vendor(厂商名称)

Vendor 字段是一个单字节索引,指向 SMBIOS 结构末尾的字符串表。该字符串应包含 BIOS 供应商的完整名称,例如 "American Megatrends Inc."、"Phoenix Technologies Ltd." 或 "Insyde Software Corp."。在定制开发中,如果基于开源固件(如 coreboot、TianoCore)构建,建议将 Vendor 设置为项目名称或公司名称。

4.2.2 BIOS Version(版本号)

BIOS Version 字段同样为字符串索引,其内容通常遵循厂商内部的版本命名规则,例如 "5.12"、"F.20"、"1.0.0.8" 或 "R1.2.3"。该版本号会被操作系统(如 Windows 系统信息、Linux dmidecode)直接读取并显示给用户。定制时,版本号应反映固件构建的迭代信息,建议采用 主版本.次版本.构建号 的格式。

4.2.3 BIOS Release Date(发布日期)

Release Date 字段的字符串格式必须为 mm/dd/yyyy(例如 "01/15/2025")。这是 SMBIOS 规范强制要求的格式,不符合此格式的日期字符串可能导致部分管理软件解析失败。在定制场景中,该日期通常与固件构建脚本的构建时间戳绑定,实现自动化更新。

4.2.4 BIOS Characteristics(64 位特性位图)

Characteristics 字段是 Type 0 中信息密度最高的部分,它使用 64 个比特位(8 字节)来描述 BIOS 所支持的功能特性。每个比特位对应一个特定的能力标志。下表列出了最常用的特性位及其含义:

位索引特性名称说明
0BIOS Characteristics Not Supported该位必须为 0,表示支持特性位图
1ISA Supported支持 ISA 总线
2MCA Supported支持微通道架构
3EISA Supported支持 EISA 总线
4PCI Supported支持 PCI 总线(现代系统必须置 1)
5PC Card (PCMCIA) Supported支持 PC 卡
6Plug and Play Supported支持即插即用
7APM Supported支持高级电源管理
8BIOS Upgradable (Flash)BIOS 可闪存升级
9BIOS Shadowing Allowed允许 BIOS 影子内存
10VLB Supported支持 VESA 局部总线
11ESCD Supported支持扩展系统配置数据
12Boot from CD Supported支持从 CD-ROM 启动
13Selectable Boot Supported支持可选择的启动设备
14BIOS ROM Is SocketedBIOS ROM 为插槽式
15Boot from PC Card (PCMCIA) Supported支持从 PC 卡启动
16EDD Supported支持增强型磁盘驱动规范
17Int 13h - Japanese Floppy Supported支持日本软盘规格
18Int 13h - Japanese Floppy (Toshiba) Supported支持东芝软盘规格
19Int 13h - 5.25"/360KB Floppy Supported支持 5.25 英寸 360KB 软盘
20Int 13h - 5.25"/1.2MB Floppy Supported支持 5.25 英寸 1.2MB 软盘
21Int 13h - 3.5"/720KB Floppy Supported支持 3.5 英寸 720KB 软盘
22Int 13h - 3.5"/2.88MB Floppy Supported支持 3.5 英寸 2.88MB 软盘
23Int 13h - Print Screen Supported支持打印屏幕功能
24Int 13h - 8042 Keyboard Services Supported支持 8042 键盘服务
25Int 13h - Serial Services Supported支持串口服务
26Int 13h - Printer Services Supported支持打印机服务
27Int 13h - CGA/Mono Video Services Supported支持 CGA/单色视频服务
28NEC PC-98 Services Supported支持 NEC PC-98 服务
29-31Reserved保留位,必须为 0
定制警告: 在设置 Characteristics 位图时,不要随意将保留位(29-31)置 1,这可能导致 SMBIOS 解析器行为异常。对于现代 UEFI 系统,位 4(PCI Supported)、位 8(BIOS Upgradable)、位 12(Boot from CD)通常必须置 1。

4.3 定制场景与实战案例

4.3.1 场景一:基于 coreboot 的定制固件

在 coreboot 项目中,SMBIOS Type 0 的数据通常在 src/smbios/smbios.c 或设备树配置中定义。以下是一个典型的定制代码片段:

/* 在 coreboot 的 smbios.c 中设置 Type 0 字段 */
static int smbios_write_type0(unsigned long *current, int handle)
{
    struct smbios_type0 *t = (struct smbios_type0 *)*current;
    int len = sizeof(struct smbios_type0);

    memset(t, 0, len);
    t->type = SMBIOS_BIOS_INFORMATION;
    t->length = len;
    t->handle = handle;

    /* 设置字符串索引 */
    t->vendor = smbios_add_string("MyCompany Coreboot");
    t->bios_version = smbios_add_string("4.21-1234-gabcdef");
    t->bios_release_date = smbios_add_string("03/10/2025");

    /* 设置特性位图:PCI + Flash + Boot from CD + Selectable Boot */
    t->characteristics[0] = (1 << 4) | (1 << 8) | (1 << 12) | (1 << 13);

    /* 设置 BIOS 起始段地址 */
    t->bios_starting_address_segment = 0xF000;

    /* 设置 ROM 大小:1MB = 16 * 64KB */
    t->bios_rom_size = 16;

    *current += len;
    return len;
}

4.3.2 场景二:UEFI 固件中的 SMBIOS 动态更新

在 UEFI 环境中,可以通过 gEfiSmbiosProtocolGuid 在运行时动态修改 Type 0 字段。以下示例展示了如何在 DXE 驱动中更新 BIOS 版本号:

#include <Protocol/Smbios.h>

EFI_STATUS UpdateSmbiosType0Version(CHAR16 *NewVersion)
{
    EFI_STATUS                Status;
    EFI_SMBIOS_PROTOCOL       *Smbios;
    EFI_SMBIOS_HANDLE         SmbiosHandle;
    SMBIOS_TABLE_ENTRY_POINT  *EntryPoint;
    SMBIOS_STRUCTURE_POINTER  SmbiosStruct;

    Status = gBS->LocateProtocol(&gEfiSmbiosProtocolGuid, NULL, (VOID**)&Smbios);
    if (EFI_ERROR(Status)) return Status;

    /* 遍历所有 SMBIOS 结构,找到 Type 0 */
    SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
    while (TRUE) {
        Status = Smbios->GetNext(Smbios, &SmbiosHandle, NULL, &SmbiosStruct, NULL);
        if (EFI_ERROR(Status)) break;
        if (SmbiosStruct.Hdr->Type == 0) {
            /* 此处简化处理:实际需要重新分配字符串表并更新索引 */
            /* 真实场景中应调用 Smbios->Remove() 再 Add() */
            break;
        }
    }
    return EFI_SUCCESS;
}
关键要点: 在 UEFI 中更新 SMBIOS 时,不能直接修改已安装的结构,必须使用 Remove() 移除旧结构,再使用 Add() 添加新结构。字符串表也需要整体重建,因为字符串索引是相对于结构末尾的偏移量。

4.4 Type 0 字段间的依赖关系

下图展示了 Type 0 各字段之间的数据依赖与引用关系,帮助理解字符串索引与特性位图在整体结构中的位置:

SMBIOS Type 0 字段依赖关系图 Type 0 Header Vendor (索引) BIOS Version (索引) Release Date (索引) Characteristics (64-bit bitmap) 字符串表 [1] "MyCompany Coreboot" [2] "4.21-1234-gabcdef" [3] "03/10/2025" [4] "" (结束标记) 特性位示例 Bit 4: PCI Supported Bit 8: Flash Upgradable Extension Bytes

从上图可以清晰地看到:Vendor、Version、Release Date 三个字段通过索引值间接引用字符串表中的内容;Characteristics 位图独立描述功能特性;扩展字节则提供额外的版本信息。在定制时,修改字符串表内容后必须同步更新对应的索引值,否则会导致字符串错位。

4.5 定制开发中的常见陷阱与最佳实践

  • 字符串表必须以双 NULL 结尾: SMBIOS 字符串表在最后一个字符串之后必须紧跟一个额外的 NULL 字节(即连续两个 NULL),表示字符串表结束。如果遗漏,SMBIOS 解析器会继续读取后续结构数据作为字符串,导致乱码。
  • 日期格式严格性: 即使系统使用非美国英语区域,Release Date 也必须使用 mm/dd/yyyy 格式。部分 Linux 发行版的 dmidecode 工具会尝试解析其他格式,但 Windows 系统信息工具只认标准格式。
  • 特性位图与硬件能力匹配: 不要为了“看起来功能丰富”而随意设置特性位。例如,如果系统不支持 PCMCIA,却将位 5 置 1,可能导致操作系统加载错误的驱动程序或服务。
  • BIOS ROM Size 的计算: 该字段的单位是 64KB。例如,2MB 的 SPI Flash 应设置为 32(2 * 1024 / 64 = 32)。如果设置为 0,表示大小未知。
  • Handle 的唯一性: 每个 SMBIOS 结构的 Handle 必须在整个表中唯一。在动态添加 Type 0 时,建议使用 SMBIOS_HANDLE_PI_RESERVED 让协议自动分配,或维护一个全局计数器。
调试技巧: 在 Linux 下使用 dmidecode -t 0 可以快速验证 Type 0 的定制结果。如果输出显示乱码或字段缺失,首先检查字符串表是否以双 NULL 结尾,其次检查 Length 字段是否与实际结构大小一致。

4.6 扩展字节与版本字段的定制

SMBIOS 2.4 及更高版本引入了 BIOS Characteristics Extension Bytes(偏移 12h-13h)以及四个版本字段(偏移 14h-17h)。这些字段在定制时同样重要:

扩展字节位含义定制建议
Extension Byte 1, Bit 0ACPI Supported现代系统必须置 1
Extension Byte 1, Bit 1USB Legacy Supported如果支持 USB 键盘/鼠标,置 1
Extension Byte 1, Bit 2AGP Supported仅旧系统需要
Extension Byte 1, Bit 3I2O Boot Supported通常置 0
Extension Byte 1, Bit 4LS-120 SuperDisk Boot通常置 0
Extension Byte 1, Bit 5ATAPI ZIP Drive Boot通常置 0
Extension Byte 1, Bit 61394 Boot Supported通常置 0
Extension Byte 1, Bit 7Smart Battery Supported如果支持智能电池,置 1
Extension Byte 2, Bit 0BIOS Boot Specification Supported通常置 1
Extension Byte 2, Bit 1Function key-initiated Network Service Boot如果支持网络启动热键,置 1
Extension Byte 2, Bit 2-7Reserved必须为 0

版本字段(System BIOS Major/Minor Release、Embedded Controller Firmware Major/Minor Release)为可选字段,但如果存在,必须与 BIOS Version 字符串中的版本号保持一致。例如,如果 BIOS Version 字符串为 "5.12.345",则 Major Release 应为 5,Minor Release 应为 12。

定制检查清单: 在完成 Type 0 定制后,建议使用以下命令进行验证:
dmidecode -t 0 | grep -E "Vendor|Version|Release|Characteristics|ROM Size"
确保所有字段显示正确,且无乱码或空值。