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 <agraf@suse.de>
This commit is contained in:
parent
851bda8148
commit
51735ae0ea
|
@ -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 */
|
/* Called by board init to initialize the EFI memory map */
|
||||||
int efi_memory_init(void);
|
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 */
|
/* Convert strings from normal C strings to uEFI strings */
|
||||||
static inline void ascii2unicode(u16 *unicode, char *ascii)
|
static inline void ascii2unicode(u16 *unicode, char *ascii)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,3 +7,12 @@ config EFI_LOADER
|
||||||
on top of U-Boot. If this option is enabled, U-Boot will expose EFI
|
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
|
interfaces to a loaded EFI application, enabling it to reuse U-Boot's
|
||||||
device drivers.
|
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.
|
||||||
|
|
|
@ -76,9 +76,6 @@ static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this,
|
||||||
int blocks;
|
int blocks;
|
||||||
unsigned long n;
|
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);
|
diskobj = container_of(this, struct efi_disk_obj, ops);
|
||||||
if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index)))
|
if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index)))
|
||||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
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))
|
if (buffer_size & (blksz - 1))
|
||||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||||
|
|
||||||
if (direction == EFI_DISK_READ)
|
if (direction == EFI_DISK_READ) {
|
||||||
n = desc->block_read(desc, lba, blocks, buffer);
|
n = desc->block_read(desc, lba, blocks, buffer);
|
||||||
else
|
} else {
|
||||||
n = desc->block_write(desc, lba, blocks, buffer);
|
n = desc->block_write(desc, lba, blocks, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
/* We don't do interrupts, so check for timers cooperatively */
|
/* We don't do interrupts, so check for timers cooperatively */
|
||||||
efi_timer_check();
|
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,
|
u32 media_id, u64 lba, unsigned long buffer_size,
|
||||||
void *buffer)
|
void *buffer)
|
||||||
{
|
{
|
||||||
return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
|
void *real_buffer = buffer;
|
||||||
EFI_DISK_READ);
|
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,
|
static efi_status_t efi_disk_write_blocks(struct efi_block_io *this,
|
||||||
u32 media_id, u64 lba, unsigned long buffer_size,
|
u32 media_id, u64 lba, unsigned long buffer_size,
|
||||||
void *buffer)
|
void *buffer)
|
||||||
{
|
{
|
||||||
return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer,
|
void *real_buffer = buffer;
|
||||||
EFI_DISK_WRITE);
|
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)
|
static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
|
||||||
|
|
|
@ -27,6 +27,10 @@ struct efi_mem_list {
|
||||||
/* This list contains all memory map items */
|
/* This list contains all memory map items */
|
||||||
LIST_HEAD(efi_mem);
|
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
|
* 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_add_memory_map(runtime_start, runtime_pages,
|
||||||
EFI_RUNTIME_SERVICES_CODE, false);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue