一、SMBIOS简介
SMBIOS的全称为System Management BIOS,它不是一个BIOS,只是与BIOS相关。它是一个规范,定义了BIOS传递给操作系统的系统管理信息。它也表示了一系列的数据结构,包含了各类信息,由BIOS启动过程中创建并放在特定的内存,之后操作系统可以拿来用。
整个 SMBIOS_STRUCTURE
结构体定义了 SMBIOS 表中的一个基本单元。每个 SMBIOS 结构都包含一个类型、长度和句柄,后面跟着与该类型相关的特定数据。这些结构体被组织成一个链表,可以在系统启动时由系统固件、操作系统或管理工具读取,以获取系统的硬件配置和管理信息。
typedef struct {
SMBIOS_TYPE Type;
UINT8 Length;
SMBIOS_HANDLE Handle;
} SMBIOS_STRUCTURE;
二、Shell下读取SMBIOS信息
首先介绍结构体_EFI_SMBIOS_PROTOCOL,用于定义在EFI应用程序和驱动程序中管理SMBIOS表的接口。其代码原型为:
struct _EFI_SMBIOS_PROTOCOL {
EFI_SMBIOS_ADD Add; //添加SMBIOS记录
EFI_SMBIOS_UPDATE_STRING UpdateString;
EFI_SMBIOS_REMOVE Remove;
EFI_SMBIOS_GET_NEXT GetNext;
UINT8 MajorVersion; ///< The major revision of the SMBIOS specification supported.
UINT8 MinorVersion; ///< The minor revision of the SMBIOS specification supported.
};
其中添加SMBIOS记录的为EFI_SMBIOS_ADD,
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_ADD)(
IN CONST EFI_SMBIOS_PROTOCOL *This, //手动添加的This指针,始终指向当前的EFI_SMBIOS_PROTOCOL实例
IN EFI_HANDLE ProducerHandle OPTIONAL, //与 SMBIOS 信息关联的控制器或驱动程序的句柄。NULL 表示没有句柄。
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, //输出参数,要添加的 SMBIOS 记录的句柄。如果为 FFFEh,则将为 SMBIOS 记录分配唯一句柄。如果 SMBIOS 句柄已在使用中,则会返回 EFI_ALREADY_STARTED,并且不会更新 SMBIOS 记录。
IN EFI_SMBIOS_TABLE_HEADER *Record //SMBIOS记录的固定部分的数据
);
更新SMBIOS信息字符串用EFI_SMBIOS_UPDATE_STRING
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_UPDATE_STRING)(
IN CONST EFI_SMBIOS_PROTOCOL *This, //EFI_SMBIOS_PROTOCOL实例
IN EFI_SMBIOS_HANDLE *SmbiosHandle, //要更新字符串的SMBIOS句柄
IN UINTN *StringNumber, //要更新的字符串
IN CHAR8 *String //用于更新的字符串
);
删除SMBIOS记录用EFI_SMBIOS_REMOVE
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_REMOVE)(
IN CONST EFI_SMBIOS_PROTOCOL *This,
IN EFI_SMBIOS_HANDLE SmbiosHandle //要删除的SMBIOS记录的句柄
);
允许调用者查找全部或部分SMBIOS记录用EFI_SMBIOS_GET_NEXT
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_GET_NEXT)(
IN CONST EFI_SMBIOS_PROTOCOL *This,
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, //进入时,指向 SMBIOS 记录的上一个句柄。退出时,指向下一个 SMBIOS 记录句柄。如果输入时为 FFFEh,则将返回第一个 SMBIOS 记录句柄。如果它在退出时返回 FFFEh,则没有更多的 SMBIOS 记录。
IN EFI_SMBIOS_TYPE *Type OPTIONAL, //进入时,它指向要返回的下一条 SMBIOS 记录的类型。如果为 NULL,则表示将返回任何类型的下一条记录。此函数不会修改 Type。
OUT EFI_SMBIOS_TABLE_HEADER **Record, //输出参数,退出时,指向指向 SMBIOS 记录的指针,该记录由格式化区域和未格式化区域组成。未格式化区域(可选)包含文本字符串。
OUT EFI_HANDLE *ProducerHandle OPTIONAL //输出参数,带有 SmbiosHandle 的 SMBIOS 记录是最后一条可用记录。
);
在使用SMBIOSProtocol的时候,首先通过LocateProtocol找到实例
Status = gBS->LocateProtocol (
&gEfiSmbiosProtocolGuid, //gEfiSmbiosProtocolGuid是一个全局变量,用于唯一标识EFI_SMBIOS_PROTOCOL
NULL,
(VOID**)&Smbios //输出参数,指向找到的EFI_SMBIOS_PROCOTOL实例
);
宏定义SMBIOS_HANDLE_PI_RESERVED设定了一个特定的值0xFFFE,这个值在SMBIOS协议中被保留用于特定的目的,SMBIOS_HANDLE通常是一个唯一标识符,用在SMBIOS表中引起特定的SMBIOS结构
//根据SMBIOS 2.7版本的6.1.2章节,以及UEFI平台初始化规范,宏定义SMBIOS_HANDLE_PI_RESERVED 设置为 0xFFFE,当 EFI_SMBIOS_PROTOCOL.Add() 函数的 Handle 参数被设置为 0xFFFE 时,它指示固件自动分配一个未被使用的句柄号给新的 SMBIOS 结构。这意味着调用者不需要指定具体的句柄号,而是由固件来选择一个合适的、未被占用的句柄号。
//0xFFFE这个值不会被用作任何其他目的,它专门用作自动分配句柄号
#define SMBIOS_HANDLE_PI_RESERVED 0xFFFE
完整代码如下:
#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/Smbios.h>
EFI_STATUS
EFIAPI
MyHelloWorldSmbiosAppEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status = EFI_SUCCESS;
EFI_SMBIOS_PROTOCOL *Smbios;
EFI_SMBIOS_HANDLE SmbiosHandle;
EFI_SMBIOS_TABLE_HEADER *Record;
DEBUG ((EFI_D_ERROR , "[MyHelloWorldSmbios] MyHelloWorldSmbiosAppEntry Start..\n"));
//
// Find the SMBIOS protocol
//
Status = gBS->LocateProtocol (
&gEfiSmbiosProtocolGuid,
NULL,
(VOID**)&Smbios
);
if (EFI_ERROR (Status)) { //需要注意的是EFI_SUCCESS通常被定义为0,即成功为0,不成功为非0
return Status;
}
SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
Status = Smbios->GetNext (Smbios, &SmbiosHandle, NULL, &Record, NULL);
while (!EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "[MyHelloWorldSmbios] %d ..\n", Record->Type));
// if (Record->Type == SMBIOS_TYPE_BIOS_INFORMATION) {
// Type0Record = (SMBIOS_TABLE_TYPE0 *) Record;
// StrIndex = Type0Record->BiosVersion;
// GetOptionalStringByIndex ((CHAR8*)((UINT8*)Type0Record + Type0Record->Hdr.Length), StrIndex, &NewString);
// FirmwareVersionString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
// if (*FirmwareVersionString != 0x0000 ) {
// FreePool (NewString);
// NewString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
// UiCustomizeFrontPageBanner (3, TRUE, &NewString);
// HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
// } else {
// UiCustomizeFrontPageBanner (3, TRUE, &NewString);
// HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
// FreePool (NewString);
// }
// }
Status = Smbios->GetNext (Smbios, &SmbiosHandle, NULL, &Record, NULL);
}
DEBUG ((EFI_D_ERROR, "[MyHelloWorldSmbios] MyHelloWorldSmbiosAppEntry End..\n"));
return Status;
}