summaryrefslogtreecommitdiffstats
path: root/sys/boot/efi/loader
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2015-08-30 23:58:53 +0000
committermarcel <marcel@FreeBSD.org>2015-08-30 23:58:53 +0000
commit58e5250bc17f63c7b59be2af81b0e50067fa3415 (patch)
treebcfcd1c51290c653c21eb701a9a23af08c76d75e /sys/boot/efi/loader
parent55b48887c419a089e944eccc1a25c2e4efb327e3 (diff)
downloadFreeBSD-src-58e5250bc17f63c7b59be2af81b0e50067fa3415.zip
FreeBSD-src-58e5250bc17f63c7b59be2af81b0e50067fa3415.tar.gz
Add support for the UGA draw protocol. This includes adding a
command called 'uga' to show whether UGA is implemented by the firmware and what the settings are. It also includes filling the efi_fb structure from the UGA information when GOP isn't implemented by the firmware. Since UGA does not provide information about the stride, we set the stride to the horizontal resolution. This is likely not correct and we should determine the stride by trial and error. For now, this should show something on the console rather than nothing. Refactor this file to maximize code reuse. PR: 202730
Diffstat (limited to 'sys/boot/efi/loader')
-rw-r--r--sys/boot/efi/loader/arch/amd64/framebuffer.c252
1 files changed, 196 insertions, 56 deletions
diff --git a/sys/boot/efi/loader/arch/amd64/framebuffer.c b/sys/boot/efi/loader/arch/amd64/framebuffer.c
index 291fda2..3fbedc6 100644
--- a/sys/boot/efi/loader/arch/amd64/framebuffer.c
+++ b/sys/boot/efi/loader/arch/amd64/framebuffer.c
@@ -30,36 +30,27 @@
__FBSDID("$FreeBSD$");
#include <bootstrap.h>
+#include <sys/endian.h>
#include <stand.h>
#include <efi.h>
#include <efilib.h>
+#include <efiuga.h>
+#include <efipciio.h>
#include <machine/metadata.h>
static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
+static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
-int
-efi_find_framebuffer(struct efi_fb *efifb)
+static int
+efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
+ EFI_PIXEL_BITMASK *pixinfo)
{
- EFI_GRAPHICS_OUTPUT *gop;
- EFI_STATUS status;
- EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode;
- EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
-
- status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
- if (EFI_ERROR(status))
- return (1);
+ int result;
- mode = gop->Mode;
- info = gop->Mode->Info;
-
- efifb->fb_addr = mode->FrameBufferBase;
- efifb->fb_size = mode->FrameBufferSize;
- efifb->fb_height = info->VerticalResolution;
- efifb->fb_width = info->HorizontalResolution;
- efifb->fb_stride = info->PixelsPerScanLine;
-
- switch (info->PixelFormat) {
+ result = 0;
+ switch (pixfmt) {
case PixelRedGreenBlueReserved8BitPerColor:
efifb->fb_mask_red = 0x000000ff;
efifb->fb_mask_green = 0x0000ff00;
@@ -73,53 +64,164 @@ efi_find_framebuffer(struct efi_fb *efifb)
efifb->fb_mask_reserved = 0xff000000;
break;
case PixelBitMask:
- efifb->fb_mask_red = info->PixelInformation.RedMask;
- efifb->fb_mask_green = info->PixelInformation.GreenMask;
- efifb->fb_mask_blue = info->PixelInformation.BlueMask;
- efifb->fb_mask_reserved =
- info->PixelInformation.ReservedMask;
+ efifb->fb_mask_red = pixinfo->RedMask;
+ efifb->fb_mask_green = pixinfo->GreenMask;
+ efifb->fb_mask_blue = pixinfo->BlueMask;
+ efifb->fb_mask_reserved = pixinfo->ReservedMask;
break;
default:
+ result = 1;
+ break;
+ }
+ return (result);
+}
+
+static int
+efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
+{
+ int result;
+
+ efifb->fb_addr = mode->FrameBufferBase;
+ efifb->fb_size = mode->FrameBufferSize;
+ efifb->fb_height = info->VerticalResolution;
+ efifb->fb_width = info->HorizontalResolution;
+ efifb->fb_stride = info->PixelsPerScanLine;
+ result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
+ &info->PixelInformation);
+ return (result);
+}
+
+static int
+efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga)
+{
+ uint8_t *buf;
+ EFI_PCI_IO_PROTOCOL *pciio;
+ EFI_HANDLE handle;
+ EFI_STATUS status;
+ UINTN bufofs, bufsz;
+ uint64_t address, length;
+ uint32_t horiz, vert, depth, refresh;
+ u_int bar;
+
+ status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
+ if (EFI_ERROR(status))
+ return (1);
+ efifb->fb_height = vert;
+ efifb->fb_width = horiz;
+ efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
+ NULL);
+ /* Find all handles that support the UGA protocol. */
+ bufsz = 0;
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL)
return (1);
+ buf = malloc(bufsz);
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz,
+ (EFI_HANDLE *)buf);
+ if (status != EFI_SUCCESS) {
+ free(buf);
+ return (1);
+ }
+ /* Get the PCI I/O interface of the first handle that supports it. */
+ pciio = NULL;
+ for (bufofs = 0; bufofs < bufsz; bufofs += sizeof(EFI_HANDLE)) {
+ handle = *(EFI_HANDLE *)(buf + bufofs);
+ status = BS->HandleProtocol(handle, &pciio_guid,
+ (void **)&pciio);
+ if (status == EFI_SUCCESS)
+ break;
+ }
+ free(buf);
+ if (pciio == NULL)
+ return (1);
+ /* Attempt to get the frame buffer address (imprecise). */
+ efifb->fb_addr = 0;
+ efifb->fb_size = 0;
+ for (bar = 0; bar < 6; bar++) {
+ status = pciio->GetBarAttributes(pciio, bar, NULL,
+ (EFI_HANDLE *)&buf);
+ if (status != EFI_SUCCESS)
+ continue;
+ /* XXX magic offsets and constants. */
+ if (buf[0] == 0x87 && buf[3] == 0) {
+ /* 32-bit address space descriptor (MEMIO) */
+ address = le32dec(buf + 10);
+ length = le32dec(buf + 22);
+ } else if (buf[0] == 0x8a && buf[3] == 0) {
+ /* 64-bit address space descriptor (MEMIO) */
+ address = le64dec(buf + 14);
+ length = le64dec(buf + 38);
+ } else {
+ address = 0;
+ length = 0;
+ }
+ BS->FreePool(buf);
+ if (address == 0 || length == 0)
+ continue;
+ /* We assume the largest BAR is the frame buffer. */
+ if (length > efifb->fb_size) {
+ efifb->fb_addr = address;
+ efifb->fb_size = length;
+ }
}
+ if (efifb->fb_addr == 0 || efifb->fb_size == 0)
+ return (1);
+ /* TODO determine the stride. */
+ efifb->fb_stride = efifb->fb_width; /* XXX */
return (0);
}
-COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
+int
+efi_find_framebuffer(struct efi_fb *efifb)
+{
+ EFI_GRAPHICS_OUTPUT *gop;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info));
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_uga(efifb, uga));
+
+ return (1);
+}
static void
-command_gop_display(u_int mode, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
+print_efifb(int mode, struct efi_fb *efifb, int verbose)
{
+ uint32_t mask;
+ u_int depth;
- printf("mode %u: %ux%u, stride=%u, color=", mode,
- info->HorizontalResolution, info->VerticalResolution,
- info->PixelsPerScanLine);
- switch (info->PixelFormat) {
- case PixelRedGreenBlueReserved8BitPerColor:
- printf("32-bit (RGB)");
- break;
- case PixelBlueGreenRedReserved8BitPerColor:
- printf("32-bit (BGR)");
- break;
- case PixelBitMask:
- printf("mask (R=%x, G=%x, B=%x, X=%x)",
- info->PixelInformation.RedMask,
- info->PixelInformation.GreenMask,
- info->PixelInformation.BlueMask,
- info->PixelInformation.ReservedMask);
- break;
- case PixelBltOnly:
- printf("unsupported (blt only)");
- break;
- default:
- printf("unsupported (unknown)");
- break;
+ if (mode >= 0)
+ printf("mode %d: ", mode);
+ mask = efifb->fb_mask_red | efifb->fb_mask_green |
+ efifb->fb_mask_blue | efifb->fb_mask_reserved;
+ if (mask > 0) {
+ for (depth = 1; mask != 1; depth++)
+ mask >>= 1;
+ } else
+ depth = 0;
+ printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
+ depth, efifb->fb_stride);
+ if (verbose) {
+ printf("\n frame buffer: address=%jx, size=%jx",
+ (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
+ printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
+ efifb->fb_mask_red, efifb->fb_mask_green,
+ efifb->fb_mask_blue);
}
}
+COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
+
static int
command_gop(int argc, char *argv[])
{
+ struct efi_fb efifb;
EFI_GRAPHICS_OUTPUT *gop;
EFI_STATUS status;
u_int mode;
@@ -131,7 +233,7 @@ command_gop(int argc, char *argv[])
return (CMD_ERROR);
}
- if (argc == 1)
+ if (argc < 2)
goto usage;
if (!strcmp(argv[1], "set")) {
@@ -152,20 +254,24 @@ command_gop(int argc, char *argv[])
return (CMD_ERROR);
}
} else if (!strcmp(argv[1], "get")) {
- command_gop_display(gop->Mode->Mode, gop->Mode->Info);
- printf("\n frame buffer: address=%jx, size=%lx\n",
- (uintmax_t)gop->Mode->FrameBufferBase,
- gop->Mode->FrameBufferSize);
+ if (argc != 2)
+ goto usage;
+ efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
+ print_efifb(gop->Mode->Mode, &efifb, 1);
+ printf("\n");
} else if (!strcmp(argv[1], "list")) {
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
UINTN infosz;
+ if (argc != 2)
+ goto usage;
pager_open();
for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
status = gop->QueryMode(gop, mode, &infosz, &info);
if (EFI_ERROR(status))
continue;
- command_gop_display(mode, info);
+ efifb_from_gop(&efifb, gop->Mode, info);
+ print_efifb(mode, &efifb, 0);
if (pager_output("\n"))
break;
}
@@ -178,3 +284,37 @@ command_gop(int argc, char *argv[])
argv[0]);
return (CMD_ERROR);
}
+
+COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
+
+static int
+command_uga(int argc, char *argv[])
+{
+ struct efi_fb efifb;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (EFI_ERROR(status)) {
+ sprintf(command_errbuf, "%s: UGA Protocol not present "
+ "(error=%lu)", argv[0], status & ~EFI_ERROR_MASK);
+ return (CMD_ERROR);
+ }
+
+ if (argc != 1)
+ goto usage;
+
+ if (efifb_from_uga(&efifb, uga) != CMD_OK) {
+ sprintf(command_errbuf, "%s: Unable to get UGA information",
+ argv[0]);
+ return (CMD_ERROR);
+ }
+
+ print_efifb(-1, &efifb, 1);
+ printf("\n");
+ return (CMD_OK);
+
+ usage:
+ sprintf(command_errbuf, "usage: %s", argv[0]);
+ return (CMD_ERROR);
+}
OpenPOWER on IntegriCloud