mutex wrote:
I use Grub header to request video mode. Grub EFI and Grub MBR will both honor this bit. So if you try to request for example 1024x768x32 you are in most cases going to get your wish granted.
mutex, I definitely appreciate the thoughts. I don't, however, use Grub, instead relying on the UEFI framework alone to launch the OS.
brendan wrote:
Not necessarily. VGA is obsolete (and has been obsolete for over 20 years now)...
Understood. To be clear, I was specifically working to avoid using VGA. I was just curious about why it generally does not appear to be initialized when using UEFI and what the sensible alternative is. Your explanation was thorough, and I appreciate that, as well.
brendan wrote:
Obviously this means that the only future-proof ways of handling video is to use the UEFI services to setup a video mode and LFB during boot...
This turns out to be the key I was seeking. In my previous perusal of the UEFI spec, I assumed that only services explicitly identified as run-time (i.e., in section 7) are available. However, section 11.9 -- regarding the Graphics Output Protocol -- reads:
Quote:
Graphics output may also be required as part of the startup of an operating system. There are potentially times in modern operating systems prior to the loading of a high performance OS graphics driver where access to graphics output device is required. The Graphics Output Protocol supports this capability by providing the EFI OS loader access to a hardware frame buffer and enough information to allow the OS to draw directly to the graphics output device.
I didn't realize that the GOP provided a facility upon which one could rely at run-time (i.e., after ExitBootServices() is invoked). However, one can get a frame buffer via an instance of EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE before exiting boot services. The exact details of how one draws in this region can vary, but the possibilities are constrained and described by the enum EFI_GRAPHICS_PIXEL_FORMAT for the selected mode. This means that, to write text, one must implement a font renderer and provide a font definition, but that's pretty straight-forward.
In case others may have this question, I provide code below that compiles using the edk2 package, and which works, at least on the UEFI firmware provided in VMWare Workstation 9. For brevity, I omitted comments. The error checking is obviously not particularly robust. One may receive more than one handler for the EFI_GRAPHICS_OUTPUT_PROTOCOL (e.g., if there is more than one connected display), but I just grab the first one. I look for a particular desired mode, and if it doesn't exist, I bail. The HandleProtocol() method is deprecated (section 6.3), but it was easy and the specification contains an example using it. Again, these are all simplifications to illustrate the main point. Assuming this is not foolishly done, perhaps others may find it either instructive or helpful.
Code:
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <stdio.h>
#include <wchar.h>
#define MEMMAP_SIZE 1024*1024
UINT8 memmap[MEMMAP_SIZE * sizeof(EFI_MEMORY_DESCRIPTOR)];
void AwaitKeyboardInput( EFI_SIMPLE_TEXT_INPUT_PROTOCOL* conin );
void writeStringAt( const char* s, EFI_PHYSICAL_ADDRESS lfb_base_addr, UINT16 row, UINT16 col );
void drawTriangle( EFI_PHYSICAL_ADDRESS lfb_base_addr, UINTN center_x, UINTN center_y, UINTN width, UINT32 color );
void PreBootHalt( EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* conerr, UINT16* msg );
#define DESIRED_HREZ 1024
#define DESIRED_VREZ 768
#define DESIRED_PIXEL_FORMAT PixelBlueGreenRedReserved8BitPerColor
EFI_STATUS
EFIAPI
UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_SYSTEM_TABLE *gST;
EFI_BOOT_SERVICES *gBS;
UINTN memmap_size = MEMMAP_SIZE;
UINTN map_key, descriptor_size;
UINT32 descriptor_version;
UINTN mode_num;
EFI_STATUS status;
EFI_HANDLE* handle_buffer;
UINTN handle_count = 0;
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* gop_mode_info;
UINTN size_of_info;
if (!(gST = SystemTable))
return EFI_LOAD_ERROR;
if (!(gBS = SystemTable->BootServices))
return EFI_LOAD_ERROR;
if (!(gST->ConIn) || gST->ConIn->Reset( gST->ConIn, 0 ) != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"Input Connection Device Unavailable" );
status = gBS->LocateHandleBuffer( ByProtocol,
&gEfiGraphicsOutputProtocolGuid,
NULL,
&handle_count,
&handle_buffer );
if (status != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"LocateHandleBuffer() failed" );
status = gBS->HandleProtocol( handle_buffer[0],
&gEfiGraphicsOutputProtocolGuid,
(VOID **)&gop );
if (status != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"HandleProtocol() failed" );
for (mode_num = 0;
(status = gop->QueryMode( gop, mode_num, &size_of_info, &gop_mode_info )) == EFI_SUCCESS;
mode_num++) {
if (gop_mode_info->HorizontalResolution == DESIRED_HREZ &&
gop_mode_info->VerticalResolution == DESIRED_VREZ &&
gop_mode_info->PixelFormat == DESIRED_PIXEL_FORMAT)
break;
}
if (status != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"Failed to find desired mode" );
if (gop->SetMode( gop, mode_num) != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"SetMode() failed" );
status = gBS->GetMemoryMap( &memmap_size,
(EFI_MEMORY_DESCRIPTOR*)memmap,
&map_key,
&descriptor_size,
&descriptor_version );
if (status != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"GetMemoryMap() failed" );
if (gBS->ExitBootServices( ImageHandle, map_key ) != EFI_SUCCESS)
PreBootHalt( gST->ConOut, L"ExitBootServices() failed" );
drawTriangle( gop->Mode->FrameBufferBase, DESIRED_HREZ / 2, DESIRED_VREZ / 2 - 25, 100, 0x00ff99ff );
for ( ;; ) ;
}
void drawTriangle( EFI_PHYSICAL_ADDRESS lfb_base_addr, UINTN center_x, UINTN center_y, UINTN width, UINT32 color ) {
UINT32* at = (UINT32*)lfb_base_addr;
UINTN row, col;
at += (DESIRED_HREZ * (center_y - width / 2) + center_x - width / 2);
for (row = 0; row < width / 2; row++) {
for (col = 0; col < width - row * 2; col++)
*at++ = color;
at += (DESIRED_HREZ - col);
for (col = 0; col < width - row * 2; col++)
*at++ = color;
at += (DESIRED_HREZ - col + 1);
}
}
void PreBootHalt( EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* conerr, UINT16* msg ) {
conerr->OutputString( conerr, msg );
for (;;) ;
}