跳到主要内容

E300 SMBIOS 身份信息写入指南

概述

背景

E300 平台需要在生产阶段写入 SMBIOS/DMI 身份信息(产品型号、整机序列号、主板序列号、SoC 序列号等),以便操作系统和上层管理工具能够正确识别硬件。U-Boot 作为启动固件负责生成 SMBIOS 表并传递给操作系统。

需求

  • 支持工厂生产时通过脚本从 Linux 系统写入 SMBIOS 身份信息
  • 写入的数据需经过完整性校验(CRC32),防止 SPI flash 位翻转导致的数据损坏
  • 写入的信息在 U-Boot 启动时自动导入并覆盖 SMBIOS 表的默认值
  • 导入的 SMBIOS 变量不能被 saveenv 固化到普通 U-Boot 环境分区,避免重复存储
  • 不影响现有的 UEFI 变量写入方式(UEFI 分支保留原有逻辑)

适用范围

  • 硬件平台:E300 系列(aimodule)
  • 固件:U-Boot v2025.04@mt

总体架构

本方案分多层实现:

+---------------------------------------------------------------+
| Linux 工厂脚本 |
| ApolloDmiTools.sh |
| 生成 MSMB blob → 擦除 SPI flash → 写入 → 回读验证 |
+------------------------------+--------------------------------+
|
v
+------------------------------+--------------------------------+
| SPI Flash (0x787000, 4 KiB) |
| +-----------+------------------------------------------------|
| | MSMB Hdr | smbios.system.manufacturer=MOORETHREADS |
| | (32Byte) | smbios.system.product=E300 |
| | | ... |
| | | (double-NUL terminator) |
| +-----------+------------------------------------------------+
+------------------------------+--------------------------------+
| board_late_init() 阶段读取
v
+------------------------------+--------------------------------+
| U-Boot smbios_env 子系统 |
| smbios_env_import_from_flash() |
| → 校验 MSMB header (magic/version/CRC32) |
| → 解析 key=value payload |
| → 仅导入 smbios.* 前缀变量到 env (env_set) |
| → 跟踪已导入 key,防止 saveenv 固化 |
+------------------------------+--------------------------------+
| env_get("smbios.system.product")
v
+------------------------------+--------------------------------+
| U-Boot SMBIOS 表生成 (lib/smbios.c) |
| smbios_add_env_prop_si() |
| → 优先读取 env 变量 (smbios.*) |
| → fallback 到 device tree / sysinfo 属性 |
+---------------------------------------------------------------+

SMBIOS 环境变量覆盖机制

新增函数 smbios_add_env_prop_si()

static int smbios_add_env_prop_si(struct smbios_ctx *ctx, const char *env_name,
const char *prop, int sysinfo_id,
const char *dval)
{
const char *env_val;

env_val = env_get(env_name);
if (env_val && *env_val)
return smbios_add_string(ctx, env_val);

return smbios_add_prop_si(ctx, prop, sysinfo_id, dval);
}

优先级链:环境变量 smbios.* → device tree 属性 → sysinfo → 默认值

覆盖的 SMBIOS 字段

SMBIOS Type字段环境变量名默认值
Type 1 (System)Manufacturersmbios.system.manufacturer"AIMODULE"
Type 1 (System)Product Namesmbios.system.product"AIMODULE"
Type 1 (System)Versionsmbios.system.versionNULL
Type 1 (System)Serial Numbersmbios.system.serial (fallback: serial#)
Type 2 (Baseboard)Manufacturersmbios.board.manufacturer"AIMODULE"
Type 2 (Baseboard)Productsmbios.board.productNULL
Type 2 (Baseboard)Versionsmbios.board.versionNULL
Type 2 (Baseboard)Serialsmbios.board.serialNULL
Type 3 (Chassis)Manufacturersmbios.chassis.manufacturer"AIMODULE"
Type 4 (Processor)Serial Numbersmbios.processor.serialNULL

UUID 生成

从序列号通过 MD5 生成 UUID(RFC 4122 第 3 版命名空间 UUID):

unsigned char digest[16];

md5_wd((const unsigned char *)serial_str, strlen(serial_str),
digest, MD5_DEF_CHUNK_SZ);
digest[6] = (digest[6] & 0x0f) | 0x30; /* UUID version 3 */
digest[8] = (digest[8] & 0x3f) | 0x80; /* UUID variant DCE */
memcpy(t->uuid, digest, sizeof(t->uuid));

同一序列号每次启动生成相同的 UUID,确保可追溯性。

默认环境固化

为支持 env 覆盖机制,board_late_init() 中增加 save_default_env_if_needed(),首次启动时将默认环境写入 SPI flash。

static void save_default_env_if_needed(void)
{
if (!(gd->flags & GD_FLG_ENV_DEFAULT))
return;
ret = env_save();
}

SMBIOS 环境 Blob 子系统

SPI Flash 布局调整

原布局:

0x780000 +------------------+
| U-Boot env | 32 KiB (0x8000)
| |
0x788000 +------------------+

新布局:

0x780000 +------------------+
| U-Boot env | 28 KiB (0x7000)
| |
0x787000 +------------------+
| SMBIOS blob | 4 KiB (0x1000)
0x788000 +------------------+

对应 Kconfig 变更:

配置项原值新值
CONFIG_ENV_SIZE0x80000x7000
CONFIG_SMBIOS_ENV_BLOB_OFFSET0x787000
CONFIG_SMBIOS_ENV_BLOB_SIZE0x1000

MSMB Blob 二进制格式

Blob 总大小 4 KiB,由 Header (32 字节) + Payload + Padding (0xFF) 组成:

Offset Size Field
0x00 4 magic = 0x424d534d ("MSMB", LE)
0x04 2 version = 1
0x06 2 header_size = 32
0x08 4 payload_size = N
0x0C 4 payload_crc32 = crc32(payload)
0x10 16 reserved[4] = 0 (保留)
0x20 N payload = key=value (NUL分隔, 双NUL结尾)
0x20+N * padding = 0xFF ... 0xFF

对应 C 结构体:

struct smbios_env_hdr {
u32 magic; // 魔数 0x424d534d ("MSMB")
u16 version; // 版本号 (当前为 1)
u16 header_size; // 头部大小 (32 字节)
u32 payload_size; // payload 字节数
u32 payload_crc32; // payload 的 CRC32 校验值
u32 reserved[4]; // 保留字段
};

Payload 格式

Payload 采用 U-Boot 环境变量格式,每对 key=value 以 NUL 结尾,整体以双 NUL 结尾。只有 smbios.* 前缀的变量会被导入,其他前缀的条目会被静默忽略。

示例:

smbios.system.manufacturer=MOORETHREADS
smbios.system.product=E300-Server
smbios.system.version=V1.0
smbios.system.serial=SYS20260527001
smbios.board.manufacturer=MOORETHREADS
smbios.board.product=XS
smbios.board.version=V20
smbios.board.serial=BOARD20260527001
smbios.chassis.manufacturer=MOORETHREADS
smbios.processor.serial=SOC20260527001

(各条目以 NUL 分隔,整体以双 NUL 结尾)

校验流程

smbios_env_validate() 执行以下 8 项检查:

  1. blob 非空且大小至少为 32 字节
  2. magic == 0x424d534d ("MSMB")
  3. version == 1
  4. header_size == 32
  5. payload_size > 0
  6. header_size + payload_size 不溢出且不超过 CONFIG_SMBIOS_ENV_BLOB_SIZE
  7. payload 以双 NUL 结尾
  8. crc32(payload) == hdr->payload_crc32

全部通过返回 0,任一失败返回 -EINVAL

启动时导入流程

board_late_init() 阶段调用:

board_late_init()
→ smbios_env_import_from_flash()
→ spi_flash_probe() // 探测 SPI flash
→ spi_flash_read() // 读取 4 KiB
→ smbios_env_import()
→ smbios_env_validate() // 校验 header + CRC
→ smbios_env_forget_imported() // 清除上次导入记录
→ smbios_env_import_payload() // 逐个解析 key=value
→ smbios_env_name_is_allowed() // 只接受 smbios.* 前缀
→ env_set(name, value) // 设置环境变量
→ smbios_env_track_imported() // 记录已导入的 key

容错设计:如果 blob 校验失败(如首次启动时 SPI flash 该区域为全 0xFF),静默返回 0,不阻塞启动。

saveenv 过滤机制

为防止 smbios.* 变量被 saveenv 写入普通 env 区域,在 env_export() 中插入过滤:

// env/common.c: env_export()
if (IS_ENABLED(CONFIG_SMBIOS_ENV_BLOB))
smbios_env_export_prepare(); // 保存并移除 smbios.* 变量
len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
if (IS_ENABLED(CONFIG_SMBIOS_ENV_BLOB))
smbios_env_export_restore(); // 恢复 smbios.* 变量
  • export_prepare(): 遍历已跟踪的 key,用 strdup 保存当前值,再 env_set(name, NULL) 临时删除
  • export_restore(): 将保存的值恢复回 env,释放临时副本

U-Boot 写入命令

smbiosenv write <addr> <size>
  • <addr>: 内存中 blob 数据的物理地址
  • <size>: blob 字节数(必须 ≤ CONFIG_SMBIOS_ENV_BLOB_SIZE
  • 执行流程: map_sysmemsmbios_env_write_to_flash()unmap_sysmem

Kconfig 选项

声明 SMBIOS Blob 编译依赖的宏

config SMBIOS_ENV_BLOB
bool "SMBIOS environment blob support"
depends on ENV_SUPPORT && SMBIOS

config SMBIOS_ENV_BLOB_SPI_FLASH
bool "Read SMBIOS environment blob from SPI flash"
depends on SMBIOS_ENV_BLOB && ENV_IS_IN_SPI_FLASH && DM_SPI_FLASH

config SMBIOS_ENV_BLOB_OFFSET
hex "SMBIOS environment blob SPI flash offset"
depends on SMBIOS_ENV_BLOB_SPI_FLASH
default 0x787000 if ARCH_M1000

config SMBIOS_ENV_BLOB_SIZE
hex "SMBIOS environment blob size"
depends on SMBIOS_ENV_BLOB
default 0x1000

Linux 工厂脚本

脚本定位

ApolloDmiTools.sh 运行于 Linux 系统,通过 ac_tool 直接操作 SPI flash 设备。

非 UEFI 分支写入流程

1. 交互输入: 产品型号、整机SN、主板SN、SoC SN
2. Python 生成 MSMB blob:
- 构建 key=value payload (10 个字段)
- 计算 CRC32
- 填充 header (magic/version/header_size/payload_size/crc32)
- 填充 0xFF 至 4 KiB
3. ac_tool firmware -e 0x787000 -s 0x1000 flash (擦除)
4. ac_tool firmware -w 0x787000 -s 0x1000 -f blob flash (写入)
5. ac_tool firmware -r 0x787000 -s 0x1000 -f verify flash (回读)
6. cmp blob verify (验证)

UEFI 分支

保持不变,通过 /sys/firmware/efi/efivars/SmbiosData-{GUID} 写入。


文件清单

SMBIOS 环境变量覆盖

文件变更
lib/smbios.c新增 smbios_add_env_prop_si();修改 Type1/2/3/4 生成函数;MD5 生成 UUID
board/mthreads/E300_aimodule/E300_aimodule.c新增 save_default_env_if_needed()

SMBIOS 环境 Blob 子系统

文件变更
include/smbios_env.h新增:公开 API 和数据结构定义
lib/smbios_env.c新增:核心实现(validate/import/flash/export过滤)
lib/Kconfig新增:SMBIOS_ENV_BLOB 系列选项
lib/Makefile新增:smbios_env.o 编译目标
cmd/smbiosenv.c新增:smbiosenv write 命令
cmd/Kconfig新增:CMD_SMBIOSENV 选项
cmd/Makefile新增:smbiosenv.o 编译目标
env/common.c修改:env_export() 插入过滤逻辑
configs/E300_aimodule_defconfigENV_SIZE→0x7000;启用 blob 功能
board/mthreads/E300_aimodule/E300_aimodule.c新增 smbios_env_import_from_flash() 调用

数据流总结

工厂生产 终端用户启动
======== ============

Linux 脚本 U-Boot
│ │
│ 1. 输入产品信息 │
│ 2. 构建 MSMB blob │
│ 3. 写入 SPI flash │
│ (0x787000, 4 KiB) │
│ │
└────────────┬───────────────────┘

v
SPI Flash
+-------------+
| MSMB blob |
+-------------+

│ 4. board_late_init()
│ smbios_env_import_from_flash()
│ 校验 → 导入 smbios.* 变量

v
U-Boot env
+-------------+
| smbios.* |
+-------------+

│ 5. smbios_write_type1/2/3/4()
│ smbios_add_env_prop_si()
│ 优先读取 smbios.* env

v
SMBIOS Table
+-------------+
| Type 1/2/3/4 |
+-------------+

│ 6. 传递给 OS (Linux)

v
dmidecode 可见