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) |
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 位) |
_DMI_ 中间锚点,结构表地址扩展为 64 位,且不再直接提供结构体数量(需遍历计算)。UEFI 固件应优先使用 SMBIOS3_TABLE_GUID 获取此结构。
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 保留为无效值。
3.3 字符串区域机制
SMBIOS 的字符串区域紧跟在每个结构体的格式化区域之后。它采用了一种紧凑、索引化的存储方式,以节省空间并支持多语言。
3.3.1 字符串存储规则
- 连续存储:所有字符串以
\0结尾的 ASCII 字符串形式连续存放。 - 双空字节终止:整个字符串区域以两个连续的
\0字节(即空字符串)结束。 - 索引引用:格式化区域中的字符串字段不直接存储字符串内容,而是存储一个 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,则表示该字段未设置。
3.4 结构体遍历与解析流程
操作系统或工具软件遍历 SMBIOS 结构表的典型步骤如下:
- 定位入口点:通过 UEFI 配置表或 Legacy 内存扫描找到入口点结构。
- 验证校验和:计算入口点结构的校验和,确保数据未被破坏。
- 获取结构表地址:从入口点结构中读取结构表的物理基地址。
- 遍历结构体:从基地址开始,依次读取每个结构体:
- 读取 4 字节表头,获取 Type、Length、Handle。
- 跳过 Length 指定的格式化区域。
- 扫描字符串区域,直到遇到双空字节。
- 计算下一个结构体的起始地址:
当前地址 + Length + 字符串区域长度。
- 终止条件:当遇到 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 结构、修改系统信息)的必备基础。