[U-Boot] [PATCH 09/14] efi_loader: Implement memory allocation and map

Simon Glass
2016-01-31T15:23:58Z

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> Due to popular request, this is a separate patch implementing all of the memory
> allocation and memory mapping bits.
>
> We assume we always have a linear RAM map. At TOM U-Boot resides. Inside of
> U-Boot there is the runtime region that we need to explicitly expose via the
> EFI memory map. Below U-Boot, we reserve 128MB of RAM for LOADER_DATA.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
> lib/efi_loader/efi_boottime.c | 99 ++++++++++++++++++++++++++++++++++++++-
> lib/efi_loader/efi_image_loader.c | 28 ++++++++++-
> 2 files changed, 123 insertions(+), 4 deletions(-)
>
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 45217ef..ff3f969 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -98,6 +98,8 @@ static void efi_restore_tpl(unsigned long old_tpl)
> static void *efi_alloc(uint64_t len, int memory_type)
> {
> switch (memory_type) {
> + case EFI_LOADER_DATA:
> + return efi_loader_alloc(len);
> default:
> return malloc(len);
> }
> @@ -143,16 +145,109 @@ static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
> return EFI_EXIT(EFI_SUCCESS);
> }
>
> -/* Will be implemented in a later patch */
> +/*
> + * Returns the EFI memory map. In our case, this looks pretty simple:
> + *
> + * ____________________________ TOM
> + * | |
> + * | Second half of U-Boot |

What does 'second half' mean?

> + * |____________________________| &__efi_runtime_stop
> + * | |
> + * | EFI Runtime Services |
> + * |____________________________| &__efi_runtime_start
> + * | |
> + * | First half of U-Boot |
> + * |____________________________| start of EFI loader allocation space
> + * | |
> + * | Free RAM |
> + * |____________________________| CONFIG_SYS_SDRAM_BASE
> + *
> + * All pointers are extended to live on a 4k boundary. After exiting the boot
> + * services, only the EFI Runtime Services chunk of memory stays alive.
> + */
> static efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
> struct efi_mem_desc *memory_map,
> unsigned long *map_key,
> unsigned long *descriptor_size,
> uint32_t *descriptor_version)
> {
> + struct efi_mem_desc efi_memory_map[] = {
> + {
> + /* RAM before U-Boot */
> + .type = EFI_CONVENTIONAL_MEMORY,
> + .attribute = 1 << EFI_MEMORY_WB_SHIFT,
> + },
> + {
> + /* First half of U-Boot */
> + .type = EFI_LOADER_DATA,
> + .attribute = 1 << EFI_MEMORY_WB_SHIFT,
> + },
> + {
> + /* EFI Runtime Services */
> + .type = EFI_RUNTIME_SERVICES_CODE,
> + .attribute = (1 << EFI_MEMORY_WB_SHIFT) |
> + (1ULL << EFI_MEMORY_RUNTIME_SHIFT),
> + },
> + {
> + /* Second half of U-Boot */
> + .type = EFI_LOADER_DATA,
> + .attribute = 1 << EFI_MEMORY_WB_SHIFT,
> + },
> + };
> + ulong runtime_start, runtime_end, runtime_len_pages, runtime_len;
> +
> EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key,
> descriptor_size, descriptor_version);
> - return EFI_EXIT(EFI_UNSUPPORTED);
> +
> + runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
> + runtime_end = ((ulong)&__efi_runtime_stop + 0xfff) & ~0xfffULL;
> + runtime_len_pages = (runtime_end - runtime_start) >> 12;
> + runtime_len = runtime_len_pages << 12;
> +
> + /* Fill in where normal RAM is (up to U-Boot's top of stack) */
> + efi_memory_map[0].num_pages = gd->start_addr_sp >> 12;
> +#ifdef CONFIG_SYS_SDRAM_BASE

If not defined, what happens?

> + efi_memory_map[0].physical_start = CONFIG_SYS_SDRAM_BASE;
> + efi_memory_map[0].virtual_start = CONFIG_SYS_SDRAM_BASE;
> + efi_memory_map[0].num_pages -= CONFIG_SYS_SDRAM_BASE >> 12;
> +#endif
> +
> + /* Give us some space for the stack */
> + efi_memory_map[0].num_pages -= (16 * 1024 * 1024) >> 12;
> +
> + /* Reserve the EFI loader pool */
> + efi_memory_map[0].num_pages -= EFI_LOADER_POOL_SIZE >> 12;
> +
> + /* Cut out the runtime services */
> + efi_memory_map[2].physical_start = runtime_start;
> + efi_memory_map[2].virtual_start = efi_memory_map[2].physical_start;
> + efi_memory_map[2].num_pages = runtime_len_pages;
> +
> + /* Allocate the rest to U-Boot */
> + efi_memory_map[1].physical_start = efi_memory_map[0].physical_start +
> + (efi_memory_map[0].num_pages << 12);
> + efi_memory_map[1].virtual_start = efi_memory_map[1].physical_start;
> + efi_memory_map[1].num_pages = (runtime_start -
> + efi_memory_map[1].physical_start) >> 12;
> +
> + efi_memory_map[3].physical_start = runtime_start + runtime_len;
> + efi_memory_map[3].virtual_start = efi_memory_map[3].physical_start;
> + efi_memory_map[3].num_pages = (gd->ram_top -
> + efi_memory_map[3].physical_start) >> 12;
> +
> + *memory_map_size = sizeof(efi_memory_map);
> +
> + if (descriptor_size)
> + *descriptor_size = sizeof(struct efi_mem_desc);
> +
> + if (*memory_map_size < sizeof(efi_memory_map)) {
> + return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
> + }
> +
> + if (memory_map)
> + memcpy(memory_map, efi_memory_map, sizeof(efi_memory_map));
> +
> + return EFI_EXIT(EFI_SUCCESS);
> }
>
> static efi_status_t efi_allocate_pool(int pool_type, unsigned long size, void **buffer)
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index a7788bf..67c4b06 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -29,10 +29,34 @@ efi_status_t efi_return_handle(void *handle, efi_guid_t *protocol,
> return EFI_SUCCESS;
> }
>
> -/* Will be implemented in a later patch */
> +/*
> + * EFI payloads potentially want to load pretty big images into memory,
> + * so our small malloc region isn't enough for them. However, they usually
> + * don't need a smart allocator either.
> + *
> + * So instead give them a really dumb one. We just reserve EFI_LOADER_POOL_SIZE
> + * bytes from 16MB below the stack start to give the stack some space.
> + * Then every allocation gets a 4k aligned chunk from it. We never free.
> + */
> void *efi_loader_alloc(uint64_t len)
> {
> - return NULL;
> + static unsigned long loader_pool;
> + void *r;
> +
> + if (!loader_pool) {
> + loader_pool = ((gd->start_addr_sp >> 12) << 12) -
> + (16 * MB) - EFI_LOADER_POOL_SIZE;

I think it would be better to reserve this in board_f() with a
reserve...() function. Perhaps store the address in global_data.

> + }
> +
> + len = ROUND_UP(len, 4096);
> + /* Out of memory */
> + if ((loader_pool + len) >= (gd->relocaddr - TOTAL_MALLOC_LEN))

debug() here?

> + return NULL;
> +
> + r = (void *)loader_pool;
> + loader_pool += len;
> +
> + return r;
> }
>
> /*
> --
> 2.1.4

Regards,
Simon