efi_loader: Add bounce buffer support
authorAlexander Graf <agraf@suse.de>
Wed, 11 May 2016 16:25:48 +0000 (18:25 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 27 May 2016 19:39:48 +0000 (15:39 -0400)
Some hardware that is supported by U-Boot can not handle DMA above 32bits.
For these systems, we need to come up with a way to expose the disk interface
in a safe way.

This patch implements EFI specific bounce buffers. For non-EFI cases, this
apparently was no issue so far, since we can just define our environment
variables conveniently.

Signed-off-by: Alexander Graf <agraf@suse.de>
include/efi_loader.h
lib/efi_loader/Kconfig
lib/efi_loader/efi_disk.c
lib/efi_loader/efi_memory.c

index 8005454af36dda980a7626dacf904c5a65345b45..b1ca4ba26b668ed70259e32633d2b0a89ff62c29 100644 (file)
@@ -139,6 +139,11 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
 /* Called by board init to initialize the EFI memory map */
 int efi_memory_init(void);
 
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+extern void *efi_bounce_buffer;
+#define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
+#endif
+
 /* Convert strings from normal C strings to uEFI strings */
 static inline void ascii2unicode(u16 *unicode, char *ascii)
 {
index 14c99ec9cf959d72b0f7ca6ef405facc65f0de9a..37a0dd60a5e08747d16316deac34cbc6bca7b033 100644 (file)
@@ -7,3 +7,12 @@ config EFI_LOADER
          on top of U-Boot. If this option is enabled, U-Boot will expose EFI
          interfaces to a loaded EFI application, enabling it to reuse U-Boot's
          device drivers.
+
+config EFI_LOADER_BOUNCE_BUFFER
+       bool "EFI Applications use bounce buffers for DMA operations"
+       depends on EFI_LOADER && ARM64
+       default n
+       help
+         Some hardware does not support DMA to full 64bit addresses. For this
+         hardware we can create a bounce buffer so that payloads don't have to
+         worry about platform details.
index 075fd3401450d41320353d2db42ad9fdb03ccc59..c7d4515321ea431b3ccc5184d76da6d3494f0ba8 100644 (file)
@@ -76,9 +76,6 @@ static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this,
        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 = blk_get_dev(diskobj->ifname, diskobj->dev_index)))
                return EFI_EXIT(EFI_DEVICE_ERROR);
@@ -95,10 +92,11 @@ static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this,
        if (buffer_size & (blksz - 1))
                return EFI_EXIT(EFI_DEVICE_ERROR);
 
-       if (direction == EFI_DISK_READ)
+       if (direction == EFI_DISK_READ) {
                n = desc->block_read(desc, lba, blocks, buffer);
-       else
+       } else {
                n = desc->block_write(desc, lba, blocks, buffer);
+       }
 
        /* We don't do interrupts, so check for timers cooperatively */
        efi_timer_check();
@@ -116,16 +114,70 @@ 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);
+       void *real_buffer = buffer;
+       efi_status_t r;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+       if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+               r = efi_disk_read_blocks(this, media_id, lba,
+                       EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+               if (r != EFI_SUCCESS)
+                       return r;
+               return efi_disk_read_blocks(this, media_id, lba +
+                       EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+                       buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+                       buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+       }
+
+       real_buffer = efi_bounce_buffer;
+#endif
+
+       EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba,
+                 buffer_size, buffer);
+
+       r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+                              EFI_DISK_READ);
+
+       /* Copy from bounce buffer to real buffer if necessary */
+       if ((r == EFI_SUCCESS) && (real_buffer != buffer))
+               memcpy(buffer, real_buffer, buffer_size);
+
+       return EFI_EXIT(r);
 }
 
 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);
+       void *real_buffer = buffer;
+       efi_status_t r;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+       if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+               r = efi_disk_write_blocks(this, media_id, lba,
+                       EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+               if (r != EFI_SUCCESS)
+                       return r;
+               return efi_disk_write_blocks(this, media_id, lba +
+                       EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+                       buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+                       buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+       }
+
+       real_buffer = efi_bounce_buffer;
+#endif
+
+       EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba,
+                 buffer_size, buffer);
+
+       /* Populate bounce buffer if necessary */
+       if (real_buffer != buffer)
+               memcpy(real_buffer, buffer, buffer_size);
+
+       r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+                              EFI_DISK_WRITE);
+
+       return EFI_EXIT(r);
 }
 
 static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
index 71a3d192696cbb51cc63c7400dc80add2d93f88c..9e669f510266a653e5c81f3b9df9e85486e37040 100644 (file)
@@ -27,6 +27,10 @@ struct efi_mem_list {
 /* This list contains all memory map items */
 LIST_HEAD(efi_mem);
 
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+void *efi_bounce_buffer;
+#endif
+
 /*
  * Sorts the memory list from highest address to lowest address
  *
@@ -349,5 +353,17 @@ int efi_memory_init(void)
        efi_add_memory_map(runtime_start, runtime_pages,
                           EFI_RUNTIME_SERVICES_CODE, false);
 
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+       /* Request a 32bit 64MB bounce buffer region */
+       uint64_t efi_bounce_buffer_addr = 0xffffffff;
+
+       if (efi_allocate_pages(1, EFI_LOADER_DATA,
+                              (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
+                              &efi_bounce_buffer_addr) != EFI_SUCCESS)
+               return -1;
+
+       efi_bounce_buffer = (void*)(uintptr_t)efi_bounce_buffer_addr;
+#endif
+
        return 0;
 }