aboutsummaryrefslogtreecommitdiffstats
path: root/Lesson_29
diff options
context:
space:
mode:
authorKonstantin Aladyshev <aladyshev22@gmail.com>2021-07-03 00:47:16 +0300
committerKonstantin Aladyshev <aladyshev22@gmail.com>2021-07-03 16:18:55 +0300
commit0872ca11d741d039846c8b301eed1d720d640ed0 (patch)
tree67c57919a56aba4b4ab5bed9b462d847078c587e /Lesson_29
parent560f0303beaac1bfa8966de3fe636657825724da (diff)
downloadUEFI-Lessons-0872ca11d741d039846c8b301eed1d720d640ed0.tar.gz
UEFI-Lessons-0872ca11d741d039846c8b301eed1d720d640ed0.tar.bz2
UEFI-Lessons-0872ca11d741d039846c8b301eed1d720d640ed0.zip
Add lesson 28
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Diffstat (limited to 'Lesson_29')
-rw-r--r--Lesson_29/README.md427
1 files changed, 427 insertions, 0 deletions
diff --git a/Lesson_29/README.md b/Lesson_29/README.md
new file mode 100644
index 0000000..1b613e7
--- /dev/null
+++ b/Lesson_29/README.md
@@ -0,0 +1,427 @@
+In the last lesson we've discovered that our system has BGRT ACPI table.
+
+According to the ACPI 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 actually have a pointer to image data, check structure definition under https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Acpi63.h:
+```
+///
+/// Boot Graphics Resource Table definition.
+///
+typedef struct {
+ EFI_ACPI_DESCRIPTION_HEADER Header;
+ ///
+ /// 2-bytes (16 bit) version ID. This value must be 1.
+ ///
+ UINT16 Version;
+ ///
+ /// 1-byte status field indicating current status about the table.
+ /// Bits[7:1] = Reserved (must be zero)
+ /// Bit [0] = Valid. A one indicates the boot image graphic is valid.
+ ///
+ UINT8 Status;
+ ///
+ /// 1-byte enumerated type field indicating format of the image.
+ /// 0 = Bitmap
+ /// 1 - 255 Reserved (for future use)
+ ///
+ UINT8 ImageType;
+ ///
+ /// 8-byte (64 bit) physical address pointing to the firmware's in-memory copy
+ /// of the image bitmap.
+ ///
+ UINT64 ImageAddress;
+ ///
+ /// A 4-byte (32-bit) unsigned long describing the display X-offset of the boot image.
+ /// (X, Y) display offset of the top left corner of the boot image.
+ /// The top left corner of the display is at offset (0, 0).
+ ///
+ UINT32 ImageOffsetX;
+ ///
+ /// A 4-byte (32-bit) unsigned long describing the display Y-offset of the boot image.
+ /// (X, Y) display offset of the top left corner of the boot image.
+ /// The top left corner of the display is at offset (0, 0).
+ ///
+ UINT32 ImageOffsetY;
+} EFI_ACPI_6_3_BOOT_GRAPHICS_RESOURCE_TABLE;
+```
+
+
+Let's create an app that would save an image from BGRT.
+
+This time to get BGRT table we would utilize `EFI_ACPI_SDT_PROTOCOL` protocol.
+
+To get ACPI table data we would use `GetAcpiTable()` function from this protocol:
+```
+EFI_ACPI_SDT_PROTOCOL.GetAcpiTable()
+
+Summary:
+Returns a requested ACPI table.
+
+Prototype:
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ACPI_GET_ACPI_TABLE) (
+ IN UINTN Index,
+ OUT EFI_ACPI_SDT_HEADER **Table,
+ OUT EFI_ACPI_TABLE_VERSION *Version,
+ OUT UINTN *TableKey
+ );
+
+Parameters:
+Index The zero-based index of the table to retrieve.
+Table Pointer for returning the table buffer.
+Version On return, updated with the ACPI versions to which this table belongs.
+TableKey On return, points to the table key for the specified ACPI system definition table.
+
+Description:
+The GetAcpiTable() function returns a pointer to a buffer containing the ACPI table associated with the Index that was input. The following structures are not considered elements in the list of ACPI tables:
+- Root System Description Pointer (RSD_PTR)
+- Root System Description Table (RSDT)
+- Extended System Description Table (XSDT)
+```
+In edk2 it is defined here: https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/AcpiSystemDescriptionTable.h
+
+To get all tables we need to call `GetAcpiTable` with incrementing values for `Index` starting with 0, while function returns `EFI_SUCCESS`.
+
+On every success call we would get a pointer to a common header for a ACPI table:
+```
+typedef struct {
+ UINT32 Signature;
+ UINT32 Length;
+ UINT8 Revision;
+ UINT8 Checksum;
+ CHAR8 OemId[6];
+ CHAR8 OemTableId[8];
+ UINT32 OemRevision;
+ UINT32 CreatorId;
+ UINT32 CreatorRevision;
+} EFI_ACPI_SDT_HEADER;
+```
+
+To use `EFI_ACPI_SDT_PROTOCOL` we need to add include to our file:
+```
+#include <Protocol/AcpiSystemDescriptionTable.h>
+```
+And add protocol to the *.inf file:
+```
+[Protocols]
+ gEfiAcpiSdtProtocolGuid
+```
+
+Here is a code finding BGRT ACPI table:
+```
+EFI_ACPI_SDT_PROTOCOL* AcpiSdtProtocol;
+EFI_STATUS Status = gBS->LocateProtocol (
+ &gEfiAcpiSdtProtocolGuid,
+ NULL,
+ (VOID**)&AcpiSdtProtocol
+ );
+if (EFI_ERROR (Status)) {
+ return Status;
+}
+
+BOOLEAN BGRT_found = FALSE;
+UINTN Index = 0;
+EFI_ACPI_SDT_HEADER* Table;
+EFI_ACPI_TABLE_VERSION Version;
+UINTN TableKey;
+while (TRUE) {
+ Status = AcpiSdtProtocol->GetAcpiTable(Index,
+ &Table,
+ &Version,
+ &TableKey
+ );
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ if (((CHAR8)((Table->Signature >> 0) & 0xFF) == 'B') &&
+ ((CHAR8)((Table->Signature >> 8) & 0xFF) == 'G') &&
+ ((CHAR8)((Table->Signature >> 16) & 0xFF) == 'R') &&
+ ((CHAR8)((Table->Signature >> 24) & 0xFF) == 'T')) {
+ BGRT_found = TRUE;
+ break;
+ }
+ Index++;
+}
+if (!BGRT_found) {
+ Print(L"BGRT table is not present in the system\n");
+ return EFI_UNSUPPORTED;
+}
+```
+
+Now we need to save an image from BGRT table.
+
+Currently ACPI specification support only BMP image type https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#image-type
+
+So first we check if the type is actually BMP:
+```
+EFI_ACPI_6_3_BOOT_GRAPHICS_RESOURCE_TABLE* BGRT = (EFI_ACPI_6_3_BOOT_GRAPHICS_RESOURCE_TABLE*)Table;
+if (BGRT->ImageType == 0) {
+ ...
+}
+```
+
+Now we need to actually save a BMP image. BGRT doesn't contain any size for an image, only offset to data: `ImageAddress`.
+
+To get image size we need to look at BMP header.
+
+In edk2 it is defined under https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Bmp.h:
+```
+typedef struct {
+ CHAR8 CharB;
+ CHAR8 CharM;
+ UINT32 Size;
+ UINT16 Reserved[2];
+ UINT32 ImageOffset;
+ UINT32 HeaderSize;
+ UINT32 PixelWidth;
+ UINT32 PixelHeight;
+ UINT16 Planes; ///< Must be 1
+ UINT16 BitPerPixel; ///< 1, 4, 8, or 24
+ UINT32 CompressionType;
+ UINT32 ImageSize; ///< Compressed image size in bytes
+ UINT32 XPixelsPerMeter;
+ UINT32 YPixelsPerMeter;
+ UINT32 NumberOfColors;
+ UINT32 ImportantColors;
+} BMP_IMAGE_HEADER;
+```
+
+Don't forget to include this file in our program:
+```
+#include <IndustryStandard/Bmp.h>
+```
+
+When we know that the image is BMP, we can check its signature (`BM`), parse its size and actually write its data to a file. Here we use `EFI_STATUS WriteFile(CHAR16* FileName, VOID* Data, UINTN* Size)` function to write data to a file, we will define it in a minute:
+```
+BMP_IMAGE_HEADER* BMP = (BMP_IMAGE_HEADER*)(BGRT->ImageAddress);
+
+if ((BMP->CharB != 'B') || (BMP->CharM != 'M')) {
+ Print(L"BMP image has wrong signature!\n");
+ return EFI_UNSUPPORTED;
+}
+Print(L"BGRT conatins BMP image with %dx%d resolution\n", BMP->PixelWidth, BMP->PixelHeight);
+UINTN Size = BMP->Size;
+Status = WriteFile(L"BGRT.bmp", BMP, &Size);
+if (EFI_ERROR(Status)) {
+ Print(L"Error! Can't write BGRT.bmp file\n");
+}
+```
+
+Last time we've used `EFI_SHELL_PROTOCOL` to create a file and write data to it. This time we will try to utilize ShelLib:
+
+https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Library/ShellLib.h
+
+https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLib/UefiShellLib.c
+
+Again we will need 3 functions: for file open, write and close:
+```
+/**
+ This function will open a file or directory referenced by filename.
+ If return is EFI_SUCCESS, the Filehandle is the opened file's handle;
+ otherwise, the Filehandle is NULL. Attributes is valid only for
+ EFI_FILE_MODE_CREATE.
+ @param[in] FileName The pointer to file name.
+ @param[out] FileHandle The pointer to the file handle.
+ @param[in] OpenMode The mode to open the file with.
+ @param[in] Attributes The file's file attributes.
+ ...
+**/
+
+EFI_STATUS
+EFIAPI
+ShellOpenFileByName(
+ IN CONST CHAR16 *FileName,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ );
+```
+```
+/**
+ Write data to a file.
+ This function writes the specified number of bytes to the file at the current
+ file position. The current file position is advanced the actual number of bytes
+ written, which is returned in BufferSize. Partial writes only occur when there
+ has been a data error during the write attempt (such as "volume space full").
+ The file is automatically grown to hold the data if required. Direct writes to
+ opened directories are not supported.
+ @param[in] FileHandle The opened file for writing.
+ @param[in, out] BufferSize On input the number of bytes in Buffer. On output
+ the number of bytes written.
+ @param[in] Buffer The buffer containing data to write is stored.
+ ...
+**/
+
+EFI_STATUS
+EFIAPI
+ShellWriteFile(
+ IN SHELL_FILE_HANDLE FileHandle,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+```
+```
+/**
+ Close an open file handle.
+ This function closes a specified file handle. All "dirty" cached file data is
+ flushed to the device, and the file is closed. In all cases the handle is
+ closed.
+ @param[in] FileHandle The file handle to close.
+**/
+
+EFI_STATUS
+EFIAPI
+ShellCloseFile (
+ IN SHELL_FILE_HANDLE *FileHandle
+ );
+```
+
+Advantage of using `ShellLib` is that now we don't need to find `EFI_SHELL_PROTOCOL` and work with it manually.
+
+
+Our `WriteFile` function would look like this:
+```
+EFI_STATUS WriteFile(CHAR16* FileName, VOID* Data, UINTN* Size)
+{
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_STATUS Status = ShellOpenFileByName(
+ FileName,
+ &FileHandle,
+ EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ,
+ 0
+ );
+ if (!EFI_ERROR(Status)) {
+ Print(L"Save it to %s\n", FileName);
+ UINTN ToWrite = *Size;
+ Status = ShellWriteFile(
+ FileHandle,
+ Size,
+ Data
+ );
+ if (EFI_ERROR(Status)) {
+ Print(L"Can't write file: %r\n", Status);
+ }
+ if (*Size != ToWrite) {
+ Print(L"Error! Not all data was written\n");
+ }
+ Status = ShellCloseFile(
+ &FileHandle
+ );
+ if (EFI_ERROR(Status)) {
+ Print(L"Can't close file: %r\n", Status);
+ }
+ } else {
+ Print(L"Can't open file: %r\n", Status);
+ }
+ return Status;
+}
+```
+
+To use ShellLib we need to include a header in our program:
+```
+#include <Library/ShellLib.h>
+```
+
+Also we need to add `ShellPkg.dec` to our packages and add `ShellLib` to our library classes:
+```
+[Packages]
+ MdePkg/MdePkg.dec
++ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+ UefiLib
++ ShellLib
+```
+Besides that our package `*.dsc` file needs to include a `ShellLib` library class:
+```
+[LibraryClasses]
+ ...
+ ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+```
+
+Unfortunately this is not enough, our current build would fail with a message, because `ShellLib` by itself needs another library:
+```
+build.py...
+/home/kostr/tiano/edk2/UefiLessonsPkg/UefiLessonsPkg.dsc(...): error 4000: Instance of library class [FileHandleLib] is not found
+```
+
+To find it use our standard tactic:
+```
+$ grep FileHandleLib -r ./ --include=*.inf | grep LIBRARY_CLASS
+```
+
+In the end we had to add several more LibraryClasses to make our build succeed:
+```
+[LibraryClasses]
+ ...
+ FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+ HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+ SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+ UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+```
+
+Build our app and execute it under OVMF:
+```
+FS0:\> SaveBGRT.efi
+BGRT conatins BMP image with 193x58 resolution
+Save it to BGRT.bmp7
+FS0:\>
+```
+
+If you look at the BGRT.bmp picture that are app have produced, it would have the same content as https://raw.githubusercontent.com/tianocore/edk2/master/MdeModulePkg/Logo/Logo.bmp
+
+The file itself wouldn't be the same since BGRT driver don't use an image from flash, but actually grabs a boot screen and transforms it to a BMP image. For the proof checkout how https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.c uses `TranslateGopBltToBmp` function from the https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/BaseBmpSupportLib/BmpSupportLib.c library.
+If you find it strange that BGRT grabs a screen instead of using an image from flash, remember how BGRT is defined in ACPI 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
+```
+
+The file GUID for binary boot logo image is defined in the file https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Logo/Logo.inf
+```
+FILE_GUID = 7BB28B99-61BB-11D5-9A5D-0090273FC14D
+```
+It is a GUID that is usually used for the Logo image in BIOS. It is even hardcoded to https://github.com/tianocore/edk2/blob/master/BaseTools/Source/Python/Eot/Report.py
+```
+## GenerateFfs() method
+#
+# Generate FFS information
+#
+# @param self: The object pointer
+# @param FfsObj: FFS object after FV image is parsed
+#
+def GenerateFfs(self, FfsObj):
+ self.FfsIndex = self.FfsIndex + 1
+ if FfsObj is not None and FfsObj.Type in [0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xA]:
+ FfsGuid = FfsObj.Guid
+ FfsOffset = FfsObj._OFF_
+ FfsName = 'Unknown-Module'
+ FfsPath = FfsGuid
+ FfsType = FfsObj._TypeName[FfsObj.Type]
+
+ # Hard code for Binary INF
+ if FfsGuid.upper() == '7BB28B99-61BB-11D5-9A5D-0090273FC14D':
+ FfsName = 'Logo'
+
+ if FfsGuid.upper() == '7E374E25-8E01-4FEE-87F2-390C23C606CD':
+ FfsName = 'AcpiTables'
+
+ if FfsGuid.upper() == '961578FE-B6B7-44C3-AF35-6BC705CD2B1F':
+ FfsName = 'Fat'
+ ...
+```
+
+If you want to know how Logo and BGRT are work in edk2, checkout these drivers:
+- https://github.com/tianocore/edk2/tree/master/MdeModulePkg/Library/BootLogoLib/
+- https://github.com/tianocore/edk2/tree/master/MdeModulePkg/Logo/
+- https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/
+- https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/BaseBmpSupportLib/
+