This is a shoehorned-in implementation of an ACPI BGRT table, ported pretty much directly from the version used under CorebootPayloadPkg. EDK2 provides a facility to do this already, but it assumes the ACPI tables already exist as EFI structures, so would need to write code to populate those using the tables already in RAM created by coreboot. This seemed like the easier option ATM. Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
		
			
				
	
	
		
			284 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <IndustryStandard/Acpi.h>
 | |
| #include <IndustryStandard/Bmp.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/BootLogoLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| #include <Protocol/FirmwareVolume2.h>
 | |
| #include <Protocol/SimpleFileSystem.h>
 | |
| 
 | |
| /** RSDP (Root System Description Pointer) */
 | |
| typedef struct {
 | |
|   CHAR8  signature[8];
 | |
|   UINT8  checksum;
 | |
|   CHAR8  oem_id[6];
 | |
|   UINT8  revision;
 | |
|   UINT32 rsdt_address;
 | |
|   UINT32 length;
 | |
|   UINT64 xsdt_address;
 | |
|   UINT8  extended_checksum;
 | |
|   UINT8  reserved[3];
 | |
| } ACPI_20_RSDP;
 | |
| 
 | |
| /** SDT (System Description Table) entry header */
 | |
| typedef struct {
 | |
|     CHAR8  signature[4];
 | |
|     UINT32 length;
 | |
|     UINT8  revision;
 | |
|     UINT8  checksum;
 | |
|     CHAR8  oem_id[6];
 | |
|     CHAR8  oem_table_id[8];
 | |
|     UINT32 oem_revision;
 | |
|     UINT32 asl_compiler_id;
 | |
|     UINT32 asl_compiler_revision;
 | |
| } ACPI_SDT_HEADER;
 | |
| 
 | |
| /** BGRT structure */
 | |
| typedef struct {
 | |
|     ACPI_SDT_HEADER header;
 | |
|     UINT16 version;
 | |
|     UINT8  status;
 | |
|     UINT8  image_type;
 | |
|     UINT64 image_address;
 | |
|     UINT32 image_offset_x;
 | |
|     UINT32 image_offset_y;
 | |
| } ACPI_BGRT;
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| LoadBmp(
 | |
|     OUT EFI_PHYSICAL_ADDRESS *BmpAddress,
 | |
|     OUT UINT32 *BmpSize
 | |
| )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   UINTN                         FvProtocolCount;
 | |
|   EFI_HANDLE                    *FvHandles;
 | |
|   EFI_FIRMWARE_VOLUME2_PROTOCOL  *Fv;
 | |
|   UINTN                         Index;
 | |
|   UINT32                        AuthenticationStatus;
 | |
| 
 | |
|   UINT8                         *Buffer;
 | |
|   UINTN                         BmpBufferSize;
 | |
| 
 | |
|   Buffer = 0;
 | |
|   FvHandles       = NULL;
 | |
| 
 | |
|   Status = gBS->LocateHandleBuffer (
 | |
|     ByProtocol,
 | |
|     &gEfiFirmwareVolume2ProtocolGuid,
 | |
|     NULL,
 | |
|     &FvProtocolCount,
 | |
|     &FvHandles
 | |
|     );
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     for (Index = 0; Index < FvProtocolCount; Index++) {
 | |
|       Status = gBS->HandleProtocol (
 | |
|                       FvHandles[Index],
 | |
|                       &gEfiFirmwareVolume2ProtocolGuid,
 | |
|                       (VOID **) &Fv
 | |
|                       );
 | |
|       BmpBufferSize = 0;
 | |
|       Status = Fv->ReadSection (
 | |
|                      Fv,
 | |
|                      (EFI_GUID *)PcdGetPtr(PcdLogoFile),
 | |
|                      EFI_SECTION_RAW,
 | |
|                      0,
 | |
|                     (void **)&Buffer,
 | |
|                      &BmpBufferSize,
 | |
|                      &AuthenticationStatus
 | |
|                      );
 | |
| 
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         *BmpAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
 | |
|         *BmpSize = (UINT32)BmpBufferSize;
 | |
|         Status = EFI_SUCCESS;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     Status = EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   if (FvHandles != NULL) {
 | |
|     gBS->FreePool (FvHandles);
 | |
|     FvHandles = NULL;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| UINT8 SumBytes(const UINT8* arr, UINTN size) {
 | |
|   UINT8 sum = 0;
 | |
|   UINTN i;
 | |
|   for (i = 0; i < size; ++i) {
 | |
|     sum += arr[i];
 | |
|   }
 | |
|   return sum;
 | |
| }
 | |
| 
 | |
| int VerifyAcpiRsdp2Checksums(const void* data) {
 | |
|   const UINT8* arr = data;
 | |
|   UINTN size = *(const UINT32*)&arr[20];
 | |
|   return SumBytes(arr, 20) == 0 && SumBytes(arr, size) == 0;
 | |
| }
 | |
| 
 | |
| void SetAcpiRsdp2Checksums(void* data) {
 | |
|   UINT8* arr = data;
 | |
|   UINTN size = *(const UINT32*)&arr[20];
 | |
|   arr[9] = 0;
 | |
|   arr[32] = 0;
 | |
|   arr[9] = -SumBytes(arr, 20);
 | |
|   arr[32] = -SumBytes(arr, size);
 | |
| }
 | |
| 
 | |
| int VerifyAcpiSdtChecksum(const void* data) {
 | |
|   const UINT8* arr = data;
 | |
|   UINTN size = *(const UINT32*)&arr[4];
 | |
|   return SumBytes(arr, size) == 0;
 | |
| }
 | |
| 
 | |
| void SetAcpiSdtChecksum(void* data) {
 | |
|   UINT8* arr = data;
 | |
|   UINTN size = *(const UINT32*)&arr[4];
 | |
|   arr[9] = 0;
 | |
|   arr[9] = -SumBytes(arr, size);
 | |
| }
 | |
| 
 | |
| const CHAR16* TmpStr(CHAR8 *src, int length) {
 | |
|   static CHAR16 arr[4][16];
 | |
|   static int j;
 | |
|   CHAR16* dest = arr[j = (j+1) % 4];
 | |
|   int i;
 | |
|   for (i = 0; i < length && i < 16-1 && src[i]; ++i) {
 | |
|     dest[i] = src[i];
 | |
|   }
 | |
|   dest[i] = 0;
 | |
|   return dest;
 | |
| }
 | |
| 
 | |
| static UINT32 min(UINT32 first, UINT32 second){
 | |
|   if (first < second)
 | |
|     return first;
 | |
|   return second;
 | |
| }
 | |
| 
 | |
| ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
 | |
|   ACPI_SDT_HEADER* xsdt = 0;
 | |
|   UINT32 xsdt_len = (UINT32)(sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64));
 | |
|   gBS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
 | |
|   if (!xsdt) {
 | |
|     DEBUG ((EFI_D_INFO, "HackBGRT: Failed to allocate memory for XSDT.\n"));
 | |
|     return 0;
 | |
|   }
 | |
|   ZeroMem(xsdt, xsdt_len);
 | |
|   CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
 | |
|   xsdt->length = xsdt_len;
 | |
|   SetAcpiSdtChecksum(xsdt);
 | |
|   return xsdt;
 | |
| }
 | |
| 
 | |
| static ACPI_BGRT* HandleAcpiTables(ACPI_BGRT* bgrt) {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < gST->NumberOfTableEntries; i++) {
 | |
|     EFI_GUID* vendor_guid = &gST->ConfigurationTable[i].VendorGuid;
 | |
|     ACPI_20_RSDP *rsdp;
 | |
|     ACPI_SDT_HEADER *xsdt;
 | |
|     UINT64 *entry_arr;
 | |
|     UINT32 entry_arr_length;
 | |
| 
 | |
|     if (!CompareGuid(vendor_guid, &gEfiAcpiTableGuid) && !CompareGuid(vendor_guid, &gEfiAcpi20TableGuid)) {
 | |
|       continue;
 | |
|     }
 | |
|     rsdp = (ACPI_20_RSDP *) gST->ConfigurationTable[i].VendorTable;
 | |
|     if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
 | |
|       continue;
 | |
|     }
 | |
|     DEBUG ((EFI_D_INFO, "RSDP: revision = %d, OEM ID = %s\n", rsdp->revision, TmpStr(rsdp->oem_id, 6)));
 | |
| 
 | |
|     xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
 | |
|     if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
 | |
|       DEBUG ((EFI_D_INFO, "* XSDT: missing or invalid\n"));
 | |
|       continue;
 | |
|     }
 | |
|     entry_arr = (UINT64*)&xsdt[1];
 | |
|     entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
 | |
| 
 | |
|     DEBUG ((EFI_D_INFO, "* XSDT: OEM ID = %s, entry count = %d\n", TmpStr(xsdt->oem_id, 6), entry_arr_length));
 | |
| 
 | |
|     if (bgrt) {
 | |
|       DEBUG ((EFI_D_INFO, " - Adding missing BGRT.\n"));
 | |
|       xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
 | |
|       entry_arr = (UINT64*)&xsdt[1];
 | |
|       entry_arr[entry_arr_length++] = (UINTN) bgrt;
 | |
|       rsdp->xsdt_address = (UINTN) xsdt;
 | |
|       SetAcpiRsdp2Checksums(rsdp);
 | |
|     }
 | |
|     SetAcpiSdtChecksum(xsdt);
 | |
|   }
 | |
|   return bgrt;
 | |
| }
 | |
| 
 | |
| VOID
 | |
| AddBGRT (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                         Status;
 | |
|   ACPI_BGRT                          *bgrt;
 | |
|   EFI_GRAPHICS_OUTPUT_PROTOCOL       *GraphicsOutput;
 | |
|   EFI_PHYSICAL_ADDRESS               BmpAddress;
 | |
|   UINT32                             BmpSize;
 | |
|   BMP_IMAGE_HEADER                   *BmpHeader;
 | |
|   const char data[0x38] =
 | |
|     "BGRT" "\x38\x00\x00\x00" "\x00" "\xd6" "INTEL " "    EDK2"
 | |
|     "\x20\x17\x00\x00" "PTL " "\x02\x00\x00\x00"
 | |
|     "\x01\x00" "\x00" "\x00";
 | |
| 
 | |
|   BmpAddress = 0;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "HackBGRT Start\n"));
 | |
| 
 | |
|   Status = gBS->HandleProtocol (
 | |
|     gST->ConsoleOutHandle,
 | |
|     &gEfiGraphicsOutputProtocolGuid,
 | |
|     (VOID**)&GraphicsOutput
 | |
|     );
 | |
| 
 | |
|   // Replace missing = allocate new.
 | |
|   gBS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
 | |
|   if (!bgrt) {
 | |
|     DEBUG ((EFI_D_INFO, "HackBGRT MEM ERR\n"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "HackBGRT Load Bmp\n"));
 | |
|   Status = LoadBmp(&BmpAddress, &BmpSize);
 | |
|   if (EFI_ERROR(Status)){
 | |
|     DEBUG ((EFI_D_INFO, "HackBGRT BMP Load ERR\n"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "HackBGRT Set Table; BMP Size: %d\n", BmpSize));
 | |
|   // Clear the BGRT.
 | |
|   CopyMem(bgrt, data, sizeof(data));
 | |
| 
 | |
|   if (GraphicsOutput != NULL && GraphicsOutput->Mode != NULL && GraphicsOutput->Mode->Info != NULL) 
 | |
|   {
 | |
|       BmpHeader = (BMP_IMAGE_HEADER *)BmpAddress;
 | |
|       bgrt->image_address = (UINTN)BmpAddress;
 | |
|       bgrt->image_offset_x = (GraphicsOutput->Mode->Info->HorizontalResolution - BmpHeader->PixelWidth) / 2;
 | |
|       bgrt->image_offset_y = ((GraphicsOutput->Mode->Info->VerticalResolution * 382) / 1000) -
 | |
|                              (BmpHeader->PixelHeight / 2);
 | |
|       DEBUG ((EFI_D_INFO, "HackBGRT Set checksum\n"));
 | |
|       SetAcpiSdtChecksum(bgrt);
 | |
|       DEBUG ((EFI_D_INFO, "HackBGRT Add Table\n"));
 | |
|       HandleAcpiTables(bgrt);
 | |
|   } else {
 | |
|       DEBUG ((EFI_D_INFO, "HackBGRT no display connected, skip adding table\n"));
 | |
|   }
 | |
| }
 |