[PATCH 0/7] efi: Support video output on ARM

From: Simon Glass <sjg@chromium.org> So far the EFI app supports video on x86 but not ARM. This series adds this feature, using EFI GOP's 'blt' feature. Support for bootstd is enabled at the same time, so that the app can boot an OS automatically. Simon Glass (7): video: efi: Store the GOP in priv data efi: Support the EFI GOP blit method efi: Add an enum for the GOP blt operation scripts: Adjust EFI script to support a root disk efi: Support bootstd and video with ARM app dm: core: Provide oftree_dispose() when OF_LIVE is disabled boot: Enable the VBE-OS bootmeth for EFI app and payload boot/Kconfig | 1 + configs/efi-arm_app64_defconfig | 13 ++-- drivers/video/efi.c | 113 +++++++++++++++++++++++++------- include/dm/ofnode.h | 22 ++++--- include/efi_api.h | 19 +++++- scripts/build-efi | 5 +- scripts/build_helper.py | 8 ++- 7 files changed, 130 insertions(+), 51 deletions(-) -- 2.43.0 base-commit: 9179000b5094fb2146e336c130e0d8ee35ef85e2

From: Simon Glass <sjg@chromium.org> At present the EFI GOP is read in the probe() method but is not stored, so it is not possible to use it later. With the EFI app we need to be able to access it after probing, when blitting is used. Create a struct to hold it. Store the framebuffer pointer in there as well, to simplify the code. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/video/efi.c | 66 ++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/drivers/video/efi.c b/drivers/video/efi.c index 94d4c93dac3..48b734d619d 100644 --- a/drivers/video/efi.c +++ b/drivers/video/efi.c @@ -29,6 +29,17 @@ static const struct efi_framebuffer { [EFI_GOT_BGRA8] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} }, }; +/** + * struct efi_video_priv - private information for this driver + * + * @fb: Framebuffer address + * @gop: Pointer to the EFI GOP struct + */ +struct efi_video_priv { + u64 fb; + struct efi_gop *gop; +}; + static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size) { u8 first, len; @@ -55,15 +66,16 @@ static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size) /** * get_mode_info() - Ask EFI for the mode information * - * Gets info from the graphics-output protocol + * Get info from the graphics-output protocol and retain the GOP protocol itself * * @vesa: Place to put the mode information - * @fbp: Returns the address of the frame buffer + * @priv: Pointer to priv data * @infop: Returns a pointer to the mode info * Returns: 0 if OK, -ENOSYS if boot services are not available, -ENOTSUPP if * the protocol is not supported by EFI */ -static int get_mode_info(struct vesa_mode_info *vesa, u64 *fbp, +static int get_mode_info(struct vesa_mode_info *vesa, + struct efi_video_priv *priv, struct efi_gop_mode_info **infop) { efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; @@ -83,7 +95,8 @@ static int get_mode_info(struct vesa_mode_info *vesa, u64 *fbp, (ulong)mode->fb_base, (ulong)mode->fb_size); vesa->phys_base_ptr = mode->fb_base; - *fbp = mode->fb_base; + priv->fb = mode->fb_base; + priv->gop = gop; vesa->x_resolution = mode->info->width; vesa->y_resolution = mode->info->height; *infop = mode->info; @@ -123,16 +136,17 @@ static int get_mode_from_entry(struct vesa_mode_info *vesa, u64 *fbp, return 0; } -static int save_vesa_mode(struct vesa_mode_info *vesa, u64 *fbp) +static int save_vesa_mode(struct vesa_mode_info *vesa, + struct efi_video_priv *priv) { const struct efi_framebuffer *fbinfo; struct efi_gop_mode_info *info; int ret; if (IS_ENABLED(CONFIG_EFI_APP)) - ret = get_mode_info(vesa, fbp, &info); + ret = get_mode_info(vesa, priv, &info); else - ret = get_mode_from_entry(vesa, fbp, &info); + ret = get_mode_from_entry(vesa, &priv->fb, &info); if (ret) { printf("EFI graphics output protocol not found (err=%dE)\n", ret); @@ -185,20 +199,20 @@ static int efi_video_probe(struct udevice *dev) struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct vesa_state mode_info; struct vesa_mode_info *vesa = &mode_info.vesa; - u64 fb; - int ret; + struct efi_video_priv *priv = dev_get_priv(dev); + long ret; /* Initialize vesa_mode_info structure */ - ret = save_vesa_mode(vesa, &fb); + ret = save_vesa_mode(vesa, priv); if (ret) goto err; - ret = vesa_setup_video_priv(vesa, fb, uc_priv, plat); + ret = vesa_setup_video_priv(vesa, priv->fb, uc_priv, plat); if (ret) goto err; printf("Video: %dx%dx%d @ %lx\n", uc_priv->xsize, uc_priv->ysize, - vesa->bits_per_pixel, (ulong)fb); + vesa->bits_per_pixel, (ulong)priv->fb); return 0; @@ -209,19 +223,20 @@ err: static int efi_video_bind(struct udevice *dev) { - if (IS_ENABLED(CONFIG_VIDEO_COPY)) { - struct video_uc_plat *plat = dev_get_uclass_plat(dev); - struct vesa_mode_info vesa; - int ret; - u64 fb; - - /* - * Initialise vesa_mode_info structure so we can figure out the - * required framebuffer size. If something goes wrong, just do - * without a copy framebuffer - */ - ret = save_vesa_mode(&vesa, &fb); - if (!ret) { + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct efi_video_priv tmp_priv; + + struct vesa_mode_info vesa; + int ret; + + /* + * Initialise vesa_mode_info structure so we can figure out the + * required framebuffer size. If something goes wrong, just do + * without a copy framebuffer + */ + ret = save_vesa_mode(&vesa, &tmp_priv); + if (!ret) { + if (IS_ENABLED(CONFIG_VIDEO_COPY)) { /* this is not reached if the EFI call failed */ plat->copy_size = vesa.bytes_per_scanline * vesa.y_resolution; @@ -242,4 +257,5 @@ U_BOOT_DRIVER(efi_video) = { .of_match = efi_video_ids, .bind = efi_video_bind, .probe = efi_video_probe, + .priv_auto = sizeof(struct efi_video_priv), }; -- 2.43.0

From: Simon Glass <sjg@chromium.org> With arm64 we generally use the virtio-gpu driver, which does not support direct access to a framebuffer. Add support for this, so that the display works as expected. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/video/efi.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/video/efi.c b/drivers/video/efi.c index 48b734d619d..eb084b31a41 100644 --- a/drivers/video/efi.c +++ b/drivers/video/efi.c @@ -34,12 +34,36 @@ static const struct efi_framebuffer { * * @fb: Framebuffer address * @gop: Pointer to the EFI GOP struct + * @use_blit: true to use a blit operation to draw on the display, false to use + * the normal bitmap display */ struct efi_video_priv { u64 fb; struct efi_gop *gop; + bool use_blit; }; +static int efi_video_sync(struct udevice *dev) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev); + struct efi_video_priv *priv = dev_get_priv(dev); + efi_status_t ret; + + if (priv->use_blit) { + /* redraw the entire display */ + ret = priv->gop->blt(priv->gop, vid_priv->fb, + EFI_GOP_BLIT_WRITE, 0, 0, 0, 0, + vid_priv->xsize, vid_priv->ysize, + vid_priv->line_length); + if (ret) { + log_err("GOP Blt failed: %lx\n", ret); + return -EIO; + } + } + + return 0; +} + static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size) { u8 first, len; @@ -185,6 +209,20 @@ static int save_vesa_mode(struct vesa_mode_info *vesa, vesa->reserved_mask_size; vesa->bytes_per_scanline = (info->pixels_per_scanline * vesa->bits_per_pixel) / 8; + } else if (info->pixel_format == EFI_GOT_BITBLT) { + priv->use_blit = true; + vesa->x_resolution = info->width; + vesa->y_resolution = info->height; + vesa->red_mask_pos = 0; + vesa->red_mask_size = 8; + vesa->green_mask_pos = 8; + vesa->green_mask_size = 8; + vesa->blue_mask_pos = 16; + vesa->blue_mask_size = 8; + vesa->reserved_mask_pos = 24; + vesa->reserved_mask_size = 8; + vesa->bits_per_pixel = 32; + vesa->bytes_per_scanline = info->pixels_per_scanline * 4; } else { log_err("Unknown framebuffer format: %d\n", info->pixel_format); return -EINVAL; @@ -207,6 +245,8 @@ static int efi_video_probe(struct udevice *dev) if (ret) goto err; + if (priv->use_blit) + priv->fb = plat->base; ret = vesa_setup_video_priv(vesa, priv->fb, uc_priv, plat); if (ret) goto err; @@ -242,10 +282,16 @@ static int efi_video_bind(struct udevice *dev) vesa.y_resolution; } } + if (tmp_priv.use_blit) + plat->size = vesa.bytes_per_scanline * vesa.y_resolution; return 0; } +const static struct video_ops efi_video_ops = { + .video_sync = efi_video_sync, +}; + static const struct udevice_id efi_video_ids[] = { { .compatible = "efi-fb" }, { } @@ -257,5 +303,6 @@ U_BOOT_DRIVER(efi_video) = { .of_match = efi_video_ids, .bind = efi_video_bind, .probe = efi_video_probe, + .ops = &efi_video_ops, .priv_auto = sizeof(struct efi_video_priv), }; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Create an enum for the 'operation' argument, so that drivers can make use of this. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi_api.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/efi_api.h b/include/efi_api.h index 6ab6b0b9750..9b795c2a20d 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1428,6 +1428,7 @@ struct efi_hii_config_access_protocol { #define EFI_GOT_RGBA8 0 #define EFI_GOT_BGRA8 1 #define EFI_GOT_BITMASK 2 +#define EFI_GOT_BITBLT 3 struct efi_gop_mode_info { u32 version; @@ -1459,6 +1460,21 @@ struct efi_gop_pixel { #define EFI_BLT_BUFFER_TO_VIDEO 2 #define EFI_BLT_VIDEO_TO_VIDEO 3 +/** + * enum efi_gop_blt_op - blt() operations for efi_gop + * + * @EFI_GOP_BLIT_VIDEO_FILL: Fill an area + * @EFI_GOP_BLIT_READ: Read from video to a buffer + * @EFI_GOP_BLIT_WRITE: Write from a buffer to video + * @EFI_GOP_BLIT_COPY: Copy from one video area to another + */ +enum efi_gop_blt_op { + EFI_GOP_BLIT_VIDEO_FILL, + EFI_GOP_BLIT_READ, + EFI_GOP_BLIT_WRITE, + EFI_GOP_BLIT_COPY, +}; + struct efi_gop { efi_status_t (EFIAPI *query_mode)(struct efi_gop *this, u32 mode_number, efi_uintn_t *size_of_info, @@ -1466,7 +1482,8 @@ struct efi_gop { efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number); efi_status_t (EFIAPI *blt)(struct efi_gop *this, struct efi_gop_pixel *buffer, - u32 operation, efi_uintn_t sx, + enum efi_gop_blt_op operation, + efi_uintn_t sx, efi_uintn_t sy, efi_uintn_t dx, efi_uintn_t dy, efi_uintn_t width, efi_uintn_t height, efi_uintn_t delta); -- 2.43.0

From: Simon Glass <sjg@chromium.org> At present the root disk conflicts with the boot disk used to hold the app (or payload). Use virtio for this disk and number the others after that. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-efi | 5 ++--- scripts/build_helper.py | 8 +++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/build-efi b/scripts/build-efi index adf37cd4aef..634d57e08ed 100755 --- a/scripts/build-efi +++ b/scripts/build-efi @@ -83,8 +83,7 @@ class BuildEfi: '-drive', f'if=pflash,format=raw,file={var_store}' ] extra += ['-drive', - f'id=hd0,file={self.img},if=none,format=raw', - '-device', 'virtio-blk-device,drive=hd0'] + f'if=virtio,file={self.img},format=raw,id=hd0'] else: # x86 if self.helper.bitness == 64: qemu_arch = 'x86_64' @@ -128,8 +127,8 @@ class BuildEfi: cmd = [qemu] cmd += '-m', mem cmd += '-nic', 'none' - self.helper.add_qemu_args(self.args, cmd) cmd += extra + self.helper.add_qemu_args(self.args, cmd, base_hd=1) tout.info(' '.join(cmd)) command.run(*cmd) diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 52c84861ffe..57c900f251f 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -126,7 +126,7 @@ sct_mnt = /mnt/sct else: shutil.copy2(tmp.name, fname) - def add_qemu_args(self, args, cmd): + def add_qemu_args(self, args, cmd, base_hd=0): """Add QEMU arguments according to the selected options This helps in creating the command-line used to run QEMU. @@ -134,6 +134,7 @@ sct_mnt = /mnt/sct Args: args (list of str): Existing arguments to add to cmd (argparse.Namespace): Program arguments + base_hd (int): Base number to use for QEMU hd device """ cmdline = [] if args.kernel: @@ -171,10 +172,11 @@ sct_mnt = /mnt/sct cmd.extend([ '-device', f'virtio-scsi-pci,id=scsi0,{MODERN_PCI}', - '-device', f'scsi-hd,bus=scsi0.0,drive=hd{i}']) + '-device', + f'scsi-hd,bus=scsi0.0,drive=hd{base_hd + i}']) cmd.extend([ '-drive', - f'if={iface},file={disk},format=raw,id=hd{i}']) + f'if={iface},file={disk},format=raw,id=hd{base_hd + i}']) else: tout.warning(f"Disk image '{disk}' not found") -- 2.43.0

From: Simon Glass <sjg@chromium.org> We need CONFIG_BOARD_EARLY_INIT_R to enable block devices. Enable bootstd as well, so that booting is supported. Provide the 'lsblk' command to allow listing block devices. Use white-on-black as this is a little easier on the eyes. Signed-off-by: Simon Glass <sjg@chromium.org> --- configs/efi-arm_app64_defconfig | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/configs/efi-arm_app64_defconfig b/configs/efi-arm_app64_defconfig index 5298f2d84b0..55a635b104a 100644 --- a/configs/efi-arm_app64_defconfig +++ b/configs/efi-arm_app64_defconfig @@ -11,30 +11,24 @@ CONFIG_TARGET_EFI_ARM_APP64=y CONFIG_EFI_CLIENT=y CONFIG_EFI_APP_64BIT=y CONFIG_FIT=y -# CONFIG_BOOTSTD is not set +CONFIG_BOOTSTD_FULL=y CONFIG_SHOW_BOOT_PROGRESS=y CONFIG_USE_BOOTARGS=y CONFIG_SYS_PBSIZE=532 CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_LOG=y -CONFIG_HUSH_PARSER=y +CONFIG_BOARD_EARLY_INIT_R=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_MEMINFO=y CONFIG_CMD_MEMINFO_MAP=y CONFIG_CMD_DM=y -CONFIG_CMD_PART=y +CONFIG_CMD_LSBLK=y CONFIG_CMD_DNS=y CONFIG_CMD_WGET=y CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y -CONFIG_CMD_EXT2=y -CONFIG_CMD_EXT4=y CONFIG_CMD_EXT4_WRITE=y -CONFIG_CMD_FAT=y -CONFIG_CMD_FS_GENERIC=y CONFIG_MAC_PARTITION=y -CONFIG_ISO_PARTITION=y -CONFIG_EFI_PARTITION=y CONFIG_OF_LIVE=y CONFIG_ENV_OVERWRITE=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y @@ -46,5 +40,6 @@ CONFIG_EFI_NET=y CONFIG_DM_RNG=y CONFIG_SYSRESET=y CONFIG_VIDEO=y +CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_CMD_DHRYSTONE=y -- 2.43.0

From: Simon Glass <sjg@chromium.org> This function is only defined if CONFIG_OF_LIVE is enabled. Provide a static inline to handle it being disabled. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/dm/ofnode.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 120393426db..cc6db4f5ae8 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -79,6 +79,16 @@ oftree oftree_from_fdt(void *fdt); */ ofnode noffset_to_ofnode(ofnode other_node, int of_offset); +/** + * oftree_dispose() - Dispose of an oftree + * + * This can be used to dispose of a tree that has been created (other than + * the control FDT which must not be disposed) + * + * @tree: Tree to dispose + */ +void oftree_dispose(oftree tree); + #else /* !OFNODE_MULTI_TREE */ static inline void oftree_reset(void) {} @@ -126,6 +136,8 @@ static inline ofnode noffset_to_ofnode(ofnode other_node, int of_offset) return node; } +static inline void oftree_dispose(oftree tree) {} + #endif /* OFNODE_MULTI_TREE */ /** @@ -375,16 +387,6 @@ static inline oftree oftree_from_np(struct device_node *root) return tree; } -/** - * oftree_dispose() - Dispose of an oftree - * - * This can be used to dispose of a tree that has been created (other than - * the control FDT which must not be disposed) - * - * @tree: Tree to dispose - */ -void oftree_dispose(oftree tree); - /** * ofnode_name_eq() - Check a node name ignoring its unit address * -- 2.43.0

From: Simon Glass <sjg@chromium.org> This method is useful for locating an OS using the VBE approach. Enable it for the app and payload, for both x86 and ARM. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/boot/Kconfig b/boot/Kconfig index ddb5b26ec15..a68d8af047b 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -823,6 +823,7 @@ config BOOTMETH_VBE_ABREC_OS imply SPL_CRC8 imply VPL_CRC8 default y if SANDBOX && !SPL + default y if EFI_CLIENT help Enables support for VBE 'abrec' boot. This allows updating one of an A or B Operating System in boot media such as MMC. The new OS is -- 2.43.0
participants (1)
-
Simon Glass