From 51735ae0ea2f5d67c0f7cc4d1f938f36955e1fe7 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 11 May 2016 18:25:48 +0200 Subject: efi_loader: Add bounce buffer support 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 --- lib/efi_loader/Kconfig | 9 ++++++ lib/efi_loader/efi_disk.c | 70 +++++++++++++++++++++++++++++++++++++++------ lib/efi_loader/efi_memory.c | 16 +++++++++++ 3 files changed, 86 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 14c99ec9cf..37a0dd60a5 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -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. diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 075fd34014..c7d4515321 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -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) diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 71a3d19269..9e669f5102 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -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; } -- cgit v1.2.1