313 lines
7.2 KiB
C
313 lines
7.2 KiB
C
/*
|
|
* Copyright 2011-2014 Intel Corporation - All Rights Reserved
|
|
*/
|
|
|
|
#include <syslinux/linux.h>
|
|
#include "efi.h"
|
|
#include <string.h>
|
|
|
|
extern EFI_GUID GraphicsOutputProtocol;
|
|
|
|
static uint32_t console_default_attribute;
|
|
static bool console_default_cursor;
|
|
|
|
/*
|
|
* We want to restore the console state when we boot a kernel or return
|
|
* to the firmware.
|
|
*/
|
|
void efi_console_save(void)
|
|
{
|
|
SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
|
|
SIMPLE_TEXT_OUTPUT_MODE *mode = out->Mode;
|
|
|
|
console_default_attribute = mode->Attribute;
|
|
console_default_cursor = mode->CursorVisible;
|
|
}
|
|
|
|
void efi_console_restore(void)
|
|
{
|
|
SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
|
|
|
|
uefi_call_wrapper(out->SetAttribute, 2, out, console_default_attribute);
|
|
uefi_call_wrapper(out->EnableCursor, 2, out, console_default_cursor);
|
|
}
|
|
|
|
__export void writechr(char data)
|
|
{
|
|
efi_write_char(data, 0);
|
|
}
|
|
|
|
static inline EFI_STATUS open_protocol(EFI_HANDLE handle, EFI_GUID *protocol,
|
|
void **interface, EFI_HANDLE agent,
|
|
EFI_HANDLE controller, UINT32 attributes)
|
|
{
|
|
return uefi_call_wrapper(BS->OpenProtocol, 6, handle, protocol,
|
|
interface, agent, controller, attributes);
|
|
}
|
|
|
|
static inline EFI_STATUS
|
|
gop_query_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINTN *size,
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **info)
|
|
{
|
|
return uefi_call_wrapper(gop->QueryMode, 4, gop,
|
|
gop->Mode->Mode, size, info);
|
|
}
|
|
|
|
static inline void bit_mask(uint32_t mask, uint8_t *pos, uint8_t *size)
|
|
{
|
|
*pos = 0;
|
|
*size = 0;
|
|
|
|
if (mask) {
|
|
while (!(mask & 0x1)) {
|
|
mask >>= 1;
|
|
(*pos)++;
|
|
}
|
|
|
|
while (mask & 0x1) {
|
|
mask >>= 1;
|
|
(*size)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int setup_gop(struct screen_info *si)
|
|
{
|
|
EFI_HANDLE *handles = NULL;
|
|
EFI_STATUS status;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, *found;
|
|
EFI_GRAPHICS_PIXEL_FORMAT pixel_fmt;
|
|
EFI_PIXEL_BITMASK pixel_info;
|
|
uint32_t pixel_scanline;
|
|
UINTN i, nr_handles;
|
|
UINTN size;
|
|
uint16_t lfb_width, lfb_height;
|
|
uint32_t lfb_base, lfb_size;
|
|
int err = 0;
|
|
void **gop_handle = NULL;
|
|
|
|
size = 0;
|
|
status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &GraphicsOutputProtocol,
|
|
NULL, &size, gop_handle);
|
|
/* LibLocateHandle handle already returns the number of handles.
|
|
* There is no need to divide by sizeof(EFI_HANDLE)
|
|
*/
|
|
status = LibLocateHandle(ByProtocol, &GraphicsOutputProtocol,
|
|
NULL, &nr_handles, &handles);
|
|
if (status == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
handles = AllocatePool(nr_handles);
|
|
if (!handles)
|
|
return 0;
|
|
|
|
status = LibLocateHandle(ByProtocol, &GraphicsOutputProtocol,
|
|
NULL, &nr_handles, &handles);
|
|
}
|
|
if (status != EFI_SUCCESS)
|
|
goto out;
|
|
|
|
found = NULL;
|
|
for (i = 0; i < nr_handles; i++) {
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
|
|
EFI_PCI_IO *pciio = NULL;
|
|
EFI_HANDLE *h = handles[i];
|
|
|
|
status = uefi_call_wrapper(BS->HandleProtocol, 3, h,
|
|
&GraphicsOutputProtocol, (void **)&gop);
|
|
if (status != EFI_SUCCESS)
|
|
continue;
|
|
uefi_call_wrapper(BS->HandleProtocol, 3, h,
|
|
&PciIoProtocol, (void **)&pciio);
|
|
status = gop_query_mode(gop, &size, &info);
|
|
if (status == EFI_SUCCESS && (!found || pciio)) {
|
|
lfb_width = info->HorizontalResolution;
|
|
lfb_height = info->VerticalResolution;
|
|
lfb_base = gop->Mode->FrameBufferBase;
|
|
lfb_size = gop->Mode->FrameBufferSize;
|
|
pixel_fmt = info->PixelFormat;
|
|
pixel_info = info->PixelInformation;
|
|
pixel_scanline = info->PixelsPerScanLine;
|
|
if (pciio)
|
|
break;
|
|
found = gop;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
goto out;
|
|
|
|
err = 1;
|
|
|
|
dprintf("setup_screen: set up screen parameters for EFI GOP\n");
|
|
si->orig_video_isVGA = 0x70; /* EFI framebuffer */
|
|
|
|
si->lfb_base = lfb_base;
|
|
si->lfb_size = lfb_size;
|
|
si->lfb_width = lfb_width;
|
|
si->lfb_height = lfb_height;
|
|
si->pages = 1;
|
|
|
|
dprintf("setup_screen: lfb_base 0x%x lfb_size %d lfb_width %d lfb_height %d\n", lfb_base, lfb_size, lfb_width, lfb_height);
|
|
switch (pixel_fmt) {
|
|
case PixelRedGreenBlueReserved8BitPerColor:
|
|
si->lfb_depth = 32;
|
|
si->lfb_linelength = pixel_scanline * 4;
|
|
si->red_size = 8;
|
|
si->red_pos = 0;
|
|
si->green_size = 8;
|
|
si->green_pos = 8;
|
|
si->blue_size = 8;
|
|
si->blue_pos = 16;
|
|
si->rsvd_size = 8;
|
|
si->rsvd_pos = 24;
|
|
break;
|
|
case PixelBlueGreenRedReserved8BitPerColor:
|
|
si->lfb_depth = 32;
|
|
si->lfb_linelength = pixel_scanline * 4;
|
|
si->red_size = 8;
|
|
si->red_pos = 16;
|
|
si->green_size = 8;
|
|
si->green_pos = 8;
|
|
si->blue_size = 8;
|
|
si->blue_pos = 0;
|
|
si->rsvd_size = 8;
|
|
si->rsvd_pos = 24;
|
|
break;
|
|
case PixelBitMask:
|
|
bit_mask(pixel_info.RedMask, &si->red_pos,
|
|
&si->red_size);
|
|
bit_mask(pixel_info.GreenMask, &si->green_pos,
|
|
&si->green_size);
|
|
bit_mask(pixel_info.BlueMask, &si->blue_pos,
|
|
&si->blue_size);
|
|
bit_mask(pixel_info.ReservedMask, &si->rsvd_pos,
|
|
&si->rsvd_size);
|
|
si->lfb_depth = si->red_size + si->green_size +
|
|
si->blue_size + si->rsvd_size;
|
|
si->lfb_linelength = (pixel_scanline * si->lfb_depth) / 8;
|
|
break;
|
|
default:
|
|
si->lfb_depth = 4;;
|
|
si->lfb_linelength = si->lfb_width / 2;
|
|
si->red_size = 0;
|
|
si->red_pos = 0;
|
|
si->green_size = 0;
|
|
si->green_pos = 0;
|
|
si->blue_size = 0;
|
|
si->blue_pos = 0;
|
|
si->rsvd_size = 0;
|
|
si->rsvd_pos = 0;
|
|
break;
|
|
}
|
|
dprintf("setup_screen: depth %d line %d rpos %d rsize %d gpos %d gsize %d bpos %d bsize %d rsvpos %d rsvsize %d\n",
|
|
si->lfb_depth, si->lfb_linelength,
|
|
si->red_pos, si->red_size,
|
|
si->green_pos, si->green_size,
|
|
si->blue_pos, si->blue_size,
|
|
si->blue_pos, si->blue_size,
|
|
si->rsvd_pos, si->rsvd_size);
|
|
|
|
out:
|
|
if (handles) FreePool(handles);
|
|
|
|
return err;
|
|
}
|
|
|
|
#define EFI_UGA_PROTOCOL_GUID \
|
|
{ \
|
|
0x982c298b, 0xf4fa, 0x41cb, {0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39 } \
|
|
}
|
|
|
|
typedef struct _EFI_UGA_DRAW_PROTOCOL EFI_UGA_DRAW_PROTOCOL;
|
|
|
|
typedef
|
|
EFI_STATUS
|
|
(EFIAPI *EFI_UGA_DRAW_PROTOCOL_GET_MODE) (
|
|
IN EFI_UGA_DRAW_PROTOCOL *This,
|
|
OUT UINT32 *Width,
|
|
OUT UINT32 *Height,
|
|
OUT UINT32 *Depth,
|
|
OUT UINT32 *Refresh
|
|
)
|
|
;
|
|
|
|
struct _EFI_UGA_DRAW_PROTOCOL {
|
|
EFI_UGA_DRAW_PROTOCOL_GET_MODE GetMode;
|
|
void *SetMode;
|
|
void *Blt;
|
|
};
|
|
|
|
static int setup_uga(struct screen_info *si)
|
|
{
|
|
EFI_UGA_DRAW_PROTOCOL *uga, *first;
|
|
EFI_GUID UgaProtocol = EFI_UGA_PROTOCOL_GUID;
|
|
UINT32 width, height;
|
|
EFI_STATUS status;
|
|
EFI_HANDLE *handles;
|
|
UINTN i, nr_handles;
|
|
int rv = 0;
|
|
|
|
status = LibLocateHandle(ByProtocol, &UgaProtocol,
|
|
NULL, &nr_handles, &handles);
|
|
if (status != EFI_SUCCESS)
|
|
return rv;
|
|
|
|
for (i = 0; i < nr_handles; i++) {
|
|
EFI_PCI_IO *pciio = NULL;
|
|
EFI_HANDLE *handle = handles[i];
|
|
UINT32 w, h, depth, refresh;
|
|
|
|
status = uefi_call_wrapper(BS->HandleProtocol, 3, handle,
|
|
&UgaProtocol, (void **)&uga);
|
|
if (status != EFI_SUCCESS)
|
|
continue;
|
|
|
|
uefi_call_wrapper(BS->HandleProtocol, 3, handle,
|
|
&PciIoProtocol, (void **)&pciio);
|
|
|
|
status = uefi_call_wrapper(uga->GetMode, 5, uga, &w, &h,
|
|
&depth, &refresh);
|
|
|
|
if (status == EFI_SUCCESS && (!first || pciio)) {
|
|
width = w;
|
|
height = h;
|
|
|
|
if (pciio)
|
|
break;
|
|
|
|
first = uga;
|
|
}
|
|
}
|
|
|
|
if (!first)
|
|
goto out;
|
|
rv = 1;
|
|
|
|
si->orig_video_isVGA = 0x70; /* EFI framebuffer */
|
|
|
|
si->lfb_depth = 32;
|
|
si->lfb_width = width;
|
|
si->lfb_height = height;
|
|
|
|
si->red_size = 8;
|
|
si->red_pos = 16;
|
|
si->green_size = 8;
|
|
si->green_pos = 8;
|
|
si->blue_size = 8;
|
|
si->blue_pos = 0;
|
|
si->rsvd_size = 8;
|
|
si->rsvd_pos = 24;
|
|
|
|
out:
|
|
FreePool(handles);
|
|
return rv;
|
|
}
|
|
|
|
void setup_screen(struct screen_info *si)
|
|
{
|
|
memset(si, 0, sizeof(*si));
|
|
|
|
if (!setup_gop(si))
|
|
setup_uga(si);
|
|
}
|