Some time ago a reader of this blog contacted me for assistance with enumerating possible screen modes from the UEFI shell. This post is in response to that request for help.
The original EFI (Extensible Firmware Interface) specification and EDK (EFI Development Kit) supported a text output protocol and UGA (Universal Graphic Adapter), a device-independent VGA-derived graphics protocol. In 2005, Intel handed EFI standardization over to an industry consortium, UEFI, and that consortium decided to replace UGA with GOP (Graphics Output Protocol) to remove the remaining VGA hardware dependencies.
The following code should work with any of the UDK (UEFI Development Kit) releases:
// // Copyright (c) 2015 Finnbarr P. Murphy. All rights reserved. // // Display TEXT and GRAPHIC screen mode information // // 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/SimpleFileSystem.h> #include <Protocol/GraphicsOutput.h> #include "ConsoleControl.h" #include "UgaDraw.h" static int memcmp(const void *s1, const void *s2, UINTN n) { const unsigned char *c1 = s1, *c2 = s2; int d = 0; if (!s1 && !s2) return 0; if (s1 && !s2) return 1; if (!s1 && s2) return -1; while (n--) { d = (int)*c1++ - (int)*c2++; if (d) break; } return d; } EFI_STATUS PrintUGA(EFI_UGA_DRAW_PROTOCOL *Uga) { EFI_STATUS Status = EFI_SUCCESS; UINT32 HorzResolution = 0; UINT32 VertResolution = 0; UINT32 ColorDepth = 0; UINT32 RefreshRate = 0; Status = Uga->GetMode( Uga, &HorzResolution, &VertResolution, &ColorDepth, &RefreshRate); if (EFI_ERROR (Status)) { Print(L"ERROR: UGA GetMode failed [%d]\n", Status ); } else { Print(L"Horizontal Resolution: %d\n", HorzResolution); Print(L"Vertical Resolution: %d\n", VertResolution); Print(L"Color Depth: %d\n", ColorDepth); Print(L"Refresh Rate: %d\n", RefreshRate); Print(L"\n"); } return Status; } EFI_STATUS CheckUGA(BOOLEAN Verbose) { EFI_HANDLE *HandleBuffer = NULL; UINTN HandleCount = 0; EFI_STATUS Status = EFI_SUCCESS; EFI_UGA_DRAW_PROTOCOL *Uga; // get from ConsoleOutHandle? Status = gBS->HandleProtocol( gST->ConsoleOutHandle, &gEfiUgaDrawProtocolGuid, (VOID **) &Uga); if (EFI_ERROR (Status)) { Print(L"No UGA handle found via HandleProtocol\n"); } else { Print(L"UGA handle found via HandleProtocol\n"); if (Verbose) PrintUGA(Uga); } // try locating directly Status = gBS->LocateProtocol( &gEfiUgaDrawProtocolGuid, NULL, (VOID **) &Uga); if (EFI_ERROR(Status) || Uga == NULL) { Print(L"No UGA handle found via LocateProtocol\n"); } else { Print(L"Found UGA handle via LocateProtocol\n"); if (Verbose) PrintUGA(Uga); } // try locating by handle Status = gBS->LocateHandleBuffer( ByProtocol, &gEfiUgaDrawProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { Print(L"No UGA handles found via LocateHandleBuffer\n"); } else { Print(L"Found %d UGA handles via LocateHandleBuffer\n", HandleCount); for (int i = 0; i < HandleCount; i++) { Status = gBS->HandleProtocol( HandleBuffer[i], &gEfiUgaDrawProtocolGuid, (VOID*) &Uga); if (!EFI_ERROR (Status)) { if (Verbose) PrintUGA(Uga); } } FreePool(HandleBuffer); } Print(L"\n"); return Status; } EFI_STATUS PrintGOP(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop) { int i, imax; EFI_STATUS Status; imax = gop->Mode->MaxMode; Print(L"GOP reports MaxMode %d\n", imax); for (i = 0; i < imax; i++) { EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; UINTN SizeOfInfo; Status = gop->QueryMode(gop, i, &SizeOfInfo, &Info); if (EFI_ERROR(Status) && Status == EFI_NOT_STARTED) { gop->SetMode(gop, gop->Mode->Mode); Status = gop->QueryMode(gop, i, &SizeOfInfo, &Info); } if (EFI_ERROR(Status)) { Print(L"ERROR: Bad response from QueryMode: %d\n", Status); continue; } Print(L"%c%d: %dx%d ", memcmp(Info,gop->Mode->Info,sizeof(*Info)) == 0 ? '*' : ' ', i, Info->HorizontalResolution, Info->VerticalResolution); switch(Info->PixelFormat) { case PixelRedGreenBlueReserved8BitPerColor: Print(L"RGBRerserved"); break; case PixelBlueGreenRedReserved8BitPerColor: Print(L"BGRReserved"); break; case PixelBitMask: Print(L"Red:%08x Green:%08x Blue:%08x Reserved:%08x", Info->PixelInformation.RedMask, Info->PixelInformation.GreenMask, Info->PixelInformation.BlueMask, Info->PixelInformation.ReservedMask); break; case PixelBltOnly: Print(L"(blt only)"); break; default: Print(L"(Invalid pixel format)"); break; } Print(L" Pixels %d\n", Info->PixelsPerScanLine); } Print(L"\n"); return EFI_SUCCESS; } EFI_STATUS CheckGOP(BOOLEAN Verbose) { EFI_HANDLE *HandleBuffer = NULL; UINTN HandleCount = 0; EFI_STATUS Status = EFI_SUCCESS; EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; // get from ConsoleOutHandle? Status = gBS->HandleProtocol( gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **) &Gop); if (EFI_ERROR (Status)) { Print(L"No GOP handle found via HandleProtocol\n"); } else { Print(L"GOP handle found via HandleProtocol\n"); if (Verbose) PrintGOP(Gop); } // try locating directly Status = gBS->LocateProtocol( &gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &Gop); if (EFI_ERROR(Status) || Gop == NULL) { Print(L"No GOP handle found via LocateProtocol\n"); } else { Print(L"Found GOP handle via LocateProtocol\n"); if (Verbose) PrintGOP(Gop); } // try locating by handle Status = gBS->LocateHandleBuffer( ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { Print(L"No GOP handles found via LocateHandleBuffer\n"); } else { Print(L"Found %d GOP handles via LocateHandleBuffer\n", HandleCount); for (int i = 0; i < HandleCount; i++) { Status = gBS->HandleProtocol( HandleBuffer[i], &gEfiGraphicsOutputProtocolGuid, (VOID*) &Gop); if (!EFI_ERROR (Status)) { if (Verbose) PrintGOP(Gop); } } FreePool(HandleBuffer); } Print(L"\n"); return Status; } EFI_STATUS PrintCCP(EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl) { EFI_STATUS Status = EFI_SUCCESS; EFI_CONSOLE_CONTROL_SCREEN_MODE Mode; BOOLEAN GopUgaExists; BOOLEAN StdInLocked; Status = ConsoleControl->GetMode(ConsoleControl, &Mode, &GopUgaExists, &StdInLocked); if (EFI_ERROR (Status)) { Print(L"ERROR: ConsoleControl GetMode failed [%d]\n", Status ); return Status; } Print(L"Screen mode: "); switch (Mode) { case EfiConsoleControlScreenText: Print(L"Text"); break; case EfiConsoleControlScreenGraphics: Print(L"Graphics"); break; case EfiConsoleControlScreenMaxValue: Print(L"MaxValue"); break; } Print(L"\n"); Print(L"Graphics Support Avalaiable: "); if (GopUgaExists) Print(L"Yes"); else Print(L"No"); Print(L"\n"); Print(L"\n"); return EFI_SUCCESS; } EFI_STATUS CheckCCP(BOOLEAN Verbose) { EFI_GUID gEfiConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; EFI_HANDLE *HandleBuffer = NULL; UINTN HandleCount = 0; EFI_STATUS Status = EFI_SUCCESS; // get from ConsoleOutHandle? Status = gBS->HandleProtocol( gST->ConsoleOutHandle, &gEfiConsoleControlProtocolGuid, (VOID **) &ConsoleControl); if (EFI_ERROR (Status)) { Print(L"No ConsoleControl handle found via HandleProtocol\n"); } else { Print(L"ConsoleControl handle found via HandleProtocol\n"); if (Verbose) PrintCCP(ConsoleControl); } // try locating directly Status = gBS->LocateProtocol( &gEfiConsoleControlProtocolGuid, NULL, (VOID **) &ConsoleControl); if (EFI_ERROR(Status) || ConsoleControl == NULL) { Print(L"No ConsoleControl handle found via LocateProtocol\n"); } else { Print(L"Found ConsoleControl handle via LocateProtocol\n"); if (Verbose) PrintCCP(ConsoleControl); } // try locating by handle Status = gBS->LocateHandleBuffer( ByProtocol, &gEfiConsoleControlProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { Print(L"No ConsoleControl handles found via LocateHandleBuffer\n"); } else { Print(L"Found %d ConsoleControl handles via LocateHandleBuffer\n", HandleCount); for (int i = 0; i < HandleCount; i++) { Status = gBS->HandleProtocol( HandleBuffer[i], &gEfiConsoleControlProtocolGuid, (VOID*) &ConsoleControl); if (!EFI_ERROR (Status)) if (Verbose) PrintCCP(ConsoleControl); } FreePool(HandleBuffer); } Print(L"\n"); return Status; } static void Usage(void) { Print(L"Usage: screenmodes [-v|--verbose]\n"); } INTN EFIAPI ShellAppMain(UINTN Argc, CHAR16 **Argv) { EFI_STATUS Status = EFI_SUCCESS; BOOLEAN Verbose = FALSE; if (Argc == 2) { if (!StrCmp(Argv[1], L"--verbose") || !StrCmp(Argv[1], L"-v")) { Verbose = TRUE; } if (!StrCmp(Argv[1], L"--help") || !StrCmp(Argv[1], L"-h") || !StrCmp(Argv[1], L"-?")) { Usage(); return Status; } } // First check for older EDK ConsoleControl protocol support CheckCCP(Verbose); // Next check for UGA support (probably none) CheckUGA(Verbose); // Finally check for GOP support CheckGOP(Verbose); return Status; }
The two headers ConsoleControl.h and UgaDraw.h can be found in the EDK1 sources which are available on SourceForge and elsewhere.
This utility checks the following protocols:
- Console Control – Not documented in UEFI or PI specifications but still widely supported in current UEFI firmware. It was part of the EDK reference implementation.
- UGA – Not documented in UEFI specifications but was widely supported in early versions of EFI firmware. Have not come cross it in any new hardware since about 2012.
- GOP – Fully documented in UEFI specifications. Based on the concept of a framebuffer. Badly implemented in some early UEFI firmware. A default mode of 80 x 25 must be supported.
It uses 3 different ways to check for each protocol. Note, if more than one GOP handle is found and you have only one screen, use the last handle.
The PrintGOP code came, pretty much verbatim, from one of the sample applications, modelist in Nigel Croxon’s GNU EFI sources.
Here is a suitable .INF file for building the utility in a UDK environment:
[Defines] INF_VERSION = 0x00010006 BASE_NAME = screenmodes FILE_GUID = 4ea87c51-7795-4dcd-0055-747010f3ce51 MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 0.1 ENTRY_POINT = ShellCEntryLib VALID_ARCHITECTURES = X64 [Sources] screenmodes.c [Packages] MdePkg/MdePkg.dec ShellPkg/ShellPkg.dec [LibraryClasses] ShellCEntryLib ShellLib BaseLib BaseMemoryLib UefiLib [Protocols] [BuildOptions] [Pcd]
Here is sample output from a Lenovo T450 laptop:
Usage: screenmodes [-v|--verbose] No ConsoleControl handle found via HandleProtocol Found ConsoleControl handle via LocateProtocol Found 1 ConsoleControl handles via LocateHandleBuffer No UGA handle found via HandleProtocol No UGA handle found via LocateProtocol No UGA handles found via LocateHandleBuffer No GOP handle found via HandleProtocol Found GOP handle via LocateProtocol Found 2 GOP handles via LocateHandleBuffer No ConsoleControl handle found via HandleProtocol Found ConsoleControl handle via LocateProtocol Screen mode: Text Graphics Support Avalaiable: Yes Found 1 ConsoleControl handles via LocateHandleBuffer Screen mode: Text Graphics Support Avalaiable: Yes No UGA handle found via HandleProtocol No UGA handle found via LocateProtocol No UGA handles found via LocateHandleBuffer No GOP handle found via HandleProtocol Found GOP handle via LocateProtocol GOP reports MaxMode 4 *0: 1600x900 BGRReserved Pixels 1600 1: 640x480 BGRReserved Pixels 640 2: 800x600 BGRReserved Pixels 800 3: 1024x768 BGRReserved Pixels 1024 Found 2 GOP handles via LocateHandleBuffer GOP reports MaxMode 4 *0: 1600x900 BGRReserved Pixels 1600 1: 640x480 BGRReserved Pixels 640 2: 800x600 BGRReserved Pixels 800 3: 1024x768 BGRReserved Pixels 1024 GOP reports MaxMode 4 *0: 1600x900 BGRReserved Pixels 1600 1: 640x480 BGRReserved Pixels 640 2: 800x600 BGRReserved Pixels 800 3: 1024x768 BGRReserved Pixels 1024
By the way, my blog posts will not be as frequent in 2015 as in previous years due to pressure of other ongoing work. However, please feel free to contact me if you need help with a UEFI-related problem.
Update December 2015: Tested that the utility build on UDK2015.