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 值在同一个表中必须唯一,但不同表之间可以重复。
SMBIOS 2.x 与 3.x Entry Point 结构对比 SMBIOS 2.x Entry Point (31 字节) _SM_ 校验和 版本 保留 + 格式化区 _DMI_ 中间校验 表长度 表地址 (32位) 需要中间结构 SMBIOS 3.x Entry Point (24 字节) _SM3_ 校验和 版本 保留 + 文档修订 表最大长度 表地址 (64位) 直接指向结构体表 3.x 取消中间 Entry Point,使用 64 位地址,结构更紧凑,校验更简单 SMBIOS 结构体表布局与遍历流程 结构体表 (Structure Table) Type 0 (BIOS Info) Length=24 Handle=0x0000 Type 1 (System Info) Length=27 Handle=0x0001 Type 2 (Baseboard) Length=18 Handle=0x0002 … Type 127 (End-of-Table) Length=4 字符串区 "American" "Megatrends" 遍历规则:从表起始地址开始,依次读取每个结构体的 Type 和 Length 跳过字符串区域 (双 0x00 结束),直到遇到 Type=127 停止 while (hdr->type != 127) { next = current + hdr->length; while (*next != 0 || *(next+1) != 0) next++; current = next + 2; }

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 校验和与结构体表地址对齐。