Skip to content

Commit

Permalink
efi: libstub: simplify efi_get_memory_map() and struct efi_boot_memmap
Browse files Browse the repository at this point in the history
Currently, struct efi_boot_memmap is a struct that is passed around
between callers of efi_get_memory_map() and the users of the resulting
data, and which carries pointers to various variables whose values are
provided by the EFI GetMemoryMap() boot service.

This is overly complex, and it is much easier to carry these values in
the struct itself. So turn the struct into one that carries these data
items directly, including a flex array for the variable number of EFI
memory descriptors that the boot service may return.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
  • Loading branch information
Ard Biesheuvel committed Sep 26, 2022
1 parent f80d260 commit eab3126
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 157 deletions.
17 changes: 4 additions & 13 deletions drivers/firmware/efi/libstub/arm64-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,17 @@ efi_status_t check_platform_features(void)
*/
static bool check_image_region(u64 base, u64 size)
{
unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *memory_map;
struct efi_boot_memmap map;
struct efi_boot_memmap *map;
efi_status_t status;
bool ret = false;
int map_offset;

map.map = &memory_map;
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(&map);
if (status != EFI_SUCCESS)
return false;

for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
efi_memory_desc_t *md = (void *)memory_map + map_offset;
for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {
efi_memory_desc_t *md = (void *)map->map + map_offset;
u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE;

/*
Expand All @@ -74,7 +65,7 @@ static bool check_image_region(u64 base, u64 size)
}
}

efi_bs_call(free_pool, memory_map);
efi_bs_call(free_pool, map);

return ret;
}
Expand Down
26 changes: 12 additions & 14 deletions drivers/firmware/efi/libstub/efi-stub-helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,6 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
/**
* efi_exit_boot_services() - Exit boot services
* @handle: handle of the exiting image
* @map: pointer to receive the memory map
* @priv: argument to be passed to @priv_func
* @priv_func: function to process the memory map before exiting boot services
*
Expand All @@ -432,14 +431,13 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
*
* Return: status code
*/
efi_status_t efi_exit_boot_services(void *handle,
struct efi_boot_memmap *map,
void *priv,
efi_status_t efi_exit_boot_services(void *handle, void *priv,
efi_exit_boot_map_processing priv_func)
{
struct efi_boot_memmap *map;
efi_status_t status;

status = efi_get_memory_map(map);
status = efi_get_memory_map(&map);

if (status != EFI_SUCCESS)
goto fail;
Expand All @@ -451,7 +449,7 @@ efi_status_t efi_exit_boot_services(void *handle,
if (efi_disable_pci_dma)
efi_pci_disable_bridge_busmaster();

status = efi_bs_call(exit_boot_services, handle, *map->key_ptr);
status = efi_bs_call(exit_boot_services, handle, map->map_key);

if (status == EFI_INVALID_PARAMETER) {
/*
Expand All @@ -467,13 +465,13 @@ efi_status_t efi_exit_boot_services(void *handle,
* buffer should account for any changes in the map so the call
* to get_memory_map() is expected to succeed here.
*/
*map->map_size = *map->buff_size;
map->map_size = map->buff_size;
status = efi_bs_call(get_memory_map,
map->map_size,
*map->map,
map->key_ptr,
map->desc_size,
map->desc_ver);
&map->map_size,
&map->map,
&map->map_key,
&map->desc_size,
&map->desc_ver);

/* exit_boot_services() was called, thus cannot free */
if (status != EFI_SUCCESS)
Expand All @@ -484,7 +482,7 @@ efi_status_t efi_exit_boot_services(void *handle,
if (status != EFI_SUCCESS)
goto fail;

status = efi_bs_call(exit_boot_services, handle, *map->key_ptr);
status = efi_bs_call(exit_boot_services, handle, map->map_key);
}

/* exit_boot_services() was called, thus cannot free */
Expand All @@ -494,7 +492,7 @@ efi_status_t efi_exit_boot_services(void *handle,
return EFI_SUCCESS;

free_map:
efi_bs_call(free_pool, *map->map);
efi_bs_call(free_pool, map);
fail:
return status;
}
Expand Down
15 changes: 2 additions & 13 deletions drivers/firmware/efi/libstub/efistub.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,6 @@ void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
*/
#define EFI_MMAP_NR_SLACK_SLOTS 8

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;
};

typedef struct efi_generic_dev_path efi_device_path_protocol_t;

typedef void *efi_event_t;
Expand Down Expand Up @@ -850,9 +841,7 @@ typedef efi_status_t (*efi_exit_boot_map_processing)(
struct efi_boot_memmap *map,
void *priv);

efi_status_t efi_exit_boot_services(void *handle,
struct efi_boot_memmap *map,
void *priv,
efi_status_t efi_exit_boot_services(void *handle, void *priv,
efi_exit_boot_map_processing priv_func);

efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
Expand Down Expand Up @@ -891,7 +880,7 @@ void efi_apply_loadoptions_quirk(const void **load_options, int *load_options_si

char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len);

efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
efi_status_t efi_get_memory_map(struct efi_boot_memmap **map);

efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
unsigned long max);
Expand Down
37 changes: 16 additions & 21 deletions drivers/firmware/efi/libstub/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,25 +170,25 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
if (node < 0)
return EFI_LOAD_ERROR;

fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
fdt_val64 = cpu_to_fdt64((unsigned long)map->map);

err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-start", fdt_val64);
if (err)
return EFI_LOAD_ERROR;

fdt_val32 = cpu_to_fdt32(*map->map_size);
fdt_val32 = cpu_to_fdt32(map->map_size);

err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-size", fdt_val32);
if (err)
return EFI_LOAD_ERROR;

fdt_val32 = cpu_to_fdt32(*map->desc_size);
fdt_val32 = cpu_to_fdt32(map->desc_size);

err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32);
if (err)
return EFI_LOAD_ERROR;

fdt_val32 = cpu_to_fdt32(*map->desc_ver);
fdt_val32 = cpu_to_fdt32(map->desc_ver);

err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32);
if (err)
Expand All @@ -198,21 +198,24 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
}

struct exit_boot_struct {
struct efi_boot_memmap *boot_memmap;
efi_memory_desc_t *runtime_map;
int runtime_entry_count;
void *new_fdt_addr;
};

static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
void *priv)
static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
{
struct exit_boot_struct *p = priv;

p->boot_memmap = map;

/*
* Update the memory map with virtual addresses. The function will also
* populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
* entries so that we can pass it straight to SetVirtualAddressMap()
*/
efi_get_virtmap(*map->map, *map->map_size, *map->desc_size,
efi_get_virtmap(map->map, map->map_size, map->desc_size,
p->runtime_map, &p->runtime_entry_count);

return update_fdt_memmap(p->new_fdt_addr, map);
Expand Down Expand Up @@ -243,20 +246,11 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
unsigned long fdt_addr,
unsigned long fdt_size)
{
unsigned long map_size, desc_size, buff_size;
unsigned long desc_size;
u32 desc_ver;
unsigned long mmap_key;
efi_memory_desc_t *memory_map;
efi_status_t status;
struct efi_boot_memmap map;
struct exit_boot_struct priv;

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;

if (!efi_novamap) {
status = efi_alloc_virtmap(&priv.runtime_map, &desc_size,
&desc_ver);
Expand All @@ -268,7 +262,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,

efi_info("Exiting boot services...\n");

map.map = &memory_map;
status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, ULONG_MAX);
if (status != EFI_SUCCESS) {
efi_err("Unable to allocate memory for new device tree.\n");
Expand All @@ -286,7 +279,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,

priv.new_fdt_addr = (void *)*new_fdt_addr;

status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);
status = efi_exit_boot_services(handle, &priv, exit_boot_func);

if (status == EFI_SUCCESS) {
efi_set_virtual_address_map_t *svam;
Expand All @@ -305,6 +298,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
* incoming kernel but proceed normally otherwise.
*/
if (status != EFI_SUCCESS) {
efi_memory_desc_t *p;
int l;

/*
Expand All @@ -313,8 +307,9 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
* the incoming kernel that no virtual translation has
* been installed.
*/
for (l = 0; l < map_size; l += desc_size) {
efi_memory_desc_t *p = (void *)memory_map + l;
for (l = 0; l < priv.boot_memmap->map_size;
l += priv.boot_memmap->desc_size) {
p = (void *)priv.boot_memmap->map + l;

if (p->attribute & EFI_MEMORY_RUNTIME)
p->virt_addr = 0;
Expand Down
74 changes: 24 additions & 50 deletions drivers/firmware/efi/libstub/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,45 @@

#include "efistub.h"

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_get_memory_map() - get memory map
* @map: on return pointer to memory map
* @map: pointer to memory map pointer to which to assign the
* newly allocated memory map
*
* Retrieve the UEFI memory map. The allocated memory leaves room for
* up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries.
*
* Return: status code
*/
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
efi_status_t efi_get_memory_map(struct efi_boot_memmap **map)
{
efi_memory_desc_t *m = NULL;
struct efi_boot_memmap *m, tmp;
efi_status_t status;
unsigned long key;
u32 desc_version;
unsigned long size;

*map->desc_size = sizeof(*m);
*map->map_size = *map->desc_size * 32;
*map->buff_size = *map->map_size;
again:
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
*map->map_size, (void **)&m);
tmp.map_size = 0;
status = efi_bs_call(get_memory_map, &tmp.map_size, NULL, &tmp.map_key,
&tmp.desc_size, &tmp.desc_ver);
if (status != EFI_BUFFER_TOO_SMALL)
return EFI_LOAD_ERROR;

size = tmp.map_size + tmp.desc_size * EFI_MMAP_NR_SLACK_SLOTS;
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*m) + size,
(void **)&m);
if (status != EFI_SUCCESS)
goto fail;
return status;

*map->desc_size = 0;
key = 0;
status = efi_bs_call(get_memory_map, map->map_size, m,
&key, map->desc_size, &desc_version);
if (status == EFI_BUFFER_TOO_SMALL ||
!mmap_has_headroom(*map->buff_size, *map->map_size,
*map->desc_size)) {
efi_bs_call(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;
}
m->buff_size = m->map_size = size;
status = efi_bs_call(get_memory_map, &m->map_size, m->map, &m->map_key,
&m->desc_size, &m->desc_ver);
if (status != EFI_SUCCESS)
goto free_map;

if (status == EFI_SUCCESS) {
if (map->key_ptr)
*map->key_ptr = key;
if (map->desc_ver)
*map->desc_ver = desc_version;
} else {
efi_bs_call(free_pool, m);
}
*map = m;
return EFI_SUCCESS;

fail:
*map->map = m;
free_map:
efi_bs_call(free_pool, m);
return status;
}

Expand Down
Loading

0 comments on commit eab3126

Please sign in to comment.