18 smbios

2010-10-03 14:10:00 zhoudaxia 阅读数 23349

 

     先介绍 DMI DMI 是英文单词 Desktop Management Interface 的缩写,也就是桌面管理界面,它含有关于系统硬件的配置信息。计算机每次启动时都对 DMI 数据进行校验,如果该数据出错或硬件有所变动,就会对机器进行检测,并把测试的数据写入 BIOS 芯片保存。所以如果我们在 BIOS 设置中禁止了 BIOS 芯片的刷新功能或者在主板使用跳线禁止了 BIOS 芯片的刷新功能,那这台机器的 DMI 数据将不能被更新。如果你更换了硬件配置,那么在进行 WINDOWS 系统时,机器仍旧按老系统的配置进行工作。这样就不能充分发挥新添加硬件的性能,有时还会出现这样或那样的故障。

     SMBIOS(System Management BIOS SMBIOS) 是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范。 DMI(Desktop Management Interface, DMI) 就是帮助收集电脑系统信息的管理系统, DMI 信息的收集必须在严格遵照 SMBIOS 规范的前提下进行。 SMBIOS DMI 是由行业指导机构 Desktop Management Task Force (DMTF) 起草的开放性的技术标准,其中, DMI 设计适用于任何的平台和操作系统。 DMI 充当了管理工具和系统层之间接口的角色。它建立了标准的可管理系统更加方便了电脑厂商和用户对系统的了解。

DMI 的主要组成部分是 Management Information Format (MIF) 数据库。这个数据库包括了所有有关电脑系统和配件的信息。通过 DMI ,用户可以获取序列号、电脑厂商、串口信息以及其它系统配件信息。

     对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息,共有两种办法可以访问:

     1. 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用。

     2 .基于表结构的方法,表内容是 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 结构表。

     对于非 EFI 的系统,访问 SMBIOS EPS 表的操作过程如下:

     1 .从物理内存 0x000F0000-0x000FFFFF 之间寻找关键字 “ _SM_”

     2 .找到后再向后 16 个字节,看后面 5 BYTE 是否是关键字 “_DMI_” ,如果是, EPS 表即找到。

     对于 UEFI (是 BIOS 的下一代版本)系统,可能通过搜索 EFI 配置表中的 SMBIOS GUID(SMBIOS_TABLE_GUID) ,然后使用指向 SMBIOS 的指针来定位 EPS 表。具体可参考 UEFI 规范。

     SMBIOS EPS 表结构如下:

位置

名称

长度

描述

00H

关键字

4BYTE

固定是”_SM_”

04H

校验和

1BYTE

用于校验数据

05H

表结构长度

1BYTE

Entry Point Structure 表的长度

06H

Major 版本号

1BYTE

用于判断SMBIOS 版本

07H

Minor 版本号

1BYTE

用于判断SMBIOS 版本

08H

表结构大小

2BYTE

用于即插即用接口方法获得数据表结构长度

0AH

EPS 修正

1BYTE

 

0B-0FH

格式区域

5BYTE

存放解释EPS 修正的信息

10H

关键字

5BYTE

固定为“_DMI_”

15H

校验和

1BYTE

Intermediate Entry Point Structure (IEPS) 的校验和

16H

结构表长度

2BYTE

SMBIOS 结构表的长度

18H

结构表地址

4BYTE

SMBIOS 结构表的真实内存位置

1CH

结构表个数

2BYTE

SMBIOS 结构表数目

1EH

Smbios BCD 修正

1BYTE

 

     通过 EPS 表结构中 16H 以及 18H 处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表。从 EPS 表中的 1CH 处可得知数据表结构的总数,其中 TYPE 0 结构就是 BIOS information TYPE 1 结构就是 SYSTEM Information

     每个结构的头部是相同的,格式如下:

位置

名称

长度

描述

00H

TYPE

1BYTE

结构的TYPE

01H

长度

1BYTE

本结构的长度,就此TYPE 号的结构而言

02H

句柄

2BYTE

用于获得本SMBIOS 结构,其值不定

    每个结构都分为格式区域和字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域。结构 01H 处标识的结构长度仅是格式区域的长度,字符串区域的长度是不固定的。有的结构有字符串区域,有的则没有。

     下面以 TYPE 0 BIOS information )为例说明格式区域和字符串区域的关系。 TYPE 0 BIOS information )格式区域如下:

位置

名称

长度

描述

00H

TYPE

1BYTE

结构的TYPE 号,此处是0

01H

长度

1BYTE

TYPE 0 格式区域的长度,一般为14H ,也有13H

02H

句柄

2BYTE

本结构的句柄,一般为0000H

04H

Bios 厂商信息

1BYTE

此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串

05H

BIOS 版本

1BYTE

BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串

06H

Bios 开始地址段

2BYTE

用于计算常驻BIOS 镜像大小的计算,方法为

10000H-BIOS 开始地址段)×16

08H

BIOS 发布日期

1BYTE

一般为03H ,表示字符区第三个字符串

09H

BIOS rom size

1BYTE

计算方法为(n1×64Kn 为此处读出数值

0AH

BIOS 特征

8BYTE

Bios 的功能支持特征,如PCI,PCMCIA,FLASH

12H

Bios 特征扩展

不定

 

    紧随 TYPE 0 BIOS information )结构区域之后,即在 Bios 特征扩展域后面的就是 TYPE 0 BIOS information )字符串区域,一个例子如下所示:


     每个字符串都以 00H 作为结束标志,上面的例子中有三个字符串。如果我们要找下一个 TYPE ,因为最后一个字符串以 00H 结尾,而整个字符区域又以 00H 结尾,故只要在字符串区域找到连续的 0000H 即可。例如,一个带字符串域的完整 BIOS Information 例子如下:


     一个不带字符串域的完整 BIOS Information 例子:


     注意,当从 EPS 表中得到结构表的开始地址后,可以直接按结构来寻找相应的 TYPE 号,找到后直接读取就是该 TYPE 对应的结构的格式区域信息,然后向后移动结构区域长度(结构区域长度由该结构的 01H 处读出)个 BYTE ,即是该 TYPE 结构的字符串区域。

     由上面介绍可知,获得 BIOS 信息的办法就是:

     1 .通过 EPS 表的 12H 14H 数据找到 TYPE 结构表,然后找到 TYPE 0 的内存地址 ( 不一定是首个 )

     2 .由 TYPE 0 结构区域中得出相应 BIOS 信息是否存在(存在则是上面所述的    01H,02H,03H 依次排布,不存在则是相应的位置上为 00H )。

      3 .如存在信息,则从字符串区域中读取对应 BIOS 信息。

      获得其他类型的 SMBIOS 结构信息的方法类似,只是 TYPE 结构区域有所不同。

     相信大家都用过一些系统检测软件 , 或者至少用过 Windows 优化大师里面的系统检测。实际上,应用程序程序就可以通过访问 SMBIOS 来获得这些信息。这里举一个例子,演示如何找到 BIOS 的版本号 (BIOS Version) 和电脑的厂商 (Manufacturer) 。不过我们不采用编程的方式,而是通过系统自带的 debug 命令来进行相关的操作。

     内存的物理地址区间 0x000F0000-0x000FFFFF 使用了 32 位中的 20 位,先要在这个区间内寻找关键字 “ _SM_” ,以定位到 EPS 表。我们要采用 20 位地址模式,即高位字左移 12 位,因此值 0x000F0000 表示为 F000:0000 ,地址区间为 F000:0000 - F000:FFFF 。在 cmd 中输入 "debug", 回车 , 这样就进入了 debug 环境,它的命令提示符为 - 以后出现在 - 后面的内容都为将要输入的命令。

     在地址区间内搜索关键字 "_SM_" 的起始地址:

-s f000:0 ffff '_SM_'

     以我的电脑为例 , 响应可能是下面这样的:

-s f000:0 ffff '_SM_'

F000:6C00

     这个地址就是 EPS 表的起始地址。也有可能你的电脑会显示多个查找结果,不妨先随意选择一个进行下面的操作:

-d F000:6C00

     会打印该地址后面 16*8 个字节的内容,可能的响应:


     如果嫌显示的行数太少 , 可以用 -d 继续打印后面的信息。注意到了吗, _SM_ (其 ASCII 编码为 5F 53 4D 5F ),还有 10H 偏移处(即偏移 16 个字节)的 _DMI_ 。如果你在上一步中有多个结果,那么只要某个结果通过 d 命令查看的结果符合这两个特征,我们就可以用它来继续下面的操作。至这一步, EPS 已经找到了。

     EPS 的偏移 18H 处的 4 字节为 10 30 0E 00 ,是 SMBIOS 结构表的 32 位地址。这 4 个字节是从低字节到高字节,因为 x86 采用小端字节序,低字节存放的是高位的值,因此重新写成 " 高位 低位 " 的格式就是 000E 3010 ,转换成 20 位格式就把高位左移 12 位,变成 E000:3010 的形式。打印该地址处的内容:

-d e000:3010

可能的响应:


     首先,第一个字节 ( 零偏移处 ) 00 ,根据规范,这说明从这里开始的信息为 TYPE 0 结构,也即 BIOS 信息区。第二个字节 ( 偏移 01H ) 14 ,说明 TYPE 0 区域的基本大小是 14H 。根据规范,偏移 02H 处的两个字节为 00 00 ,表示句柄。偏移 04H 处的字节为 01 ,表示 BIOS 厂商这个字符串在 Type 0 字符串区域中的编号,即字符串区域中的第 01 个字符串就 BIOS 厂商。偏移 05H 处的字节为 02 ,表示 BIOS 版本字符串在字符串区域中的编号。偏移 08H 处的字节为 03 ,表示 BIOS 发布日期字符串在字符串区域中的编号。

    TYPE 0 区的基本大小为 14H ,则偏移 14H 处是 TYPE 0 字符串区域的开始。偏移 14H 处的值是 4E (右面对应的 ASCII 字符是 N ),这就是 TYPE 0 字符串区域的开始,它们是不包括在 TYPE 的大小计算中的。每个字符串不定长,由 00H 作为结束标志。熟悉 C/C++ 的人应该对这一标志符比较亲切吧。从第一个字符串依次编号为 01 02... 。第 01 个字符串为 ”NEC” (这里结束标志 00 前还插入了若干个控制字符 20 ),是 BIOS 厂商,第 02 个字符串为 "NOTE BIOS Version / 369A0600 " ,是 BIOS 版本,第 03 个字符串为 ”09/10/2003” ,是 BIOS 发布日期,这样我们的第一个目标 BIOS 版本就找到了。

整个字符串区的结束用 00 来标志,又根据字符串的结束符为 00 ,所以找到 00 00 ,从 00 00 的下一个地址开始就是下一个信息区了。这个例子中,就是 E000:3056 处,内容是 01 ,作为下一个区域的第一个字节,它标志着此 TYPE 的类型 , 这里就是 TYPE 1, 也即 System Information

根据与 TYPE 0 类似的方法 , 我们从 TYPE 1 的偏移 01H 处得到 TYPE 1 的大小为 19H SMBIOS 规范中说明, TYPE 1 的偏移 04H 处即为 Manufacturer 字符串的编号,这里即为 01 。转到 TYPE 1 的偏移 19H 处,即为 TYPE 1 的字符串区的开始,由于我们要找的信息编号为 01 ,所以从这里开始的信息 "NEC Computers International" 即为厂商的信息。

SMBIOS 支持在保护模式下工作,因此,在 Windows 中使用 16 位汇编进行程序编写将使得对物理内存进行访问变得非常容易。  

2018-07-13 08:57:54 Lq19880521 阅读数 5633

需要了解的Smbios知识

(1)什么是Smbios

Smbios在百度百科中是这样解释的:Smbios(system management bios)是主板或者系统制造厂商以标准格式显示产品信息所遵循的统一规范。Smbios规范标准定义了收集的电脑信息都包含哪方面的信息,對應手冊中其實就是包含很多c语言中的结构体,每一个结构体代表一种信息。在Smbios中都有。具体请看Smbios标准。

每个主板厂商或者OEM厂商所生产的电脑,在出厂的时候都会按照smbios标准将信息写入到bios中。主要就是包含了电脑各个模块信息。厂商将这些信息写好存入到bios芯片中。这部分信息是如何存入的,当然毫无疑问肯定是以bios代码的是形式实现的,最终将代码的二进制写入到开机启动的flash芯片中。这部分代码是属于bios的一部分。在uefi中属于专门的一个驱动模块。

(2)什么是DMI

DMI(Desktop Management Interface)也就是桌面管理接口,它含有关于系统硬件的配置信息。DMI 的主要组成部分是 Management Information Format (MIF) 数据库。这个数据库包括了所有有关电脑系统和配件的信息。通过 DMI ,用户可以获取序列号、电脑厂商、串口信息以及其它系统配件信息。计算机每次启动时都对 DMI 数据进行校验,如果该数据出错或硬件有所变动,就会对机器进行检测,并把测试的数据写入 BIOS 芯片保存。DMI搜集信息,也是按照Smbios标准所进行的。因此DMI的实现,就是按照Smbios标准所实现的。

Smbiso的访问方式:

对于符合 Smbios规范的计算机,可以通过访问 Smbios 的结构获得系统信息,共有两种办法可以访问Smbios数据结构:

     1. 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用。

     2 .基于表结构的方法,表内容是 table entry point 的数据,这个访问方法从 SMBIOS 2.1 以后开始被使用,从 2.1 开始,以后的版本都推荐使用这种访问方式。在 2.1 版本中允许支持这两种方法中的任意一种和两种都支持,但在 2.2 以后的版本,必须支持方法 2 。

     目前主流的访问方式都是基于表结构的访问方式。基于表结构访问 SMBIOS 的过程是先找到 Entry Point Structure ( EPS )表,然后通过 Entry Point Structure ( EPS )表的数据找到 SMBIOS 结构表。

     对于非 EFI 的系统,访问 SMBIOS EPS 表的操作过程如下:

     1 .从物理内存 0x000F0000-0x000FFFFF 之间寻找关键字 “ _SM_” 。

     2 .找到后再向后 16 个字节,看后面 5 个 BYTE 是否是关键字 “_DMI_” ,如果是, EPS 表即找到。

满足上面的两个条件,即可认为找到了Smbios的Entry Point Structure数据结构。然后程序解析这个结构中的数据来获取其他的信息。

     对于 UEFI 系统,可能通过搜索 EFI 配置表中的 SMBIOS GUID(SMBIOS_TABLE_GUID) ,然后使用指向 SMBIOS 的指针来定位 EPS 表。具体可参考 UEFI 规范。(主要就是以protocol的方式实现的)

我们这里实现的方式并没有严格按照uefi的规范来实现,还是通过前面非uefi的系统来实现的。因为我们的内核版本中现在还没有严格的按照uefi标准来解析。所以只能按照上面的非uefi系统来实现。

Smbios的数据结构:

SMBIOS EPS 表结构如下:

位置

名称

长度

描述

00H

关键字

4BYTE

固定是”_SM_”

04H

校验和

1BYTE

用于校验数据

05H

表结构长度

1BYTE

Entry Point Structure 表的长度

06H

Major 版本号

1BYTE

用于判断SMBIOS 版本

07H

Minor 版本号

1BYTE

用于判断SMBIOS 版本

08H

表结构大小

2BYTE

用于即插即用接口方法获得数据表结构长度

0AH

EPS 修正

1BYTE

 

0B-0FH

格式区域

5BYTE

存放解释EPS 修正的信息

10H

关键字

5BYTE

固定为“_DMI_”

15H

校验和

1BYTE

Intermediate Entry Point Structure (IEPS)的校验和

16H

结构表长度

2BYTE

SMBIOS 结构表的长度

18H

结构表地址

4BYTE

SMBIOS 结构表的真实内存位置

1CH

结构表个数

2BYTE

SMBIOS 结构表数目

1EH

Smbios BCD 修正

1BYTE

 

这里面主要介绍介幾個比较有用的数据:

00H和10H是用于匹配是否是entry point structure的数据。16H和18H是实现的表的起始地址和总长度。1CH是表的个数。

linux下dmidecode命令就是用用来解析这些信息的。dmidecode先到特定的地址去找到EPS,然后从EPS中拿到18H这个位置所存放的地址,然后去这个地址去解析数据。

这些数据包含了多个表的结构,每个表的含义不同,BIOS 信息 (Type 0) 、系统信息 (Type 1) 、基板(或模块)信息 (Type 2) 、系统外围或底架 (Type 3) 、处理器信息 (Type 4) 、存储控制器信息(Type 5, 已 废弃 ) 、存储模块信息 (Type 6, 已废弃 ) 、调整缓存信息 (Type 7) 、端口连接器信息 (Type 8) 、系统插槽 (Type 9) 。板载设备信息 (Type 10) 、 OEM 字符串 (Type 11) 、系统配置选项 (Type 12) 、 BIOS 语言信息 (Type 13) 、组相联 (Type 14) 、系统事件日志(Type 15) 、物理存储阵列 (Type 16) 、存储设备 (Type 17) 、 32-bit 内存错误信息 (Type 18) 、存储阵列映射地址 (Type 19) 。存储设备映射地址 (Type 20) 、内建指针设备 (Type 21) 、便携式电池 (Type 22) 、系统重置 (Type 23) 、硬件安全 (Type 24) 、系统电源控制 (Type 25) 、电压探针 (Type 26) 、冷却设备 (Type 27) 、温度传感器 (Type 28) 、电流探头 (Type 29) 。越界远程访问 (Type 30) 、引导完整性服务 (BIS) 入口点 (Type 31) 、系统引导信息 (Type 32) 、 64-bit 内存错误信息 (Type 33) 、管理设备 (Type 34) 、管理设备组件 (Type 35) 、管理设备门槛数据 (Type 36) 、存储信道 (Type 37) 、 IPMI 设备信息 (Type 38) 、系统供电电源 (Type 39) 。附加信息 (Type 40) 、板载设备扩展信息 (Type 41) 、管理控制器主机接口 (Type 42) 。不活动指示 (Type 126) 、表格结束指示 (Type 127) 。

SMBIOS 的大部分结构表数据在 CIM 模型中都有对应的实现,比如底架信息对应 CIM_Chassis ,处理器信息对应 CIM_Processor ,内存信息对应CIM_Memory 等。

每个表都含有一个相同的信息,就是表头其格式如下:

位置

名称

长度

描述

00H

TYPE 号

1BYTE

结构的TYPE 号

01H

长度

1BYTE

本结构的长度,就此TYPE 号的结构而言

02H

句柄

2BYTE

用于获得本SMBIOS 结构,其值不定

每个结构都分为格式区域字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域。结构 01H 处标识的结构长度仅是格式区域的长度,字符串区域的长度是不固定的。有的结构有字符串区域,有的则没有。

下面以 TYPE 0 ( BIOS information )为例说明格式区域和字符串区域的关系。 TYPE 0 ( BIOS information )格式区域如下:

位置

名称

长度

描述

00H

TYPE 号

1BYTE

结构的TYPE 号,此处是0

01H

长度

1BYTE

TYPE 0 格式区域的长度,一般为14H ,也有13H

02H

句柄

2BYTE

本结构的句柄,一般为0000H

04H

Bios 厂商信息

1BYTE

此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串

05H

BIOS 版本

1BYTE

BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串

06H

Bios 开始地址段

2BYTE

用于计算常驻BIOS 镜像大小的计算,方法为

(10000H-BIOS 开始地址段)×16

08H

BIOS 发布日期

1BYTE

一般为03H ,表示字符区第三个字符串

09H

BIOS rom size

1BYTE

计算方法为(n +1 )×64K ,n 为此处读出数值

0AH

BIOS 特征

8BYTE

Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等

12H

Bios 特征扩展

不定

紧随 TYPE 0 ( BIOS information )结构区域之后,即在 Bios 特征扩展域后面的就是 TYPE 0 ( BIOS information )字符串区域,每个字符串都以 00H 作为结束标志,上面的例子中有三个字符串。如果我们要找下一个 TYPE ,因为最后一个字符串以 00H 结尾,而整个字符区域又以 00H 结尾,故只要在字符串区域找到连续的 0000H 即可。


代码实现:

感覺這部分代碼的架構比較好,所以還是寫下來總結一下吧,後面在寫代碼的時候可以用得上。

首先,在uefi中Smbios部分是分爲兩部分的,第一部分是SmbiosDxe驅動,這部分主要的功能就是爲後一部分SmbiosTable部分提供接口函數的。下面看一下SmbisoDxe部分的主要代碼

(1)SmbiosDxe

這部分驅動的入韓函數爲

SmbiosDriverEntryPoint

詳細的代碼如下:

EFI_STATUS
EFIAPI
SmbiosDriverEntryPoint (
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{
  EFI_STATUS            Status;

  mPrivateData.Signature                = SMBIOS_INSTANCE_SIGNATURE;
  mPrivateData.Smbios.Add               = SmbiosAdd;
  mPrivateData.Smbios.UpdateString      = SmbiosUpdateString;
  mPrivateData.Smbios.Remove            = SmbiosRemove;
  mPrivateData.Smbios.GetNext           = SmbiosGetNext;
  mPrivateData.Smbios.MajorVersion      = (UINT8) (PcdGet16 (PcdSmbiosVersion) >> 8);
  mPrivateData.Smbios.MinorVersion      = (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x00ff);

  InitializeListHead (&mPrivateData.DataListHead);
  InitializeListHead (&mPrivateData.AllocatedHandleListHead);
  EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY);

  //
  // Make a new handle and install the protocol
  //
  mPrivateData.Handle = NULL;
  Status = gBS->InstallProtocolInterface (
                  &mPrivateData.Handle,                                                                                                                                                  
                  &gEfiSmbiosProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mPrivateData.Smbios
                  );   

  return Status;
}

代碼中mPrivateData是一個本模塊的全局變量,其定義如下:SMBIOS_INSTANCE mPrivateData

SMBIOS_INSTANCE結構都包含哪些變量呢?如下:

typedef struct {
  UINT32                Signature;
  EFI_HANDLE            Handle;
  //
  // Produced protocol
  //
  EFI_SMBIOS_PROTOCOL   Smbios;
  //
  // Updates to record list must be locked.
  //
  EFI_LOCK              DataLock;
  //
  // List of EFI_SMBIOS_ENTRY structures.
  //
  LIST_ENTRY            DataListHead;
  //
  // List of allocated SMBIOS handle.
  //
  LIST_ENTRY            AllocatedHandleListHead;

} SMBIOS_INSTANCE;

這裏面最重要的變量就是EFI_SMBIOS_PROTOCOL   Smbios。mPrivateData內部這些變量的值就是在這個函數中初始化的,初始化之後,將&gEfiSmbiosProtocolGuid這個protocol安裝上,爲後面使用。這裏面最主要的函數就mPrivateData.Smbios.Add               = SmbiosAdd;

SmbiosAdd的函數就是添加每一張表的時候需要調用的函數。裏面包含了構建EPS表的代碼和要傳入數據的表還有結束表。將每張表的信息寫入到規定好的內存地址上。我們這裏規定EPS(Entry Point Structure)的位置在0x980000000fffe000的虛擬地址上,映射的物理地址就是0x0fffe000,在內存地址低256M的高16M之內,這部分地址就是bios爲內核需要的參數預留的,內核需要使用的數據在bios階段都將數據存入到高16M之內。而EPS中表的地址,是固定在0x980000000f000000,映射的物理地址就是0x0f000000對應內存地址就是低256M的高16M的起始地址。其代碼具體實現不再詳細介紹。這部分代碼我們根據我們平臺的內存地址的使用情況做了適當的調整。和源碼是不一樣。

(1)SmbiosTable

其實這部分的代碼才是最值得寫的,其架構的實現,可以爲後續在一些地方用得到。

其入口函數爲SmbiosDriverEntryPoint,其代碼如下:

EFI_STATUS
EFIAPI
SmbiosDriverEntryPoint (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable

  )

{
  UINTN                Index;
  EFI_STATUS           EfiStatus;
  EFI_SMBIOS_PROTOCOL  *Smbios;

  EfiStatus = gBS->LocateProtocol(&gEfiSmbiosProtocolGuid, NULL, (VOID**)&Smbios);

  if (EFI_ERROR(EfiStatus)) {
    DEBUG((EFI_D_ERROR, "Could not locate SMBIOS protocol.  %r\n", EfiStatus));
    return EfiStatus;
  }

  mHiiHandle = HiiAddPackages (
                 &gEfiCallerIdGuid,
                 NULL,
                 SmbiosTableStrings,
                 NULL
                 );
  ASSERT (mHiiHandle != NULL);
  for (Index = 0; Index < mSmbiosDataTableEntries; ++Index) {
    //
    // If the entry have a function pointer, just log the data.
    //
    if (mSmbiosDataTable[Index].Function != NULL) {
      EfiStatus = (*mSmbiosDataTable[Index].Function)(
        mSmbiosDataTable[Index].RecordData,
        Smbios
        );

      if (EFI_ERROR(EfiStatus)) {
        DEBUG((EFI_D_ERROR, "Misc smbios store error.  Index=%d, ReturnStatus=%r\n", Index, EfiStatus));
        return EfiStatus;
      }
    }
  }
  return EfiStatus;
}   

首先進入函數內部之後,先loacte smbios protocol,如果locate成功,獲取到Smbios Protocol之後才能調用Protocol中的SmbiosAdd函數,添加每一張表的信息。獲取成功之後,就遍歷mSmbiosDataTableEntries表,依次執行這個表中的成員對應的函數來添加表象。mSmbiosDataTable其定義如下:

//
// Data Table.
//
EFI_MISC_SMBIOS_DATA_TABLE  mSmbiosDataTable[] = {
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscBiosInfo,MiscBiosInfo ),//type 0
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscSystemInfo, MiscSystemInfo),//type 1
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscBoardInfo, MiscBoardInfo),//type 2
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscChassisInfo, MiscChassisInfo),//type 3
////MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscProcessorInfo, MiscProcessorInfo),//type 4                                                                                          
  //MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscCacheInfo, MiscCacheInfo),//type 7
  //MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscOemString, MiscOemString),//type 11
  //MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscPhysicalStoreInfo, MiscPhysicalStoreInfo),//type 16
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscStoreDeviceInfo, MiscStoreDeviceInfo),//type 17
  //MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscMemMappInfo, MiscMemMappInfo),//type 19
  MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscTemperatureInfo, MiscTemperatureInfo),//type 28
  //MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscBootInformationStatus, MiscBootInformationStatus),//type 32

};

EFI_MISC_SMBIOS_DATA_TABLE又是如何定義的呢?代碼如下:

//
// Data table entry definition.
//
typedef struct {
  //  
  // intermediat input data for SMBIOS record
  //  
  VOID                              *RecordData;
  EFI_MISC_SMBIOS_DATA_FUNCTION     *Function;

} EFI_MISC_SMBIOS_DATA_TABLE;

這裏面EFI_MISC_SMBIOS_DATA_FUNCTION 的定義又如下:

//
// Data table entry update function.

//

#define EFIAPI (定義EFIAPI爲空)

typedef EFI_STATUS (EFIAPI EFI_MISC_SMBIOS_DATA_FUNCTION) (
  IN  VOID                 *RecordData,
  IN  EFI_SMBIOS_PROTOCOL  *Smbios

  );定義了EFI_MISC_SMBIOS_DATA_FUNCTION的返回值爲EFI_STATUS中的類型,函數參數包含這兩個參數。只要符合這個參數的函數都可以使用這個類型。

MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION的定義是這樣的:

// Data Table entries
//
#define MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION(NAME1, NAME2) \
{ \
  & NAME1 ## Data, \
  & NAME2 ## Function \
}

也就是將第一個參數後面加上Data在取地址,第二個參數加上Function再去地址。所以拿第一個參數爲例子,

MISC_SMBIOS_TABLE_ENTRY_DATA_AND_FUNCTION( MiscBiosInfo,MiscBiosInfo ),//type 0

其實就是


&MiscBiosInfoData,//對應*RecordData;成員

&MiscBiosInfoFunction  //對應EFI_MISC_SMBIOS_DATA_FUNCTION     *Function成員

其他的一次類推。這裏面MiscBiosInfoData是一個全局變量,其定義如下:

//
// Static (possibly build generated) Bios infomation data (type 0).
//

MISC_SMBIOS_TABLE_DATA(EFI_MISC_BIOS_DATA, MiscBiosInfo) = {
  {
    EFI_SMBIOS_TYPE_BIOS_INFORMATION,
    sizeof (SMBIOS_TABLE_TYPE0),
    0   
  },  
  1,//STRING_TOKEN(STR_MISC_BIOS_VENDOR),       // BiosVendor
  2,//STRING_TOKEN(STR_MISC_BIOS_VERSION),      // BiosVersion
  0xe000, // BiosStartingAddress
  3,//STRING_TOKEN(STR_MISC_BIOS_RELEASE_DATE), // BiosReleaseDate
  0,       // BiosPhysicalDeviceSize
  {       // BiosCharacteristics1
    0x18,
    0x05,
    0,  
    0,  
    0,  
    0,  
    0,  
    0   
  },  
  {       // BiosCharacteristicsExten
    0x80,    // BiosReserved                      :16
    0x02     // Reserved                          :32
  },  
  1,  
  0,  
  0xff,
  0xff
}; 
#defineMISC_SMBIOS_TABLE_DATA(NAME1,NAME2) \                                                                                                                           

  NAME1 NAME2 ## Data

所以上面的全局變量其實就是這樣的

EFI_MISC_BIOS_DATA MiscBiosInfoData = {
  {
    EFI_SMBIOS_TYPE_BIOS_INFORMATION,
    sizeof (SMBIOS_TABLE_TYPE0),
    0
  },
  1,//STRING_TOKEN(STR_MISC_BIOS_VENDOR),       // BiosVendor
  2,//STRING_TOKEN(STR_MISC_BIOS_VERSION),      // BiosVersion
  0xe000, // BiosStartingAddress
  3,//STRING_TOKEN(STR_MISC_BIOS_RELEASE_DATE), // BiosReleaseDate
  0,       // BiosPhysicalDeviceSize
  {       // BiosCharacteristics1
    0x18,
    0x05,
    0,
    0,
    0,
    0,
    0,
    0
  },
  {       // BiosCharacteristicsExten
    0x80,    // BiosReserved                      :16
    0x02     // Reserved                          :32
  },
  1,
  0,
  0xff,
  0xff
};


MiscBiosInfoFunction就是要執行的函數,這個函數是怎麼定義的呢?

MISC_SMBIOS_TABLE_FUNCTION(MiscBiosInfo)                                                                                                                                                 
{
  CHAR8                 *OptionalStrStart;
  UINTN                 VendorStrLen;
  UINTN                 VerStrLen;
  UINTN                 DateStrLen;
  CHAR16                *Version;
  CHAR16                *ReleaseDate;
  EFI_STATUS            Status;
  EFI_STRING            Char16String;
  STRING_REF            TokenToGet;
  STRING_REF            TokenToUpdate;
  SMBIOS_TABLE_TYPE0    *SmbiosRecord;
  EFI_SMBIOS_HANDLE     SmbiosHandle;
  EFI_MISC_BIOS_DATA    *ForType0InputData;

  ForType0InputData        = (EFI_MISC_BIOS_DATA *)RecordData;

  //
  // First check for invalid parameters.
  //
  if (RecordData == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Version = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
  if (StrLen (Version) > 0) {
    TokenToUpdate = STRING_TOKEN (STR_MISC_BIOS_VERSION);
    HiiSetString (mHiiHandle, TokenToUpdate, Version, NULL);
  }

  ReleaseDate = (CHAR16 *) PcdGetPtr (PcdFirmwareReleaseDateString);
  if (StrLen(ReleaseDate) > 0) {
    TokenToUpdate = STRING_TOKEN (STR_MISC_BIOS_RELEASE_DATE);
    HiiSetString (mHiiHandle, TokenToUpdate, ReleaseDate, NULL);
  }

  TokenToGet = STRING_TOKEN (STR_MISC_BIOS_VENDOR);
  Char16String = HiiGetPackageString(&gEfiCallerIdGuid, TokenToGet, NULL);
  VendorStrLen = StrLen(Char16String);
  if (VendorStrLen > SMBIOS_STRING_MAX_LENGTH) {
    return EFI_UNSUPPORTED;
  }

  TokenToGet = STRING_TOKEN (STR_MISC_BIOS_VERSION);
  Version = HiiGetPackageString(&gEfiCallerIdGuid, TokenToGet, NULL);
  VerStrLen = StrLen(Version);
  if (VerStrLen > SMBIOS_STRING_MAX_LENGTH) {
    return EFI_UNSUPPORTED;
  }

  TokenToGet = STRING_TOKEN (STR_MISC_BIOS_RELEASE_DATE);
  ReleaseDate = HiiGetPackageString(&gEfiCallerIdGuid, TokenToGet, NULL);
  DateStrLen = StrLen(ReleaseDate);
  if (DateStrLen > SMBIOS_STRING_MAX_LENGTH) {
    return EFI_UNSUPPORTED;
  }

  //
  // Two zeros following the last string.
  //
  SmbiosRecord = AllocatePool(sizeof (SMBIOS_TABLE_TYPE0) + VendorStrLen + 1 + VerStrLen + 1 + DateStrLen + 1 + 1 );
  ZeroMem(SmbiosRecord, sizeof (SMBIOS_TABLE_TYPE0) + VendorStrLen + 1 + VerStrLen + 1 + DateStrLen + 1 + 1 );
  CopyMem(SmbiosRecord, ForType0InputData, sizeof(SMBIOS_TABLE_TYPE0));

  size_t FlashSize = 0;
  tgt_flashinfo(0xbfc00000, &FlashSize);
  SmbiosRecord->BiosSize = (FlashSize>>16) - 1;

  OptionalStrStart = (CHAR8 *)(SmbiosRecord + 1);
  UnicodeStrToAsciiStr(Char16String, OptionalStrStart);
  UnicodeStrToAsciiStr(Version, OptionalStrStart + VendorStrLen + 1);
  UnicodeStrToAsciiStr(ReleaseDate, OptionalStrStart + VendorStrLen + 1 + VerStrLen + 1);

  DbgPrint(DEBUG_INFO,"%a() (type:%d) (size:0x%x)\n",__FUNCTION__,SmbiosRecord->Hdr.Type,
  sizeof (SMBIOS_TABLE_TYPE0) + VendorStrLen + 1 + VerStrLen + 1 + DateStrLen + 1 + 1 );
  //
  // Now we have got the full smbios record, call smbios protocol to add this record.
  //
  Status = AddSmbiosRecord (Smbios, &SmbiosHandle, (EFI_SMBIOS_TABLE_HEADER *) SmbiosRecord);

  FreePool(SmbiosRecord);
  return Status;
}

第一眼一看這個函數爲什麼沒有函數入參呢?其實是有的,看MISC_SMBIOS_TABLE_FUNCTION的定義:

#define MISC_SMBIOS_TABLE_FUNCTION(NAME2)   \
  EFI_STATUS EFIAPI NAME2 ## Function( \
  IN  VOID                  *RecordData, \
  IN  EFI_SMBIOS_PROTOCOL   *Smbios \

  )其參數就是*RecordData,和*Smbios,所以上面函數的第一行其實就是

EFI_STATUS EFIAPI MiscBiosInfoFunction(

IN  VOID                  *RecordData, \

  IN  EFI_SMBIOS_PROTOCOL   *Smbios \)

所以是有參數的。

再看這個函數是如何執行的,移植傳入的入參又是什麼呢?這就是在函數SmbiosDriverEntryPoint內的for循環中執行的,看代碼

  if (mSmbiosDataTable[Index].Function != NULL) {
      EfiStatus = (*mSmbiosDataTable[Index].Function)(
        mSmbiosDataTable[Index].RecordData,
        Smbios

        );

現在還是拿第一個爲例,即Index等於0的時候,mSmbiosDataTable[Index].Function是不爲NULL的因爲,我們已經把函數&MiscBiosInfoFunction的地址添加到mSmbiosDataTable表中了,所以接下來執行這個表中的Function對應的函數即

EfiStatus = (*mSmbiosDataTable[Index].Function)而需要的兩個參數是誰呢,也就是mSmbiosDataTable[Index].RecordData,

 和Smbios。所以MiscBiosInfoData的地址就是傳入的第一個參數。

到現在是不是清晰了,寫每個表其實就是執行一個函數,這個函數寫的主要數據也就是第一個參數,葉居士對應的Data的全局變量的值。

所以這種架構,在初始化一些相同結構的代碼的時候,可以使用這種架構。代碼簡介整齊,修改起來也方便。

2017-05-25 00:17:24 u010479322 阅读数 2334

先介绍 DMI 。 DMI 是英文单词 Desktop Management Interface 的缩写,也就是桌面管理界面,它含有关于系统硬件的配置信息。计算机每次启动时都对 DMI 数据进行校验,如果该数据出错或硬件有所变动,就会对机器进行检测,并把测试的数据写入 BIOS 芯片保存。所以如果我们在 BIOS 设置中禁止了 BIOS 芯片的刷新功能或者在主板使用跳线禁止了 BIOS 芯片的刷新功能,那这台机器的 DMI 数据将不能被更新。如果你更换了硬件配置,那么在进行 WINDOWS 系统时,机器仍旧按老系统的配置进行工作。这样就不能充分发挥新添加硬件的性能,有时还会出现这样或那样的故障。

     SMBIOS(System Management BIOS , SMBIOS) 是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范。 DMI(Desktop Management Interface, DMI) 就是帮助收集电脑系统信息的管理系统, DMI 信息的收集必须在严格遵照 SMBIOS 规范的前提下进行。 SMBIOS 和 DMI 是由行业指导机构 Desktop Management Task Force (DMTF) 起草的开放性的技术标准,其中, DMI 设计适用于任何的平台和操作系统。 DMI 充当了管理工具和系统层之间接口的角色。它建立了标准的可管理系统更加方便了电脑厂商和用户对系统的了解。

DMI 的主要组成部分是 Management Information Format (MIF) 数据库。这个数据库包括了所有有关电脑系统和配件的信息。通过 DMI ,用户可以获取序列号、电脑厂商、串口信息以及其它系统配件信息。

     对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息,共有两种办法可以访问:

     1. 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用。

     2 .基于表结构的方法,表内容是 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 结构表。

     对于非 EFI 的系统,访问 SMBIOS EPS 表的操作过程如下:

     1 .从物理内存 0x000F0000-0x000FFFFF 之间寻找关键字 “ _SM_” 。

     2 .找到后再向后 16 个字节,看后面 5 个 BYTE 是否是关键字 “_DMI_” ,如果是, EPS 表即找到。

     对于 UEFI (是 BIOS 的下一代版本)系统,可能通过搜索 EFI 配置表中的 SMBIOS GUID(SMBIOS_TABLE_GUID) ,然后使用指向 SMBIOS 的指针来定位 EPS 表。具体可参考 UEFI 规范。

     SMBIOS EPS 表结构如下:

位置

名称

长度

描述

00H

关键字

4BYTE

固定是”_SM_”

04H

校验和

1BYTE

用于校验数据

05H

表结构长度

1BYTE

Entry Point Structure 表的长度

06H

Major 版本号

1BYTE

用于判断SMBIOS 版本

07H

Minor 版本号

1BYTE

用于判断SMBIOS 版本

08H

表结构大小

2BYTE

用于即插即用接口方法获得数据表结构长度

0AH

EPS 修正

1BYTE

 

0B-0FH

格式区域

5BYTE

存放解释EPS 修正的信息

10H

关键字

5BYTE

固定为“_DMI_”

15H

校验和

1BYTE

Intermediate Entry Point Structure (IEPS)的校验和

16H

结构表长度

2BYTE

SMBIOS 结构表的长度

18H

结构表地址

4BYTE

SMBIOS 结构表的真实内存位置

1CH

结构表个数

2BYTE

SMBIOS 结构表数目

1EH

Smbios BCD 修正

1BYTE

 

     通过 EPS 表结构中 16H 以及 18H 处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表。从 EPS 表中的 1CH 处可得知数据表结构的总数,其中 TYPE 0 结构就是 BIOS information , TYPE 1 结构就是 SYSTEM Information 。

     每个结构的头部是相同的,格式如下:

位置

名称

长度

描述

00H

TYPE 号

1BYTE

结构的TYPE 号

01H

长度

1BYTE

本结构的长度,就此TYPE 号的结构而言

02H

句柄

2BYTE

用于获得本SMBIOS 结构,其值不定

    每个结构都分为格式区域和字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域。结构 01H 处标识的结构长度仅是格式区域的长度,字符串区域的长度是不固定的。有的结构有字符串区域,有的则没有。

     下面以 TYPE 0 ( BIOS information )为例说明格式区域和字符串区域的关系。 TYPE 0 ( BIOS information )格式区域如下:

位置

名称

长度

描述

00H

TYPE 号

1BYTE

结构的TYPE 号,此处是0

01H

长度

1BYTE

TYPE 0 格式区域的长度,一般为14H ,也有13H

02H

句柄

2BYTE

本结构的句柄,一般为0000H

04H

Bios 厂商信息

1BYTE

此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串

05H

BIOS 版本

1BYTE

BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串

06H

Bios 开始地址段

2BYTE

用于计算常驻BIOS 镜像大小的计算,方法为

(10000H-BIOS 开始地址段)×16

08H

BIOS 发布日期

1BYTE

一般为03H ,表示字符区第三个字符串

09H

BIOS rom size

1BYTE

计算方法为(n +1 )×64K ,n 为此处读出数值

0AH

BIOS 特征

8BYTE

Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等

12H

Bios 特征扩展

不定

 

    紧随 TYPE 0 ( BIOS information )结构区域之后,即在 Bios 特征扩展域后面的就是 TYPE 0 ( BIOS information )字符串区域,一个例子如下所示:

  1. db ‘System BIOS Vendor Name’,0   ;  字符串以零结尾,第一个字符串:BIOS厂商  
  2. db ‘4.04’,0                      ;  第二个:BIOS版本  
  3. db ‘00/00/0000’,0                ;  第三个:BIOS发布日期  
  4. db 0                           ;  以0为整个字符中区域的结尾  



     每个字符串都以 00H 作为结束标志,上面的例子中有三个字符串。如果我们要找下一个 TYPE ,因为最后一个字符串以 00H 结尾,而整个字符区域又以 00H 结尾,故只要在字符串区域找到连续的 0000H 即可。例如,一个带字符串域的完整 BIOS Information 例子如下:

  1. BIOS_Info LABEL BYTE  
  2. db 0                                  ; Indicates BIOS Structure Type  
  3. db 13h                                ; Length of information in bytes  
  4. dw ?                                  ; Reserved for handle  
  5. db 01h                               ; String 1 is the Vendor Name  
  6. db 02h                               ; String 2 is the BIOS version  
  7. dw 0E800h                           ; BIOS Starting Address  
  8. db 03h                               ; String 3 is the BIOS Build Date  
  9. db 1                                 ; Size of BIOS ROM is 128K (64K * (1 + 1))  
  10. dq BIOS_Char                       ; BIOS Characteristics  
  11. db 0                                 ; BIOS Characteristics Extension Byte 1  
  12. db ‘System BIOS Vendor Name’,0  ;  
  13. db ‘4.04’,0                         ;  
  14. db ‘00/00/0000’,0                 ;  
  15. db 0                                 ; End of strings  



     一个不带字符串域的完整 BIOS Information 例子:

  1. BIOS_Info LABEL BYTE  
  2. db 0                ; Indicates BIOS Structure Type  
  3. db 13h             ; Length of information in bytes  
  4. dw ?               ; Reserved for handle  
  5. db 00h             ; No Vendor Name provided  
  6. db 00h             ; No BIOS version provided  
  7. dw 0E800h         ; BIOS Starting Address  
  8. db 00h             ; No BIOS Build Date provided  
  9. db 1               ; Size of BIOS ROM is 128K (64K * (1 + 1))  
  10. dq BIOS_Char     ; BIOS Characteristics  
  11. db 0               ; BIOS Characteristics Extension Byte 1  
  12. dw 0000h          ; Structure terminator  



     注意,当从 EPS 表中得到结构表的开始地址后,可以直接按结构来寻找相应的 TYPE 号,找到后直接读取就是该 TYPE 对应的结构的格式区域信息,然后向后移动结构区域长度(结构区域长度由该结构的 01H 处读出)个 BYTE ,即是该 TYPE 结构的字符串区域。

     由上面介绍可知,获得 BIOS 信息的办法就是:

     1 .通过 EPS 表的 12H 和 14H 数据找到 TYPE 结构表,然后找到 TYPE 0 的内存地址 ( 不一定是首个 ) 。

     2 .由 TYPE 0 结构区域中得出相应 BIOS 信息是否存在(存在则是上面所述的    01H,02H,03H 依次排布,不存在则是相应的位置上为 00H )。

      3 .如存在信息,则从字符串区域中读取对应 BIOS 信息。

      获得其他类型的 SMBIOS 结构信息的方法类似,只是 TYPE 结构区域有所不同。

     相信大家都用过一些系统检测软件 , 或者至少用过 Windows 优化大师里面的系统检测。实际上,应用程序程序就可以通过访问 SMBIOS 来获得这些信息。这里举一个例子,演示如何找到 BIOS 的版本号 (BIOS Version) 和电脑的厂商 (Manufacturer) 。不过我们不采用编程的方式,而是通过系统自带的debug 命令来进行相关的操作。

     内存的物理地址区间 0x000F0000-0x000FFFFF 使用了 32 位中的 20 位,先要在这个区间内寻找关键字 “ _SM_” ,以定位到 EPS 表。我们要采用 20位地址模式,即高位字左移 12 位,因此值 0x000F0000 表示为 F000:0000 ,地址区间为 F000:0000 - F000:FFFF 。在 cmd 中输入 “debug”, 回车 , 这样就进入了 debug 环境,它的命令提示符为 - , 以后出现在 - 后面的内容都为将要输入的命令。

     在地址区间内搜索关键字 “_SM_” 的起始地址:

-s f000:0 ffff ‘_SM_’

     以我的电脑为例 , 响应可能是下面这样的:

-s f000:0 ffff ‘_SM_’

F000:6C00

     这个地址就是 EPS 表的起始地址。也有可能你的电脑会显示多个查找结果,不妨先随意选择一个进行下面的操作:

-d F000:6C00

     会打印该地址后面 16*8 个字节的内容,可能的响应:

  1. -d F000:6C00  
  2. F000:6C00  5F 53 4D 5F 16 1F 02 1F-4B 01 00 00 00 00 00 00   _SM_….K…….  
  3. F000:6C10  5F 44 4D 49 5F 3D 98 09-10 30 0E 00 3C 00 00 00   _DMI_=…0..<…  
  4. F000:6C20  52 53 44 20 50 54 52 20-DE 4E 45 43 20 20 20 00   RSD PTR .NEC .  
  5. F000:6C30  34 95 F7 0D 00 00 00 00-00 00 00 00 00 00 00 00   4……………  
  6. F000:6C40  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    …………….  
  7. F000:6C50  24 46 53 58 56 0C F8 00-B1 00 E0 00 00 00 00 00   FSXV</span>...........&nbsp;&nbsp;</span></li><li><span style="color:black">F000:6C60&nbsp;&nbsp;5F&nbsp;33&nbsp;32&nbsp;5F&nbsp;20&nbsp;D7&nbsp;0F&nbsp;00-00&nbsp;01&nbsp;D6&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;_32_&nbsp;...........&nbsp;&nbsp;</span></li><li style="color:inherit"><span style="color:black">F000:6C70&nbsp;&nbsp;24&nbsp;50&nbsp;44&nbsp;4D&nbsp;01&nbsp;0B&nbsp;1A&nbsp;5D-88&nbsp;00&nbsp;F0&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;<span style="color:rgb(170,119,0)">PDM…]……..  



     如果嫌显示的行数太少 , 可以用 -d 继续打印后面的信息。注意到了吗, _SM_ (其 ASCII 编码为 5F 53 4D 5F ),还有 10H 偏移处(即偏移 16 个字节)的 _DMI_ 。如果你在上一步中有多个结果,那么只要某个结果通过 d 命令查看的结果符合这两个特征,我们就可以用它来继续下面的操作。至这一步, EPS 已经找到了。

     EPS 的偏移 18H 处的 4 字节为 10 30 0E 00 ,是 SMBIOS 结构表的 32 位地址。这 4 个字节是从低字节到高字节,因为 x86 采用小端字节序,低字节存放的是高位的值,因此重新写成 ” 高位 低位 ” 的格式就是 000E 3010 ,转换成 20 位格式就把高位左移 12 位,变成 E000:3010 的形式。打印该地址处的内容:

-d e000:3010

可能的响应:

  1. -d e000:3010  
  2. E000:3010  00 14 00 00 01 02 F5 E4-03 07 90 DF 99 7C 00 00   ………….|..  
  3. E000:3020  00 00 05 03 4E 45 43 20-20 20 20 20 00 4E 4F 54    ….NEC .NOT  
  4. E000:3030  45 20 42 49 4F 53 20 56-65 72 73 69 6F 6E 20 2F    E BIOS Version /  
  5. E000:3040  33 36 39 41 30 36 30 30-20 00 30 39 2F 31 30 2F    369A0600 .09/10/  
  6. E000:3050  32 30 30 33 00 00 01 19-01 00 01 02 03 04 8B B0    2003…………  
  7. E000:3060  DE 65 92 46 40 EB 86 FE-56 28 BD 80 79 13 06 4   E .e.F@…V(..y..N  
  8. E000:3070  45 43 20 43 6F 6D 70 75-74 65 72 73 20 49 6E 74    EC Computers Int  
  9. E000:3080  65 72 6E 61 74 69 6F 6E-61 6C 00 50 43 2D 43 56   ernational.PC-CV  



     首先,第一个字节 ( 零偏移处 ) 是 00 ,根据规范,这说明从这里开始的信息为 TYPE 0 结构,也即 BIOS 信息区。第二个字节 ( 偏移 01H 处 ) 是 14,说明 TYPE 0 区域的基本大小是 14H 。根据规范,偏移 02H 处的两个字节为 00 00 ,表示句柄。偏移 04H 处的字节为 01 ,表示 BIOS 厂商这个字符串在 Type 0 字符串区域中的编号,即字符串区域中的第 01 个字符串就 BIOS 厂商。偏移 05H 处的字节为 02 ,表示 BIOS 版本字符串在字符串区域中的编号。偏移 08H 处的字节为 03 ,表示 BIOS 发布日期字符串在字符串区域中的编号。

    TYPE 0 区的基本大小为 14H ,则偏移 14H 处是 TYPE 0 字符串区域的开始。偏移 14H 处的值是 4E (右面对应的 ASCII 字符是 N ),这就是 TYPE 0 字符串区域的开始,它们是不包括在 TYPE 的大小计算中的。每个字符串不定长,由 00H 作为结束标志。熟悉 C/C++ 的人应该对这一标志符比较亲切吧。从第一个字符串依次编号为 01 , 02… 。第 01 个字符串为 ”NEC” (这里结束标志 00 前还插入了若干个控制字符 20 ),是 BIOS 厂商,第 02 个字符串为 “NOTE BIOS Version / 369A0600 “ ,是 BIOS 版本,第 03 个字符串为 ”09/10/2003” ,是 BIOS 发布日期,这样我们的第一个目标 BIOS 版本就找到了。

整个字符串区的结束用 00 来标志,又根据字符串的结束符为 00 ,所以找到 00 00 ,从 00 00 的下一个地址开始就是下一个信息区了。这个例子中,就是 E000:3056 处,内容是 01 ,作为下一个区域的第一个字节,它标志着此 TYPE 的类型 , 这里就是 TYPE 1, 也即 System Information 。

根据与 TYPE 0 类似的方法 , 我们从 TYPE 1 的偏移 01H 处得到 TYPE 1 的大小为 19H , 而 SMBIOS 规范中说明, TYPE 1 的偏移 04H 处即为Manufacturer 字符串的编号,这里即为 01 。转到 TYPE 1 的偏移 19H 处,即为 TYPE 1 的字符串区的开始,由于我们要找的信息编号为 01 ,所以从这里开始的信息 “NEC Computers International” 即为厂商的信息。

SMBIOS 支持在保护模式下工作,因此,在 Windows 中使用 16 位汇编进行程序编写将使得对物理内存进行访问变得非常容易。  

从 SMBIOS 2.3 版本开始,兼容 SMBIOS 的实现必须包含以下 10 个数据表结构: BIOS 信息 (Type 0) 、系统信息 (Type 1) 、系统外围或底架(Type 3) 、处理器信息 (Type 4) 、高速缓存信息 (Type 7) 、系统插槽 (Type 9) 、物理存储阵列 (Type 16) 、存储设备 (Type 17) 、存储阵列映射地址(Type 19) 、系统引导信息 (Type 32) 。

其他的结构表则可根据实际来决定是否需要实现。最新的 SMBIOS 2.7.0 版中定义的所有结构表包括 Type 0-Type 42 ,其中 Type 5 、 Type 6 和Type 10 已经废弃。另外还有 Type 126 和 Type 127 两个特殊结构表。如下:

BIOS 信息 (Type 0) 、系统信息 (Type 1) 、基板(或模块)信息 (Type 2) 、系统外围或底架 (Type 3) 、处理器信息 (Type 4) 、存储控制器信息(Type 5, 已 废弃 ) 、存储模块信息 (Type 6, 已废弃 ) 、调整缓存信息 (Type 7) 、端口连接器信息 (Type 8) 、系统插槽 (Type 9) 。

板载设备信息 (Type 10) 、 OEM 字符串 (Type 11) 、系统配置选项 (Type 12) 、 BIOS 语言信息 (Type 13) 、组相联 (Type 14) 、系统事件日志(Type 15) 、物理存储阵列 (Type 16) 、存储设备 (Type 17) 、 32-bit 内存错误信息 (Type 18) 、存储阵列映射地址 (Type 19) 。

存储设备映射地址 (Type 20) 、内建指针设备 (Type 21) 、便携式电池 (Type 22) 、系统重置 (Type 23) 、硬件安全 (Type 24) 、系统电源控制 (Type 25) 、电压探针 (Type 26) 、冷却设备 (Type 27) 、温度传感器 (Type 28) 、电流探头 (Type 29) 。

越界远程访问 (Type 30) 、引导完整性服务 (BIS) 入口点 (Type 31) 、系统引导信息 (Type 32) 、 64-bit 内存错误信息 (Type 33) 、管理设备 (Type 34) 、管理设备组件 (Type 35) 、管理设备门槛数据 (Type 36) 、存储信道 (Type 37) 、 IPMI 设备信息 (Type 38) 、系统供电电源 (Type 39) 。

附加信息 (Type 40) 、板载设备扩展信息 (Type 41) 、管理控制器主机接口 (Type 42) 。

不活动指示 (Type 126) 、表格结束指示 (Type 127) 。

SMBIOS 的大部分结构表数据在 CIM 模型中都有对应的实现,比如底架信息对应 CIM_Chassis ,处理器信息对应 CIM_Processor ,内存信息对应CIM_Memory 等。

下面介绍主要的结构表。

1 、 BIOS 信息 (Type 0) :

位置

名称

长度

描述

00h

TYPE 号

1BYTE

结构的TYPE 号,此处是0

01h

长度

1BYTE

TYPE 0 格式区域的长度,一般为14h ,也有13h

02h

句柄

2BYTE

指向本结构的句柄

04h

Bios 厂商信息

1BYTE

此处是bios 卖方的信息,可能是OEM 厂商名,一般为01h ,代表紧随格式区域后的字符串区域的第一个字符串

05h

BIOS 版本

1BYTE

BIOS 版本号,一般为02h ,代表字符串区域的第二个字符串

06h

Bios 开始地址段

2BYTE

用于计算常驻BIOS 镜像大小的计算,方法为

(10000h-BIOS 开始地址段)×16

08h

BIOS 发布日期

1BYTE

一般为03h ,表示字符区第三个字符串

09h

BIOS ROM size

1BYTE

计算方法为(n +1 )×64K ,n 为此处读出数值

0Ah

BIOS 特征

8BYTE

Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等

12h

BIOS 特征扩展

不定

  从2.4 版开始包含两个扩展字节(12h-13h) ,以及字节14h-17h

14h

BIOS 版本主号

1BYTE

若系统不支持此域,则值设为ffh

15h

BIOS 版本从号

1BYTE

若系统不支持此域,则值设为ffh

16h

内置控制器固件的版主号

1BYTE

若系统不支持此域,则值设为ffh

17h

内置控制器固件的版从号

1BYTE

若系统不支持此域,则值设为ffh

BIOS 特征域表示BIOS 对一些特性的支持情况,Bit 1 和Bit 1 保留,Bit 2 未知,Bit 3 表示本BIOS 特征域是否被支持。

Bit 4-Bit 19 分别表示是否支持ISA 、MCA 、EISA 、PCI 、PCMCIA 、PnP 、APM 、BIOS 刷新、BIOS 影像(把 BIOS 的只读内容拷贝到快一些的内存中)、VL-VESA 、ESCD 、从CD 引导、可选择的引导、BIOS ROM 是否被插装、从PCMCIA 引导、EDD 规范。

Bit 20-Bit 25 表示对各种型号的软盘支持情况(均为中断Int 13h ),包括NEC 9800 1.2MB 软盘、Toshiba 1.2MB 软盘、5.25”/360KB 软盘、5.25”/1.2MB 软盘、3.5”/720KB 软盘、3.5”/2.88MB 软盘。

Bit 26-Bit  29 表示是否支持中断Int 5h(PrintScreen 屏幕截取服务) 、Int 9h(8042 键盘服务) 、Int 14h( 串口服务) 、Int 17h( 打印机服务)、Int 10h(CGA/Mono 视频服务) 、NEC PC-98 。

Bit 32:47 保留给BIOS 厂商使用。

Bit 48:63 保留给计算机系统厂商使用。

BIOS 特征扩展字节1( 偏移12h 处) 表示对另外一些设备的支持情况,Bit 0-Bit 1 分别表示是否支持ACPI 、USB 遗留、AGP 、I2O 引导、LS-120 SuperDisk 引导、ATAPI ZIP 驱动器引导、1394 引导、智能电池。

BIOS 特征扩展字节2( 偏移13h 处) 表示对一些高级特性的支持情况。Bit 1-Bit3 表示是否支持功能键初始化网络服务引导、激活目标内容分发、UEFI 规范。Bit 4 表示SMBIOS 表描述的是一台虚拟机,Bit 5:7 保留。

2 、系统信息 (Type 1) : SMBIOS 实现只关联一个单一的系统实例,并且包含且只包含一个系统信息结构。

位置

名称

长度

描述

00h

TYPE 号

1BYTE

结构的TYPE 号,此处是1

01h

长度

1BYTE

格式区域总长度,2.0 版为08h ,2.1-2.3.4 版为19h,从2.4 版开始为1Bh

02h

句柄

2BYTE

指向本结构的句柄

04h

电脑制造商

1BYTE

一般为01h ,表示在字符串区域中的编号

05h

产品名称

1BYTE

在字符串区域中的编号

06h

版本号

1BYTE

在字符串区域中的编号

07h

序列号

1BYTE

在字符串区域中的编号

09h

UUID

16BYTE

通用唯一标识符

18h

唤醒类型

BYTE

用来标识导致系统开电启动的事件

19h

SKU 号

BYTE

在字符串区域中的编号,SKU 号通常为产品ID 或采购订单号

1Ah

产品家族

1BYTE

在字符串区域中的编号

UUID 为 128 bit 长,是一个穿越时间和空间的唯一标识符,不需要集中的注册过程。它的格式在 RFC 4122 有详细的描述,内容比较繁锁,但SMBIOS 并不关注这个,它只关注字节序。 UUID 的字节顺序以及在 RFC 4122 中对应域的名字如下:

time_low :偏移 00h 处, 4BYTEs ,为时间戳的低位域部分。

time_mid : 04h 处, 2BYTEs , 为时间戳的中间域部分。

time_hi_and_version : 06h 处, 2BYTEs ,为时间戳的高位域,同时还包含版本号。

clock_seq_hi_and_reserved : 08h 处, 1BYTE ,时钟序列的高位域部分,同时还包含保留部分。

clock_seq_low : 09h 处, 1BYTE ,时钟序列的低位域部分。

Node : 0Ah 处, 6BYTEs ,唯一结点标识符。

虽然 RFC 4122 建议对所有域都使用网络字节序(为大端字节序),但 PC 工业界(包括 ACPI , UEFI 和微软的规范)都对前面的三个域 time_low, time_mid , time_hi_and_version 使用小端字节序。同样地,电报格式的编码也被用来描述 SMBIOS 规范中的 UUID 。因此, UUID {00112233-4455-6677-8899-AABBCCDDEEFF} 被表示为 33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF 。如果 ID 的所有字节都为 FFh ,表示当前 ID 在系统中不存在但可以被设置。如果所有字节都为 00h ,表示 ID 在系统中不存在。

对于唤醒类型域, 00h 保留, 01h 为其他, 02h 未知, 03h 为 APM 定时器, 04h 调制解调器拨响, 05h 为 LAN 远程, 06h 电源开关, 07h 为PCI PME# , 08h 为 AC 电源恢复。

其他各个结构表的详细格式可参考 SMBIOS 规范,在 http://www.dmtf.org/standards/smbios 处。这里主要概述一下主要结构表所包含的对我们有用重要信息。

基板或模块单元信息 (Type 2) :制造商、产品名、版本、序列号、资产标签、特征标志、基板在底架上的位置、底架句柄、基板类型、包含的对象句柄个数、包含的个各对象句柄。

系统外围或底架 (Type 3) :制造商、类型、版本、序列号、资产标签号、启动状态、供电电源状态、热量状态、安全状态、 OEM 定义信息、高度、电源线个数、包含的单元个数、包含的单元记录长度、包含的各个单元、 SKU 号。

处理器信息 (Type 4) :插座指示、处理器类型、处理器家族、制造商、 ID 、版本、电压伏特数、外部时钟频率 (MHz) 、最大速率 (MHz) 、当前速率、状态、处理器升级、 L1 级缓存信息结构的句柄、 L2 级缓存信息结构的句柄、 L3 级缓存信息结构的句柄、序列号、资产标签、部件号码、内核个数、激活的内核个数、线程个数、处理器特征、处理器家族 2 。

高速缓存信息 (Type 7) :插座指示、缓存配置、最大缓存容量、已安装的容量、支持的 SRAM 类型、当前 SRAM 类型、缓存速率、纠错类型、系统缓存类型、关联性。

端口连接器信息 (Type 8) :例如并口、串口、键盘、鼠标器端口等都属于端口连接器。包含的信息有内部引用指示符、内部连接器类型、外部引用指示符、外部引用类型、端口类型。

系统插槽 (Type 9) :插槽指示符、插槽类型、插槽数据总线宽度、当前是否在使用、插槽长度、插槽 ID 、插槽特征 1 、插槽牲 2 、段组编号、总线编号、设备 / 函数编号。

OEM 字符串 (Type 11) :由 OEM 指定的描述字符串。

系统配置选项 (Type 12) :用来配置基板跳线和开关的信息字符串。

BIOS 语言信息 (Type 13) :可安装的语言、标志位字节、保留的 15 字节、当前语言。这里的语言是指英语、法语、汉语等国家语言,而不是计算机编程语言。

组相联 (Type 14) :组名、本项的类型、本结构的句柄。级相联用于指明某些部件的布局或层次,例如指明两个 CPU 共享一个外部缓存系统。

系统事件日志 (Type 15) :事件日志存放在非易失性的存储单元中,占据固定长度的区域, 以 一个固定长度(和特定于厂商)的头部开始,后跟一个或多个可变长度的日志记录。应用程序可以通过周期性地读取系统事件日志结构(通过它的句柄)并在日志改 变标记中搜索日志的更新,以实现事件日志改变通知。这里日志改变标记唯一地标识事件日志最后一次更新的时间。本结构包含的信息有日志区域长度、日志头部起 始偏移、日志数据起始偏移、存取方法(如索引 I/O, 内存映射物理地址,通知目的非易失性数据函数等)、日志状态、日志改变标记、存取方法地址、日志头部格式、支持的日志类型描述符、每个日志类型描述符的长度、日志类型描述符列表。这里存取方法地址可用以下联合类型来表示:

  1. union {  
  2.          struct {  
  3.                    short IndexAddr;  
  4.                    short DataAddr;  
  5.          } IO;  
  6.          long PhysicalAddr32;  
  7.          short GPNVHandle;  
  8. } AccessMethodAddress;  



每个日志记录的格式都包含 8 字节的记录头部(事件类型、长度、日期时间域),后跟不定长的日志数据。

物理存储器阵列 (Type 16) :位置(系统板卡或附加板卡上)、存储功能、内存纠错、最大容量、存储错误信息句柄、存储设备数目、已扩展的最大容量。

存储设备 (Type 17) : 用于描述物理存储器阵列中的单个存储设备。在物理存储器阵列中的句柄、存储错误信息句柄、存储总宽度、数据宽度、存储容量、形体尺寸、设备集、设备定位 器、记忆槽定位器、存储器类型、类型额外细节、速率、制造商、序列号、资产标签、部件号码、属性标志、已扩展的容量、已配置的存储时钟速率。

32-bit 错误信息 (Type 18) :错误类型、错误粒度、错误操作、特定于制造商的错误表现、存储阵列错误地址、设备错误地址、错误解析。

存储阵列映射地址 (Type 19) :起始地址、结束地址、存储阵列句柄、分区宽度、已扩展的起始地址、已扩展的结束地址。

存储设备映射地址 (Type 20) :起始地址、结束地址、存储设备句柄、存储阵列映射地址句柄、分区行位置、交叉位置、交叉的数据深度、已扩展的起始地址、已扩展的结束地址。

内置指针设备 (Type 21) :指针设备类型、接口类型、按钮个数。

智能电池 (Type 22) :位置、制造商、制造日期、序列号、设备名、设备化学属性、设计容量、设计电压伏特数、 SBDS 版本号、电池数据的最大错误百分比、 SBDS 序列号、 SBDS 制造日期、 SBDS 设备化学属性、设计容量倍增因子、特定于 OEM 的信息。

系统引导信息 (Type 32) :保留域 (00h) 、引导状态描述 (10 字节 ) 。引导状态描述主要有“没有检测到错误”、“没有可引导的媒介”、“操作系统载入失败”、“ BIOS 硬件检测失败”、“操作系统硬件检测失败”、“用户请求引导(通过一个按键)”、“违反系统安全”、“预先请求映像(通过 PXE 引导)”、“系统监控记时器激活,导致系统重启”,特定于厂商引导状态描述等。

IPMI 设备信息 (Type 38) : BMC 接口类型、 IPMI 规范修改版本、 I2C 从地址、 NV 存储设备地址、基地址、基地址修饰符 / 中断信息、中断号。

不活动指示 (Type 126) :用来表明某个 SMBIOS 结构当前不活动,因此不应用被上层的软件使用。它没有字符串区域,只有结构头部(即三个头部域类型、长度、句柄)。

表格结束指示 (Type 127) :表示整个 SMBIOS 结构表的结束。它也只有结构头部,没有字符串区域。

可以看出,很多设备都包含一些类似的信息域,比如制造商、产品 ID(SKU 号 ) 、产品名称、版本、出厂日期、序列号、资产标签号、设备类型等。

应用软件可以使用下面的方法来解析基于表格的 SMBIOS 结构。 FindStructure 函数用于查找指定类型的第一个结构(注意同一个类型的结构可能会多个),返回这个结构的句柄,如果没找到,则返回 0xFFFF 。 TableAddress 和 StructureCount 的值可以通过在内存中定位 EPS 表来获得, EPS 中偏移 18h 处即为 TableAddress 的值,偏移 1Ch 处即为 StructureCount 的值。

  1. typedef unsigned short ushort;  
  2. typedef unsigned char uchar;  
  3. typedef struct {  
  4.          uchar Type;  
  5.          uchar Length;  
  6.          ushort Handle;  
  7. } HEADER;  
  8. ushort FindStructure(char* TableAddress, ushort StructureCount, uchar Type) {  
  9.          ushort i, handle;  
  10.          uchar lasttype;  
  11.          i=0;  
  12.          handle=0xFFFF;  
  13.          /* 从整个SMBIOS的首个结构开始搜索指定类型的第一个结构 */  
  14.          while(i<StructureCount && handle==0xFFFF) {  
  15.                    i++;  
  16.                    lasttype=((HEADER *)TableAddress)->Type; /* 获取当前结构的类型 */  
  17.                    if(lasttype==Type) {  
  18.                             handle=((HEADER *)TableAddress)->Handle;  
  19.                    }else {  
  20.                             /* 移动到当前结构的字符串区域处 */  
  21.                             TableAddress+=((HEADER *)TableAddress)->Length;  
  22.                             while(*((int *)TableAddress)!=0) /* 移动到字符串区域的结尾符0000h处 */  
  23.                                      TableAddress++;  
  24.                             TableAddress+=2;  /* 跳过0000h,移动到下一结构起始处 */  
  25.                    }  
  26.          }  
  27.          return ha## 标题 ##ndle;  
  28. }  


   

2015-06-09 19:44:16 stupid_haiou 阅读数 648

 SMBIOS 2.3 版本开始,兼容 SMBIOS 的实现必须包含以下 10 个数据表结构: BIOS 信息 (Type 0) 系统信息 (Type 1)、系统外围或底架 (Type 3) 、处理器信息 (Type 4) 、高速缓存信息 (Type 7) 、系统插槽 (Type 9) 、物理存储阵列 (Type 16) 、存储设备 (Type 17) 、存储阵列映射地址 (Type 19) 、系统引导信息 (Type 32) 

其他的结构表则可根据实际来决定是否需要实现。最新的 SMBIOS 2.7.0 版中定义的所有结构表包括 Type 0-Type 42 ,其中Type 5  Type 6  Type 10 已经废弃。另外还有 Type 126  Type 127 两个特殊结构表。如下:

BIOS 信息 (Type 0) 、系统信息 (Type 1) 、基板(或模块)信息 (Type 2) 、系统外围或底架 (Type 3) 、处理器信息 (Type 4) 、存储控制器信息 (Type 5,  废弃 ) 、存储模块信息 (Type 6, 已废弃 ) 、调整缓存信息 (Type 7) 、端口连接器信息 (Type 8)、系统插槽 (Type 9) 

板载设备信息 (Type 10)  OEM 字符串 (Type 11) 、系统配置选项 (Type 12)  BIOS 语言信息 (Type 13) 、组相联 (Type 14) 、系统事件日志 (Type 15) 、物理存储阵列 (Type 16) 、存储设备 (Type 17)  32-bit 内存错误信息 (Type 18) 、存储阵列映射地址 (Type 19) 

存储设备映射地址 (Type 20) 、内建指针设备 (Type 21) 、便携式电池 (Type 22) 、系统重置 (Type 23) 、硬件安全 (Type 24) 、系统电源控制 (Type 25) 、电压探针 (Type 26) 、冷却设备 (Type 27) 、温度传感器 (Type 28) 、电流探头 (Type 29) 

越界远程访问 (Type 30) 、引导完整性服务 (BIS) 入口点 (Type 31) 、系统引导信息 (Type 32)  64-bit 内存错误信息 (Type 33) 、管理设备 (Type 34) 、管理设备组件 (Type 35) 、管理设备门槛数据 (Type 36) 、存储信道 (Type 37)  IPMI 设备信息(Type 38) 、系统供电电源 (Type 39) 

附加信息 (Type 40) 、板载设备扩展信息 (Type 41) 、管理控制器主机接口 (Type 42) 

不活动指示 (Type 126) 、表格结束指示 (Type 127) 

SMBIOS 的大部分结构表数据在 CIM 模型中都有对应的实现,比如底架信息对应 CIM_Chassis ,处理器信息对应CIM_Processor ,内存信息对应 CIM_Memory 等。

下面介绍主要的结构表。

1  BIOS 信息 (Type 0) 

位置

名称

长度

描述

00h

TYPE 

1BYTE

结构的TYPE 号,此处是0

01h

长度

1BYTE

TYPE 0 格式区域的长度,一般为14h ,也有13h

02h

句柄

2BYTE

指向本结构的句柄

04h

Bios 厂商信息

1BYTE

此处是bios 卖方的信息,可能是OEM 厂商名,一般为01h ,代表紧随格式区域后的字符串区域的第一个字符串

05h

BIOS 版本

1BYTE

BIOS 版本号,一般为02h ,代表字符串区域的第二个字符串

06h

Bios 开始地址段

2BYTE

用于计算常驻BIOS 镜像大小的计算,方法为

10000h-BIOS 开始地址段)×16

08h

BIOS 发布日期

1BYTE

一般为03h ,表示字符区第三个字符串

09h

BIOS ROM size

1BYTE

计算方法为(n +1 )×64K ,n 为此处读出数值

0Ah

BIOS 特征

8BYTE

Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等

12h

BIOS 特征扩展

不定

  2.4 版开始包含两个扩展字节(12h-13h) ,以及字节14h-17h

14h

BIOS 版本主号

1BYTE

若系统不支持此域,则值设为ffh

15h

BIOS 版本从号

1BYTE

若系统不支持此域,则值设为ffh

16h

内置控制器固件的版主号

1BYTE

若系统不支持此域,则值设为ffh

17h

内置控制器固件的版从号

1BYTE

若系统不支持此域,则值设为ffh

BIOS 特征域表示BIOS 对一些特性的支持情况,Bit 1 和Bit 1 保留,Bit 2 未知,Bit 3 表示本BIOS 特征域是否被支持。

Bit 4-Bit 19 分别表示是否支持ISA 、MCA 、EISA 、PCI 、PCMCIA 、PnP 、APM 、BIOS 刷新、BIOS 影像(把 BIOS 的只读内容拷贝到快一些的内存中)、VL-VESA 、ESCD 、从CD 引导、可选择的引导、BIOS ROM 是否被插装、从PCMCIA 引导、EDD 规范。

Bit 20-Bit 25 表示对各种型号的软盘支持情况(均为中断Int 13h ),包括NEC 9800 1.2MB 软盘、Toshiba 1.2MB 软盘、5.25”/360KB 软盘、5.25”/1.2MB 软盘、3.5”/720KB 软盘、3.5”/2.88MB 软盘。

Bit 26-Bit  29 表示是否支持中断Int 5h(PrintScreen 屏幕截取服务) 、Int 9h(8042 键盘服务) 、Int 14h( 串口服务) 、Int 17h( 打印机服务) 、Int 10h(CGA/Mono 视频服务) 、NEC PC-98 。

Bit 32:47 保留给BIOS 厂商使用。

Bit 48:63 保留给计算机系统厂商使用。

BIOS 特征扩展字节1( 偏移12h 处) 表示对另外一些设备的支持情况,Bit 0-Bit 1 分别表示是否支持ACPI 、USB 遗留、AGPI2O 引导、LS-120 SuperDisk 引导、ATAPI ZIP 驱动器引导、1394 引导、智能电池。

BIOS 特征扩展字节2( 偏移13h 处) 表示对一些高级特性的支持情况。Bit 1-Bit3 表示是否支持功能键初始化网络服务引导、激活目标内容分发、UEFI 规范。Bit 4 表示SMBIOS 表描述的是一台虚拟机,Bit 5:7 保留。

2 、系统信息 (Type 1)  SMBIOS 实现只关联一个单一的系统实例,并且包含且只包含一个系统信息结构。

位置

名称

长度

描述

00h

TYPE 

1BYTE

结构的TYPE 号,此处是1

01h

长度

1BYTE

格式区域总长度,2.0 版为08h ,2.1-2.3.4 版为19h,从2.4 版开始为1Bh

02h

句柄

2BYTE

指向本结构的句柄

04h

电脑制造商

1BYTE

一般为01h ,表示在字符串区域中的编号

05h

产品名称

1BYTE

在字符串区域中的编号

06h

版本号

1BYTE

在字符串区域中的编号

07h

序列号

1BYTE

在字符串区域中的编号

09h

UUID

16BYTE

通用唯一标识符

18h

唤醒类型

BYTE

用来标识导致系统开电启动的事件

19h

SKU 

BYTE

在字符串区域中的编号,SKU 号通常为产品ID 或采购订单号

1Ah

产品家族

1BYTE

在字符串区域中的编号

UUID  128 bit 长,是一个穿越时间和空间的唯一标识符,不需要集中的注册过程。它的格式在 RFC 4122 有详细的描述,内容比较繁锁,但 SMBIOS 并不关注这个,它只关注字节序。 UUID 的字节顺序以及在 RFC 4122 中对应域的名字如下:

time_low :偏移 00h 处, 4BYTEs ,为时间戳的低位域部分。

time_mid  04h 处, 2BYTEs  为时间戳的中间域部分。

time_hi_and_version  06h 处, 2BYTEs ,为时间戳的高位域,同时还包含版本号。

clock_seq_hi_and_reserved  08h 处, 1BYTE ,时钟序列的高位域部分,同时还包含保留部分。

clock_seq_low  09h 处, 1BYTE ,时钟序列的低位域部分。

Node  0Ah 处, 6BYTEs ,唯一结点标识符。

虽然 RFC 4122 建议对所有域都使用网络字节序(为大端字节序),但 PC 工业界(包括 ACPI  UEFI 和微软的规范)都对前面的三个域 time_low  time_mid  time_hi_and_version 使用小端字节序。同样地,电报格式的编码也被用来描述 SMBIOS规范中的 UUID 。因此, UUID {00112233-4455-6677-8899-AABBCCDDEEFF} 被表示为 33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF 。如果 ID 的所有字节都为 FFh ,表示当前 ID 在系统中不存在但可以被设置。如果所有字节都为 00h ,表示 ID 在系统中不存在。

对于唤醒类型域, 00h 保留, 01h 为其他, 02h 未知, 03h  APM 定时器, 04h 调制解调器拨响, 05h  LAN 远程,06h 电源开关, 07h  PCI PME#  08h  AC 电源恢复。

其他各个结构表的详细格式可参考 SMBIOS 规范,在 http://www.dmtf.org/standards/smbios 处。这里主要概述一下主要结构表所包含的对我们有用重要信息。

基板或模块单元信息 (Type 2) :制造商、产品名、版本、序列号、资产标签、特征标志、基板在底架上的位置、底架句柄、基板类型、包含的对象句柄个数、包含的个各对象句柄。

系统外围或底架 (Type 3) :制造商、类型、版本、序列号、资产标签号、启动状态、供电电源状态、热量状态、安全状态、OEM 定义信息、高度、电源线个数、包含的单元个数、包含的单元记录长度、包含的各个单元、 SKU 号。

处理器信息 (Type 4) :插座指示、处理器类型、处理器家族、制造商、 ID 、版本、电压伏特数、外部时钟频率 (MHz) 、最大速率 (MHz) 、当前速率、状态、处理器升级、 L1 级缓存信息结构的句柄、 L2 级缓存信息结构的句柄、 L3 级缓存信息结构的句柄、序列号、资产标签、部件号码、内核个数、激活的内核个数、线程个数、处理器特征、处理器家族 2 

高速缓存信息 (Type 7) :插座指示、缓存配置、最大缓存容量、已安装的容量、支持的 SRAM 类型、当前 SRAM 类型、缓存速率、纠错类型、系统缓存类型、关联性。

端口连接器信息 (Type 8) :例如并口、串口、键盘、鼠标器端口等都属于端口连接器。包含的信息有内部引用指示符、内部连接器类型、外部引用指示符、外部引用类型、端口类型。

系统插槽 (Type 9) :插槽指示符、插槽类型、插槽数据总线宽度、当前是否在使用、插槽长度、插槽 ID 、插槽特征 1 、插槽牲 2 、段组编号、总线编号、设备 / 函数编号。

OEM 字符串 (Type 11) :由 OEM 指定的描述字符串。

系统配置选项 (Type 12) :用来配置基板跳线和开关的信息字符串。

BIOS 语言信息 (Type 13) :可安装的语言、标志位字节、保留的 15 字节、当前语言。这里的语言是指英语、法语、汉语等国家语言,而不是计算机编程语言。

组相联 (Type 14) :组名、本项的类型、本结构的句柄。级相联用于指明某些部件的布局或层次,例如指明两个 CPU 共享一个外部缓存系统。

系统事件日志 (Type 15) :事件日志存放在非易失性的存储单元中,占据固定长度的区域, 以 一个固定长度(和特定于厂商)的头部开始,后跟一个或多个可变长度的日志记录。应用程序可以通过周期性地读取系统事件日志结构(通过它的句柄)并在日志改 变标记中搜索日志的更新,以实现事件日志改变通知。这里日志改变标记唯一地标识事件日志最后一次更新的时间。本结构包含的信息有日志区域长度、日志头部起 始偏移、日志数据起始偏移、存取方法(如索引 I/O, 内存映射物理地址,通知目的非易失性数据函数等)、日志状态、日志改变标记、存取方法地址、日志头部格式、支持的日志类型描述符、每个日志类型描述符的长度、日志类型描述符列表。这里存取方法地址可用以下联合类型来表示:

  1. union {  
  2.          struct {  
  3.                    short IndexAddr;  
  4.                    short DataAddr;  
  5.          } IO;  
  6.          long PhysicalAddr32;  
  7.          short GPNVHandle;  
  8. } AccessMethodAddress;  

每个日志记录的格式都包含 8 字节的记录头部(事件类型、长度、日期时间域),后跟不定长的日志数据。

物理存储器阵列 (Type 16) :位置(系统板卡或附加板卡上)、存储功能、内存纠错、最大容量、存储错误信息句柄、存储设备数目、已扩展的最大容量。

存储设备 (Type 17) : 用于描述物理存储器阵列中的单个存储设备。在物理存储器阵列中的句柄、存储错误信息句柄、存储总宽度、数据宽度、存储容量、形体尺寸、设备集、设备定位 器、记忆槽定位器、存储器类型、类型额外细节、速率、制造商、序列号、资产标签、部件号码、属性标志、已扩展的容量、已配置的存储时钟速率。

32-bit 错误信息 (Type 18) :错误类型、错误粒度、错误操作、特定于制造商的错误表现、存储阵列错误地址、设备错误地址、错误解析。

存储阵列映射地址 (Type 19) :起始地址、结束地址、存储阵列句柄、分区宽度、已扩展的起始地址、已扩展的结束地址。

存储设备映射地址 (Type 20) :起始地址、结束地址、存储设备句柄、存储阵列映射地址句柄、分区行位置、交叉位置、交叉的数据深度、已扩展的起始地址、已扩展的结束地址。

内置指针设备 (Type 21) :指针设备类型、接口类型、按钮个数。

智能电池 (Type 22) :位置、制造商、制造日期、序列号、设备名、设备化学属性、设计容量、设计电压伏特数、 SBDS 版本号、电池数据的最大错误百分比、 SBDS 序列号、 SBDS 制造日期、 SBDS 设备化学属性、设计容量倍增因子、特定于 OEM的信息。

系统引导信息 (Type 32) :保留域 (00h) 、引导状态描述 (10 字节 ) 。引导状态描述主要有“没有检测到错误”、“没有可引导的媒介”、“操作系统载入失败”、“ BIOS 硬件检测失败”、“操作系统硬件检测失败”、“用户请求引导(通过一个按键)”、“违反系统安全”、“预先请求映像(通过 PXE 引导)”、“系统监控记时器激活,导致系统重启”,特定于厂商引导状态描述等。

IPMI 设备信息 (Type 38)  BMC 接口类型、 IPMI 规范修改版本、 I2C 从地址、 NV 存储设备地址、基地址、基地址修饰符 /中断信息、中断号。

不活动指示 (Type 126) :用来表明某个 SMBIOS 结构当前不活动,因此不应用被上层的软件使用。它没有字符串区域,只有结构头部(即三个头部域类型、长度、句柄)。

表格结束指示 (Type 127) :表示整个 SMBIOS 结构表的结束。它也只有结构头部,没有字符串区域。

可以看出,很多设备都包含一些类似的信息域,比如制造商、产品 ID(SKU  ) 、产品名称、版本、出厂日期、序列号、资产标签号、设备类型等。

应用软件可以使用下面的方法来解析基于表格的 SMBIOS 结构。 FindStructure 函数用于查找指定类型的第一个结构(注意同一个类型的结构可能会多个),返回这个结构的句柄,如果没找到,则返回 0xFFFF  TableAddress  StructureCount 的值可以通过在内存中定位 EPS 表来获得, EPS 中偏移 18h 处即为 TableAddress 的值,偏移 1Ch 处即为 StructureCount 的值。

  1. typedef unsigned short ushort;  
  2. typedef unsigned char uchar;  
  3. typedef struct {  
  4.          uchar Type;  
  5.          uchar Length;  
  6.          ushort Handle;  
  7. } HEADER;  
  8. ushort FindStructure(char* TableAddress, ushort StructureCount, uchar Type) {  
  9.          ushort i, handle;  
  10.          uchar lasttype;  
  11.          i=0;  
  12.          handle=0xFFFF;  
  13.          /* 从整个SMBIOS的首个结构开始搜索指定类型的第一个结构 */  
  14.          while(i<StructureCount && handle==0xFFFF) {  
  15.                    i++;  
  16.                    lasttype=((HEADER *)TableAddress)->Type; /* 获取当前结构的类型 */  
  17.                    if(lasttype==Type) {  
  18.                             handle=((HEADER *)TableAddress)->Handle;  
  19.                    }else {  
  20.                             /* 移动到当前结构的字符串区域处 */  
  21.                             TableAddress+=((HEADER *)TableAddress)->Length;  
  22.                             while(*((int *)TableAddress)!=0) /* 移动到字符串区域的结尾符0000h处 */  
  23.                                      TableAddress++;  
  24.                             TableAddress+=2;  /* 跳过0000h,移动到下一结构起始处 */  
  25.                    }  
  26.          }  
  27.          return handle;  
2016-12-17 10:26:08 ronnieh7 阅读数 3315

1.SMBIOS简介

SMBIOSSystem Management BIOS的概念, SMBIOS是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范, 显示有关系统管理BIOS主板的信息。SMBIOS 使您能够找出有关的计算机制造商的详细信息、 模型类型、 序列号和 BIOS 版本、 处理器数量、 物理内存,等等。

对一些特殊制定需求,如只能针对某一厂商机器使用,往往需要通过获取SMBIOS内容解析得到该信息,添加到代码中,实现该自定义定制需求。本章主要介绍windows API获取SMBIOS信息的方法

2.SMBIOS结构信息

 SMBIOS 2.3 版本开始,兼容 SMBIOS 的实现必须包含以下 10 个数据表结构: BIOS 信息 (Type 0)  、系统信息 (Type 1) 、系统外围或底架(Type 3) 、处理器信息 (Type 4) 、高速缓存信息 (Type 7) 、系统插槽 (Type 9) 、物理存储阵列 (Type 16) 、存储设备 (Type 17) 、存储阵列映射地址(Type 19) 、系统引导信息 (Type 32) 

其他还有一些根据需求实现的类型,如存储设备映射地址 (Type 20) 、内建指针设备 (Type 21) 、便携式电池 (Type 22) 等等,

每种类型都有对应的结构表,详细信息可以根据不同类型去查阅对应结构表去解析,本文主要以解析BIOS(Type 0)为例。

 SMBIOS结构表BIOS(Type 0) 参考图1-1

参考网页:http://blog.csdn.net/zhoudaxia/article/details/5919871

 

图1-1

 

3.Windows API获取SMBIOS流程

Windows提供了直接API GetSystemFirmwareTable 来获取SMBIOS信息。该函数从firmware table provider中检索特定的firmware table信息。请参考: https://msdn.microsoft.com/zh-cn/library/ms724379%28v=VS.85%29.aspx?f=255&MSPPError=-2147217396

详细使用流程:

3.1 调用GetSystemFirmwareTable 获取信息buffer大小

DWORD iSignature =             'R'; 
iSignature = iSignature << 8 | 'S';
iSignature = iSignature << 8 | 'M';
iSignature = iSignature << 8 | 'B';
int iBufferSizeNeeded = GetSystemFirmwareTable( iSignature, 0, 0, 0 );

参数只有FirmwareTableProviderSignature,其余都是NULL即可。

iSignature=RSMB,代表是SMBIOS firmware table provider,将是获取该部分内容。

 

其中,FirmwareTableProviderSignature参数列表:

Value

Meaning

'ACPI'

The ACPI firmware table provider.

'FIRM'

The raw firmware table provider.

'RSMB'

The raw SMBIOS firmware table provider.

3.2 再次调用GetSystemFirmwareTable 获取RawSMBIOSData内容

char buff[1024*2] = {0};
GetSystemFirmwareTable( iSignature, 0, buff, iBufferSizeNeeded);

这里的buff内容就是RawSMBIOSData内容

 

3.3 解析RawSMBIOSData内容

首先我们要先了解该结构体才能去解析内容,在windows.h定义如下:

struct RawSMBIOSData
{
    BYTE    Used20CallingMethod;
    BYTE    SMBIOSMajorVersion;
    BYTE    SMBIOSMinorVersion;
    BYTE    DmiRevision;
    DWORD    Length;
    BYTE    SMBIOSTableData[];
};

从结构体中,我们可以获取SMBIOS的版本信息和长度以及SMBIOSTableData的信息,
RawSMBIOSData *p;
p = (RawSMBIOSData *)buff;
获取SMBIOSTableData buffer的首地址:
p->SMBIOSTableData = (BYTE *)(buff + 8);//跳过前面8个字节

SMBIOSTableData可以理解为 格式区+数据内容 两部分,而格式区有一个头,类似:

struct SMBios_Thunk  

{  

    BYTE flag;  

    BYTE data_offset;  

}; 

flag是硬件类型,data_offset表示该格式区长度。所有type SMBIOS结构表都有这个头存在。后面我们就可以通过移动p->SMBIOSTableData 指针以及SMBIOS type的结构表来获取具体的硬件信息了。详细参考下面SMBIOS数据解析示例。

4.SMBIOS数据解析示例

如上面所讲,SMBIOSTableData可以理解为格式区+数据内容两部分组成,可以直接获取前两个字节分析出type类型(本例type=0)和格式区长度(本例length =18)

另外可以通过下面通俗解释确认信息段,该段数据以00 00结尾(因为字符串以00结尾,类型块结尾也是00,所以可以理解为遇到00 00为一个类型段结束),如图4-1,棕色线部分为一个完整类型段:

 

图4-1

 

根据上述信息,获取type 0字段,分析结构区以找出需要读取内容,注意红色标记部分的查找方式,结构区字符如下图4-2

 

图4-2

根据SMBIOS结构表分析(举例字节见红色线,格式区长度数据见黄色线标注):

1个字节offset 0,为0x00,代表结构的TYPE号为0

2个字节offset 10x18,代表格式区长度为18,即图中蓝色段;

3-42 bytes)字节offset 2,30x01 0x00,本结构的句柄,

5字节offset 40x01, 为厂商信息,位置在格式区后紧跟的第1个字符串

......

格式区后面紧跟的为字符串内容:根据格式区指定index查找。每个字符串以00结尾,参考下图绿线部分

 

图4-3

01个字符串:..A...Dell Inc.

02个字符串:A11

.......

依次获取SMBIOS中信息

同样,以对应SMBIOS方式分析其他类型段的内容。

 

5.WMIC方法

上面描述的方式需要有一定的软件编程背景和对SMBIOS有一定解析能力基础者使用,对于普通使用者并不太适用,比较幸运的是windows提供另一种简便快捷的方法,只需通过命令行方式就能获取这些BIOS信息,就是使用WMIC

WMIC扩展WMIWindows Management InstrumentationWindows管理工具) ,提供了从命令行接口和批命令脚本执行系统管理的支持。它是一个非常强大的管理工具。我们单独就查询BIOSManufacturer信息举例说明如何使用。

1.开启命令行,运行” WMIC”进入WMIC命令行环境,如果你不知道如何操作可以输入 /? 查询。


在帮助中信息中可以看到

 

2.我们需要知道BIOS信息,输入 bios /?

 

可以看到其完整支持命令和使用方法,因为我们只是想要获取其信息,那么很明显就是使用get命令。

3.同样的方式来确认get 的使用

 

因为我们需要获取的是厂商信息,即对应Manufacturer属性的使用

4.完整输入获取命令: wmic bios get manufacturer

 

你可以得到Manufacturer信息。

同样的,你可以类似方法去其他信息获取或操作,对不确定使用/?来查询辅助。

 

另外,

在命令行环境上,当然你也可以直接输入完整命令:wmic bios get manufacturer,获取设备厂商信息,结果和操作与上面都是一致的。

 

SMBIOS

阅读数 1751

smbios I-概念

阅读数 25

SMBIOS 转帖

阅读数 1322

SMBIOS-初识

阅读数 47