u-boot/drivers/video/efi.c
Simon Glass 1834c081d3 efi: Add video support to the app
The current EFI video driver only works when running in the stub. In that
case the stub calls boot services (before jumping to U-Boot proper) and
copies the graphics info over to the efi table. This is necessary because
the stub exits boot services before jumping to U-Boot.

The app maintains access to boot services throughout its life, so does not
need to do this. Update the driver to support calling boot services
directly.

Enable video output for the app. Note that this uses the
EFI_GRAPHICS_OUTPUT_PROTOCOL protocol, even though it mentions vesa.

A sample qemu command-line for this case is:

   qemu-system-x86_64 -bios /usr/share/edk2.git/ovmf-ia32/OVMF-pure-efi.fd
   -drive id=disk,file=try.img,if=none,format=raw -nic none
   -device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
2021-11-07 18:36:55 +01:00

176 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
*
* EFI framebuffer driver based on GOP
*/
#include <common.h>
#include <dm.h>
#include <efi_api.h>
#include <log.h>
#include <vbe.h>
#include <video.h>
struct pixel {
u8 pos;
u8 size;
};
static const struct efi_framebuffer {
struct pixel red;
struct pixel green;
struct pixel blue;
struct pixel rsvd;
} efi_framebuffer_format_map[] = {
[EFI_GOT_RGBA8] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
[EFI_GOT_BGRA8] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
};
static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size)
{
u8 first, len;
first = 0;
len = 0;
if (mask) {
while (!(mask & 0x1)) {
mask = mask >> 1;
first++;
}
while (mask & 0x1) {
mask = mask >> 1;
len++;
}
}
*pos = first;
*size = len;
}
static int get_mode_info(struct vesa_mode_info *vesa)
{
efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
struct efi_boot_services *boot = efi_get_boot();
struct efi_gop_mode *mode;
struct efi_gop *gop;
int ret;
if (!boot)
return log_msg_ret("sys", -ENOSYS);
ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
if (ret)
return log_msg_ret("prot", -ENOTSUPP);
mode = gop->mode;
vesa->phys_base_ptr = mode->fb_base;
vesa->x_resolution = mode->info->width;
vesa->y_resolution = mode->info->height;
return 0;
}
static int save_vesa_mode(struct vesa_mode_info *vesa)
{
struct efi_entry_gopmode *mode;
const struct efi_framebuffer *fbinfo;
int size;
int ret;
if (IS_ENABLED(CONFIG_EFI_APP)) {
ret = get_mode_info(vesa);
if (ret) {
printf("EFI graphics output protocol not found\n");
return -ENXIO;
}
} else {
ret = efi_info_get(EFIET_GOP_MODE, (void **)&mode, &size);
if (ret == -ENOENT) {
printf("EFI graphics output protocol mode not found\n");
return -ENXIO;
}
vesa->phys_base_ptr = mode->fb_base;
vesa->x_resolution = mode->info->width;
vesa->y_resolution = mode->info->height;
}
if (mode->info->pixel_format < EFI_GOT_BITMASK) {
fbinfo = &efi_framebuffer_format_map[mode->info->pixel_format];
vesa->red_mask_size = fbinfo->red.size;
vesa->red_mask_pos = fbinfo->red.pos;
vesa->green_mask_size = fbinfo->green.size;
vesa->green_mask_pos = fbinfo->green.pos;
vesa->blue_mask_size = fbinfo->blue.size;
vesa->blue_mask_pos = fbinfo->blue.pos;
vesa->reserved_mask_size = fbinfo->rsvd.size;
vesa->reserved_mask_pos = fbinfo->rsvd.pos;
vesa->bits_per_pixel = 32;
vesa->bytes_per_scanline = mode->info->pixels_per_scanline * 4;
} else if (mode->info->pixel_format == EFI_GOT_BITMASK) {
efi_find_pixel_bits(mode->info->pixel_bitmask[0],
&vesa->red_mask_pos,
&vesa->red_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[1],
&vesa->green_mask_pos,
&vesa->green_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[2],
&vesa->blue_mask_pos,
&vesa->blue_mask_size);
efi_find_pixel_bits(mode->info->pixel_bitmask[3],
&vesa->reserved_mask_pos,
&vesa->reserved_mask_size);
vesa->bits_per_pixel = vesa->red_mask_size +
vesa->green_mask_size +
vesa->blue_mask_size +
vesa->reserved_mask_size;
vesa->bytes_per_scanline = (mode->info->pixels_per_scanline *
vesa->bits_per_pixel) / 8;
} else {
debug("efi set unknown framebuffer format: %d\n",
mode->info->pixel_format);
return -EINVAL;
}
return 0;
}
static int efi_video_probe(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct vesa_mode_info *vesa = &mode_info.vesa;
int ret;
/* Initialize vesa_mode_info structure */
ret = save_vesa_mode(vesa);
if (ret)
goto err;
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
if (ret)
goto err;
printf("Video: %dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
vesa->bits_per_pixel);
return 0;
err:
printf("No video mode configured in EFI!\n");
return ret;
}
static const struct udevice_id efi_video_ids[] = {
{ .compatible = "efi-fb" },
{ }
};
U_BOOT_DRIVER(efi_video) = {
.name = "efi_video",
.id = UCLASS_VIDEO,
.of_match = efi_video_ids,
.probe = efi_video_probe,
};