mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
463 lines
13 KiB
C
463 lines
13 KiB
C
/** @file
|
|
Test graphics output protocol.
|
|
|
|
Copyright (c) 2020, vit9696. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
#include <Guid/AppleFile.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcBootManagementLib.h>
|
|
#include <Library/OcConsoleLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcFileLib.h>
|
|
#include <Library/UefiApplicationEntryPoint.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Protocol/DevicePath.h>
|
|
#include <Protocol/GraphicsOutput.h>
|
|
|
|
STATIC
|
|
VOID
|
|
AnalyzeGopHandle (
|
|
IN EFI_HANDLE Handle,
|
|
IN UINTN GopIndex,
|
|
IN UINTN HandleCount,
|
|
OUT CHAR8 *Report
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
UINT32 Index;
|
|
CHAR8 Tmp[256];
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
|
|
UINTN InfoSize;
|
|
UINTN Width;
|
|
UINTN Height;
|
|
INTN NewMode;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
(VOID **)&Gop
|
|
);
|
|
|
|
//
|
|
// GOP report:
|
|
//
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"GOP #%u of total %u, %a - %r\n",
|
|
(UINT32)(GopIndex + 1),
|
|
(UINT32)HandleCount,
|
|
gST->ConsoleOutHandle == Handle ? "console" : "auxiliary",
|
|
Status
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"Current mode %u, max mode %u, FB: %p, FBS: %LX\n",
|
|
Gop->Mode->Mode,
|
|
Gop->Mode->MaxMode,
|
|
(UINT64)Gop->Mode->FrameBufferBase,
|
|
(UINT32)Gop->Mode->FrameBufferSize
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
|
|
Info = Gop->Mode->Info;
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"Current: %u x %u, pixel %d (%X %X %X %X), scan: %u\n",
|
|
Info->HorizontalResolution,
|
|
Info->VerticalResolution,
|
|
Info->PixelFormat,
|
|
Info->PixelInformation.RedMask,
|
|
Info->PixelInformation.GreenMask,
|
|
Info->PixelInformation.BlueMask,
|
|
Info->PixelInformation.ReservedMask,
|
|
Info->PixelsPerScanLine
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
|
|
Width = 0;
|
|
Height = 0;
|
|
NewMode = -1;
|
|
|
|
for (Index = 0; Index < Gop->Mode->MaxMode; ++Index) {
|
|
Status = Gop->QueryMode (
|
|
Gop,
|
|
Index,
|
|
&InfoSize,
|
|
&Info
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"%u: %r\n",
|
|
Index,
|
|
Status
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
continue;
|
|
}
|
|
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"%u: %u x %u, pixel %d (%X %X %X %X), scan: %u\n",
|
|
Index,
|
|
Info->HorizontalResolution,
|
|
Info->VerticalResolution,
|
|
Info->PixelFormat,
|
|
Info->PixelInformation.RedMask,
|
|
Info->PixelInformation.GreenMask,
|
|
Info->PixelInformation.BlueMask,
|
|
Info->PixelInformation.ReservedMask,
|
|
Info->PixelsPerScanLine
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
|
|
if ( (Info->HorizontalResolution > Width)
|
|
|| ((Info->HorizontalResolution == Width) && (Info->VerticalResolution > Height)))
|
|
{
|
|
Width = Info->HorizontalResolution;
|
|
Height = Info->VerticalResolution;
|
|
NewMode = (INTN)Index;
|
|
}
|
|
|
|
FreePool (Info);
|
|
}
|
|
|
|
if (NewMode >= 0) {
|
|
Status = Gop->SetMode (
|
|
Gop,
|
|
(UINT32)NewMode
|
|
);
|
|
|
|
Info = Gop->Mode->Info;
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"New %u <-> %u: max mode %u, FB: %p, FBS: %LX - %r\n",
|
|
(UINT32)NewMode,
|
|
Gop->Mode->Mode,
|
|
Gop->Mode->MaxMode,
|
|
(UINT64)Gop->Mode->FrameBufferBase,
|
|
(UINT32)Gop->Mode->FrameBufferSize,
|
|
Status
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
AsciiSPrint (
|
|
Tmp,
|
|
sizeof (Tmp),
|
|
"New %u <-> %u: %u x %u, pixel %d (%X %X %X %X), scan: %u\n",
|
|
(UINT32)NewMode,
|
|
Gop->Mode->Mode,
|
|
Info->HorizontalResolution,
|
|
Info->VerticalResolution,
|
|
Info->PixelFormat,
|
|
Info->PixelInformation.RedMask,
|
|
Info->PixelInformation.GreenMask,
|
|
Info->PixelInformation.BlueMask,
|
|
Info->PixelInformation.ReservedMask,
|
|
Info->PixelsPerScanLine
|
|
);
|
|
DEBUG ((DEBUG_WARN, "GSTT: %a", Tmp));
|
|
AsciiStrCatS (Report, EFI_PAGE_SIZE, Tmp);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
RunGopTest (
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
UINTN Index;
|
|
UINT32 ChunkX;
|
|
UINT32 ChunkY;
|
|
UINT32 ChunkW;
|
|
UINT32 ChunkH;
|
|
UINT32 ColorIndex;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
(VOID **)&Gop
|
|
);
|
|
|
|
if ( EFI_ERROR (Status)
|
|
|| (Gop->Mode->Info->HorizontalResolution == 0)
|
|
|| (Gop->Mode->Info->VerticalResolution == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL mGraphicsEfiColors[16] = {
|
|
//
|
|
// B G R reserved
|
|
//
|
|
{ 0x00, 0x00, 0x00, 0x00 }, // 0 - BLACK
|
|
{ 0x98, 0x00, 0x00, 0x00 }, // 1 - LIGHTBLUE
|
|
{ 0x00, 0x98, 0x00, 0x00 }, // 2 - LIGHGREEN
|
|
{ 0x98, 0x98, 0x00, 0x00 }, // 3 - LIGHCYAN
|
|
{ 0x00, 0x00, 0x98, 0x00 }, // 4 - LIGHRED
|
|
{ 0x98, 0x00, 0x98, 0x00 }, // 5 - MAGENTA
|
|
{ 0x00, 0x98, 0x98, 0x00 }, // 6 - BROWN
|
|
{ 0x98, 0x98, 0x98, 0x00 }, // 7 - LIGHTGRAY
|
|
{ 0x30, 0x30, 0x30, 0x00 }, // 8 - DARKGRAY - BRIGHT BLACK
|
|
{ 0xff, 0x00, 0x00, 0x00 }, // 9 - BLUE
|
|
{ 0x00, 0xff, 0x00, 0x00 }, // 10 - LIME
|
|
{ 0xff, 0xff, 0x00, 0x00 }, // 11 - CYAN
|
|
{ 0x00, 0x00, 0xff, 0x00 }, // 12 - RED
|
|
{ 0xff, 0x00, 0xff, 0x00 }, // 13 - FUCHSIA
|
|
{ 0x00, 0xff, 0xff, 0x00 }, // 14 - YELLOW
|
|
{ 0xff, 0xff, 0xff, 0x00 } // 15 - WHITE
|
|
};
|
|
|
|
STATIC UINT32 mColorsTest[9] = { 12, 10, 9, 15, 7, 0, 11, 5, 14 };
|
|
|
|
if (Gop->Mode->FrameBufferBase != 0) {
|
|
//
|
|
// 1. Fill screen with Red (#FF0000) in direct mode.
|
|
//
|
|
SetMem32 (
|
|
(VOID *)(UINTN)Gop->Mode->FrameBufferBase,
|
|
Gop->Mode->Info->VerticalResolution * Gop->Mode->Info->PixelsPerScanLine * sizeof (UINT32),
|
|
*(UINT32 *)&mGraphicsEfiColors[mColorsTest[0]]
|
|
);
|
|
|
|
//
|
|
// 2. Wait 5 seconds.
|
|
// Note: Ensure that stall value is within UINT32 in nanoseconds.
|
|
//
|
|
for (Index = 0; Index < 5; ++Index) {
|
|
gBS->Stall (SECONDS_TO_MICROSECONDS (1));
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3. Fill screen with 4 Red (#FF0000) / Green (#00FF00) / Blue (#0000FF) / White (#FFFFFF) rectangles.
|
|
// The user should visually ensure that the colours match and that rectangles equally split the screen in 4 parts.
|
|
//
|
|
ChunkW = Gop->Mode->Info->HorizontalResolution / 2;
|
|
ChunkH = Gop->Mode->Info->VerticalResolution / 2;
|
|
ColorIndex = 0;
|
|
for (ChunkY = 0; ChunkY + ChunkH <= Gop->Mode->Info->VerticalResolution; ChunkY += ChunkH) {
|
|
for (ChunkX = 0; ChunkX + ChunkW <= Gop->Mode->Info->HorizontalResolution; ChunkX += ChunkW) {
|
|
Gop->Blt (
|
|
Gop,
|
|
&mGraphicsEfiColors[mColorsTest[ColorIndex]],
|
|
EfiBltVideoFill,
|
|
0,
|
|
0,
|
|
ChunkX,
|
|
ChunkY,
|
|
ChunkW,
|
|
ChunkH,
|
|
0
|
|
);
|
|
++ColorIndex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 4. Wait 5 seconds.
|
|
// Note: Ensure that stall value is within UINT32 in nanoseconds.
|
|
//
|
|
for (Index = 0; Index < 5; ++Index) {
|
|
gBS->Stall (SECONDS_TO_MICROSECONDS (1));
|
|
}
|
|
|
|
//
|
|
// TODO:
|
|
// 5. Fill screen with white text on black screen in normal mode.
|
|
// The screen should effectively be filled with 9 equal rectangles.
|
|
// From left to right top to bottom they should contain the following symbols: A B C D # F G H I.
|
|
// Instead of # there should be current GOP number. The user should visually ensure text and
|
|
// background colour, rectangle sizes, rectangle data.
|
|
// 6. Wait 5 seconds.
|
|
// 7. Fill screen with white text on black screen in HiDPI mode. This should repeat the previous test
|
|
// but the text should be twice bigger. The user should ensure all previous requirements and the
|
|
// fact that the text got bigger.
|
|
// 8. Wait 5 seconds.
|
|
// 9. Print all GOP reports one by one (on separate screens) with white text in black screen in
|
|
// normal mode. Wait 5 seconds after each. The user should screenshot these and later compare
|
|
// to the file if available.
|
|
//
|
|
|
|
//
|
|
// 10. Fill screen with 9 rectangles of different colours. From left to right top to bottom they
|
|
// should contain the following colours: Red (#FF0000), Green (#00FF00), Blue (#0000FF), White (#FFFFFF),
|
|
// Light Grey (#989898), Black (#000000), Cyan (#00FFFF), Magenta (#FF00FF), Yellow (#FFFF00). The user should
|
|
// visually ensure that the colours match and that rectangles equally split the screen in 9 parts.
|
|
//
|
|
ChunkW = Gop->Mode->Info->HorizontalResolution / 3;
|
|
ChunkH = Gop->Mode->Info->VerticalResolution / 3;
|
|
ColorIndex = 0;
|
|
for (ChunkY = 0; ChunkY + ChunkH <= Gop->Mode->Info->VerticalResolution; ChunkY += ChunkH) {
|
|
for (ChunkX = 0; ChunkX + ChunkW <= Gop->Mode->Info->HorizontalResolution; ChunkX += ChunkW) {
|
|
Gop->Blt (
|
|
Gop,
|
|
&mGraphicsEfiColors[mColorsTest[ColorIndex]],
|
|
EfiBltVideoFill,
|
|
0,
|
|
0,
|
|
ChunkX,
|
|
ChunkY,
|
|
ChunkW,
|
|
ChunkH,
|
|
0
|
|
);
|
|
++ColorIndex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 11. Wait 5 seconds.
|
|
// Note: Ensure that stall value is within UINT32 in nanoseconds.
|
|
//
|
|
for (Index = 0; Index < 5; ++Index) {
|
|
gBS->Stall (SECONDS_TO_MICROSECONDS (1));
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UefiMain (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN Index;
|
|
|
|
CHAR8 (*Reports)[EFI_PAGE_SIZE];
|
|
CHAR8 *FinalReport;
|
|
CHAR16 Filename[64];
|
|
EFI_TIME Date;
|
|
|
|
//
|
|
// 1. Disable watchdog timer.
|
|
//
|
|
gBS->SetWatchdogTimer (0, 0, 0, NULL);
|
|
|
|
//
|
|
// 2. Gather all N available GOP protocols.
|
|
//
|
|
HandleCount = 0;
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"GSTT: Found %u handles with GOP protocol - %r\n",
|
|
(UINT32)HandleCount,
|
|
Status
|
|
));
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 3. Allocate N buffer for GOP reports (PAGE_SIZE).
|
|
//
|
|
Reports = AllocateZeroPool (EFI_PAGE_SIZE * HandleCount);
|
|
if (Reports == NULL) {
|
|
DEBUG ((DEBUG_WARN, "GSTT: Cannot allocate memory for GOP reports\n"));
|
|
FreePool (HandleBuffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// 4. Get GOP reports for every GOP and set maximum resolution.
|
|
//
|
|
for (Index = 0; Index < HandleCount; ++Index) {
|
|
AnalyzeGopHandle (HandleBuffer[Index], Index, HandleCount, Reports[Index]);
|
|
}
|
|
|
|
//
|
|
// 5. Save GOP reports to file to any writable fs (gop-date.txt).
|
|
//
|
|
FinalReport = AllocateZeroPool (EFI_PAGE_SIZE * HandleCount);
|
|
if (FinalReport != NULL) {
|
|
for (Index = 0; Index < HandleCount; ++Index) {
|
|
AsciiStrCatS (FinalReport, EFI_PAGE_SIZE * HandleCount, Reports[Index]);
|
|
AsciiStrCatS (FinalReport, EFI_PAGE_SIZE * HandleCount, "\n\n");
|
|
}
|
|
|
|
Status = gRT->GetTime (&Date, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
ZeroMem (&Date, sizeof (Date));
|
|
}
|
|
|
|
UnicodeSPrint (
|
|
Filename,
|
|
sizeof (Filename),
|
|
L"gop-%04u-%02u-%02u-%02u%02u%02u.txt",
|
|
(UINT32)Date.Year,
|
|
(UINT32)Date.Month,
|
|
(UINT32)Date.Day,
|
|
(UINT32)Date.Hour,
|
|
(UINT32)Date.Minute,
|
|
(UINT32)Date.Second
|
|
);
|
|
|
|
OcSetFileData (NULL, Filename, FinalReport, (UINT32)AsciiStrLen (FinalReport));
|
|
|
|
FreePool (FinalReport);
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "GSTT: Cannot allocate memory for final report\n"));
|
|
}
|
|
|
|
//
|
|
// 6. Run tests in every GOP with non-zero resolution.
|
|
//
|
|
for (Index = 0; Index < HandleCount; ++Index) {
|
|
RunGopTest (HandleBuffer[Index]);
|
|
}
|
|
|
|
FreePool (Reports);
|
|
FreePool (HandleBuffer);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|