diff mbox

[U-Boot,7/9] efi_loader: Add disk interfaces

Message ID 1450792676-109541-8-git-send-email-agraf@suse.de
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Alexander Graf Dec. 22, 2015, 1:57 p.m. UTC
A EFI applications usually want to access storage devices to load data from.

This patch adds support for EFI disk interfaces. It loops through all block
storage interfaces known to U-Boot and creates an EFI object for each existing
one. EFI applications can then through these objects call U-Boot's read and
write functions.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 include/efi_loader.h      |   1 +
 lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 228 insertions(+)
 create mode 100644 lib/efi_loader/efi_disk.c

Comments

Simon Glass Jan. 15, 2016, 1:37 a.m. UTC | #1
Hi Alexander,

On 22 December 2015 at 06:57, Alexander Graf <agraf@suse.de> wrote:
> A EFI applications usually want to access storage devices to load data from.
>
> This patch adds support for EFI disk interfaces. It loops through all block
> storage interfaces known to U-Boot and creates an EFI object for each existing
> one. EFI applications can then through these objects call U-Boot's read and
> write functions.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  include/efi_loader.h      |   1 +
>  lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 228 insertions(+)
>  create mode 100644 lib/efi_loader/efi_disk.c

Reviewed-by: Simon Glass <sjg@chromium.org>

Some nits below.

>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index af1c88f..7e821e5 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -72,6 +72,7 @@ struct efi_object {
>  };
>  extern struct list_head efi_obj_list;
>
> +int efi_disk_register(void);
>  efi_status_t efi_return_handle(void *handle,
>                 efi_guid_t *protocol, void **protocol_interface,
>                 void *agent_handle, void *controller_handle,
> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> new file mode 100644
> index 0000000..1804e3e
> --- /dev/null
> +++ b/lib/efi_loader/efi_disk.c
> @@ -0,0 +1,227 @@
> +/*
> + *  EFI application disk support
> + *
> + *  Copyright (c) 2015 Alexander Graf
> + *
> + *  This library is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This library is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + *  You should have received a copy of the GNU Lesser General Public
> + *  License along with this library; if not, write to the Free Software
> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
> + *
> + *  SPDX-License-Identifier:     LGPL-2.1+

Can we have GPL 2 / 2+? Also drop the text?

> + */
> +
> +#include <common.h>
> +#include <efi_loader.h>
> +#include <part.h>
> +#include <malloc.h>
> +#include <inttypes.h>

inttypes.h should go above part.h

> +
> +static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
> +
> +struct efi_disk_obj {

Could use comments on these members

> +       struct efi_object parent;
> +       struct efi_block_io ops;
> +       const char *ifname;
> +       int dev_index;
> +       struct efi_block_io_media media;
> +       struct efi_device_path_file_path *dp;
> +};
> +
> +static void ascii2unicode(u16 *unicode, char *ascii)
> +{
> +       while (*ascii)
> +               *(unicode++) = *(ascii++);
> +}
> +
> +static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol,
> +                       void **protocol_interface, void *agent_handle,
> +                       void *controller_handle, uint32_t attributes)
> +{
> +       struct efi_disk_obj *diskobj = handle;
> +
> +       *protocol_interface = &diskobj->ops;
> +
> +       return EFI_SUCCESS;
> +}
> +
> +static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol,
> +                       void **protocol_interface, void *agent_handle,
> +                       void *controller_handle, uint32_t attributes)
> +{
> +       struct efi_disk_obj *diskobj = handle;
> +
> +       *protocol_interface = diskobj->dp;
> +
> +       return EFI_SUCCESS;
> +}
> +
> +static efi_status_t efi_disk_reset(struct efi_block_io *this,
> +                       char extended_verification)
> +{
> +       EFI_ENTRY("%p, %x", this, extended_verification);
> +       return EFI_EXIT(EFI_DEVICE_ERROR);
> +}
> +
> +enum efi_disk_direction {
> +       EFI_DISK_READ,
> +       EFI_DISK_WRITE,
> +};
> +
> +static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
> +                       u32 media_id, u64 lba, unsigned long buffer_size,
> +                       void *buffer, enum efi_disk_direction direction)
> +{
> +       struct efi_disk_obj *diskobj;
> +       struct block_dev_desc *desc;
> +       int blksz;
> +       int blocks;
> +       unsigned long n;
> +
> +       EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba,
> +                 buffer_size, buffer);
> +
> +       diskobj = container_of(this, struct efi_disk_obj, ops);
> +       if (!(desc = get_dev(diskobj->ifname, diskobj->dev_index)))
> +               return EFI_EXIT(EFI_DEVICE_ERROR);
> +       blksz = desc->blksz;
> +       blocks = buffer_size / blksz;
> +
> +#ifdef DEBUG_EFI
> +       printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__,
> +              __LINE__, blocks, lba, blksz, direction);
> +#endif
> +
> +       /* We only support full block access */
> +       if (buffer_size & (blksz - 1))
> +               return EFI_EXIT(EFI_DEVICE_ERROR);
> +
> +       if (direction == EFI_DISK_READ)
> +               n = desc->block_read(desc->dev, lba, blocks, buffer);
> +       else
> +               n = desc->block_write(desc->dev, lba, blocks, buffer);
> +
> +       /* We don't do interrupts, so check for timers cooperatively */
> +       efi_timer_check();
> +
> +#ifdef DEBUG_EFI
> +       printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks);
> +#endif
> +       if (n != blocks)
> +               return EFI_EXIT(EFI_DEVICE_ERROR);
> +
> +       return EFI_EXIT(EFI_SUCCESS);
> +}
> +
> +static efi_status_t efi_disk_read_blocks(struct efi_block_io *this,
> +                       u32 media_id, u64 lba, unsigned long buffer_size,
> +                       void *buffer)
> +{
> +       return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
> +                                 EFI_DISK_READ);
> +}
> +
> +static efi_status_t efi_disk_write_blocks(struct efi_block_io *this,
> +                       u32 media_id, u64 lba, unsigned long buffer_size,
> +                       void *buffer)
> +{
> +       return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
> +                                 EFI_DISK_WRITE);
> +}
> +
> +static efi_status_t efi_disk_flush_blocks(struct efi_block_io *this)
> +{
> +       /* We always write synchronously */
> +       return EFI_SUCCESS;
> +}
> +
> +static const struct efi_block_io block_io_disk_template = {
> +       .reset = &efi_disk_reset,
> +       .read_blocks = &efi_disk_read_blocks,
> +       .write_blocks = &efi_disk_write_blocks,
> +       .flush_blocks = &efi_disk_flush_blocks,
> +};
> +
> +/*
> + * U-Boot doesn't have a list of all online disk devices. So when running our
> + * EFI payload, we scan through all of the potentially available ones and
> + * store them in our object pool.
> + *
> + * This gets called from do_bootefi_exec().
> + */
> +int efi_disk_register(void)
> +{
> +       const char **cur_drvr;
> +       int i;
> +       int disks = 0;
> +
> +       /* Search for all available disk devices */
> +       for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) {
> +               printf("Scanning disks on %s...\n", *cur_drvr);
> +               for (i = 0; i < 4; i++) {

What is 4 for?

> +                       block_dev_desc_t *desc;
> +                       struct efi_disk_obj *diskobj;
> +                       struct efi_device_path_file_path *dp;
> +                       int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
> +                       char devname[16];
> +
> +                       desc = get_dev(*cur_drvr, i);
> +                       if (!desc)
> +                               continue;
> +
> +                       diskobj = malloc(objlen);

Could use calloc() to avoid memset() if you like.

> +                       memset(diskobj, 0, objlen);
> +
> +                       /* Fill in object data */
> +
> +                       diskobj->parent.protocols[0].guid = &efi_block_io_guid;
> +                       diskobj->parent.protocols[0].open = efi_disk_open_block;
> +                       diskobj->parent.protocols[1].guid = &efi_guid_device_path;
> +                       diskobj->parent.protocols[1].open = efi_disk_open_dp;
> +                       diskobj->parent.handle = diskobj;
> +                       diskobj->ops = block_io_disk_template;
> +                       diskobj->ifname = *cur_drvr;
> +                       diskobj->dev_index = i;
> +
> +                       /* Fill in EFI IO Media info (for read/write callbacks) */
> +
> +                       diskobj->media.removable_media = desc->removable;
> +                       diskobj->media.media_present = 1;
> +                       diskobj->media.block_size = desc->blksz;
> +                       diskobj->media.io_align = desc->blksz;
> +                       diskobj->media.last_block = desc->lba;
> +                       diskobj->ops.media = &diskobj->media;
> +
> +                       /* Fill in device path */
> +
> +                       dp = (void*)&diskobj[1];
> +                       diskobj->dp = dp;
> +                       dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
> +                       dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
> +                       dp[0].dp.length = sizeof(*dp);
> +                       sprintf(devname, "%s%d", *cur_drvr, i);
> +                       ascii2unicode(dp[0].str, devname);
> +
> +                       dp[1].dp.type = DEVICE_PATH_TYPE_END;
> +                       dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
> +                       dp[1].dp.length = sizeof(*dp);
> +
> +                       /* Hook up to the device list */
> +
> +                       list_add_tail(&diskobj->parent.link, &efi_obj_list);
> +                       disks++;
> +               }
> +       }
> +       printf("Found %d disks\n", disks);
> +
> +       return 0;
> +}
> --
> 2.1.4
>

Regards,
Simon
Alexander Graf Jan. 15, 2016, 2:40 a.m. UTC | #2
On 15.01.16 02:37, Simon Glass wrote:
> Hi Alexander,
> 
> On 22 December 2015 at 06:57, Alexander Graf <agraf@suse.de> wrote:
>> A EFI applications usually want to access storage devices to load data from.
>>
>> This patch adds support for EFI disk interfaces. It loops through all block
>> storage interfaces known to U-Boot and creates an EFI object for each existing
>> one. EFI applications can then through these objects call U-Boot's read and
>> write functions.
>>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>> ---
>>  include/efi_loader.h      |   1 +
>>  lib/efi_loader/efi_disk.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 228 insertions(+)
>>  create mode 100644 lib/efi_loader/efi_disk.c
> 
> Reviewed-by: Simon Glass <sjg@chromium.org>
> 
> Some nits below.
> 
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index af1c88f..7e821e5 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -72,6 +72,7 @@ struct efi_object {
>>  };
>>  extern struct list_head efi_obj_list;
>>
>> +int efi_disk_register(void);
>>  efi_status_t efi_return_handle(void *handle,
>>                 efi_guid_t *protocol, void **protocol_interface,
>>                 void *agent_handle, void *controller_handle,
>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>> new file mode 100644
>> index 0000000..1804e3e
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_disk.c
>> @@ -0,0 +1,227 @@
>> +/*
>> + *  EFI application disk support
>> + *
>> + *  Copyright (c) 2015 Alexander Graf
>> + *
>> + *  This library is free software; you can redistribute it and/or
>> + *  modify it under the terms of the GNU Lesser General Public
>> + *  License as published by the Free Software Foundation; either
>> + *  version 2.1 of the License, or (at your option) any later version.
>> + *
>> + *  This library is distributed in the hope that it will be useful,
>> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + *  Lesser General Public License for more details.
>> + *
>> + *  You should have received a copy of the GNU Lesser General Public
>> + *  License along with this library; if not, write to the Free Software
>> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
>> + *
>> + *  SPDX-License-Identifier:     LGPL-2.1+
> 
> Can we have GPL 2 / 2+? Also drop the text?
> 
>> + */
>> +
>> +#include <common.h>
>> +#include <efi_loader.h>
>> +#include <part.h>
>> +#include <malloc.h>
>> +#include <inttypes.h>
> 
> inttypes.h should go above part.h
> 
>> +
>> +static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
>> +
>> +struct efi_disk_obj {
> 
> Could use comments on these members
> 
>> +       struct efi_object parent;
>> +       struct efi_block_io ops;
>> +       const char *ifname;
>> +       int dev_index;
>> +       struct efi_block_io_media media;
>> +       struct efi_device_path_file_path *dp;
>> +};
>> +
>> +static void ascii2unicode(u16 *unicode, char *ascii)
>> +{
>> +       while (*ascii)
>> +               *(unicode++) = *(ascii++);
>> +}
>> +
>> +static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol,
>> +                       void **protocol_interface, void *agent_handle,
>> +                       void *controller_handle, uint32_t attributes)
>> +{
>> +       struct efi_disk_obj *diskobj = handle;
>> +
>> +       *protocol_interface = &diskobj->ops;
>> +
>> +       return EFI_SUCCESS;
>> +}
>> +
>> +static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol,
>> +                       void **protocol_interface, void *agent_handle,
>> +                       void *controller_handle, uint32_t attributes)
>> +{
>> +       struct efi_disk_obj *diskobj = handle;
>> +
>> +       *protocol_interface = diskobj->dp;
>> +
>> +       return EFI_SUCCESS;
>> +}
>> +
>> +static efi_status_t efi_disk_reset(struct efi_block_io *this,
>> +                       char extended_verification)
>> +{
>> +       EFI_ENTRY("%p, %x", this, extended_verification);
>> +       return EFI_EXIT(EFI_DEVICE_ERROR);
>> +}
>> +
>> +enum efi_disk_direction {
>> +       EFI_DISK_READ,
>> +       EFI_DISK_WRITE,
>> +};
>> +
>> +static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
>> +                       u32 media_id, u64 lba, unsigned long buffer_size,
>> +                       void *buffer, enum efi_disk_direction direction)
>> +{
>> +       struct efi_disk_obj *diskobj;
>> +       struct block_dev_desc *desc;
>> +       int blksz;
>> +       int blocks;
>> +       unsigned long n;
>> +
>> +       EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba,
>> +                 buffer_size, buffer);
>> +
>> +       diskobj = container_of(this, struct efi_disk_obj, ops);
>> +       if (!(desc = get_dev(diskobj->ifname, diskobj->dev_index)))
>> +               return EFI_EXIT(EFI_DEVICE_ERROR);
>> +       blksz = desc->blksz;
>> +       blocks = buffer_size / blksz;
>> +
>> +#ifdef DEBUG_EFI
>> +       printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__,
>> +              __LINE__, blocks, lba, blksz, direction);
>> +#endif
>> +
>> +       /* We only support full block access */
>> +       if (buffer_size & (blksz - 1))
>> +               return EFI_EXIT(EFI_DEVICE_ERROR);
>> +
>> +       if (direction == EFI_DISK_READ)
>> +               n = desc->block_read(desc->dev, lba, blocks, buffer);
>> +       else
>> +               n = desc->block_write(desc->dev, lba, blocks, buffer);
>> +
>> +       /* We don't do interrupts, so check for timers cooperatively */
>> +       efi_timer_check();
>> +
>> +#ifdef DEBUG_EFI
>> +       printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks);
>> +#endif
>> +       if (n != blocks)
>> +               return EFI_EXIT(EFI_DEVICE_ERROR);
>> +
>> +       return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_disk_read_blocks(struct efi_block_io *this,
>> +                       u32 media_id, u64 lba, unsigned long buffer_size,
>> +                       void *buffer)
>> +{
>> +       return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
>> +                                 EFI_DISK_READ);
>> +}
>> +
>> +static efi_status_t efi_disk_write_blocks(struct efi_block_io *this,
>> +                       u32 media_id, u64 lba, unsigned long buffer_size,
>> +                       void *buffer)
>> +{
>> +       return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
>> +                                 EFI_DISK_WRITE);
>> +}
>> +
>> +static efi_status_t efi_disk_flush_blocks(struct efi_block_io *this)
>> +{
>> +       /* We always write synchronously */
>> +       return EFI_SUCCESS;
>> +}
>> +
>> +static const struct efi_block_io block_io_disk_template = {
>> +       .reset = &efi_disk_reset,
>> +       .read_blocks = &efi_disk_read_blocks,
>> +       .write_blocks = &efi_disk_write_blocks,
>> +       .flush_blocks = &efi_disk_flush_blocks,
>> +};
>> +
>> +/*
>> + * U-Boot doesn't have a list of all online disk devices. So when running our
>> + * EFI payload, we scan through all of the potentially available ones and
>> + * store them in our object pool.
>> + *
>> + * This gets called from do_bootefi_exec().
>> + */
>> +int efi_disk_register(void)
>> +{
>> +       const char **cur_drvr;
>> +       int i;
>> +       int disks = 0;
>> +
>> +       /* Search for all available disk devices */
>> +       for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) {
>> +               printf("Scanning disks on %s...\n", *cur_drvr);
>> +               for (i = 0; i < 4; i++) {
> 
> What is 4 for?

4 disks should be enough for everyone ;).

I couldn't find an easy way to limit the number of devices we have to
scan. You may have 4 MMC slots, but only slot 3 is actually populated.
How do you know how many there really are?

So we have to use some upper bound for the scan. The 4 is simply because
most devices I'm aware of that U-Boot runs on don't have much more than
4 devices per block bus attached to them. I had 128 in here first, but
then I ran into endless timeouts with MMC scans.


Thanks a bunch for the review :)

Alex
diff mbox

Patch

diff --git a/include/efi_loader.h b/include/efi_loader.h
index af1c88f..7e821e5 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -72,6 +72,7 @@  struct efi_object {
 };
 extern struct list_head efi_obj_list;
 
+int efi_disk_register(void);
 efi_status_t efi_return_handle(void *handle,
 		efi_guid_t *protocol, void **protocol_interface,
 		void *agent_handle, void *controller_handle,
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
new file mode 100644
index 0000000..1804e3e
--- /dev/null
+++ b/lib/efi_loader/efi_disk.c
@@ -0,0 +1,227 @@ 
+/*
+ *  EFI application disk support
+ *
+ *  Copyright (c) 2015 Alexander Graf
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ *  SPDX-License-Identifier:     LGPL-2.1+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <part.h>
+#include <malloc.h>
+#include <inttypes.h>
+
+static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+
+struct efi_disk_obj {
+	struct efi_object parent;
+	struct efi_block_io ops;
+	const char *ifname;
+	int dev_index;
+	struct efi_block_io_media media;
+	struct efi_device_path_file_path *dp;
+};
+
+static void ascii2unicode(u16 *unicode, char *ascii)
+{
+	while (*ascii)
+		*(unicode++) = *(ascii++);
+}
+
+static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol,
+			void **protocol_interface, void *agent_handle,
+			void *controller_handle, uint32_t attributes)
+{
+	struct efi_disk_obj *diskobj = handle;
+
+	*protocol_interface = &diskobj->ops;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol,
+			void **protocol_interface, void *agent_handle,
+			void *controller_handle, uint32_t attributes)
+{
+	struct efi_disk_obj *diskobj = handle;
+
+	*protocol_interface = diskobj->dp;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_disk_reset(struct efi_block_io *this,
+			char extended_verification)
+{
+	EFI_ENTRY("%p, %x", this, extended_verification);
+	return EFI_EXIT(EFI_DEVICE_ERROR);
+}
+
+enum efi_disk_direction {
+	EFI_DISK_READ,
+	EFI_DISK_WRITE,
+};
+
+static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
+			u32 media_id, u64 lba, unsigned long buffer_size,
+			void *buffer, enum efi_disk_direction direction)
+{
+	struct efi_disk_obj *diskobj;
+	struct block_dev_desc *desc;
+	int blksz;
+	int blocks;
+	unsigned long n;
+
+	EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba,
+		  buffer_size, buffer);
+
+	diskobj = container_of(this, struct efi_disk_obj, ops);
+	if (!(desc = get_dev(diskobj->ifname, diskobj->dev_index)))
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	blksz = desc->blksz;
+	blocks = buffer_size / blksz;
+
+#ifdef DEBUG_EFI
+	printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__,
+	       __LINE__, blocks, lba, blksz, direction);
+#endif
+
+	/* We only support full block access */
+	if (buffer_size & (blksz - 1))
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+
+	if (direction == EFI_DISK_READ)
+		n = desc->block_read(desc->dev, lba, blocks, buffer);
+	else
+		n = desc->block_write(desc->dev, lba, blocks, buffer);
+
+	/* We don't do interrupts, so check for timers cooperatively */
+	efi_timer_check();
+
+#ifdef DEBUG_EFI
+	printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks);
+#endif
+	if (n != blocks)
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t efi_disk_read_blocks(struct efi_block_io *this,
+			u32 media_id, u64 lba, unsigned long buffer_size,
+			void *buffer)
+{
+	return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
+				  EFI_DISK_READ);
+}
+
+static efi_status_t efi_disk_write_blocks(struct efi_block_io *this,
+			u32 media_id, u64 lba, unsigned long buffer_size,
+			void *buffer)
+{
+	return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
+				  EFI_DISK_WRITE);
+}
+
+static efi_status_t efi_disk_flush_blocks(struct efi_block_io *this)
+{
+	/* We always write synchronously */
+	return EFI_SUCCESS;
+}
+
+static const struct efi_block_io block_io_disk_template = {
+	.reset = &efi_disk_reset,
+	.read_blocks = &efi_disk_read_blocks,
+	.write_blocks = &efi_disk_write_blocks,
+	.flush_blocks = &efi_disk_flush_blocks,
+};
+
+/*
+ * U-Boot doesn't have a list of all online disk devices. So when running our
+ * EFI payload, we scan through all of the potentially available ones and
+ * store them in our object pool.
+ *
+ * This gets called from do_bootefi_exec().
+ */
+int efi_disk_register(void)
+{
+	const char **cur_drvr;
+	int i;
+	int disks = 0;
+
+	/* Search for all available disk devices */
+	for (cur_drvr = available_block_drvrs; *cur_drvr; cur_drvr++) {
+		printf("Scanning disks on %s...\n", *cur_drvr);
+		for (i = 0; i < 4; i++) {
+			block_dev_desc_t *desc;
+			struct efi_disk_obj *diskobj;
+			struct efi_device_path_file_path *dp;
+			int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
+			char devname[16];
+
+			desc = get_dev(*cur_drvr, i);
+			if (!desc)
+				continue;
+
+			diskobj = malloc(objlen);
+			memset(diskobj, 0, objlen);
+
+			/* Fill in object data */
+
+			diskobj->parent.protocols[0].guid = &efi_block_io_guid;
+			diskobj->parent.protocols[0].open = efi_disk_open_block;
+			diskobj->parent.protocols[1].guid = &efi_guid_device_path;
+			diskobj->parent.protocols[1].open = efi_disk_open_dp;
+			diskobj->parent.handle = diskobj;
+			diskobj->ops = block_io_disk_template;
+			diskobj->ifname = *cur_drvr;
+			diskobj->dev_index = i;
+
+			/* Fill in EFI IO Media info (for read/write callbacks) */
+
+			diskobj->media.removable_media = desc->removable;
+			diskobj->media.media_present = 1;
+			diskobj->media.block_size = desc->blksz;
+			diskobj->media.io_align = desc->blksz;
+			diskobj->media.last_block = desc->lba;
+			diskobj->ops.media = &diskobj->media;
+
+			/* Fill in device path */
+
+			dp = (void*)&diskobj[1];
+			diskobj->dp = dp;
+			dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+			dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+			dp[0].dp.length = sizeof(*dp);
+			sprintf(devname, "%s%d", *cur_drvr, i);
+			ascii2unicode(dp[0].str, devname);
+
+			dp[1].dp.type = DEVICE_PATH_TYPE_END;
+			dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
+			dp[1].dp.length = sizeof(*dp);
+
+			/* Hook up to the device list */
+
+			list_add_tail(&diskobj->parent.link, &efi_obj_list);
+			disks++;
+		}
+	}
+	printf("Found %d disks\n", disks);
+
+	return 0;
+}