背景
之前采集设备硬件信息使用的是命令行工具: dmidecode, 所以需要提前确认环境是否安装了该工具,而且有些嵌入式的环境, 并不方便安装工具, 所以需要用一个无依赖的库来替换.
github找了一圈, 发现并没有现成的,但幸运的是有一个读取smbios信息的库: go-smbios smbios信息读取库, 基于这个功能 再参考SMBIOS Reference 6 Specification就能写解析库了.
在开始解析数据之前, 我们先了解一些关于SMBIOS的基本概念.
什么是SMBIOS
SMBIOS是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范, 由行业指导机构 Desktop Management Task Force (DMTF) 起草的开放性的技术标准.
他为上层系统, 比如DMI(Desktop Management Interface, DMI)提供了有关电脑系统和配件的信息.
因此我们要做的第一步就是: 读取SMBIOS信息.
如何读取SMBIOS信息
对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息, 共有两种办法可以访问:
- 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用
- 基于表结构的方法,表内容是 table entry point 的数据,这个访问方法从 SMBIOS 2.1 以后开始被使用,从 2.1 开始,以后的版本都推荐使用这种访问方式。在 2.1 版本中允许支持这两种方法中的任意一种和两种都支持,但在 2.2 以后的版本,必须支持方法 2 。在最新的 2.7.0 版中第一种方法已经废弃
鉴于市场上计算机已经均支持 SMBIOS2.3 标准,所以只考虑方法 2 ,基于表结构的访问方式。基于表结构访问 SMBIOS 的过程是先找到 Entry Point Structure ( EPS )表,然后通过 Entry Point Structure ( EPS )表的数据找到 SMBIOS 结构表
如何找到EPS表, 这里不做介绍, 具体请参考SMBIOS信息概述 -- DMI, 以下描述EPS表的数据结构
位置 | 名称 | 长度 | 描述 |
---|---|---|---|
00H | 关键字 | 4字节 | 固定是”SM” |
04H | 校验和 | 1字节 | 用于校验数据 |
05H | 表结构长度 | 1字节 | Entry Point Structure 表的长度 |
06H | Major 版本号 | 1字节 | 用于判断SMBIOS 版本 |
07H | Minor 版本号 | 1字节 | 用于判断SMBIOS 版本 |
08H | 表结构大小 | 2字节 | 用于即插即用接口方法获得数据表结构长度 |
0AH | EPS 修正 | 1字节 | - |
0B-0FH | 格式区域 | 5字节 | 存放解释EPS 修正的信息 |
10H | 关键字 | 5字节 | 固定为“DMI” |
15H | 校验和 | 1字节 | Intermediate Entry Point Structure (IEPS)的校验和 |
16H | 结构表长度 | 2字节 | SMBIOS 结构表的长度 |
18H | 结构表地址 | 4字节 | SMBIOS 结构表的真实内存位置 |
1CH | 结构表个数 | 2字节 | SMBIOS 结构表数目 |
1EH | Smbios BCD 修正 | 1字节 | - |
通过 EPS 表结构中 16H 以及 18H 处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表,以下描述SMBIOS数据结构表的格式
位置 | 名称 | 长度 | 描述 |
---|---|---|---|
00H | TYPE 号 | 1字节 | 结构的TYPE 号 |
01H | 长度 | 1字节 | 本结构的长度,就此TYPE 号的结构而言 |
02H | 句柄 | 2字节 | 用于获得本SMBIOS 结构,其值不定, 每个结构都分为格式区域和字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域 |
如此复杂, 谁能帮帮我?
对github上已经有人帮你完成了读取阶段的工作: go-smbios smbios信息读取库, 并且已经做好了操作系统的适配, 支持如下操作系统:
- DragonFlyBSD (/dev/mem)
- FreeBSD (/dev/mem)
- Linux (sysfs and /dev/mem)
- NetBSD (/dev/mem)
- OpenBSD (/dev/mem)
- Solaris (/dev/mem)
- Windows (GetSystemFirmwareTable)
到此我们已经找到了SMBIOS 结构表, 并且了解了其数据结构, 剩下的就是如何解析了
如何解析SMBIOS信息
SMBIOS 结构表中TYPE号和长度是比较好解析的, 直接读取就好, 但是句柄的内容需要根据TYPE号查看文档进行解析, 这里说的解析 也主要指的是句柄(Handle)内容的解析
下面已TYPE0为列, 看看的他的数据结构:
位置 | 名称 | 长度 | 描述 |
---|---|---|---|
00H | TYPE 号 | 1字节 | 结构的TYPE 号,此处是0 |
01H | 长度 | 1字节 | TYPE 0 格式区域的长度,一般为14H ,也有13H |
02H | 句柄 | 2字节 | 本结构的句柄,一般为0000H |
04H | Bios 厂商信息 | 1字节 | 此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串 |
05H | BIOS 版本 | 1字节 | BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串 |
06H | Bios 开始地址段 | 2字节 | 用于计算常驻BIOS 镜像大小的计算,方法为(10000H-BIOS 开始地址段)×16 |
08H | BIOS 发布日期 | 1字节 | 一般为03H ,表示字符区第三个字符串 |
09H | BIOS rom size | 1字节 | 计算方法为(n +1 )×64K ,n 为此处读出数值 |
0AH | BIOS 特征 | 8字节 | Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等 |
12H | Bios 特征扩展 | 不定 | - |
根据规则解析数据就可以了, 剩下的就是体力活了
来来来,张三你照着SMBIOS Reference6 Specification把数据解析下吧!
实现一个完整的dmidecode工具
体力活我已经帮你干了, 我已经按照SPEC文档, 进行了解析, 并且根据具体设备对照着dmidecode 写了测试用例, 因此你就放心使用吧!
package main
import (
"fmt"
"os"
"github.com/yumaojun03/dmidecode"
)
func checkError(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
dmi, err := dmidecode.New()
checkError(err)
infos, err := dmi.BIOS()
// 支持以下类型的解析
// dmi.BaseBoard()
// dmi.Chassis()
// dmi.MemoryArray()
// dmi.MemoryDevice()
// dmi.Onboard()
// dmi.PortConnector()
// dmi.Processor()
// dmi.ProcessorCache()
// dmi.Slot()
// dmi.System()
checkError(err)
for i := range infos {
fmt.Println(infos[i])
}
}
有Bug请提Issue!, 当然如果可以 顺手点个Star(dmidecode)! 就差你的了
参考
SMBIOS Reference6 Specification
SMBIOS信息概述 -- DMI
go-smbios smbios信息读取库
dmidecode Golang版实现
有疑问加站长微信联系(非本文作者)