64位的SMBIOS EPS表可以根据SMBIOS 3.x GUID (SMBIOS3_TABLE_GUID, {F2FD1544-9794-4A2C-992EE5BBCF20E394})找到,这在所有遵循SMBIOS规范的BIOS中都是一样的。
#define EFI_SMBIOS3_TABLE_GUID \
{ \
0xf2fd1544, 0x9794, 0x4a2c, {0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94} \
}
代码实现的功能是读取SMBIOS的第二个Type2,并将其Handle打印出来,读取所有的Type41,打印出Type中的DeviceType、BusNum、DevFuncNum信息。根据读出来的Bus、Device、Function号读取相应PCI配置空间信息。
Type41在SMBIOS Spec中的描述是:“Onboard Devices Extended Information (Type 41)”,即板载设备扩展信息,这些设备的种类有Video、SCSI Controller、Ethernet、Token Ring、Sound、PATA Controller等。
程序读取信息的步骤可以概括为:通过GUID将EPS表找到,根据EPS表的TableAddress找到所有Type的起始地址,然后遍历Type表,找到要读取的Type读取相应信息。其中遍历每一个Type表并返回每一个Type的起始地址是最关键的部分,代码中由函数LibGetSmbiosString 实现。
代码:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <IndustryStandard/SmBios.h>
#include <Library/BaseMemoryLib.h>
#include <IndustryStandard/SmBios.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#define INVALID_HANDLE (UINT16) (-1)
#define DMI_SUCCESS 0x00
#define DMI_UNKNOWN_FUNCTION 0x81
#define DMI_FUNCTION_NOT_SUPPORTED 0x82
#define DMI_INVALID_HANDLE 0x83
#define DMI_BAD_PARAMETER 0x84
#define DMI_INVALID_SUBFUNCTION 0x85
#define DMI_NO_CHANGE 0x86
#define DMI_ADD_STRUCTURE_FAILED 0x87
#define DMI_READ_ONLY 0x8D
#define DMI_LOCK_NOT_SUPPORTED 0x90
#define DMI_CURRENTLY_LOCKED 0x91
#define DMI_INVALID_LOCK 0x92
#define PCI_BASE_ADDR 0x80000000L
#define CONFIG_ADDR 0xCF8
#define CONFIG_DATA 0xCFC
/**
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
);
#define EFI_SMBIOS3_TABLE_GUID \
{ \
0xf2fd1544, 0x9794, 0x4a2c, {0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94} \
}
EFI_GUID gEfiSmbiosTable3Guid = EFI_SMBIOS3_TABLE_GUID;
STATIC SMBIOS_STRUCTURE_POINTER m_SmbiosStruct;
STATIC SMBIOS_STRUCTURE_POINTER *mSmbiosStruct = &m_SmbiosStruct; //用引用进行初始化,否则运行会卡退
STATIC SMBIOS_TABLE_3_0_ENTRY_POINT *SmbiosTable = NULL; //Smbios EPS表结构
EFI_STATUS
EFIAPI
SmBiosTypeReadEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
SMBIOS_STRUCTURE_POINTER SmBiosStruct;
UINT8 flag = 0;
UINT32 address,data,data1,data2,data3;
// Get SMBIOS table from System Configure table
//
Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTable3Guid, &SmbiosTable);
if((Status != EFI_SUCCESS || SmbiosTable == NULL) ||
(CompareMem(SmbiosTable->AnchorString, "_SM3_", 5) != 0 )) {
Print (L"smbios table is not found!");
return Status;
}
Print (L"SmBios Table:\n");
Print (L" AnchorString:%c%c%c%c%c\n EntryPointStructureChecksum:0x%x\n EntryPointLength:0x%x\n MajorVersion:0x%x\n MinorVersion:0x%x\n TableAddress:0x%x\n",
SmbiosTable->AnchorString[0],
SmbiosTable->AnchorString[1],
SmbiosTable->AnchorString[2],
SmbiosTable->AnchorString[3],
SmbiosTable->AnchorString[4],
SmbiosTable->EntryPointStructureChecksum,
SmbiosTable->EntryPointLength,
SmbiosTable->MajorVersion,
SmbiosTable->MinorVersion,
SmbiosTable->TableAddress
);
mSmbiosStruct->Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress);
SmBiosStruct.Raw = mSmbiosStruct->Raw;
while(1) { //当到Type127时break
if(SmBiosStruct.Hdr->Handle == 0xFEFF){ //当到Type127时break
break;
}
if(SmBiosStruct.Hdr->Type == 0x08){ //读取第二个Type8
if(flag == 1){
Print(L"The Second Type8's handle:0x%x\n",SmBiosStruct.Hdr->Handle);
flag = 2;
}
else {
flag += 1;
}
}
if(SmBiosStruct.Hdr->Type == 0x29){ //读取所有的Type41,并识别其DeviceType
Print(L"Type41 handle:0x%x\n",SmBiosStruct.Hdr->Handle);
switch (SmBiosStruct.Type41->DeviceType & 0x7F)
{
case 0x01:
Print(L"Type41 DeviceType is: Other.\n");
break;
case 0x02:
Print(L"Type41 DeviceType is: Unknown.\n");
break;
case 0x03:
Print(L"Type41 DeviceType is: Video.\n");
break;
case 0x04:
Print(L"Type41 DeviceType is: SCSI Controller.\n");
break;
case 0x05:
Print(L"Type41 DeviceType is: Ethernet.\n");
break;
case 0x06:
Print(L"Type41 DeviceType is: Token Ring.\n");
break;
case 0x07:
Print(L"Type41 DeviceType is: Sound.\n");
break;
case 0x08:
Print(L"Type41 DeviceType is: PATA Controller.\n");
break;
case 0x09:
Print(L"Type41 DeviceType is: SATA Controller.\n");
break;
case 0x0A:
Print(L"Type41 DeviceType is: SAS Controller.\n");
break;
default:
break;
}
if((SmBiosStruct.Type41->DeviceType >> 7) == 0){ //输出DeviceType
Print(L"Device Status is:Disabled.\n");
}else Print(L"Device Status is:Enableabled.\n");
//输出Type41中的Bus、Device、Function号
Print(L"Bus Number:0x%x\n",SmBiosStruct.Type41->BusNum);
Print(L"Device Number:0x%x\n",(SmBiosStruct.Type41->DevFuncNum >> 0x03));
Print(L"Device Number:0x%x\n",(SmBiosStruct.Type41->DevFuncNum & 0x07));
//根据读出来的Bus、Device、Function号读取PCI配置空间信息
//IO方式读取
address = PCI_BASE_ADDR | ((SmBiosStruct.Type41->BusNum & 0xFF) << 16) | \
(((SmBiosStruct.Type41->DevFuncNum >> 0x03) & 0x1F) << 11) | \
(((SmBiosStruct.Type41->DevFuncNum & 0x07) & 0x7) << 8);
//读取vendorIDhe DeviceID
IoWrite32(CONFIG_ADDR, address);
data = IoRead32(CONFIG_DATA);
//读取sub-vendorID
IoWrite32(CONFIG_ADDR, address | 0x2C);
data1 = IoRead32(CONFIG_DATA);
//读取ClassCode
IoWrite32(CONFIG_ADDR, address | 0x08);
data2 = IoRead32(CONFIG_DATA);
//判断桥或设备
IoWrite32(CONFIG_ADDR, address | 0x0C);
data3 = IoRead32(CONFIG_DATA);
Print(L"VendorID:%2x\n DeviceID:%2x\n SubVendorID: %2x\n SubDeviceID:%2x\n HeaderType:%2x\n ClassCode:%2x\n",(data & 0xffff),((data >> 16) & 0xffff),\
(data1 & 0xffff), ((data1 >> 16) & 0xffff),\
((data3 >> 16) & 0xff),((data2 >> 8)));
}
LibGetSmbiosString (&SmBiosStruct, (UINT16) (-1)); //跳过Type的字符串区域(即找到下一个Type的起始地址,并返回给SmBiosStruct->Raw)
}
Print(L"SmBiosStruct.Hdr->Handle:0x%x\n",SmBiosStruct.Hdr->Handle); //while(1)循环结束,遍历到Type127(最后一个Type),输出其Handle验证
return Status;
}
/**
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;
}
.inf
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = SmbiosNew
FILE_GUID = CEB4D871-6C35-4E05-969D-B2539ECA8548
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = SmBiosTypeReadEntry
[Sources]
SmbiosNew.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
IoLib
BaseMemoryLib
DebugLib
[Guids]
gEfiSmbiosTableGuid
运行结果:
使用RW软件验证:
第二个Type8:
Type41:
在SMBIOS Spec中,Type41表的Bus Number和Device\Function Number是这样存的:
所以读出的Device和Function还需要分解。
而Device Type的第七位为状态值,[6:0]为设备类型。
所有的类型定义为:
根据这些就可以判断所读数据代表的含义。