/**
Compares two GUIDs.
This function compares Guid1 to Guid2. If the GUIDs are identical then TRUE is returned.
If there are any bit differences in the two GUIDs, then FALSE is returned.
If Guid1 is NULL, then ASSERT().
If Guid2 is NULL, then ASSERT().
@param Guid1 A pointer to a 128 bit GUID.
@param Guid2 A pointer to a 128 bit GUID.
@retval TRUE Guid1 and Guid2 are identical.
@retval FALSE Guid1 and Guid2 are not identical.
**/
BOOLEAN
EFIAPI
CompareGuid (
IN CONST GUID *Guid1,
IN CONST GUID *Guid2
);
잊지 말고 inf파일에GUID에 값을 추가 해주자.
[Guids]
gEfiSmbiosTableGuid
빌드 후 실행하면 아래와 같이 주소가 나오는 것을 볼 수 있다.
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at BF53F000
dmem을 통한 SMBIOS 테이블 가져오기
UEFI Shell에는 메모리를 덤프하는 dmem이라는 명령어가 존재한다.
FS0:\> dmem -? -b
Displays the contents of system or device memory.
DMEM [-b] [address] [size] [-MMIO]
-b - Displays one screen at a time.
-MMIO - Forces address cycles to the PCI bus.
address - Specifies a starting address in hexadecimal format.
size - Specifies the number of bytes to display in hexadecimal format.
NOTES:
1. This command displays the contents of system memory or device memory.
2. Enter address and size in hexadecimal format.
3. If address is not specified, the contents of the UEFI System Table
are displayed. Otherwise, memory starting at the specified address is displayed.
4. Size specifies the number of bytes to display. If size is not specified,
512 bytes are displayed.
5. If MMIO is not specified, main system memory is displayed. Otherwise,
device memory is displayed through the use of the
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
EXAMPLES:
* To display the UEFI system table pointer entries:
fs0:\> dmem
* To display memory contents from 1af3088 with size of 16 bytes:
Shell> dmem 1af3088 16
* To display memory mapped IO contents from 1af3088 with a size of 16 bytes:
Shell> dmem 1af3088 16 -MMIO
FS0:\> dmem BF53F000 30
Memory Address 00000000BF53F000 30 Bytes
BF53F000: 5F 53 4D 5F 26 1F 02 08-53 00 00 00 00 00 00 00 *_SM_&...S.......*
BF53F010: 5F 44 4D 49 5F 2B 91 01-00 E0 93 07 09 00 28 AF *_DMI_+........(.*
BF53F020: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
SMBIOS 스펙에 나와있는 SMBIOS Entry point 구조에 따르면 _SM_ 및 _DMI_가 해당 구조에서 미리 정의된 값임을 알 수 있다.
해당 구조를 따라서 덤프된 메모리를 계산하면 아래와 같이 볼 수 있다.
시스템에는 0xBF53E000에서 (0xBF53E000+0x1B2)까지 배치된 0xA개의 SMBIOS 구조가 존재한다.
(환경별로 상이할 가능성 존재)
SMBIOS구조의 주소를 구하는 것이 가능하기 때문에 이를 통해서 덤프도 가능해진다.
EFI_SMBIOS_PROTOCOL을 사용하여 SMBIOS 데이터 파싱하기
위에서 한 방식대로 직접 포인터를 이용하여 SMBIOS 테이블을 계산을 통하여 구할 수 있지만, 매우 비합리적인 일이다. 다행히 UEFI PI 스펙은 SMBIOS 데이터를 가져오는데 사용할 수 있는 EFI_SMBIOS_PROTOCOL을 정의하고 있다.
EFI_SMBIOS_PROTOCOL
Summary:
Allows consumers to log SMBIOS data records, and enables the producer to create the SMBIOS tables for a platform.
Protocol Interface Structure:
typedef struct _EFI_SMBIOS_PROTOCOL {
EFI_SMBIOS_ADD Add;
EFI_SMBIOS_UPDATE_STRINGUpdateString;
EFI_SMBIOS_REMOVE Remove;
EFI_SMBIOS_GET_NEXT GetNext;
UINT8 MajorVersion;
UINT8 MinorVersion;
} EFI_SMBIOS_PROTOCOL;
Member Description:
Add Add an SMBIOS record including the formatted area and the optional strings
that follow the formatted area.
UpdateString Update a string in the SMBIOS record.
Remove Remove an SMBIOS record.
GetNext Discover all SMBIOS records.
MajorVersion The major revision of the SMBIOS specification supported.
MinorVersion The minor revision of the SMBIOS specification supported.
Description:
This protocol provides an interface to add, remove or discover SMBIOS records. The driver which
produces this protocol is responsible for creating the SMBIOS data tables and installing the pointer
to the tables in the EFI System Configuration Table.
지금 우리는 SMBIOS 테이블의 파싱을 하기 위해서 GetNext 함수를 사용하겠다.
EFI_SMBIOS_PROTOCOL.GetNext()
Summary:
Allow the caller to discover all or some of the SMBIOS records.
Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_GET_NEXT) (
IN CONST EFI_SMBIOS_PROTOCOL *This,
IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
IN EFI_SMBIOS_TYPE *Type, OPTIONAL
OUT EFI_SMBIOS_TABLE_HEADER **Record,
OUT EFI_HANDLE *ProducerHandle OPTIONAL
);
Parameters:
This The EFI_SMBIOS_PROTOCOL instance.
SmbiosHandle On entry, points to the previous handle of the SMBIOS record. On exit, points to the
next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record
handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS
records.
Type On entry, it points to the type of the next SMBIOS record to return. If NULL, it
indicates that the next record of any type will be returned. Type is not modified by
the this function.
Record On exit, points to a pointer to the the SMBIOS Record consisting of the formatted area
followed by the unformatted area. The unformatted area optionally contains text
strings.
ProducerHandle On exit, points to the ProducerHandle registered by Add(). If no
ProducerHandle was passed into Add() NULL is returned. If a NULL pointer is
passed in no data will be returned
Description
This function allows all of the SMBIOS records to be discovered. It's possible to find
only the SMBIOS records that match the optional Type argument.
Status Codes Returned:
EFI_SUCCESS .SMBIOS record information was successfully returned in Record.
SmbiosHandle is the handle of the current SMBIOS record
EFI_NOT_FOUND The SMBIOS record with SmbiosHandle was the last available record.
우선 해당 프로토콜을 불러와야 한다.
EFI_SMBIOS_PROTOCOL* SmbiosProtocol;
EFI_STATUS Status = gBS->LocateProtocol (
&gEfiSmbiosProtocolGuid,
NULL,
(VOID**)&SmbiosProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
SMBIOS_HANDLE_PI_RESERVED를 사용하는 이유는 GetNext()함수의 두번째 파라미터인 SmbiosHandle을 충족하기 위해서다.
///
/// Reference SMBIOS 2.7, chapter 6.1.2.
/// The UEFI Platform Initialization Specification reserves handle number FFFEh for its
/// EFI_SMBIOS_PROTOCOL.Add() function to mean "assign an unused handle number automatically."
/// This number is not used for any other purpose by the SMBIOS specification.
///
#define SMBIOS_HANDLE_PI_RESERVED 0xFFFE
해당 구조에서 SMBIOS_STRUCTURE Hdr은 프로토콜 함수 호출에서 수신한 EFI_SMBIOS_TABLE_HEADER와 동일하게 필수적인 필드이다.
또한 SMBIOS_TABLE_STRING은 ASCII 문자열 배열의 문자형 숫자를 정의하는 UINT8 값이다.
///
/// Text strings associated with a given SMBIOS structure are returned in the dmiStrucBuffer, appended directly after
/// the formatted portion of the structure. This method of returning string information eliminates the need for
/// application software to deal with pointers embedded in the SMBIOS structure. Each string is terminated with a null
/// (00h) BYTE and the set of strings is terminated with an additional null (00h) BYTE. When the formatted portion of
/// a SMBIOS structure references a string, it does so by specifying a non-zero string number within the structure's
/// string-set. For example, if a string field contains 02h, it references the second string following the formatted portion
/// of the SMBIOS structure. If a string field references no string, a null (0) is placed in that string field. If the
/// formatted portion of the structure contains string-reference fields and all the string fields are set to 0 (no string
/// references), the formatted section of the structure is followed by two null (00h) BYTES.
///
typedef UINT8 SMBIOS_TABLE_STRING;
따라서 EFI_SMBIOS_TABLE_HEADER* 및 문자형 숫자에서 실제 ASCII 문자열을 반환하는 간단한 함수를 작성하겠다.
/**
Returns the size of a Null-terminated ASCII string in bytes, including the
Null terminator.
This function returns the size, in bytes, of the Null-terminated ASCII string
specified by String.
If String is NULL, then ASSERT().
If PcdMaximumAsciiStringLength is not zero and String contains more than
PcdMaximumAsciiStringLength ASCII characters, not including the Null-terminator,
then ASSERT().
@param String A pointer to a Null-terminated ASCII string.
@return The size of String.
**/
UINTN
EFIAPI
AsciiStrSize (
IN CONST CHAR8 *String
)
위의 모든 것을 바탕으로 while문을 이용해서 실제적으로 파싱을 하는 코드를 작성할 수 있다.
while (!EFI_ERROR(Status)) {
Print (L"SMBIOS Type %d \n", Record->Type);
switch (Record->Type) {
case EFI_SMBIOS_TYPE_BIOS_INFORMATION: {
SMBIOS_TABLE_TYPE0* Type0Record = (SMBIOS_TABLE_TYPE0*) Record;
Print(L"\tVendor=%a\n", GetRecordString(Record, Type0Record->Vendor));
Print(L"\tBiosVersion=%a\n", GetRecordString(Record, Type0Record->BiosVersion));
Print(L"\tBiosReleaseDate=%a\n", GetRecordString(Record, Type0Record->BiosReleaseDate));
Print(L"\tBiosSegment=0x%x\n", Type0Record->BiosSegment);
Print(L"\tSystemBiosMajorRelease=0x%x\n", Type0Record->SystemBiosMajorRelease);
Print(L"\tSystemBiosMinorRelease=0x%x\n", Type0Record->SystemBiosMinorRelease);
break;
}
default:
Print(L"\tTODO: Parsing for this table is not ready yet\n");
break;
}
Status = SmbiosProtocol->GetNext(SmbiosProtocol,
&SmbiosHandle,
NULL,
&Record,
NULL);
}
여기서 ASCII 문자열을 인쇄하기 위해서 Print 함수의 %a 옵션을 사용했다. 이후 빌드를 하면 아래와 같은 결과가 나온다.
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at 7941000
SMBIOS Type 1
TODO: Parsing for this table is not ready yet
SMBIOS Type 3
TODO: Parsing for this table is not ready yet
SMBIOS Type 4
TODO: Parsing for this table is not ready yet
SMBIOS Type 16
TODO: Parsing for this table is not ready yet
SMBIOS Type 17
TODO: Parsing for this table is not ready yet
SMBIOS Type 19
TODO: Parsing for this table is not ready yet
SMBIOS Type 32
TODO: Parsing for this table is not ready yet
SMBIOS Type 0
Vendor=EFI Development Kit II / OVMF
BiosVersion=0.0.0
BiosReleaseDate=02/06/2015
BiosSegment=0xE800
SystemBiosMajorRelease=0x0
SystemBiosMinorRelease=0x0
해당 결과는 dmem을 통해서 dump한 결과가 실제로 메모리에 존재하는 문자열임을 알 수 있다. Type 0 이외에도 다른 SMBIOS 테이블을 parse하는 코드를 추가해보겠다.
typedef struct {
SMBIOS_STRUCTURE Hdr;
SMBIOS_TABLE_STRING Manufacturer;
SMBIOS_TABLE_STRING ProductName;
SMBIOS_TABLE_STRING Version;
SMBIOS_TABLE_STRING SerialNumber;
GUID Uuid;
UINT8 WakeUpType; ///< The enumeration value from MISC_SYSTEM_WAKEUP_TYPE.
SMBIOS_TABLE_STRING SKUNumber;
SMBIOS_TABLE_STRING Family;
} SMBIOS_TABLE_TYPE1
빌드 후 실행시 아래와 같은 결과가 나온다.
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at 7941000
SMBIOS Type 1
Manufacturer=QEMU
ProductName=Standard PC (i440FX + PIIX, 1996)
Version=pc-i440fx-focal
SerialNumber=
UUID=00000000-0000-0000-0000-000000000000
WakeUpType=6
SKUNumber=
Family=
SMBIOS Type 3
TODO: Parsing for this table is not ready yet
SMBIOS Type 4
TODO: Parsing for this table is not ready yet
SMBIOS Type 16
TODO: Parsing for this table is not ready yet
SMBIOS Type 17
TODO: Parsing for this table is not ready yet
SMBIOS Type 19
TODO: Parsing for this table is not ready yet
SMBIOS Type 32
TODO: Parsing for this table is not ready yet
SMBIOS Type 0
Vendor=EFI Development Kit II / OVMF
BiosVersion=0.0.0
BiosReleaseDate=02/06/2015
BiosSegment=0xE800
SystemBiosMajorRelease=0x0
SystemBiosMinorRelease=0x0