SMBIOS驱动代码分析

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表结构长度1BYTEEntry Point Structure 表的长度
06HMajor 版本号1BYTE用于判断SMBIOS 版本
07HMinor 版本号1BYTE用于判断SMBIOS 版本
08H表结构大小2BYTE用于即插即用接口方法获得数据表结构长度
0AHEPS 修正1BYTE
0B-0FH格式区域5BYTE存放解释EPS 修正的信息
10H关键字5BYTE固定为“DMI
15H校验和1BYTEIntermediate Entry Point Structure (IEPS)的校验和
16H结构表长度2BYTESMBIOS 结构表的长度
18H结构表地址4BYTESMBIOS 结构表的真实内存位置
1CH结构表个数2BYTESMBIOS 结构表数目
1EHSmbios 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、创建表头部

位置名称长度描述
00HTYPE 号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
);

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值