Translate

Archives

Check Available Text And Graphic Modes From UEFI Shell

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.

Comments are closed.