| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Block driver for EFI devices |
| * This supports a media driver of UCLASS_EFI with a child UCLASS_BLK |
| * It allows block-level access to EFI devices made available via EFI boot |
| * services |
| * |
| * Copyright 2021 Google LLC |
| */ |
| |
| #include <common.h> |
| #include <blk.h> |
| #include <dm.h> |
| #include <efi.h> |
| #include <efi_api.h> |
| |
| struct efi_block_plat { |
| struct efi_block_io *blkio; |
| }; |
| |
| /** |
| * Read from block device |
| * |
| * @dev: device |
| * @blknr: first block to be read |
| * @blkcnt: number of blocks to read |
| * @buffer: output buffer |
| * Return: number of blocks transferred |
| */ |
| static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, |
| void *buffer) |
| { |
| struct efi_block_plat *plat = dev_get_plat(dev); |
| struct efi_block_io *io = plat->blkio; |
| efi_status_t ret; |
| |
| log_debug("read buf=%p, block=%lx, count=%lx: ", buffer, (ulong)blknr, |
| (ulong)blkcnt); |
| ret = io->read_blocks(io, io->media->media_id, blknr, |
| blkcnt * io->media->block_size, buffer); |
| log_debug("ret=%lx (dec %ld)\n", ret & ~EFI_ERROR_MASK, |
| ret & ~EFI_ERROR_MASK); |
| if (ret) |
| return 0; |
| |
| return blkcnt; |
| } |
| |
| /** |
| * Write to block device |
| * |
| * @dev: device |
| * @blknr: first block to be write |
| * @blkcnt: number of blocks to write |
| * @buffer: input buffer |
| * Return: number of blocks transferred |
| */ |
| static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, |
| const void *buffer) |
| { |
| struct efi_block_plat *plat = dev_get_plat(dev); |
| struct efi_block_io *io = plat->blkio; |
| efi_status_t ret; |
| |
| log_debug("write buf=%p, block=%lx, count=%lx: ", buffer, (ulong)blknr, |
| (ulong)blkcnt); |
| ret = io->write_blocks(io, io->media->media_id, blknr, |
| blkcnt * io->media->block_size, (void *)buffer); |
| log_debug("ret=%lx (dec %ld)\n", ret & ~EFI_ERROR_MASK, |
| ret & ~EFI_ERROR_MASK); |
| if (ret) |
| return 0; |
| |
| return blkcnt; |
| } |
| |
| /* Block device driver operators */ |
| static const struct blk_ops efi_blk_ops = { |
| .read = efi_bl_read, |
| .write = efi_bl_write, |
| }; |
| |
| U_BOOT_DRIVER(efi_block) = { |
| .name = "efi_block", |
| .id = UCLASS_BLK, |
| .ops = &efi_blk_ops, |
| .plat_auto = sizeof(struct efi_block_plat), |
| }; |
| |
| static int efi_media_bind(struct udevice *dev) |
| { |
| struct efi_media_plat *plat = dev_get_plat(dev); |
| struct efi_block_plat *blk_plat; |
| struct udevice *blk; |
| int ret; |
| |
| ret = blk_create_devicef(dev, "efi_block", "blk", IF_TYPE_EFI_MEDIA, |
| dev_seq(dev), plat->blkio->media->block_size, |
| plat->blkio->media->last_block, &blk); |
| if (ret) { |
| debug("Cannot create block device\n"); |
| return ret; |
| } |
| blk_plat = dev_get_plat(blk); |
| blk_plat->blkio = plat->blkio; |
| |
| return 0; |
| } |
| |
| U_BOOT_DRIVER(efi_media) = { |
| .name = "efi_media", |
| .id = UCLASS_EFI_MEDIA, |
| .bind = efi_media_bind, |
| .plat_auto = sizeof(struct efi_media_plat), |
| }; |