In this post, I show you how to access the ACPI 5.0 Boot Graphics Resource Table (BGRT) from the UEFI shell and how to save a copy of that boot graphic, usually an OEM logo, in BMP format to disk.
To fully understand the BGRT, you need to read the ACPI specification, version 5. Quoting from section 5.2.22 of that specification:
The Boot Graphics Resource Table (BGRT) is an optional table that provides a mechanism to indicate that an image was drawn on the screen during boot, and some information about the image.
The table is written when the image is drawn on the screen. This should be done after it is expected that any firmware components that may write to the screen are done doing so and it is known that the image is the only thing on the screen. If the boot path is interrupted (e.g. by a key press), the valid bit within the status field should be changed to 0 to indicate to the OS that the current image is invalidated.
This table is only supported on UEFI systems.
Do not expect to find this table on UEFI firmware that is older than 2010.
So where is this image sorted in memory? Quoting from section 5.2.22.4 of the ACPI 5.0 specification:
The Image Address contains the location in memory where an in-memory copy of the boot image can be found. The image should be stored in EfiBootServicesData, allowing the system to reclaim the memory when the image is no longer needed.
Note that should should be interpreted to mean a recommendation and not a requirement on an implementation. There is some discussion within the TianoCore community as to whether the boot graphic image should be stored in EfiBootServicesData or EfiReservedMemoryType memory,
Here is the source code for the utility:
// // Copyright (c) 2015 Finnbarr P. Murphy. All rights reserved. // // Show BGRT info, save image to file if option selected // // License: BSD License // #include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/ShellLib.h> #include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/PrintLib.h> #include <Protocol/EfiShell.h> #include <Protocol/LoadedImage.h> #include <Protocol/AcpiSystemDescriptionTable.h> #include <Protocol/SimpleFileSystem.h> #include <Protocol/GraphicsOutput.h> #define EFI_ACPI_TABLE_GUID \ { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d }} #define EFI_ACPI_20_TABLE_GUID \ { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 }} // Boot Graphics Resource Table definition typedef struct { EFI_ACPI_SDT_HEADER Header; UINT16 Version; UINT8 Status; UINT8 ImageType; UINT64 ImageAddress; UINT32 ImageOffsetX; UINT32 ImageOffsetY; } EFI_ACPI_BGRT; #define EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE_REVISION 1 #define EFI_ACPI_5_0_BGRT_VERSION 0x01 #define EFI_ACPI_5_0_BGRT_STATUS_NOT_DISPLAYED 0x00 #define EFI_ACPI_5_0_BGRT_STATUS_DISPLAYED 0x01 #define EFI_ACPI_5_0_BGRT_STATUS_INVALID EFI_ACPI_5_0_BGRT_STATUS_NOT_DISPLAYED #define EFI_ACPI_5_0_BGRT_STATUS_VALID EFI_ACPI_5_0_BGRT_STATUS_DISPLAYED #define EFI_ACPI_5_0_BGRT_IMAGE_TYPE_BMP 0x00 typedef struct { CHAR8 CharB; CHAR8 CharM; UINT32 Size; UINT16 Reserved[2]; UINT32 ImageOffset; UINT32 HeaderSize; UINT32 PixelWidth; UINT32 PixelHeight; UINT16 Planes; UINT16 BitPerPixel; UINT32 CompressionType; UINT32 ImageSize; UINT32 XPixelsPerMeter; UINT32 YPixelsPerMeter; UINT32 NumberOfColors; UINT32 ImportantColors; } __attribute__((__packed__)) BMP_IMAGE_HEADER; int Verbose = 0; int SaveImage = 0; static VOID AsciiToUnicodeSize( CHAR8 *String, UINT8 length, CHAR16 *UniString) { int len = length; while (*String != '\0' && len > 0) { *(UniString++) = (CHAR16) *(String++); len--; } *UniString = '\0'; } // // Save Boot Logo image as a BMP file // EFI_STATUS SaveBMP( CHAR16 *FileName, UINT8 *FileData, UINTN FileDataLength) { EFI_STATUS Status; EFI_FILE_HANDLE FileHandle; UINTN BufferSize; EFI_FILE_PROTOCOL *Root; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; Status = gBS->LocateProtocol( &gEfiSimpleFileSystemProtocolGuid, NULL, (VOID **)&SimpleFileSystem); if (EFI_ERROR(Status)) { Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n"); return Status; } Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root); if (EFI_ERROR(Status)) { Print(L"ERROR: Volume open\n"); return Status; } Status = Root->Open( Root, &FileHandle, FileName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (EFI_ERROR(Status)) { Print(L"ERROR: File open\n"); return Status; } BufferSize = FileDataLength; Status = FileHandle->Write(FileHandle, &BufferSize, FileData); FileHandle->Close(FileHandle); Print(L"Successfully saved bootgraphic.bmp\n"); return Status; } // // Parse the in-memory BMP header // EFI_STATUS ParseBMP(UINT64 BmpImage) { BMP_IMAGE_HEADER *BmpHeader = (BMP_IMAGE_HEADER *)BmpImage; EFI_STATUS Status = EFI_SUCCESS; CHAR16 Buffer[100]; if (Verbose) { // not BMP format if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { Print(L"ERROR: Unsupported image format\n"); return EFI_UNSUPPORTED; } // BITMAPINFOHEADER format unsupported if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) \ - ((UINTN) &(((BMP_IMAGE_HEADER *)0)->HeaderSize))) { Print(L"ERROR: Unsupported BITMAPFILEHEADER\n"); return EFI_UNSUPPORTED; } // compression type not 0 if (BmpHeader->CompressionType != 0) { Print(L"ERROR: Compression Type not 0\n"); return EFI_UNSUPPORTED; } // unsupported bits per pixel if (BmpHeader->BitPerPixel != 4 && BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 12 && BmpHeader->BitPerPixel != 24) { Print(L"ERROR: Bits per pixel is not one of 4, 8, 12 or 24\n"); return EFI_UNSUPPORTED; } Print(L"\n"); AsciiToUnicodeSize((CHAR8 *)BmpHeader, 2, Buffer); Print(L"BMP Signature : %s\n", Buffer); Print(L"Size : %d\n", BmpHeader->Size); Print(L"Image Offset : %d\n", BmpHeader->ImageOffset); Print(L"Header Size : %d\n", BmpHeader->HeaderSize); Print(L"Image Width : %d\n", BmpHeader->PixelWidth); Print(L"Image Height : %d\n", BmpHeader->PixelHeight); Print(L"Planes : %d\n", BmpHeader->Planes); Print(L"Bit Per Pixel : %d\n", BmpHeader->BitPerPixel); Print(L"Compression Type : %d\n", BmpHeader->CompressionType); Print(L"Image Size : %d\n", BmpHeader->ImageSize); Print(L"X Pixels Per Meter: %d\n", BmpHeader->XPixelsPerMeter); Print(L"Y Pixels Per Meter: %d\n", BmpHeader->YPixelsPerMeter); Print(L"Number of Colors : %d\n", BmpHeader->NumberOfColors); Print(L"Important Colors : %d\n", BmpHeader->ImportantColors); } // Verbose // save the boot logo to a file if (SaveImage) { Status = SaveBMP(L"bootgraphic.bmp", (UINT8 *)BmpImage, BmpHeader->Size); if (EFI_ERROR(Status)) { Print(L"ERROR: Saving boot graphic file: %x\n", Status); } } return EFI_SUCCESS; } // // Parse Boot Graphic Resource Table // static VOID ParseBGRT(EFI_ACPI_BGRT *Bgrt) { CHAR16 Buffer[100]; Print(L"\n"); AsciiToUnicodeSize((CHAR8 *)&(Bgrt->Header.Signature), 4, Buffer); Print(L"Signature : %s\n", Buffer); Print(L"Length : %d\n", Bgrt->Header.Length); Print(L"Revision : %d\n", Bgrt->Header.Revision); Print(L"Checksum : %d\n", Bgrt->Header.Checksum); AsciiToUnicodeSize((CHAR8 *)(Bgrt->Header.OemId), 6, Buffer); Print(L"Oem ID : %s\n", Buffer); AsciiToUnicodeSize((CHAR8 *)(Bgrt->Header.OemTableId), 8, Buffer); Print(L"Oem Table ID : %s\n", Buffer); Print(L"Oem Revision : %d\n", Bgrt->Header.OemRevision); AsciiToUnicodeSize((CHAR8 *)&(Bgrt->Header.CreatorId), 4, Buffer); Print(L"Creator ID : %s\n", Buffer); Print(L"Creator Revision : %d\n", Bgrt->Header.CreatorRevision); Print(L"Version : %d\n", Bgrt->Version); Print(L"Status : %d", Bgrt->Status); if (Bgrt->Status == EFI_ACPI_5_0_BGRT_STATUS_NOT_DISPLAYED) Print(L" (Not displayed)"); if (Bgrt->Status == EFI_ACPI_5_0_BGRT_STATUS_DISPLAYED) Print(L" (Displayed)"); Print(L"\n"); Print(L"Image Type : %d", Bgrt->ImageType); if (Bgrt->ImageType == EFI_ACPI_5_0_BGRT_IMAGE_TYPE_BMP) Print(L" (BMP format)"); Print(L"\n"); Print(L"Offset Y : %ld\n", Bgrt->ImageOffsetY); Print(L"Offset X : %ld\n", Bgrt->ImageOffsetX); if (Verbose) { Print(L"Physical Address : %lld\n", Bgrt->ImageAddress); } ParseBMP(Bgrt->ImageAddress); Print(L"\n"); } static int ParseRSDP( EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp, CHAR16* GuidStr) { EFI_ACPI_SDT_HEADER *Xsdt, *Entry; CHAR16 OemStr[20]; UINT32 EntryCount; UINT64 *EntryPtr; AsciiToUnicodeSize((CHAR8 *)(Rsdp->OemId), 6, OemStr); if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) { Xsdt = (EFI_ACPI_SDT_HEADER *)(Rsdp->XsdtAddress); } else { return 1; } if (Xsdt->Signature != SIGNATURE_32 ('X', 'S', 'D', 'T')) { return 1; } AsciiToUnicodeSize((CHAR8 *)(Xsdt->OemId), 6, OemStr); EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_SDT_HEADER)) / sizeof(UINT64); EntryPtr = (UINT64 *)(Xsdt + 1); for (int Index = 0; Index < EntryCount; Index++, EntryPtr++) { Entry = (EFI_ACPI_SDT_HEADER *)((UINTN)(*EntryPtr)); if (Entry->Signature == SIGNATURE_32 ('B', 'G', 'R', 'T')) { ParseBGRT((EFI_ACPI_BGRT *)((UINTN)(*EntryPtr))); } } return 0; } static void Usage(void) { Print(L"Usage: ShowBGRT [-v|--verbose] [-s|--save]\n"); } INTN EFIAPI ShellAppMain(UINTN Argc, CHAR16 **Argv) { EFI_CONFIGURATION_TABLE *ect = gST->ConfigurationTable; EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp = NULL; EFI_GUID AcpiTableGuid = EFI_ACPI_TABLE_GUID; EFI_GUID Acpi20TableGuid = EFI_ACPI_20_TABLE_GUID; EFI_STATUS Status = EFI_SUCCESS; CHAR16 GuidStr[100]; for (int i = 1; i < Argc; i++) { if (!StrCmp(Argv[i], L"--verbose") || !StrCmp(Argv[i], L"-v")) { Verbose = 1; } else if (!StrCmp(Argv[i], L"--help") || !StrCmp(Argv[i], L"-h") || !StrCmp(Argv[i], L"-?")) { Usage(); return Status; } else if (!StrCmp(Argv[i], L"--save") || !StrCmp(Argv[1], L"-s")) { SaveImage = 1; } else { Print(L"ERROR: Unknown option.\n"); Usage(); return Status; } } // locate RSDP (Root System Description Pointer) for (int i = 0; i < gST->NumberOfTableEntries; i++) { if ((CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &AcpiTableGuid)) || (CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &Acpi20TableGuid))) { if (!AsciiStrnCmp("RSD PTR ", (CHAR8 *)(ect->VendorTable), 8)) { UnicodeSPrint(GuidStr, sizeof(GuidStr), L"%g", &(gST->ConfigurationTable[i].VendorGuid)); Rsdp = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)ect->VendorTable; ParseRSDP(Rsdp, GuidStr); } } ect++; } if (Rsdp == NULL) { if (Verbose) { Print(L"ERROR: Could not find an ACPI RSDP table.\n"); } return EFI_NOT_FOUND; } return Status; }
Where the boot graphic file is saved will depend on your particular firmware implementation. In most cases it will be in the root of FS0:. I leave it to you to modify the code to specify exactly where bootgraphic.bmp is written. Another improvement to consider is modifying the code to enable multiple boot graphics files to be saved instead of just one file. This could be done by appending an incremental number to the filename or maybe by appending a timestamp to the filename.
Here is the build .INF:
[Defines] INF_VERSION = 0x00010006 BASE_NAME = ShowBGRT FILE_GUID = 4ea87c57-7795-4dcd-0055-747010f3ce51 MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 0.1 ENTRY_POINT = ShellCEntryLib VALID_ARCHITECTURES = X64 [Sources] ShowBGRT.c [Packages] MdePkg/MdePkg.dec ShellPkg/ShellPkg.dec [LibraryClasses] ShellCEntryLib ShellLib BaseLib BaseMemoryLib UefiLib [Protocols] [BuildOptions] [Pcd]
And here is sample output when run on a Lenovo T450:
fs0> ShowBGRT.efi Signature : BGRT Length : 56 Revision : 1 Checksum : 77 Oem ID : LENOVO Oem Table ID : TP-JB Oem Revision : 4480 Creator ID : PTEC Creator Revision : 2 Version : 1 Status : 1 (Displayed) Image Type : 0 (BMP format) Offset Y : 292 Offset X : 480 fs0> ShowBGRT.efi --verbose Signature : BGRT Length : 56 Revision : 1 Checksum : 77 Oem ID : LENOVO Oem Table ID : TP-JB Oem Revision : 4480 Creator ID : PTEC Creator Revision : 2 Version : 1 Status : 1 (Displayed) Image Type : 0 (BMP format) Offset Y : 292 Offset X : 480 Physical Address : 3020431384 BMP Signature : BM Size : 195894 Image Offset : 54 Header Size : 40 Image Width : 640 Image Height : 102 Planes : 1 Bit Per Pixel : 24 Compression Type : 0 Image Size : 195840 X Pixels Per Meter: 0 Y Pixels Per Meter: 0 Number of Colors : 0 Important Colors : 0
I have only built and tested this utility on an X64 platform. You may have to modify the source code and build environment to run on other platforms.