有朋友提出一个问题:UEFI 下如何读取SMBIOS信息?
查阅了一下资料,在 EFI_CONFIGURATION_TABLE 中有 SMBIOS_TABLE_GUID 可以用来获得SMBIOS信息。此外,推荐看一下 http://blog.chinaunix.net/uid-20757375-id-3531318.html 。
可供直接参考的代码有 ShellPkg\Library\UefiShellDebug1CommandsLib\SmbiosView 如果你看着觉得过于复杂的话,还可以参考 https://github.com/fpmurphy/UEFI-Utilities 。这个程序结构清晰,功能简单。每种不足是直接下载的代码有一些错误之类的,需要修正。下面就是修改之后的代码(代码很多是从EDK中直接copy出来的,因为装一些头文件和调用会很复杂,用到的只是那么一两个函数,所以最简单的就是直接copy,放在代码中了.其中头文件 smbios.h 可以在\MdePkg\Include\IndustryStandard 中找到)
//
// Copyright (c) 2012 Finnbarr P. Murphy. All rights reserved.
//
// Display Firmware Information Vendor, Version and Release Date via SMBIOS
//
// Any source code included from EDK2 is copyright Intel Corporation
//
// Licence: BSD License
//
#define GUID EFI_GUID // hack for SmBios.h
#include "IndustryStandard/SmBios.h" // from EDK2. GNU_EFI libsmbios.h is defective
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#define EFI_GLOBAL_VARIABLE \
{ \
0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C } \
}
#define EFI_SMBIOS_TABLE_GUID \
{ \
0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} \
}
#define BUFSIZE 64
#define INVALID_HANDLE (UINT16) (-1)
#define DMI_INVALID_HANDLE 0x83
#define DMI_SUCCESS 0x00
STATIC SMBIOS_TABLE_ENTRY_POINT *mSmbiosTable = 0; //NULL;
STATIC SMBIOS_STRUCTURE_POINTER m_SmbiosStruct;
STATIC SMBIOS_STRUCTURE_POINTER *mSmbiosStruct = &m_SmbiosStruct;
EFI_GUID SMBIOSTableGuid = EFI_SMBIOS_TABLE_GUID;
extern EFI_SYSTEM_TABLE *gST;
int
Strlen(const char *str)
{
const char *s;
for (s = str; *s; ++s)
;
return (s - str);
}
//
// Modified from EDK2 source code. Copyright Intel Corporation.
//
CHAR8*
LibGetSmbiosString ( SMBIOS_STRUCTURE_POINTER *Smbios,
UINT16 StringNumber)
{
UINT16 Index;
CHAR8 *String;
//ASSERT (Smbios != NULL);
String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);
for (Index = 1; Index <= StringNumber; Index++) {
if (StringNumber == Index) {
return String;
}
for (; *String != 0; String++)
;
String++;
if (*String == 0) {
Smbios->Raw = (UINT8 *)++String;
return NULL;
}
}
return NULL;
}
//
// Modified from EDK2 source code. Copyright Intel Corporation.
//
EFI_STATUS
LibGetSmbiosStructure ( UINT16 *Handle,
UINT8 **Buffer,
UINT16 *Length)
{
SMBIOS_STRUCTURE_POINTER Smbios;
SMBIOS_STRUCTURE_POINTER SmbiosEnd;
UINT8 *Raw;
if (*Handle == INVALID_HANDLE) {
*Handle = mSmbiosStruct->Hdr->Handle;
return DMI_INVALID_HANDLE;
}
if ((Buffer == NULL) || (Length == NULL)) {
Print(L"Invalid handle\n");
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;
LibGetSmbiosString (&Smbios, (UINT16) (-1));
*Length = (UINT16) (Smbios.Raw - Raw);
*Buffer = Raw;
if (Smbios.Raw < SmbiosEnd.Raw) {
*Handle = Smbios.Hdr->Handle;
} else {
*Handle = INVALID_HANDLE;
}
return DMI_SUCCESS;
}
LibGetSmbiosString (&Smbios, (UINT16) (-1));
}
*Handle = INVALID_HANDLE;
return DMI_INVALID_HANDLE;
}
CHAR16 *
ASCII_to_UCS2(const char *s, int len)
{
CHAR16 *ret = NULL;
int i;
ret = AllocateZeroPool(len*2 + 2);
if (!ret)
return NULL;
for (i = 0; i < len; i++)
ret[i] = s[i];
return ret;
}
BOOLEAN
EfiCompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
/*++
Routine Description:
Compares two GUIDs
Arguments:
Guid1 - guid to compare
Guid2 - guid to compare
Returns:
TRUE if Guid1 == Guid2
FALSE if Guid1 != Guid2
--*/
{
UINTN Index;
//
// compare byte by byte
//
for (Index = 0; Index < 16; ++Index) {
if (*(((UINT8*) Guid1) + Index) != *(((UINT8*) Guid2) + Index)) {
return FALSE;
}
}
return TRUE;
}
EFI_STATUS
EfiLibGetSystemConfigurationTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
)
/*++
Routine Description:
Get table from configuration table by name
Arguments:
TableGuid - Table name to search
Table - Pointer to the table caller wants
Returns:
EFI_NOT_FOUND - Not found the table
EFI_SUCCESS - Found the table
--*/
{
UINTN Index;
*Table = NULL;
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (EfiCompareGuid (TableGuid, &(gST->ConfigurationTable[Index].VendorGuid))==TRUE) {
*Table = gST->ConfigurationTable[Index].VendorTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Compares two memory buffers of a given length.
@param DestinationBuffer The first memory buffer.
@param SourceBuffer The second memory buffer.
@param Length Length of DestinationBuffer and SourceBuffer memory
regions to compare. Must be non-zero.
@return 0 All Length bytes of the two buffers are identical.
@retval Non-zero The first mismatched byte in SourceBuffer subtracted from the first
mismatched byte in DestinationBuffer.
**/
INTN
EFIAPI
InternalMemCompareMemZ (
IN CONST VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
while ((--Length != 0) &&
(*(INT8*)DestinationBuffer == *(INT8*)SourceBuffer)) {
DestinationBuffer = (INT8*)DestinationBuffer + 1;
SourceBuffer = (INT8*)SourceBuffer + 1;
}
return (INTN)*(UINT8*)DestinationBuffer - (INTN)*(UINT8*)SourceBuffer;
}
/**
Compares the contents of two buffers.
This function compares Length bytes of SourceBuffer to Length bytes of DestinationBuffer.
If all Length bytes of the two buffers are identical, then 0 is returned. Otherwise, the
value returned is the first mismatched byte in SourceBuffer subtracted from the first
mismatched byte in DestinationBuffer.
If Length > 0 and DestinationBuffer is NULL, then ASSERT().
If Length > 0 and SourceBuffer is NULL, then ASSERT().
If Length is greater than (MAX_ADDRESS - DestinationBuffer + 1), then ASSERT().
If Length is greater than (MAX_ADDRESS - SourceBuffer + 1), then ASSERT().
@param DestinationBuffer A pointer to the destination buffer to compare.
@param SourceBuffer A pointer to the source buffer to compare.
@param Length The number of bytes to compare.
@return 0 All Length bytes of the two buffers are identical.
@retval Non-zero The first mismatched byte in SourceBuffer subtracted from the first
mismatched byte in DestinationBuffer.
**/
INTN
EFIAPI
CompareMem (
IN CONST VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
if (Length == 0 || DestinationBuffer == SourceBuffer) {
return 0;
}
return InternalMemCompareMemZ (DestinationBuffer, SourceBuffer, Length);
}
EFI_STATUS
UefiMain (EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{
EFI_STATUS status = EFI_SUCCESS;
CHAR16 *ptr;
CHAR8 *str;
UINT16 Handle;
UINTN Index;
UINT16 Length;
UINT8 *Buffer;
SMBIOS_STRUCTURE_POINTER SmbiosStruct;
//InitializeLib(image, systab);
status = EfiLibGetSystemConfigurationTable(&SMBIOSTableGuid, (VOID**)&mSmbiosTable);
if ((status != EFI_SUCCESS || mSmbiosTable == NULL) ||
(CompareMem (mSmbiosTable->AnchorString, "_SM_", 4) != 0)) {
Print(L"ERROR: SMBIOS table not found.\n");
return status;
}
mSmbiosStruct->Raw = (UINT8 *) (UINTN) (mSmbiosTable->TableAddress);
Print(L"SMBIOS Ver: %x.%x Rev: %x Table Count: %d\n",
mSmbiosTable->MajorVersion,
mSmbiosTable->MinorVersion,
mSmbiosTable->EntryPointRevision,
mSmbiosTable->NumberOfSmbiosStructures);
Handle = INVALID_HANDLE;
LibGetSmbiosStructure (&Handle, NULL, NULL);
// loop though the tables looking for a type 0 table.
for (Index = 0; Index < mSmbiosTable->NumberOfSmbiosStructures; Index++) {
if (Handle == INVALID_HANDLE) {
break;
}
if (LibGetSmbiosStructure (&Handle, &Buffer, &Length) != DMI_SUCCESS) {
break;
}
SmbiosStruct.Raw = Buffer;
if (SmbiosStruct.Hdr->Type == 0) { // Type 0 - BIOS
/* vendor string */
str = LibGetSmbiosString(&SmbiosStruct, 1);
ptr = ASCII_to_UCS2(str, Strlen(str));
Print(L"Firmware Vendor: %s\n", ptr);
FreePool(ptr);
/* version string */
str = LibGetSmbiosString(&SmbiosStruct, 2);
ptr = ASCII_to_UCS2(str, Strlen(str));
Print(L"Firmware Version: %s\n", ptr);
FreePool(ptr);
/* release string */
str = LibGetSmbiosString(&SmbiosStruct, 3);
ptr = ASCII_to_UCS2(str, Strlen(str));
Print(L"Firmware Release: %s\n", ptr);
FreePool(ptr);
break;
}
}
return status;
}
运行结果
对比 SMBIOSVIEW
可以看到二者是相同的。
代码下载
GetVer
顺便多说一点:
SMBIOS中可以获得一些硬件信息,比如当前的内存插槽信息等等。但是,因为这个是BIOS在POST过程中加入的,很可能出现错误,甚至就是忘记做这个东西(MS的软件非常不信任BIOS提供的信息,如果有可能他们宁可自己重新获取一下)。如果只是想做个参考,并且你的软件需要兼容大量的设备,从这里获得信息是完全没问题的(比如MemTest86);但是如果你期望准确一些,最好还是自己手工做一下。另外一方面,如果你使用了从SMBIOS获得信息的方法,结果在客户现场遇到了问题,不妨考虑一下是否为 SMBIOS 不正确导致的。



