3. SMBIOS 数据结构:Entry Point Structure、结构表头与字符串区域

SMBIOS 固件接口的核心在于其标准化的数据结构布局。本章将深入解析 SMBIOS 的三大基础构件:入口点结构(Entry Point Structure)、结构表头(Structure Header)以及字符串区域(String Area)。理解这三者是进行 SMBIOS 定制开发、解析与注入的基石。

3.1 入口点结构(Entry Point Structure)

入口点结构是操作系统或应用程序定位 SMBIOS 数据表的“门牌号”。根据系统架构的不同,存在两种格式:32 位(SMBIOS 2.1 及更早)和64 位(SMBIOS 3.0 及更新)。BIOS/UEFI 固件必须至少提供其中一种,通常两种都会提供以保证兼容性。

3.1.1 32 位入口点结构(SMBIOS 2.x)

该结构通常位于物理内存的 0xF0000 到 0xFFFFF 区域,以 _SM_ 签名开头。其布局如下:

偏移量 长度(字节) 字段名 描述
00h 4 Anchor String 固定为 _SM_,用于扫描定位
04h 1 Entry Point Structure Checksum 整个结构(00h~0Fh)的校验和,要求所有字节和为 0
05h 1 Entry Point Length 固定为 0x1F(31 字节)
06h 1 Major Version SMBIOS 主版本号(如 2)
07h 1 Minor Version SMBIOS 次版本号(如 8)
08h 2 Maximum Structure Size 最大结构体的大小(字节),通常为 0x200(512 字节)
0Ah 1 Entry Point Revision 入口点修订版本,通常为 0x00
0Bh 5 Formatted Area 保留区域,通常为全零
10h 2 Intermediate Anchor String 固定为 _DMI_
12h 1 Intermediate Checksum 从 10h 到结尾的校验和
13h 2 Structure Table Length 所有 SMBIOS 结构表的总长度(字节)
15h 4 Structure Table Address SMBIOS 结构表的物理起始地址(32 位)
19h 2 Number of SMBIOS Structures 结构表中包含的结构体总数
1Bh 1 SMBIOS BCD Revision BCD 格式的版本号(如 0x28 表示 2.8)
开发提示:在 UEFI 环境下,32 位入口点通常通过 EfiGetSystemConfigurationTable 获取 SMBIOS_TABLE_GUID 来定位,而非直接扫描内存。但在 Legacy BIOS 中,扫描 0xF0000 区域查找 _SM_ 签名仍是标准做法。

3.1.2 64 位入口点结构(SMBIOS 3.x)

为了支持 64 位地址空间和更大的结构表,SMBIOS 3.0 引入了新的入口点结构,以 _SM3_ 签名开头:

偏移量 长度(字节) 字段名 描述
00h 5 Anchor String 固定为 _SM3_
05h 1 Entry Point Structure Checksum 整个结构(00h~0Bh)的校验和
06h 1 Entry Point Length 固定为 0x18(24 字节)
07h 1 Major Version SMBIOS 主版本号(如 3)
08h 1 Minor Version SMBIOS 次版本号(如 5)
09h 1 Doc Rev 文档修订版本
0Ah 1 Entry Point Revision 入口点修订版本,SMBIOS 3.x 通常为 0x01
0Bh 1 Reserved 保留字节,必须为 0
0Ch 8 Structure Table Maximum Size 结构表可能的最大大小(字节)
14h 8 Structure Table Address SMBIOS 结构表的物理起始地址(64 位)
关键差异:64 位入口点不再包含 _DMI_ 中间锚点,结构表地址扩展为 64 位,且不再直接提供结构体数量(需遍历计算)。UEFI 固件应优先使用 SMBIOS3_TABLE_GUID 获取此结构。
SMBIOS 入口点结构定位流程 SMBIOS 入口点结构定位流程 UEFI 环境 扫描 0xF0000 区域 查找 "_SM_" 或 "_SM3_" 获取 SMBIOS 表 GUID 解析入口点结构 访问 SMBIOS 结构表 或 最终 开发要点 • UEFI 优先使用 Config Table • Legacy BIOS 必须扫描内存 • 校验和验证不可省略

3.2 结构表头(Type/Length/Handle)

每个 SMBIOS 结构体都以一个固定的 4 字节表头开始。这个表头是所有结构体的通用前缀,定义了结构的类型、长度和唯一标识符。

偏移量 长度(字节) 字段名 描述
00h 1 Type 结构体类型编号(如 0 表示 BIOS Information,4 表示 Processor Information)
01h 1 Length 结构体格式化区域的长度(字节),不包含字符串区域
02h 2 Handle 结构体的唯一标识符(16 位),用于跨结构引用
常见误区:新手常误将 Length 字段理解为整个结构体(包括字符串区域)的总长度。实际上,Length 仅指从表头开始到格式化区域结束的字节数。字符串区域的结束由双空字节(\0\0)标记。

3.2.1 Type 字段详解

SMBIOS 规范定义了从 0 到 255 的结构类型。其中 0~127 为已定义类型,128~255 为OEM 自定义类型。以下是开发中最常用的几种类型:

  • Type 0 (BIOS Information):BIOS 厂商、版本、起始地址等信息。
  • Type 1 (System Information):制造商、产品名称、序列号、UUID。
  • Type 2 (Baseboard Information):主板型号、版本、资产标签。
  • Type 3 (System Enclosure):机箱类型、尺寸、锁扣状态。
  • Type 4 (Processor Information):CPU 型号、频率、核心数、插槽类型。
  • Type 17 (Memory Device):内存条大小、速度、类型、制造商。
  • Type 41 (Onboard Devices Extended Information):板载设备类型、描述、状态。

3.2.2 Handle 字段的作用

Handle 是一个 16 位的唯一标识符,用于在结构体之间建立关联。例如,Type 4(处理器)可以通过 Handle 被 Type 7(缓存)引用。Handle 值通常从 0x0000 开始递增分配,0xFFFF 保留为无效值。

定制开发场景:当您需要添加一个新的 SMBIOS 结构体(如自定义 OEM 类型)时,必须为其分配一个唯一的 Handle。如果 Handle 冲突,操作系统可能无法正确解析结构体间的引用关系,导致信息读取失败。

3.3 字符串区域机制

SMBIOS 的字符串区域紧跟在每个结构体的格式化区域之后。它采用了一种紧凑、索引化的存储方式,以节省空间并支持多语言。

3.3.1 字符串存储规则

  1. 连续存储:所有字符串以 \0 结尾的 ASCII 字符串形式连续存放。
  2. 双空字节终止:整个字符串区域以两个连续的 \0 字节(即空字符串)结束。
  3. 索引引用:格式化区域中的字符串字段不直接存储字符串内容,而是存储一个 1-based 索引。索引 1 指向字符串区域中的第一个字符串,索引 2 指向第二个,以此类推。索引 0 表示该字段为空(无字符串)。

3.3.2 字符串解析示例

假设一个 Type 1(System Information)结构体的格式化区域长度为 0x1B(27 字节),其后的字符串区域内容如下:

偏移量: 0x1B  0x1C  0x1D  0x1E  0x1F  0x20  0x21  0x22  0x23  0x24  0x25  0x26
数据:    'A'   'C'   'M'   'E'   '\0'  'S'   'N'   '1'   '2'   '3'   '\0'  '\0'
索引:     1     1     1     1           2     2     2     2     2

在这个例子中:

  • 索引 1 指向字符串 "ACME"(偏移 0x1B~0x1E)
  • 索引 2 指向字符串 "SN123"(偏移 0x20~0x24)
  • 偏移 0x25 和 0x26 的双空字节标记字符串区域结束

如果格式化区域中某个字段的值为 1,则它引用的是 "ACME";如果值为 2,则引用 "SN123";如果值为 0,则表示该字段未设置。

SMBIOS 结构体与字符串区域布局 SMBIOS 结构体内存布局 Type (1B) Length (1B) Handle (2B) 格式化区域 (Length - 4 字节) 字符串区域 (以双 \0 结束) 结构表头 (4 字节) 格式化区域 字符串区域 Str 1 Str 2 \0\0 索引 1 索引 2 字符串区域解析规则 1. 字符串连续存储,每个以 \0 结尾 2. 整个区域以双 \0 字节结束 3. 格式化区域中的字段值 = 1-based 索引(0 表示空)

3.4 结构体遍历与解析流程

操作系统或工具软件遍历 SMBIOS 结构表的典型步骤如下:

  1. 定位入口点:通过 UEFI 配置表或 Legacy 内存扫描找到入口点结构。
  2. 验证校验和:计算入口点结构的校验和,确保数据未被破坏。
  3. 获取结构表地址:从入口点结构中读取结构表的物理基地址。
  4. 遍历结构体:从基地址开始,依次读取每个结构体:
    • 读取 4 字节表头,获取 Type、Length、Handle。
    • 跳过 Length 指定的格式化区域。
    • 扫描字符串区域,直到遇到双空字节。
    • 计算下一个结构体的起始地址:当前地址 + Length + 字符串区域长度。
  5. 终止条件:当遇到 Type 为 127(End-of-Table)的结构体时,遍历结束。
代码片段:计算下一个结构体地址
// 伪代码:计算下一个 SMBIOS 结构体的起始地址
UINT8* GetNextStructure(UINT8* CurrentStruct) {
    SMBIOS_HEADER *Header = (SMBIOS_HEADER*)CurrentStruct;
    UINT8 *StringStart = CurrentStruct + Header->Length;
    UINT8 *NextStruct = StringStart;

    // 扫描字符串区域,直到找到双空字节
    while (*NextStruct != 0 || *(NextStruct + 1) != 0) {
        NextStruct++;
    }
    // 跳过双空字节,指向下一个结构体
    return NextStruct + 2;
}

3.5 本章小结

SMBIOS 的数据结构设计体现了紧凑性和可扩展性的平衡。入口点结构提供了访问入口,结构表头定义了类型与长度,而字符串区域机制则高效地管理了可变长度的文本信息。掌握这三者的内存布局与解析规则,是后续进行 SMBIOS 定制开发(如添加 OEM 结构、修改系统信息)的必备基础。