Commit dadb57ab authored by Jeffrey Hugo's avatar Jeffrey Hugo Committed by Matt Fleming

efi/libstub: Allocate headspace in efi_get_memory_map()

efi_get_memory_map() allocates a buffer to store the memory map that it
retrieves.  This buffer may need to be reused by the client after
ExitBootServices() is called, at which point allocations are not longer
permitted.  To support this usecase, provide the allocated buffer size back
to the client, and allocate some additional headroom to account for any
reasonable growth in the map that is likely to happen between the call to
efi_get_memory_map() and the client reusing the buffer.
Signed-off-by: default avatarJeffrey Hugo <jhugo@codeaurora.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarMatt Fleming <matt@codeblueprint.co.uk>
parent 4af9ed57
...@@ -1008,7 +1008,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, ...@@ -1008,7 +1008,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
void *handle, bool is64) void *handle, bool is64)
{ {
struct efi_info *efi = &boot_params->efi_info; struct efi_info *efi = &boot_params->efi_info;
unsigned long map_sz, key, desc_size; unsigned long map_sz, key, desc_size, buff_size;
efi_memory_desc_t *mem_map; efi_memory_desc_t *mem_map;
struct setup_data *e820ext; struct setup_data *e820ext;
const char *signature; const char *signature;
...@@ -1019,14 +1019,20 @@ static efi_status_t exit_boot(struct boot_params *boot_params, ...@@ -1019,14 +1019,20 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
bool called_exit = false; bool called_exit = false;
u8 nr_entries; u8 nr_entries;
int i; int i;
struct efi_boot_memmap map;
nr_desc = 0;
e820ext = NULL; nr_desc = 0;
e820ext_size = 0; e820ext = NULL;
e820ext_size = 0;
map.map = &mem_map;
map.map_size = &map_sz;
map.desc_size = &desc_size;
map.desc_ver = &desc_version;
map.key_ptr = &key;
map.buff_size = &buff_size;
get_map: get_map:
status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, status = efi_get_memory_map(sys_table, &map);
&desc_version, &key);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return status; return status;
......
...@@ -41,6 +41,8 @@ static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; ...@@ -41,6 +41,8 @@ static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE;
#define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE
#endif #endif
#define EFI_MMAP_NR_SLACK_SLOTS 8
struct file_info { struct file_info {
efi_file_handle_t *handle; efi_file_handle_t *handle;
u64 size; u64 size;
...@@ -63,49 +65,62 @@ void efi_printk(efi_system_table_t *sys_table_arg, char *str) ...@@ -63,49 +65,62 @@ void efi_printk(efi_system_table_t *sys_table_arg, char *str)
} }
} }
static inline bool mmap_has_headroom(unsigned long buff_size,
unsigned long map_size,
unsigned long desc_size)
{
unsigned long slack = buff_size - map_size;
return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS;
}
efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
efi_memory_desc_t **map, struct efi_boot_memmap *map)
unsigned long *map_size,
unsigned long *desc_size,
u32 *desc_ver,
unsigned long *key_ptr)
{ {
efi_memory_desc_t *m = NULL; efi_memory_desc_t *m = NULL;
efi_status_t status; efi_status_t status;
unsigned long key; unsigned long key;
u32 desc_version; u32 desc_version;
*map_size = sizeof(*m) * 32; *map->desc_size = sizeof(*m);
*map->map_size = *map->desc_size * 32;
*map->buff_size = *map->map_size;
again: again:
/*
* Add an additional efi_memory_desc_t because we're doing an
* allocation which may be in a new descriptor region.
*/
*map_size += sizeof(*m);
status = efi_call_early(allocate_pool, EFI_LOADER_DATA, status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
*map_size, (void **)&m); *map->map_size, (void **)&m);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto fail; goto fail;
*desc_size = 0; *map->desc_size = 0;
key = 0; key = 0;
status = efi_call_early(get_memory_map, map_size, m, status = efi_call_early(get_memory_map, map->map_size, m,
&key, desc_size, &desc_version); &key, map->desc_size, &desc_version);
if (status == EFI_BUFFER_TOO_SMALL) { if (status == EFI_BUFFER_TOO_SMALL ||
!mmap_has_headroom(*map->buff_size, *map->map_size,
*map->desc_size)) {
efi_call_early(free_pool, m); efi_call_early(free_pool, m);
/*
* Make sure there is some entries of headroom so that the
* buffer can be reused for a new map after allocations are
* no longer permitted. Its unlikely that the map will grow to
* exceed this headroom once we are ready to trigger
* ExitBootServices()
*/
*map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS;
*map->buff_size = *map->map_size;
goto again; goto again;
} }
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
efi_call_early(free_pool, m); efi_call_early(free_pool, m);
if (key_ptr && status == EFI_SUCCESS) if (map->key_ptr && status == EFI_SUCCESS)
*key_ptr = key; *map->key_ptr = key;
if (desc_ver && status == EFI_SUCCESS) if (map->desc_ver && status == EFI_SUCCESS)
*desc_ver = desc_version; *map->desc_ver = desc_version;
fail: fail:
*map = m; *map->map = m;
return status; return status;
} }
...@@ -113,13 +128,20 @@ efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, ...@@ -113,13 +128,20 @@ efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
unsigned long get_dram_base(efi_system_table_t *sys_table_arg) unsigned long get_dram_base(efi_system_table_t *sys_table_arg)
{ {
efi_status_t status; efi_status_t status;
unsigned long map_size; unsigned long map_size, buff_size;
unsigned long membase = EFI_ERROR; unsigned long membase = EFI_ERROR;
struct efi_memory_map map; struct efi_memory_map map;
efi_memory_desc_t *md; efi_memory_desc_t *md;
struct efi_boot_memmap boot_map;
status = efi_get_memory_map(sys_table_arg, (efi_memory_desc_t **)&map.map, boot_map.map = (efi_memory_desc_t **)&map.map;
&map_size, &map.desc_size, NULL, NULL); boot_map.map_size = &map_size;
boot_map.desc_size = &map.desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(sys_table_arg, &boot_map);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return membase; return membase;
...@@ -144,15 +166,22 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, ...@@ -144,15 +166,22 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
unsigned long size, unsigned long align, unsigned long size, unsigned long align,
unsigned long *addr, unsigned long max) unsigned long *addr, unsigned long max)
{ {
unsigned long map_size, desc_size; unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *map; efi_memory_desc_t *map;
efi_status_t status; efi_status_t status;
unsigned long nr_pages; unsigned long nr_pages;
u64 max_addr = 0; u64 max_addr = 0;
int i; int i;
struct efi_boot_memmap boot_map;
boot_map.map = &map;
boot_map.map_size = &map_size;
boot_map.desc_size = &desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, status = efi_get_memory_map(sys_table_arg, &boot_map);
NULL, NULL);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto fail; goto fail;
...@@ -230,14 +259,21 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, ...@@ -230,14 +259,21 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
unsigned long size, unsigned long align, unsigned long size, unsigned long align,
unsigned long *addr) unsigned long *addr)
{ {
unsigned long map_size, desc_size; unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *map; efi_memory_desc_t *map;
efi_status_t status; efi_status_t status;
unsigned long nr_pages; unsigned long nr_pages;
int i; int i;
struct efi_boot_memmap boot_map;
boot_map.map = &map;
boot_map.map_size = &map_size;
boot_map.desc_size = &desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, status = efi_get_memory_map(sys_table_arg, &boot_map);
NULL, NULL);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto fail; goto fail;
......
...@@ -175,13 +175,21 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -175,13 +175,21 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
unsigned long fdt_addr, unsigned long fdt_addr,
unsigned long fdt_size) unsigned long fdt_size)
{ {
unsigned long map_size, desc_size; unsigned long map_size, desc_size, buff_size;
u32 desc_ver; u32 desc_ver;
unsigned long mmap_key; unsigned long mmap_key;
efi_memory_desc_t *memory_map, *runtime_map; efi_memory_desc_t *memory_map, *runtime_map;
unsigned long new_fdt_size; unsigned long new_fdt_size;
efi_status_t status; efi_status_t status;
int runtime_entry_count = 0; int runtime_entry_count = 0;
struct efi_boot_memmap map;
map.map = &runtime_map;
map.map_size = &map_size;
map.desc_size = &desc_size;
map.desc_ver = &desc_ver;
map.key_ptr = &mmap_key;
map.buff_size = &buff_size;
/* /*
* Get a copy of the current memory map that we will use to prepare * Get a copy of the current memory map that we will use to prepare
...@@ -189,8 +197,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -189,8 +197,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
* subsequent allocations adding entries, since they could not affect * subsequent allocations adding entries, since they could not affect
* the number of EFI_MEMORY_RUNTIME regions. * the number of EFI_MEMORY_RUNTIME regions.
*/ */
status = efi_get_memory_map(sys_table, &runtime_map, &map_size, status = efi_get_memory_map(sys_table, &map);
&desc_size, &desc_ver, &mmap_key);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n"); pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n");
return status; return status;
...@@ -199,6 +206,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -199,6 +206,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
pr_efi(sys_table, pr_efi(sys_table,
"Exiting boot services and installing virtual address map...\n"); "Exiting boot services and installing virtual address map...\n");
map.map = &memory_map;
/* /*
* Estimate size of new FDT, and allocate memory for it. We * Estimate size of new FDT, and allocate memory for it. We
* will allocate a bigger buffer if this ends up being too * will allocate a bigger buffer if this ends up being too
...@@ -218,8 +226,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -218,8 +226,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
* we can get the memory map key needed for * we can get the memory map key needed for
* exit_boot_services(). * exit_boot_services().
*/ */
status = efi_get_memory_map(sys_table, &memory_map, &map_size, status = efi_get_memory_map(sys_table, &map);
&desc_size, &desc_ver, &mmap_key);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto fail_free_new_fdt; goto fail_free_new_fdt;
......
...@@ -73,12 +73,20 @@ efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg, ...@@ -73,12 +73,20 @@ efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
unsigned long random_seed) unsigned long random_seed)
{ {
unsigned long map_size, desc_size, total_slots = 0, target_slot; unsigned long map_size, desc_size, total_slots = 0, target_slot;
unsigned long buff_size;
efi_status_t status; efi_status_t status;
efi_memory_desc_t *memory_map; efi_memory_desc_t *memory_map;
int map_offset; int map_offset;
struct efi_boot_memmap map;
status = efi_get_memory_map(sys_table_arg, &memory_map, &map_size, map.map = &memory_map;
&desc_size, NULL, NULL); map.map_size = &map_size;
map.desc_size = &desc_size;
map.desc_ver = NULL;
map.key_ptr = NULL;
map.buff_size = &buff_size;
status = efi_get_memory_map(sys_table_arg, &map);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return status; return status;
......
...@@ -118,6 +118,15 @@ typedef struct { ...@@ -118,6 +118,15 @@ typedef struct {
u32 imagesize; u32 imagesize;
} efi_capsule_header_t; } efi_capsule_header_t;
struct efi_boot_memmap {
efi_memory_desc_t **map;
unsigned long *map_size;
unsigned long *desc_size;
u32 *desc_ver;
unsigned long *key_ptr;
unsigned long *buff_size;
};
/* /*
* EFI capsule flags * EFI capsule flags
*/ */
...@@ -1371,11 +1380,7 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, ...@@ -1371,11 +1380,7 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
efi_loaded_image_t *image, int *cmd_line_len); efi_loaded_image_t *image, int *cmd_line_len);
efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
efi_memory_desc_t **map, struct efi_boot_memmap *map);
unsigned long *map_size,
unsigned long *desc_size,
u32 *desc_ver,
unsigned long *key_ptr);
efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
unsigned long size, unsigned long align, unsigned long size, unsigned long align,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment