SMBIOS固件信息与平台开发工程 - 第2节
2.1 SMBIOS 3.x 与 2.x 核心差异
SMBIOS 从 2.x 演进到 3.x,主要解决了地址空间限制、Entry Point 结构复杂性和表数据对齐问题。下表从关键维度对比两个版本的技术差异:
| 对比维度 | SMBIOS 2.x | SMBIOS 3.x |
|---|---|---|
| Entry Point 结构 | 32 位物理地址,固定位于 0xF0000–0xFFFFF 之间 | 64 位物理地址,可位于整个系统内存空间 |
| Entry Point 签名 | _SM_ (4 字节) |
_SM3_ (4 字节) |
| 结构体表地址宽度 | 32 位指针,最大寻址 4 GB | 64 位指针,支持大内存系统 |
| 中间 Entry Point | 需要 _DMI_ 中间结构辅助定位 |
取消中间 Entry Point,直接指向结构体表 |
| 结构体表最大长度 | 受 32 位地址限制,通常 < 4 GB | 支持超过 4 GB 的表长度 |
| 校验和机制 | Entry Point 和中间 Entry Point 各有一个校验和 | 单个 Entry Point 校验和,简化验证 |
| UEFI 兼容性 | 需通过 BIOS 中断或 ACPI 表定位 | 原生支持 UEFI 运行时服务定位 |
开发提示: 新平台固件开发应优先支持 SMBIOS 3.x。如果必须兼容旧操作系统(如 Windows 7 或更早的 Linux 内核),则需同时提供 2.x Entry Point 以保证系统管理工具能正确枚举。
2.2 Entry Point 结构详解
2.2.1 32 位 Entry Point (SMBIOS 2.x)
32 位 Entry Point 位于物理内存 0xF0000–0xFFFFF 区域,以 16 字节对齐。其结构包含两个关键签名:_SM_ 用于主 Entry Point,_DMI_ 用于中间 Entry Point。
| 偏移 | 长度 (字节) | 字段 | 说明 |
|---|---|---|---|
| 0x00 | 4 | Anchor String |
固定为 _SM_ |
| 0x04 | 1 | Entry Point Structure Checksum |
整个 Entry Point 的校验和 |
| 0x05 | 1 | Entry Point Length |
固定为 0x1F (31 字节) |
| 0x06 | 1 | Major Version |
SMBIOS 主版本号 |
| 0x07 | 1 | Minor Version |
SMBIOS 次版本号 |
| 0x08 | 2 | Maximum Structure Size |
最大结构体大小 |
| 0x0A | 1 | Entry Point Revision |
修订版本 |
| 0x0B | 5 | Formatted Area |
保留区域,全零 |
| 0x10 | 4 | Intermediate Anchor String |
固定为 _DMI_ |
| 0x14 | 1 | Intermediate Checksum |
中间 Entry Point 校验和 |
| 0x15 | 2 | Structure Table Length |
结构体表总长度 |
| 0x17 | 4 | Structure Table Address |
结构体表 32 位物理地址 |
| 0x1B | 2 | Number of SMBIOS Structures |
结构体数量 |
| 0x1D | 1 | SMBIOS BCD Revision |
BCD 格式版本号 |
2.2.2 64 位 Entry Point (SMBIOS 3.x)
64 位 Entry Point 不再依赖 _DMI_ 中间结构,直接使用 64 位物理地址指向结构体表。其结构更紧凑,校验和计算更简单。
| 偏移 | 长度 (字节) | 字段 | 说明 |
|---|---|---|---|
| 0x00 | 4 | Anchor String |
固定为 _SM3_ |
| 0x04 | 1 | Entry Point Structure Checksum |
整个 Entry Point 的校验和 |
| 0x05 | 1 | Entry Point Length |
固定为 0x18 (24 字节) |
| 0x06 | 1 | Major Version |
SMBIOS 主版本号 |
| 0x07 | 1 | Minor Version |
SMBIOS 次版本号 |
| 0x08 | 1 | Doc Rev |
文档修订版本 |
| 0x09 | 1 | Entry Point Revision |
修订版本 |
| 0x0A | 1 | Reserved |
保留,全零 |
| 0x0B | 4 | Structure Table Maximum Size |
结构体表最大长度 |
| 0x0F | 8 | Structure Table Address |
结构体表 64 位物理地址 |
兼容性注意: 操作系统在扫描 SMBIOS 时,应优先查找
_SM3_ 签名。如果未找到,再回退到 _SM_ + _DMI_ 组合。UEFI 固件可以通过 EFI_SMBIOS_PROTOCOL (3.x) 或 EFI_SMBIOS_PROTOCOL (2.x) 直接获取 Entry Point 地址,无需手动扫描内存。
2.3 结构体表 (Structure Table) 布局
结构体表是 SMBIOS 数据的核心载体,由一系列连续的结构体组成,每个结构体以 Type 和 Length 字段开头,以双字节结束标记 0x00 0x00 结尾。结构体之间可以包含字符串区域。
2.3.1 结构体通用格式
| 偏移 | 长度 (字节) | 字段 | 说明 |
|---|---|---|---|
| 0x00 | 1 | Type |
结构体类型编号 (0–127 为规范定义,128–255 为 OEM 自定义) |
| 0x01 | 1 | Length |
结构体格式化区域长度 (不含字符串) |
| 0x02 | 2 | Handle |
唯一标识符,用于结构体间引用 |
| 0x04 | Length - 4 | Formatted Area |
类型特定的数据字段 |
| Length | 可变 | Strings |
以 0x00 分隔的字符串列表,最后以双 0x00 结束 |
2.3.2 结构体表终止条件
结构体表以类型为 127 的 End-of-Table 结构体结束。该结构体没有字符串区域,仅包含 Type=127、Length=4、Handle 和双字节结束标记。
// 结构体表遍历伪代码
uint8_t* current = smbios_table_address;
while (1) {
struct smbios_header* hdr = (struct smbios_header*)current;
if (hdr->type == 127) break; // End-of-Table
// 计算下一个结构体起始位置
uint8_t* next = current + hdr->length;
// 跳过字符串区域
while (*next != 0 || *(next+1) != 0) next++;
next += 2; // 跳过双 0x00 结束标记
current = next;
}
实践要点: 解析结构体表时,务必使用 Length 字段而非固定偏移来定位下一个结构体。字符串区域中的每个字符串以
0x00 结尾,整个字符串区域以两个连续的 0x00 结束。Handle 值在同一个表中必须唯一,但不同表之间可以重复。
2.4 关键开发注意事项
- Entry Point 扫描策略: 在 UEFI 环境下,优先通过
EFI_SMBIOS_PROTOCOL获取 Entry Point;在 Legacy BIOS 环境下,扫描 0xF0000–0xFFFFF 区域查找_SM3_或_SM_签名。 - 校验和验证: 2.x 需要验证主 Entry Point 和中间 Entry Point 两个校验和;3.x 只需验证一个。校验和计算方式为:从偏移 0 开始,所有字节累加后低 8 位应为 0。
- 地址转换: 3.x 的 Structure Table Address 是 64 位物理地址,在 32 位操作系统上解析时需注意地址截断问题。
- 字符串索引: 结构体格式化区域中的字符串字段通常存储为索引值 (1-based),指向字符串区域中的第 N 个字符串。索引 0 表示空字符串。
- OEM 自定义结构体: Type 128–255 留给 OEM 使用,解析器遇到未知 Type 时应跳过 Length 指定的字节数继续遍历,不应中断。
调试技巧: 在固件开发阶段,可以使用
dmidecode (Linux) 或 wmic bios (Windows) 验证 SMBIOS 数据是否正确。如果数据异常,优先检查 Entry Point 校验和与结构体表地址对齐。