diff options
-rw-r--r-- | Lesson_27/BIOS_menu.png | bin | 0 -> 7868 bytes | |||
-rw-r--r-- | Lesson_27/README.md | 696 | ||||
-rw-r--r-- | Lesson_27/SMBIOS_entry_structure.png | bin | 0 -> 90357 bytes | |||
-rw-r--r-- | Lesson_27/SMBIOS_entry_structure_dump.png | bin | 0 -> 9665 bytes | |||
-rw-r--r-- | Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.c | 88 | ||||
-rw-r--r-- | Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.inf | 24 | ||||
-rw-r--r-- | README.md | 1 |
7 files changed, 809 insertions, 0 deletions
diff --git a/Lesson_27/BIOS_menu.png b/Lesson_27/BIOS_menu.png Binary files differnew file mode 100644 index 0000000..cd28ef5 --- /dev/null +++ b/Lesson_27/BIOS_menu.png diff --git a/Lesson_27/README.md b/Lesson_27/README.md new file mode 100644 index 0000000..5248b24 --- /dev/null +++ b/Lesson_27/README.md @@ -0,0 +1,696 @@ +Let's try to get information from one of the system tables that we've discovered. + +You can find the most recent version of the "System Management BIOS (SMBIOS) Reference Specification" on the DMTF site https://www.dmtf.org/dsp/DSP0134 +For example now 3.4.0 is the most recent version https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf + +From the previous lesson we now, that SMBIOS table is declared under `gEfiSmbiosTableGuid`. + +Let's create an app, that would print the address of this table. +``` +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include <Library/BaseMemoryLib.h> + +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + for (UINTN i=0; i<SystemTable->NumberOfTableEntries; i++) { + if (CompareGuid(&(SystemTable->ConfigurationTable[i].VendorGuid), &gEfiSmbiosTableGuid)) { + Print(L"SMBIOS table is placed at %p\n", SystemTable->ConfigurationTable[i].VendorTable); + } + } + return EFI_SUCCESS; +} +``` + +In this code we've used `CompareGuid` function from the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/BaseMemoryLib.h: +``` +/** + 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 + ); +``` + +Don't forget to add: +``` +[Guids] + gEfiSmbiosTableGuid +``` +to app *inf file. + +Let's build our app and execute it under OVMF: +``` +FS0:\> SmbiosInfo.efi +SMBIOS table is placed at 7941000 +``` + +# Get SMBIOS tables with `dmem` + +UEFI shell has a command `dmem` for memory dump: +``` +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 +``` + +Let's use it to print 0x30 bytes from the 0x7941000 address that we count as a SMBIOS table pointer. +``` +FS0:\> dmem 7941000 30 +Memory Address 0000000007941000 30 Bytes + 07941000: 5F 53 4D 5F 26 1F 02 08-53 00 00 00 00 00 00 00 *_SM_&...S.......* + 07941010: 5F 44 4D 49 5F 0A 91 01-00 00 94 07 09 00 28 AF *_DMI_.........(.* + 07941020: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................* +``` + +If you look at SMBIOS specification for SMBIOS Entry Point structure you'll see, that `_SM_` and `_DMI_` are predefined values in this structure. + +![SMBIOS_entry_structure](SMBIOS_entry_structure.png?raw=true "SMBIOS_entry_structure") + +You can find definition for the structure itself in edk2 under https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h +``` +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; +``` + +If you calculate offsets for different fields you can parse memory dump: + +![SMBIOS_entry_structure_dump](SMBIOS_entry_structure_dump.png?raw=true "SMBIOS_entry_structure_dump") + +System has 9 SMBIOS structures placed from 0x07940000 to (0x07940000+0x191). + +Now when we know address for SMBIOS structures, we can dump them as well. +``` +FS0:\> dmem 07940000 191 +Memory Address 0000000007940000 191 Bytes + 07940000: 01 1B 00 01 01 02 03 00-00 00 00 00 00 00 00 00 *................* + 07940010: 00 00 00 00 00 00 00 00-06 00 00 51 45 4D 55 00 *...........QEMU.* + 07940020: 53 74 61 6E 64 61 72 64-20 50 43 20 28 69 34 34 *Standard PC (i44* + 07940030: 30 46 58 20 2B 20 50 49-49 58 2C 20 31 39 39 36 *0FX + PIIX, 1996* + 07940040: 29 00 70 63 2D 69 34 34-30 66 78 2D 66 6F 63 61 *).pc-i440fx-foca* + 07940050: 6C 00 00 03 16 00 03 01-01 02 00 00 03 03 03 02 *l...............* + 07940060: 00 00 00 00 00 00 00 00-00 51 45 4D 55 00 70 63 *.........QEMU.pc* + 07940070: 2D 69 34 34 30 66 78 2D-66 6F 63 61 6C 00 00 04 *-i440fx-focal...* + 07940080: 2A 00 04 01 03 01 02 63-06 00 00 FD FB 8B 07 03 **......c........* + 07940090: 00 00 00 D0 07 D0 07 41-01 FF FF FF FF FF FF 00 *.......A........* + 079400A0: 00 00 01 01 01 02 00 01-00 43 50 55 20 30 00 51 *.........CPU 0.Q* + 079400B0: 45 4D 55 00 70 63 2D 69-34 34 30 66 78 2D 66 6F *EMU.pc-i440fx-fo* + 079400C0: 63 61 6C 00 00 10 17 00-10 01 03 06 00 00 02 00 *cal.............* + 079400D0: FE FF 01 00 00 00 00 00-00 00 00 00 00 00 11 28 *...............(* + 079400E0: 00 11 00 10 FE FF FF FF-FF FF 80 00 09 00 01 00 *................* + 079400F0: 07 02 00 00 00 02 00 00-00 00 00 00 00 00 00 00 *................* + 07940100: 00 00 00 00 00 00 44 49-4D 4D 20 30 00 51 45 4D *......DIMM 0.QEM* + 07940110: 55 00 00 13 1F 00 13 00-00 00 00 FF FF 01 00 00 *U...............* + 07940120: 10 01 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* + 07940130: 00 00 00 00 20 0B 00 20-00 00 00 00 00 00 00 00 *.... .. ........* + 07940140: 00 00 1A 00 00 01 02 00-E8 03 00 08 00 00 00 00 *................* + 07940150: 00 00 00 00 1C 00 00 FF-FF 00 00 45 46 49 20 44 *...........EFI D* + 07940160: 65 76 65 6C 6F 70 6D 65-6E 74 20 4B 69 74 20 49 *evelopment Kit I* + 07940170: 49 20 2F 20 4F 56 4D 46-00 30 2E 30 2E 30 00 30 *I / OVMF.0.0.0.0* + 07940180: 32 2F 30 36 2F 32 30 31-35 00 00 7F 04 FF FE 00 *2/06/2015.......* + 07940190: 00 *.* +``` + +# Use EFI_SMBIOS_PROTOCOL to parse SMBIOS data + +We can use direct pointer arithmetics to parse SMBIOS tables, but that would be very tedious. + +Luckily UEFI PI specification defines a `EFI_SMBIOS_PROTOCOL`, that we can use to get SMBIOS data. + +``` +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. +``` + +Right now we are interested in SMBIOS table parsing, so we need to utilize `GetNext` function: +``` +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. +``` + +First let's get this protocol in our app. +``` +EFI_SMBIOS_PROTOCOL* SmbiosProtocol; +EFI_STATUS Status = gBS->LocateProtocol ( + &gEfiSmbiosProtocolGuid, + NULL, + (VOID**)&SmbiosProtocol + ); +if (EFI_ERROR (Status)) { + return Status; +} +``` +To use it we need to add include to our app https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h: +``` +#include <Protocol/Smbios.h> +``` +And add protocol guid to our *.inf file: +``` +[Protocols] + gEfiSmbiosProtocolGuid +``` + +Now let's try to get SMBIOS tables. We would be using `SMBIOS_HANDLE_PI_RESERVED` as `EFI_SMBIOS_HANDLE` in protocol calls. You can find explanation in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h: +``` +/// +/// 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 +``` + +Also before we start writing code look at the definition of `EFI_SMBIOS_TABLE_HEADER` structure, that we would receive on `GetNext` calls. +https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h +``` +typedef SMBIOS_STRUCTURE EFI_SMBIOS_TABLE_HEADER; +``` +https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h +``` +/// +/// The Smbios structure header. +/// +typedef struct { + SMBIOS_TYPE Type; + UINT8 Length; + SMBIOS_HANDLE Handle; +} SMBIOS_STRUCTURE; +``` + +Now we are ready to write some code. Write a code to print all types of Smbios tables that are present in the system: +``` +EFI_SMBIOS_HANDLE SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; +EFI_SMBIOS_TABLE_HEADER* Record; +Status = SmbiosProtocol->GetNext(SmbiosProtocol, + &SmbiosHandle, + NULL, + &Record, + NULL); +while (!EFI_ERROR(Status)) { + Print (L"SMBIOS Type %d \n", Record->Type); + Status = SmbiosProtocol->GetNext(SmbiosProtocol, + &SmbiosHandle, + NULL, + &Record, + NULL); +} +``` + +If you build our app and test it under OVMF you would get: +``` +SMBIOS table is placed at 7941000 +SMBIOS Type 1 +SMBIOS Type 3 +SMBIOS Type 4 +SMBIOS Type 16 +SMBIOS Type 17 +SMBIOS Type 19 +SMBIOS Type 32 +SMBIOS Type 0 +``` + +Ok, now let's write code that can parse information in these tables. + +Let's start with Type 0 table. edk2 has a structure description at https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h: +``` +/// +/// BIOS Information (Type 0). +/// +typedef struct { + SMBIOS_STRUCTURE Hdr; + SMBIOS_TABLE_STRING Vendor; + SMBIOS_TABLE_STRING BiosVersion; + UINT16 BiosSegment; + SMBIOS_TABLE_STRING BiosReleaseDate; + UINT8 BiosSize; + MISC_BIOS_CHARACTERISTICS BiosCharacteristics; + UINT8 BIOSCharacteristicsExtensionBytes[2]; + UINT8 SystemBiosMajorRelease; + UINT8 SystemBiosMinorRelease; + UINT8 EmbeddedControllerFirmwareMajorRelease; + UINT8 EmbeddedControllerFirmwareMinorRelease; + // + // Add for smbios 3.1.0 + // + EXTENDED_BIOS_ROM_SIZE ExtendedBiosSize; +} SMBIOS_TABLE_TYPE0; +``` + +In this structure `SMBIOS_STRUCTURE Hdr` is a mandatory field that is the same as `EFI_SMBIOS_TABLE_HEADER` that we receive from our protocol function call. + +Also `SMBIOS_TABLE_STRING` is just an UINT8 value that defines a string number in an ASCII string array that is placed directly after the structure. + +``` +/// +/// 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; +``` + +So let's write a simple function that returns actual ASCII string from a `EFI_SMBIOS_TABLE_HEADER*` and a string number: +``` +CHAR8* GetRecordString(EFI_SMBIOS_TABLE_HEADER* Record, UINTN number) +{ + if (!number) + return ""; + + CHAR8* String = (CHAR8*)Record + Record->Length; + UINTN i=1; + while (i < number) { + String = String + AsciiStrSize(String); + i++; + } + return String; +} +``` +Here we've used `AsciiStrSize` function that is defined in https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/String.c file. +``` +/** + 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 + ) +``` + +Now we have everything to write actual parsing code in our `while` loop: + +``` +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); +} +``` +To print ASCII strings here we've used `%a` format code (https://github.com/tianocore/edk/blob/master/Foundation/Library/Pei/PeiLib/Print/Print.c). + + +If you build and execute our app under OVMF you would get: +``` +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 +``` + +If you look closely at `dmem` dump from earlier, you'll see that these are the exact strings that were actually present in memory. + +We can easily to add code to parse other SMBIOS tables. + +For example here is some parsing code for table type 1 structure: +``` +case EFI_SMBIOS_TYPE_SYSTEM_INFORMATION: { + SMBIOS_TABLE_TYPE1* Type1Record = (SMBIOS_TABLE_TYPE1*) Record; + Print(L"\tManufacturer=%a\n", GetRecordString(Record, Type1Record->Manufacturer)); + Print(L"\tProductName=%a\n", GetRecordString(Record, Type1Record->ProductName)); + Print(L"\tVersion=%a\n", GetRecordString(Record, Type1Record->Version)); + Print(L"\tSerialNumber=%a\n", GetRecordString(Record, Type1Record->SerialNumber)); + Print(L"\tUUID=%g\n", Type1Record->Uuid); + Print(L"\tWakeUpType=%d\n", Type1Record->WakeUpType); + Print(L"\tSKUNumber=%a\n", GetRecordString(Record, Type1Record->SKUNumber)); + Print(L"\tFamily=%a\n", GetRecordString(Record, Type1Record->Family)); + break; +} +``` +Structure itself is: +``` +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; +``` + +Build and execute: +``` +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 +``` + +As you remember the same information that is present in these SMBIOS tables is present in the main BIOS menu: + +![BIOS_menu](BIOS_menu.png?raw=true "BIOS_menu") + +And in case you wonder where OVMF defines all these information for its SMBIOS structures, checkout https://github.com/tianocore/edk2/blob/master/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c and implementation of a `EFI_SMBIOS_PROTOCOL` is placed here https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c + +# `smbiosview` command + +We can use `EFI_SMBIOS_PROTOCOL` to parse SMBIOS table information, but actually if you just want to see SMBIOS information there is a better option. + +UEFI shell has a `smbiosview` command that does exactly what we need. + +You can checkout sources for this command here: https://github.com/tianocore/edk2/tree/master/ShellPkg/Library/UefiShellDebug1CommandsLib/SmbiosView + +First checkout help for this command: +``` +FS0:\> smbiosview -? +Displays SMBIOS information. + +SMBIOSVIEW [-t SmbiosType]|[-h SmbiosHandle]|[-s]|[-a] + + -t - Displays all structures of SmbiosType. + -h - Displays structure of SmbiosHandle. + -s - Displays a statistics table. + -a - Displays all information. + SmbiosType - Specifies a SMBIOS structure type. + SmbiosHandle - Specifies a SMBIOS structure unique 16-bit handle. + +NOTES: + 1. The SmbiosType parameter supports the following types: + 0 - BIOS Information + 1 - System Information + 2 - Baseboard Information + 3 - System Enclosure + 4 - Processor Information + 5 - Memory Controller Information + 6 - Memory Module Information + 7 - Cache Information + 8 - Port Connector Information + 9 - System Slots + 10 - On Board Devices Information + 11 - OEM Strings + 12 - System Configuration Options + 13 - BIOS Language Information + 14 - Group Associations + 15 - System Event Log + 16 - Physical Memory Array + 17 - Memory Device + 18 - 32-bit Memory Error Information + 19 - Memory Array Mapped Address + 20 - Memory Device Mapped Address + 21 - Built-in Pointing Device + 22 - Portable Battery + 23 - System Reset + 24 - Hardware Security + 25 - System Power Controls + 26 - Voltage Probe + 27 - Cooling Device + 28 - Temperature Probe + 29 - Electrical Current Probe + 30 - Out-Of-Band Remote Access + 31 - Boot Integrity Services (BIS) Entry Point + 32 - System Boot Information + 33 - 64-Bit Memory Error Information + 34 - Management Device + 35 - Management Device Component + 36 - Management Device Threshold Data + 37 - Memory Channel + 38 - IPMI Device Information + 39 - System Power Supply + 40 - Additional Information + 41 - Onboard Devices Extended Information + 42 - Management Controller Host Interface + 43 - TPM Device + 44 - Processor Additional Information + 2. Enter the SmbiosHandle parameter in hexadecimal format. + Do not use the '0x' prefix format for hexadecimal values. + 3. Internal commands: + :q -------- quit smbiosview + :0 -------- Change smbiosview display NONE info + :1 -------- Change smbiosview display OUTLINE info + :2 -------- Change smbiosview display NORMAL info + :3 -------- Change smbiosview display DETAIL info + /? -------- Show help +``` + +Try to dump one of the structures that we've tried to parse manually: +``` +FS0:\> smbiosview -t 0 +SMBIOS Entry Point Structure: +Anchor String: _SM_ +EPS Checksum: 0x26 +Entry Point Len: 31 +Version: 2.8 +Number of Structures: 9 +Max Struct size: 83 +Table Address: 0x7940000 +Table Length: 401 +Entry Point revision: 0x0 +SMBIOS BCD Revision: 0x28 +Inter Anchor: _DMI_ +Inter Checksum: 0xA +Formatted Area: + 00000000: 00 00 00 00 00 *.....* + +========================================================= +Query Structure, conditions are: +QueryType = 0 +QueryHandle = Random +ShowType = SHOW_DETAIL + + +========================================================= +Type=0, Handle=0x0 +Dump Structure as: +Index=7,Length=0x4A,Addr=0x7940141 +00000000: 00 1A 00 00 01 02 00 E8-03 00 08 00 00 00 00 00 *................* +FS0:\> 0: 00 00 00 1C 00 00 FF FF-00 00 45 46 49 20 44 65 *..........EFI De* +00000020: 76 65 6C 6F 70 6D 65 6E-74 20 4B 69 74 20 49 49 *velopment Kit II* +00000030: 20 2F 20 4F 56 4D 46 00-30 2E 30 2E 30 00 30 32 * / OVMF.0.0.0.02* +00000040: 2F 30 36 2F 32 30 31 35-00 00 */06/2015..* +Structure Type: BIOS Information +Format part Len : 26 +Structure Handle: 0 +Vendor: EFI Development Kit II / OVMF +BiosVersion: 0.0.0 +BiosSegment: 0xE800 +BiosReleaseDate: 02/06/2015 +BiosSize: 64 KB +BIOS Characteristics: +BIOS Characteristics Not Supported + Bits 32:47 are reserved for BIOS Vendor + Bits 48:64 are reserved for System Vendor +BIOS Characteristics Extension Byte1: +BIOS Characteristics Extension Byte2: +Enable Targeted Content Distribution +UEFI Specification is supported +The SMBIOS table describes a virtual machine + Bits 5:7 are reserved for future assignment +SystemBiosMajorRelease: 0 +SystemBiosMinorRelease: 0 +EmbeddedControllerFirmwareMajorRelease: 255 +EmbeddedControllerFirmwareMinorRelease: 255 +``` +As you can see it is all the same info that we've received using `EFI_SMBIOS_PROTOCOL`. + +You can use this command to see what is inside all of the SMBIOS structures using: +``` +FS0:\> smbiosview -b +... +``` + +The output is too big to paste here, so check it out yourself! diff --git a/Lesson_27/SMBIOS_entry_structure.png b/Lesson_27/SMBIOS_entry_structure.png Binary files differnew file mode 100644 index 0000000..f488425 --- /dev/null +++ b/Lesson_27/SMBIOS_entry_structure.png diff --git a/Lesson_27/SMBIOS_entry_structure_dump.png b/Lesson_27/SMBIOS_entry_structure_dump.png Binary files differnew file mode 100644 index 0000000..ff27b9b --- /dev/null +++ b/Lesson_27/SMBIOS_entry_structure_dump.png diff --git a/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.c b/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.c new file mode 100644 index 0000000..b75026d --- /dev/null +++ b/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.c @@ -0,0 +1,88 @@ +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include <Library/BaseMemoryLib.h> +#include <Protocol/Smbios.h> + +CHAR8* GetRecordString(EFI_SMBIOS_TABLE_HEADER* Record, UINTN number) +{ + if (!number) + return ""; + + CHAR8* String = (CHAR8*)Record + Record->Length; + UINTN i=1; + while (i < number) { + String = String + AsciiStrSize(String); + i++; + } + return String; +} + +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + for (UINTN i=0; i<SystemTable->NumberOfTableEntries; i++) { + if (CompareGuid(&(SystemTable->ConfigurationTable[i].VendorGuid), &gEfiSmbiosTableGuid)) { + Print(L"SMBIOS table is placed at %p\n\n", SystemTable->ConfigurationTable[i].VendorTable); + } + } + + EFI_SMBIOS_PROTOCOL* SmbiosProtocol; + EFI_STATUS Status = gBS->LocateProtocol ( + &gEfiSmbiosProtocolGuid, + NULL, + (VOID**)&SmbiosProtocol + ); + if (EFI_ERROR (Status)) { + return Status; + } + + EFI_SMBIOS_HANDLE SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; + EFI_SMBIOS_TABLE_HEADER* Record; + Status = SmbiosProtocol->GetNext(SmbiosProtocol, + &SmbiosHandle, + NULL, + &Record, + NULL); + 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; + } + case EFI_SMBIOS_TYPE_SYSTEM_INFORMATION: { + SMBIOS_TABLE_TYPE1* Type1Record = (SMBIOS_TABLE_TYPE1*) Record; + Print(L"\tManufacturer=%a\n", GetRecordString(Record, Type1Record->Manufacturer)); + Print(L"\tProductName=%a\n", GetRecordString(Record, Type1Record->ProductName)); + Print(L"\tVersion=%a\n", GetRecordString(Record, Type1Record->Version)); + Print(L"\tSerialNumber=%a\n", GetRecordString(Record, Type1Record->SerialNumber)); + Print(L"\tUUID=%g\n", Type1Record->Uuid); + Print(L"\tWakeUpType=%d\n", Type1Record->WakeUpType); + Print(L"\tSKUNumber=%a\n", GetRecordString(Record, Type1Record->SKUNumber)); + Print(L"\tFamily=%a\n", GetRecordString(Record, Type1Record->Family)); + break; + } + default: + Print(L"\tTODO: Parsing for this table is not ready yet\n"); + break; + } + Status = SmbiosProtocol->GetNext(SmbiosProtocol, + &SmbiosHandle, + NULL, + &Record, + NULL); + } + + return EFI_SUCCESS; +} diff --git a/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.inf b/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.inf new file mode 100644 index 0000000..ab6013a --- /dev/null +++ b/Lesson_27/UefiLessonsPkg/SmbiosInfo/SmbiosInfo.inf @@ -0,0 +1,24 @@ +[Defines] + INF_VERSION = 1.25 + BASE_NAME = SmbiosInfo + FILE_GUID = 4a8836db-9e1d-4b7d-a785-f552340fba59 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +[Sources] + SmbiosInfo.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiLib + +[Guids] + gEfiSmbiosTableGuid + +[Protocols] + gEfiSmbiosProtocolGuid + @@ -29,6 +29,7 @@ Lessons description: - Lesson 24: Dynamic/DynamicEx PCDs - Lesson 25: More on PCDs - Lesson 26: Tables referenced in `EFI_CONFIGURATION_TABLE` +- Lesson 27: Get SMBIOS information with `dmem`/`EFI_SMBIOS_PROTOCOL`/`smbiosview` ______ |