This post details recent updates to a simple UEFI shell utility for displaying information about the ACPI (Advanced Configuration and Power Interface) Boot Graphics Resource Table (BGRT) that I first released in 2015 and subsequently updated in 2017, and again this year. Source code for the previous versions is available on Github at UEFI-Utilities-2016 and UEFI-Utilities-2018 respectfully. My original post about BGRT is here.
My goals for this update were:
- Add option to display the image on screen
- Add discrete support for 1, 4, 8, 12 and 32-bit images
- Scroll screen up when necessary to fully display the image
- Save image as BMP file in current directory
These enhancements nearly doubled the size of the source code:
$ wc ShowBGRT.c ShowBGRT.c.old 704 2187 23510 ShowBGRT.c 398 1129 11634 ShowBGRT.c.org $
By the way, I expect the codebase to increase by 50% or more when I add full HII support and message catalogs in the next major release early next year.
Here is the full source code for the current version of the utility:
// // Copyright (c) 2015-2019 Finnbarr P. Murphy. All rights reserved. // // Show BGRT info, display image or save image to file // // License: BSD 2 clause License // // Portions Copyright (c) 2016-2017, Microsoft Corporation // Copyright (c) 2018, Intel Corporation. All rights reserved. // See relevant code in EDK11 for exact details // #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/LoadedImage.h> #include <Protocol/AcpiSystemDescriptionTable.h> #include <Protocol/SimpleFileSystem.h> #include <Protocol/GraphicsOutput.h> #include <Protocol/Shell.h> #include <Guid/Acpi.h> #include <IndustryStandard/Bmp.h> #include <IndustryStandard/Acpi61.h> #define UTILITY_VERSION L"20190124" #undef DEBUG // for option setting typedef enum { Verbose = 1, HexDump, SaveImageMode, DisplayImageMode } MODE; VOID AsciiToUnicodeSizeQuote(CHAR8 *, UINT8, CHAR16 *, BOOLEAN); EFI_GRAPHICS_OUTPUT_BLT_PIXEL EfiGraphicsColors[16] = { // B G R reserved {0x00, 0x00, 0x00, 0x00}, // BLACK {0x98, 0x00, 0x00, 0x00}, // LIGHTBLUE {0x00, 0x98, 0x00, 0x00}, // LIGHGREEN {0x98, 0x98, 0x00, 0x00}, // LIGHCYAN {0x00, 0x00, 0x98, 0x00}, // LIGHRED {0x98, 0x00, 0x98, 0x00}, // MAGENTA {0x00, 0x98, 0x98, 0x00}, // BROWN {0x98, 0x98, 0x98, 0x00}, // LIGHTGRAY {0x30, 0x30, 0x30, 0x00}, // DARKGRAY {0xff, 0x00, 0x00, 0x00}, // BLUE {0x00, 0xff, 0x00, 0x00}, // LIME {0xff, 0xff, 0x00, 0x00}, // CYAN {0x00, 0x00, 0xff, 0x00}, // RED {0xff, 0x00, 0xff, 0x00}, // FUCHSIA {0x00, 0xff, 0xff, 0x00}, // YELLOW {0xff, 0xff, 0xff, 0x00} // WHITE }; VOID GetBackgroundColor( EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Background ) { INTN Attribute; Attribute = gST->ConOut->Mode->Attribute & 0x7F; *Background = EfiGraphicsColors[Attribute >> 4]; // DEBUG *Background = EfiGraphicsColors[10]; } VOID GetCursorPosition( UINTN *x, UINTN *y ) { *x = gST->ConOut->Mode->CursorColumn; *y = gST->ConOut->Mode->CursorRow; } VOID SetCursorPosition( UINTN x, UINTN y ) { gST->ConOut->SetCursorPosition( gST->ConOut, x, y); } VOID AsciiToUnicodeSizeQuote( CHAR8 *String, UINT8 length, CHAR16 *UniString, BOOLEAN Quote ) { int len = length; if (Quote) *(UniString++) = L'"'; while (*String != '\0' && len > 0) { *(UniString++) = (CHAR16) *(String++); len--; } if (Quote) *(UniString++) = L'"'; *UniString = '\0'; } VOID DumpHex( UINT8 *ptr, int Count ) { int i = 0; Print(L" "); for ( i = 0; i < Count; i++ ) { if ( i > 0 && i%16 == 0 ) Print(L"\n "); Print(L"0x%02x ", 0xff & *ptr++); } Print(L"\n"); } // // Display the BMP image, convert to 24-bit if necessary, scroll screen if necessary // EFI_STATUS DisplayImage( EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, EFI_HANDLE *BmpBuffer ) { EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; BMP_IMAGE_HEADER *BmpHeader; BMP_COLOR_MAP *BmpColorMap; EFI_STATUS Status = EFI_SUCCESS; UINT32 *Palette; UINT8 *BitmapData; UINT8 *Image; UINT8 *ImageHeader; UINTN SizeOfInfo; UINTN Pixels; UINTN Width, Height; UINTN ImageIndex; UINTN Index; UINTN ImageHeight; UINTN ImageRows; UINTN CurRow, CurCol; UINTN MaxRows, MaxCols; UINTN VertPixelDelta = 0; UINTN ImagePixelDelta = 0; if (BmpBuffer == NULL) { return RETURN_INVALID_PARAMETER; } BmpHeader = (BMP_IMAGE_HEADER *) BmpBuffer; BitmapData = (UINT8*)BmpBuffer + BmpHeader->ImageOffset; Palette = (UINT32*) ((UINT8*)BmpBuffer + 0x36); Pixels = BmpHeader->PixelWidth * BmpHeader->PixelHeight; BltBuffer = AllocateZeroPool( sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Pixels); if (BltBuffer == NULL) { Print(L"ERROR: BltBuffer. No memory resources\n"); return EFI_OUT_OF_RESOURCES; } Image = (UINT8 *)BmpBuffer; BmpColorMap = (BMP_COLOR_MAP *) (Image + sizeof (BMP_IMAGE_HEADER)); Image = ((UINT8 *)BmpBuffer) + BmpHeader->ImageOffset; ImageHeader = Image; // fill blt buffer for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) { switch (BmpHeader->BitPerPixel) { case 1: // Convert 1-bit BMP to 24-bit color for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) { Blt->Blue = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blue; Blt->Green = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Green; Blt->Red = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; Blt++; Width++; } Blt--; Width--; break; case 4: // Convert 4-bit BMP Palette to 24-bit color Index = (*Image) >> 4; Blt->Blue = BmpColorMap[Index].Blue; Blt->Green = BmpColorMap[Index].Green; Blt->Red = BmpColorMap[Index].Red; if (Width < (BmpHeader->PixelWidth - 1)) { Blt++; Width++; Index = (*Image) & 0x0f; Blt->Blue = BmpColorMap[Index].Blue; Blt->Green = BmpColorMap[Index].Green; Blt->Red = BmpColorMap[Index].Red; } break; case 8: // Convert 8-bit BMP palette to 24-bit color Blt->Blue = BmpColorMap[*Image].Blue; Blt->Green = BmpColorMap[*Image].Green; Blt->Red = BmpColorMap[*Image].Red; break; case 24: // No conversion needed Blt->Blue = *Image++; Blt->Green = *Image++; Blt->Red = *Image; break; case 32: // Convert to 24-bit by ignoring final byte of each pixel. Blt->Blue = *Image++; Blt->Green = *Image++; Blt->Red = *Image++; break; default: FreePool(BltBuffer); return EFI_UNSUPPORTED; break; }; } // start each row on a 32-bit boundary! ImageIndex = (UINTN)Image - (UINTN)ImageHeader; if ((ImageIndex % 4) != 0) { Image = Image + (4 - (ImageIndex % 4)); } } // get max rows and columns for current mode gST->ConOut->QueryMode( gST->ConOut, gST->ConOut->Mode->Mode, &MaxCols, &MaxRows ); GetCursorPosition( &CurCol, &CurRow ); #ifdef DEBUG Print(L"CURSOR %02d %02d\n", CurCol, CurRow); #endif // get screen details for current mode Gop->QueryMode( Gop, Gop->Mode->Mode, &SizeOfInfo, &Info ); // calculate required image and screen properties Width = Info->HorizontalResolution; ImageHeight = BmpHeader->PixelHeight; ImageRows = ImageHeight/EFI_GLYPH_HEIGHT; if ((ImageRows * EFI_GLYPH_HEIGHT) < ImageHeight) { ImagePixelDelta = (ImageHeight - (ImageRows * EFI_GLYPH_HEIGHT))/2; ImageRows++; } if ((MaxRows * EFI_GLYPH_HEIGHT) < Info->VerticalResolution) { VertPixelDelta = (Info->VerticalResolution - (MaxRows * EFI_GLYPH_HEIGHT))/2; } // scroll required? if ((CurRow + ImageRows + 1) >= MaxRows) { GetBackgroundColor( &Background ); // calculate number of rows to scroll UINTN ScrollRows = (ImageRows - (MaxRows - CurRow) +1); #ifdef DEBUG Print(L"CurRow: %d ImageRows: %d ImagePixelDelta: %d ScrollRows: %d VertPixelDelta: %d\n", CurRow, ImageRows, ImagePixelDelta, ScrollRows, VertPixelDelta ); #endif // scroll up to make room to display image Status = Gop->Blt( Gop, NULL, EfiBltVideoToVideo, 0, VertPixelDelta + (ScrollRows * EFI_GLYPH_HEIGHT), // Source X,Y 0, VertPixelDelta, // Destination X,Y Width, (MaxRows - ScrollRows) * EFI_GLYPH_HEIGHT, 0 ); if (EFI_ERROR (Status)) { Print(L"ERROR: Scroll Up, Gop->Blt [%d]\n", Status); goto cleanup; } // color background of the scrolled area Status = Gop->Blt( Gop, &Background, EfiBltVideoFill, 0, 0, // Not Used 0, (MaxRows - ScrollRows) * EFI_GLYPH_HEIGHT, Width - 1, ScrollRows * EFI_GLYPH_HEIGHT, 0 ); if (EFI_ERROR (Status)) { Print(L"ERROR: Color Fill, Gop->Blt [%d]\n", Status); goto cleanup; } // display the image Status = Gop->Blt( Gop, BltBuffer, EfiBltBufferToVideo, 0, 0, // Source X,Y 0, ImagePixelDelta + ((MaxRows - ImageRows) * EFI_GLYPH_HEIGHT), // Destination X,Y BmpHeader->PixelWidth, BmpHeader->PixelHeight, 0 ); if (EFI_ERROR (Status)) { Print(L"ERROR: Image Display Gop->Blt [%d]\n", Status); goto cleanup; } SetCursorPosition( 0, MaxRows - 1 ); } else { // just display the image Status = Gop->Blt( Gop, BltBuffer, EfiBltBufferToVideo, 0, 0, // Source X,Y 0, ImagePixelDelta + ((CurRow + 1) * EFI_GLYPH_HEIGHT), // Destination X,Y BmpHeader->PixelWidth, BmpHeader->PixelHeight, 0 ); if (EFI_ERROR (Status)) { Print(L"ERROR: Image Display Gop->Blt [%d]\n", Status); goto cleanup; } SetCursorPosition( 0, CurRow + ImageRows ); } cleanup: FreePool(BltBuffer); return Status; } // // Save image as a BMP file // EFI_STATUS SaveImage( CHAR16 *FileName, UINT8 *FileData, UINTN FileDataLength ) { SHELL_FILE_HANDLE FileHandle = NULL; EFI_STATUS Status = EFI_SUCCESS; CONST CHAR16 *CurDir = NULL; CONST CHAR16 *PathName = NULL; CHAR16 *FullPath = NULL; UINTN Length = 0; CurDir = gEfiShellProtocol->GetCurDir(NULL); if (CurDir == NULL) { Print(L"ERROR: Cannot retrieve current directory\n"); return EFI_NOT_FOUND; } PathName = CurDir; StrnCatGrow(&FullPath, &Length, PathName, 0); StrnCatGrow(&FullPath, &Length, L"\\", 0); StrnCatGrow(&FullPath, &Length, FileName, 0); #ifdef DEBUG Print(L"FullPath: [%s]\n", FullPath); #endif Status = gEfiShellProtocol->OpenFileByName( FullPath, &FileHandle, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE); if (EFI_ERROR(Status)) { Print(L"ERROR: OpenFileByName [%s] [%d]\n", FullPath, Status); return Status; } // BufferSize = FileDataLength; Status = gEfiShellProtocol->WriteFile( FileHandle, &FileDataLength, FileData ); gEfiShellProtocol->CloseFile( FileHandle ); if (EFI_ERROR(Status)) { Print(L"ERROR: Saving image to file: %x\n", Status); } else { Print(L"Successfully saved image to %s\n", FullPath); } return Status; } VOID PrintAcpiHeader( EFI_ACPI_DESCRIPTION_HEADER *Ptr ) { CHAR16 Buffer[50]; Print(L" ACPI Header:\n"); AsciiToUnicodeSizeQuote((CHAR8 *)&(Ptr->Signature), 4, Buffer, TRUE); Print(L" Signature : %s\n", Buffer); Print(L" Length : 0x%08x (%d)\n", Ptr->Length, Ptr->Length); Print(L" Revision : 0x%02x (%d)\n", Ptr->Revision, Ptr->Revision); Print(L" Checksum : 0x%02x (%d)\n", Ptr->Checksum, Ptr->Checksum); AsciiToUnicodeSizeQuote((CHAR8 *)&(Ptr->OemId), 6, Buffer, TRUE); Print(L" OEM ID : %s\n", Buffer); AsciiToUnicodeSizeQuote((CHAR8 *)&(Ptr->OemTableId), 8, Buffer, TRUE); Print(L" OEM Table ID : %s\n", Buffer); Print(L" OEM Revision : 0x%08x (%d)\n", Ptr->OemRevision, Ptr->OemRevision); AsciiToUnicodeSizeQuote((CHAR8 *)&(Ptr->CreatorId), 4, Buffer, TRUE); Print(L" Creator ID : %s\n", Buffer); Print(L" Creator Revision : 0x%08x (%d)\n", Ptr->CreatorRevision, Ptr->CreatorRevision); Print(L"\n"); } // // Print the BMP header details // VOID PrintBMPHeader( EFI_HANDLE *BmpBuffer ) { BMP_IMAGE_HEADER *BmpHeader; CHAR16 Buffer[100]; BmpHeader = (BMP_IMAGE_HEADER *) BmpBuffer; AsciiToUnicodeSizeQuote( (CHAR8 *)BmpHeader, 2, Buffer, FALSE ); Print(L"\n"); Print(L" BMP Header:\n"); 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" Bits 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); Print(L"\n"); } // // Parse the in-memory BMP header // EFI_STATUS ParseImage( UINT64 BmpImage, MODE Mode ) { BMP_IMAGE_HEADER *BmpHeader = (BMP_IMAGE_HEADER *)BmpImage; EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop = NULL; EFI_DEVICE_PATH_PROTOCOL *Dpp; EFI_STATUS Status = EFI_SUCCESS; EFI_HANDLE *Handles = NULL; UINTN HandleCount = 0; // 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; } // supported bits per pixel if (BmpHeader->BitPerPixel != 1 && BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 12 && BmpHeader->BitPerPixel != 24 && BmpHeader->BitPerPixel != 32) { Print(L"ERROR: BitPerPixel is not one of 1, 4, 8, 12, 24 or 32\n"); return EFI_UNSUPPORTED; } if (Mode == SaveImageMode) { Status = SaveImage( L"BGRTimage.bmp", (UINT8 *)BmpImage, BmpHeader->Size ); } else if (Mode == DisplayImageMode) { // Try locating GOP by handle Status = gBS->LocateHandleBuffer( ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &HandleCount, &Handles ); if (EFI_ERROR (Status)) { Print(L"ERROR: No GOP handles found via LocateHandleBuffer\n"); return Status; } #ifdef DEBUG Print(L"Found %d GOP handles via LocateHandleBuffer\n", HandleCount); #endif // Make sure we use the correct GOP handle for (UINTN Handle = 0; Handle < HandleCount; Handle++) { Status = gBS->HandleProtocol( Handles[Handle], &gEfiDevicePathProtocolGuid, (VOID **)&Dpp ); if (!EFI_ERROR(Status)) { Status = gBS->HandleProtocol( Handles[Handle], &gEfiGraphicsOutputProtocolGuid, (VOID **)&Gop ); if (!EFI_ERROR(Status)) { break; } } } FreePool(Handles); if (Gop == NULL) { Print(L"ERROR: No graphics console found.\n"); return Status; } Status = DisplayImage( Gop, (EFI_HANDLE *)BmpImage ); } else if (Mode == Verbose) { PrintBMPHeader( (EFI_HANDLE *)BmpImage ); } return Status; } // // Parse Boot Graphic Resource Table (BGRT) // static VOID ParseBGRT( EFI_ACPI_6_1_BOOT_GRAPHICS_RESOURCE_TABLE *Bgrt, MODE Mode ) { Print(L"\n"); if ( Mode == HexDump ) { DumpHex( (UINT8 *)Bgrt, (int)sizeof(EFI_ACPI_6_1_BOOT_GRAPHICS_RESOURCE_TABLE) ); } else { if ( Mode == Verbose) { PrintAcpiHeader( (EFI_ACPI_DESCRIPTION_HEADER *)&(Bgrt->Header) ); } if ( Mode != SaveImageMode && Mode != DisplayImageMode ) { Print(L" BGRT Header:\n"); 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 (Mode == Verbose) { Print(L" Physical Address : 0x%08x\n", Bgrt->ImageAddress); } ParseImage( Bgrt->ImageAddress, Mode ); } Print(L"\n"); } static int ParseRSDP( EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp, CHAR16* GuidStr, MODE Mode ) { EFI_ACPI_DESCRIPTION_HEADER *Xsdt, *Entry; UINT32 EntryCount; UINT64 *EntryPtr; #ifdef DEBUG Print(L"\n\nACPI GUID: %s\n", GuidStr); #endif if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) { Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(Rsdp->XsdtAddress); } else { #ifdef DEBUG Print(L"ERROR: RSDP table < revision ACPI 2.0 found.\n"); #endif return 1; } if (Xsdt->Signature != SIGNATURE_32 ('X', 'S', 'D', 'T')) { return 1; } EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64); EntryPtr = (UINT64 *)(Xsdt + 1); for (int Index = 0; Index < EntryCount; Index++, EntryPtr++) { Entry = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr)); if (Entry->Signature == SIGNATURE_32 ('B', 'G', 'R', 'T')) { ParseBGRT((EFI_ACPI_6_1_BOOT_GRAPHICS_RESOURCE_TABLE *)((UINTN)(*EntryPtr)), Mode); } } return 0; } VOID Usage( VOID ) { Print(L"Usage: ShowBGRT [-v | --verbose]\n"); Print(L" ShowBGRT [-s | --save]\n"); Print(L" ShowBGRT [-D | --dump]\n"); Print(L" ShowBGRT [-d | --display]\n"); Print(L" ShowBGRT [-V | --version]\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 gAcpi20TableGuid = EFI_ACPI_20_TABLE_GUID; EFI_GUID gAcpi10TableGuid = ACPI_10_TABLE_GUID; EFI_STATUS Status = EFI_SUCCESS; CHAR16 GuidStr[100]; MODE Mode; if (Argc == 2) { if (!StrCmp(Argv[1], L"--verbose") || !StrCmp(Argv[1], L"-v")) { Mode = Verbose; } else if (!StrCmp(Argv[1], L"--dump") || !StrCmp(Argv[1], L"-D")) { Mode = HexDump; } else if (!StrCmp(Argv[1], L"--display") || !StrCmp(Argv[1], L"-d")) { Mode = DisplayImageMode; } else if (!StrCmp(Argv[1], L"--save") || !StrCmp(Argv[1], L"-s")) { Mode = SaveImageMode; } else if (!StrCmp(Argv[1], L"--version") || !StrCmp(Argv[1], L"-V")) { Print(L"Version: %s\n", UTILITY_VERSION); return Status; } else if (!StrCmp(Argv[1], L"--help") || !StrCmp(Argv[1], L"-h")) { Usage(); return Status; } else { Usage(); return Status; } } if (Argc > 2) { Usage(); return Status; } // locate RSDP (Root System Description Pointer) for (int i = 0; i < gST->NumberOfTableEntries; i++) { if ((CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &gAcpi20TableGuid)) || (CompareGuid (&(gST->ConfigurationTable[i].VendorGuid), &gAcpi10TableGuid))) { 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, Mode ); } } ect++; } if (Rsdp == NULL) { Print(L"ERROR: Could not find an ACPI RSDP table.\n"); Status = EFI_NOT_FOUND; } return Status; }
The DisplayImage function comes straight from my recently updated DisplayBMP utility and is discussed in this post which you should read if you wish to understand the nuances of this particular function.
The SaveBMP (now named SaveImage) code was completely rewritten. This is the code from the previous version of the utility:
// // Save Boot Logo image as a BMP file // EFI_STATUS SaveBMP( CHAR16 *FileName, UINT8 *FileData, UINTN FileDataLength ) { EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; EFI_FILE_PROTOCOL *Root; EFI_FILE_HANDLE FileHandle; EFI_STATUS Status = EFI_SUCCESS; UINTN BufferSize; Status = gBS->LocateProtocol( &gEfiSimpleFileSystemProtocolGuid, NULL, (VOID **)&SimpleFileSystem); if (EFI_ERROR(Status)) { Print(L"ERROR: 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); if (EFI_ERROR(Status)) { Print(L"ERROR: Saving image to file: %x\n", Status); } else { Print(L"Successfully saved image to bootlogo.bmp\n"); } return Status; }
The problem with this code is that it only saved the BMP file in the root directory of the first enumerated filesystem, i.e. fs0:. The new routine now saves the BMP file in the current directory of whatever filesystem you are currently on. So, for example, you are currently at fs2:\fpm\tmp and you invoke ShowBGRT -s, it image will be saved as fs2:\fpm\tmp\BGRTimage.bmp.
You will also notice that the new routine makes extensive use of functionality exposed by EFI_SHELL_PROTOCOL which I have written about before. Yes, I could have iterated though the handles returned by LocateHandleBuffer and figured out the correct filesystem and got the current working directory also, but using EFI_SHELL_PROTOCOL resulted in less code, code that is easier to understand and code that is easier to maintain.
The source code and a pre-built 64-bit binary for this version of the utility can be found on Github at UEFI-Utilities-2019.
Enjoy!
Can I know the device you used to verify your UEFI App?
Lenovo T480 and Lenovo T450