| /* |
| * 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); |
| } |