UEFI学习——SMBIOS笔记

365bet官网多少 🗓 2025-08-07 14:02:48 ✍ admin 👁 5921 👍 902
UEFI学习——SMBIOS笔记

SMBIOS是一套规范,对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息,这里对其介绍不再赘述。本篇文章是将我自己对EDKII代码中SMBIOS的结构体的理解进行一个总结,并结合EDKII里的两个函数对读取SMBIOS信息的程序逻辑提供一个大概的思路。

本博客介绍的SMBIOS是使用32位的EPS表(即根据SMBIOS 2.1规范)

1.SMBIOS结构介绍

上面的图包含了几个独立的结构体,因为在读取SMBIOS信息时会将它们结合起来读取,所以我将他们结合到一张图上。从左到右第一个是SMBIOS的EPS(Entry Point Structure)表的结构体,其结构体内容即简单注释如下:

typedef struct {

UINT8 AnchorString[4]; //关键字 固定是”_SM_”

UINT8 EntryPointStructureChecksum; //校验和 用于校验数据

UINT8 EntryPointLength; //表结构长度 Entry Point Structure 表的长度

UINT8 MajorVersion; //Major版本号 用于判断SMBIOS 版本

UINT8 MinorVersion; //Minor版本号 用于判断SMBIOS 版本

UINT16 MaxStructureSize; //表结构大小

UINT8 EntryPointRevision; //EPS修正

UINT8 FormattedArea[5]; //格式区域 存放解释EPS修正的信息

UINT8 IntermediateAnchorString[5]; //关键字 固定为“_DMI_”

UINT8 IntermediateChecksum; //校验和 Intermediate Entry Point Structure (IEPS)的校验和

UINT16 TableLength; //结构表长度 SMBIOS 结构表的长度(总长度)

UINT32 TableAddress; //结构表地址 SMBIOS 结构表的真实内存位置

UINT16 NumberOfSmbiosStructures; //结构表个数 SMBIOS 结构表数目

UINT8 SmbiosBcdRevision; //Smbios BCD 修正

} SMBIOS_TABLE_ENTRY_POINT;

此结构体中的TableAddress成员存放的就是SMBIOS结构表在内存中的位置,凭借它就可以知道SMBIOS结构表的首地址,并且可以开始读取SMBIOS结构表信息。

中间的是一个联合体(共用体),结构体内容如下:

typedef union {

SMBIOS_STRUCTURE *Hdr;

SMBIOS_TABLE_TYPE0 *Type0;

SMBIOS_TABLE_TYPE1 *Type1;

SMBIOS_TABLE_TYPE2 *Type2;

SMBIOS_TABLE_TYPE3 *Type3;

SMBIOS_TABLE_TYPE4 *Type4;

SMBIOS_TABLE_TYPE5 *Type5;

SMBIOS_TABLE_TYPE6 *Type6;

SMBIOS_TABLE_TYPE7 *Type7;

SMBIOS_TABLE_TYPE8 *Type8;

SMBIOS_TABLE_TYPE9 *Type9;

SMBIOS_TABLE_TYPE10 *Type10;

SMBIOS_TABLE_TYPE11 *Type11;

SMBIOS_TABLE_TYPE12 *Type12;

SMBIOS_TABLE_TYPE13 *Type13;

SMBIOS_TABLE_TYPE14 *Type14;

SMBIOS_TABLE_TYPE15 *Type15;

SMBIOS_TABLE_TYPE16 *Type16;

SMBIOS_TABLE_TYPE17 *Type17;

SMBIOS_TABLE_TYPE18 *Type18;

SMBIOS_TABLE_TYPE19 *Type19;

SMBIOS_TABLE_TYPE20 *Type20;

SMBIOS_TABLE_TYPE21 *Type21;

SMBIOS_TABLE_TYPE22 *Type22;

SMBIOS_TABLE_TYPE23 *Type23;

SMBIOS_TABLE_TYPE24 *Type24;

SMBIOS_TABLE_TYPE25 *Type25;

SMBIOS_TABLE_TYPE26 *Type26;

SMBIOS_TABLE_TYPE27 *Type27;

SMBIOS_TABLE_TYPE28 *Type28;

SMBIOS_TABLE_TYPE29 *Type29;

SMBIOS_TABLE_TYPE30 *Type30;

SMBIOS_TABLE_TYPE31 *Type31;

SMBIOS_TABLE_TYPE32 *Type32;

SMBIOS_TABLE_TYPE33 *Type33;

SMBIOS_TABLE_TYPE34 *Type34;

SMBIOS_TABLE_TYPE35 *Type35;

SMBIOS_TABLE_TYPE36 *Type36;

SMBIOS_TABLE_TYPE37 *Type37;

SMBIOS_TABLE_TYPE38 *Type38;

SMBIOS_TABLE_TYPE39 *Type39;

SMBIOS_TABLE_TYPE40 *Type40;

SMBIOS_TABLE_TYPE41 *Type41;

SMBIOS_TABLE_TYPE42 *Type42;

SMBIOS_TABLE_TYPE43 *Type43;

SMBIOS_TABLE_TYPE126 *Type126;

SMBIOS_TABLE_TYPE127 *Type127;

UINT8 *Raw;

} SMBIOS_STRUCTURE_POINTER;

其第一个成员是SMBIOS_STRUCTURE类型(结构体代码在下面)的变量,SMBIOS结构表的每个Type都有一个此类型的成员,也就是上图最右边的结构体,SMBIOS_STRUCTURE定义了每个SMBIOS_TABLE_TYPE的Type,Length和Handle。SMBIOS_STRUCTURE_POINTER中间的成员定义了SMBIOS结构表的Type,SMBIOS结构表是由一个个Type组成的,每个Type有自己的结构体,但是各个Type结构体的成员不完全一样,最后一个成员UINT8 *Raw在EDKII代码中是要被赋值为EPS结构体中TableAddress成员的内容,也就是SMBIOS结构体初始地址。

第三个结构体内容为:

typedef struct {

SMBIOS_TYPE Type;

UINT8 Length;

SMBIOS_HANDLE Handle;

} SMBIOS_STRUCTURE;

Type表示成员的类型(Type0~Typen),每个结构表都分为格式区域和字符串区域,UINT8 Length的内容只是格式区域的长度, 格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域,字符串区域的长度是不固定的。有的结构有字符串区域,有的则没有,字符串以00H结尾,字符串区域也是以00H结尾,所以只要在字符串区域找到连续的0000H,就可以找到下一个Type。

下面是我用RW软件查看本机的SMBIOS信息,以Type 0为例说明结构表的大致分布情况:

Type、Length、Handle在本结构表的前四字节,Length的值是0x1A即本结构表的格式区域大小为26字节。

2.SMBIOS相关函数

要读取SMBIOS信息首先要从系统配置表里获得SMBIOS EPS表,所需用到的函数为:

/**

This function searches the list of configuration tables stored in the EFI System

Table for a table with a GUID that matches TableGuid. If a match is found,

then a pointer to the configuration table is returned in Table, and EFI_SUCCESS

is returned. If a matching GUID is not found, then EFI_NOT_FOUND is returned.

@param TableGuid Pointer to table's GUID type..

@param Table Pointer to the table associated with TableGuid in the EFI System Table.

@retval EFI_SUCCESS A configuration table matching TableGuid was found.

@retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.

**/

EFI_STATUS

EFIAPI

EfiGetSystemConfigurationTable (

IN EFI_GUID *TableGuid,

OUT VOID **Table

);

例:Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID**)&SmbiosEpsTable);

获取到SMBIOS EPS表后就可以找到SMBIOS结构表的起始地址,将EPS表的TableAddress成员赋值给结构表的Raw成员。

SmbiosStruct->Raw = (UINT8 *) (UINTN) (SmbiosEpsTable->TableAddress);

找到起始地址就找到结构表的第一个Type,而要找其他Type就需要另一个函数:

/**

Get SMBIOS structure for the given Handle,

Handle is changed to the next handle or 0xFFFF when the end is

reached or the handle is not found.

@param[in, out] Handle 0xFFFF: get the first structure

Others: get a structure according to this value.

@param[out] Buffer The pointer to the pointer to the structure.

@param[out] Length Length of the structure.

@retval DMI_SUCCESS Handle is updated with next structure handle or

0xFFFF(end-of-list).

@retval DMI_INVALID_HANDLE Handle is updated with first structure handle or

0xFFFF(end-of-list).

**/

EFI_STATUS

LibGetSmbiosStructure (

IN OUT UINT16 *Handle,

OUT UINT8 **Buffer,

OUT UINT16 *Length

);

////////函数具体内容如下

EFI_STATUS

LibGetSmbiosStructure (

IN OUT UINT16 *Handle,

OUT UINT8 **Buffer,

OUT UINT16 *Length

)

{

SMBIOS_STRUCTURE_POINTER Smbios;

SMBIOS_STRUCTURE_POINTER SmbiosEnd;

UINT8 *Raw;

if (*Handle == INVALID_HANDLE) { //如果handle的值是0xFFFF则返回第一个结构表的Handle

*Handle = mSmbiosStruct->Hdr->Handle;

return DMI_INVALID_HANDLE;

}

if ((Buffer == NULL) || (Length == NULL)) {

return DMI_INVALID_HANDLE;

}

*Length = 0;

Smbios.Hdr = mSmbiosStruct->Hdr;

SmbiosEnd.Raw = Smbios.Raw + mSmbiosTable->TableLength; //起始地址加结构表总长度

while (Smbios.Raw < SmbiosEnd.Raw) {

if (Smbios.Hdr->Handle == *Handle) {

Raw = Smbios.Raw;

//

// Walk to next structure

//

LibGetSmbiosString (&Smbios, (UINT16) (-1)); //此函数返回给定字符串编号的SMBIOS字符串,目前没看懂。。。

//

// Length = Next structure head - this structure head

//

*Length = (UINT16) (Smbios.Raw - Raw);

*Buffer = Raw;

//

// update with the next structure handle.

//

if (Smbios.Raw < SmbiosEnd.Raw) {

*Handle = Smbios.Hdr->Handle;

} else {

*Handle = INVALID_HANDLE;

}

return DMI_SUCCESS;

}

//

// Walk to next structure

//

LibGetSmbiosString (&Smbios, (UINT16) (-1));

}

*Handle = INVALID_HANDLE;

return DMI_INVALID_HANDLE;

}

最后一个要用到的函数是LibGetSmbiosString,其内容如下:

/**

Return SMBIOS string for the given string number.

@param[in] Smbios Pointer to SMBIOS structure.

@param[in] StringNumber String number to return. -1 is used to skip all strings and

point to the next SMBIOS structure.

@return Pointer to string, or pointer to next SMBIOS strcuture if StringNumber == -1

**/

CHAR8*

LibGetSmbiosString (

IN SMBIOS_STRUCTURE_POINTER *Smbios,

IN UINT16 StringNumber

)

{

UINT16 Index;

CHAR8 *String;

ASSERT (Smbios != NULL);

//

// Skip over formatted section

//

String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);

//

// Look through unformated section

//

for (Index = 1; Index <= StringNumber; Index++) {

if (StringNumber == Index) {

return String;

}

//

// Skip string

//

for (; *String != 0; String++);

String++;

if (*String == 0) {

//

// If double NULL then we are done.

// Return pointer to next structure in Smbios.

// if you pass in a -1 you will always get here

//

Smbios->Raw = (UINT8 *)++String;

return NULL;

}

}

return NULL;

}

在EDKII源码里这个函数都是这样被调用的LibGetSmbiosString (&Smbios, (UINT16) (-1));,第二个参数是-1,查看其参数说明,是这样解释的-1 is used to skip all strings and point to the next SMBIOS structure.(-1用于跳过所有字符串,指向下一个SMBIOS结构。)此函数在源码里应该是用来跳过字符串区域,从而可以找到下一个Type。

以上是我对SMBIOS结构的理解,以及对相关函数做的笔记,比较粗糙,有时间会加上代码,用到上面列出的三个函数。

相关推荐

駁回上訴!李鐵二審維持原判刑期20年
365bet官网多少

駁回上訴!李鐵二審維持原判刑期20年

🗓 07-12 👁 6287
含有【皆是】的成语
365bet官网多少

含有【皆是】的成语

🗓 07-13 👁 8972