1、概述
在主板上电后,UEFI会将HW信息包括, CPU、Memory、FW、PM等信息以SMBIOS table形式上存储到一段内存区域中,进入OS后,OS通过解析该段内存即可获取相关配置。
SMBIOS核心服务部分主要需要完成两方面功能,首先将负责创建EPS并将其注册到固件系统;其次需要提供SMBIOS表项的操作接口。SMBIOS是通过DXE驱动加载到系统表的,入口函数为SmbiosDriverEntryPoint,该驱动程序生成一个接口。接口包含了增加、更新、移除、获取 SMBIOS的函数,入口函数还初始化数据链表和句柄头部链表。最后实现的实例安装到新的句柄上,在DXE阶段调用。
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;
}
UEFI系统中任意阶段和位置只要该SMBIOS Protocol已通过InstallProtocolInterface 安装到UEFI系统中,即可通过gEfiSmbiosProtocolGuid获取到SMBIOS相关的服务。
2、创建表结构
SmbiosCreateTable (
OUT VOID **TableEntryPointStructure
)
{
.....
EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion;
EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion;
EntryPointStructureData.SmbiosBcdRevision = (UINT8) ((PcdGet16 (PcdSmbiosVersion) >> 4) & 0xf0) | (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x0f);
PhysicalAddress = 0xffffffff;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiRuntimeServicesData,
EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
&PhysicalAddress
);
....
}
创建表结构的主要功能为
1、根据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处,获得数据表长度和数据表地址,即可访问结构表。从EPS表中的1CH处可得知SMBIOS数据表表项的总数。
与SMBIOS的SMBIOS_TABLE_ENTRY_POINT结构体对应起来
typedef struct {
UINT8 AnchorString[4];
UINT8 EntryPointStructureChecksum;
UINT8 EntryPointLength;
UINT8 MajorVersion;
UINT8 MinorVersion;
UINT16 MaxStructureSize;
UINT8 EntryPointRevision;
UINT8 FormattedArea[5];
UINT8 IntermediateAnchorString[5];
UINT8 IntermediateChecksum;
UINT16 TableLength;
UINT32 TableAddress;
UINT16 NumberOfSmbiosStructures;
UINT8 SmbiosBcdRevision;
} SMBIOS_TABLE_ENTRY_POINT;
2、创建表头部
位置 | 名称 | 长度 | 描述 |
---|---|---|---|
00H | TYPE 号 | 1BYTE | 结构的TYPE 号 |
01H | 长度 | 1BYTE | 本结构的长度,就此TYPE 号的结构而言 |
02H | 句柄 | 2BYTE | 用于获得本SMBIOS 结构,其值不定 |
EndStructure.Header.Type = SMBIOS_TYPE_END_OF_TABLE;
EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
EndStructure.Header.Handle = SmbiosHandle;
3 增加SMBIOS表
mPrivateData.Smbios.Add = SmbiosAdd;
SmbiosAdd函数能将Record的内容添加到已有的SMBIOS Record之后,并返回新添加到Record的句柄SmbiosHandle,该句柄作为此Record的身份标识,在后续移除某项记录或添加字符串,都需要使用该句柄。
EFI_STATUS
EFIAPI
SmbiosAdd (
IN CONST EFI_SMBIOS_PROTOCOL *This,
IN EFI_HANDLE ProducerHandle, OPTIONAL
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
IN EFI_SMBIOS_TABLE_HEADER *Record
)
1、核实SmbiosHandle是否在使用中
2、计算record的大小和字符数字
找到1个0,是一个字段的长度
找到两个0,就是一条record的大小
3、分配record的内存空间
TotalSize = sizeof (EFI_SMBIOS_ENTRY) + sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize;
4、构建句柄入口并且增加到链表中
HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE;
HandleEntry->SmbiosHandle = *SmbiosHandle;
InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link);
5、构建record 的头结构并且增加到内部的链表中
InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION;
InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
InternalRecord->RecordSize = RecordSize;
InternalRecord->ProducerHandle = ProducerHandle;
InternalRecord->NumberOfStrings = NumberOfStrings;
SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE;
SmbiosEntry->RecordHeader = InternalRecord;
SmbiosEntry->RecordSize = TotalSize;
SmbiosEntry->Smbios32BitTable = Smbios32BitTable;
SmbiosEntry->Smbios64BitTable = Smbios64BitTable;
InsertTailList (&Private->DataListHead, &SmbiosEntry->Link);
6、安装SMBIOS表到系统表中
typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_CONFIGURATION_TABLE)(
IN EFI_GUID *Guid,
IN VOID *Table
);
Adds, updates, or removes a configuration table entry from the EFI System Table.
4、获得SMBIOS Record
mPrivateData.Smbios.GetNext = SmbiosGetNext;
EFI_STATUS
EFIAPI
GetNextSmbiosRecord (
IN CONST EFI_SMBIOS_PROTOCOL *This,
IN OUT EFI_SMBIOS_ENTRY **CurrentSmbiosEntry,
OUT EFI_SMBIOS_TABLE_HEADER **Record
)
1、CurrentSmbiosEntry,在退出时,指针指向包含SMBIOS记录信息的入口地址,如果CurrentSmbiosEntry指针在入口时为空,第一个smbios 的入口地址将被返回。
2、Record,在退出时,指向SMBIOS记录,该记录由格式化区域组成,然后是
无格式的区域。 无格式区域可选地包含文本字符串。
通过链表遍历的方法获得SMBIOS record
5、更新或移除SMBIOS Record
mPrivateData.Smbios.UpdateString = SmbiosUpdateString;
mPrivateData.Smbios.Remove = SmbiosRemove;
SmbiosUpdateString:重新构建InternalRecord,再加载到系统表
SmbiosRemove:根据将句柄SmbiosHandle所指示的SMBIOS表项从SMBIOS整体表结构中移除
当上述核心服务函数各自功能完成之后,需要将上述服务安装到UEFI系统之中,与定义好的GUID相关联。SMBIOS驱动加载完成,可供后续使用。
6、增加一条SMBIOS table记录
smbios 驱动加载完成,怎样实际增加一条type0/type1/type2…记录
1、先根据gEfiSmbiosProtocolGuid 获得mSmbios接口
Status = gBS->LocateProtocol(&gEfiSmbiosProtocolGuid, NULL, (VOID **)&mSmbios);
2、再通过接口中add 函数加载到系统表中
EFI_SMBIOS_PROTOCOL *mSmbios;
Status = mSmbios->Add (
mSmbios,
NULL,
&SmbiosHandle,
(EFI_SMBIOS_TABLE_HEADER *)SmbiosRecord
);