/* -----------------------------------------------------------------------
*
* Copyright 2011 Intel Corporation; author Matt Fleming
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*
* ----------------------------------------------------------------------- */
#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/setup.h>
#include <asm/desc.h>
#include "eboot.h"
static efi_system_table_t *sys_table;
static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
unsigned long *desc_size)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
unsigned long key;
u32 desc_version;
*map_size = sizeof(*m) * 32;
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_phys3(sys_table->boottime->allocate_pool,
EFI_LOADER_DATA, *map_size, (void **)&m);
if (status != EFI_SUCCESS)
goto fail;
status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
m, &key, desc_size, &desc_version);
if (status == EFI_BUFFER_TOO_SMALL) {
efi_call_phys1(sys_table->boottime->free_pool, m);
goto again;
}
if (status != EFI_SUCCESS)
efi_call_phys1(sys_table->boottime->free_pool, m);
fail:
*map = m;
return status;
}
/*
* Allocate at the highest possible address that is not above 'max'.
*/
static efi_status_t high_alloc(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long max)
{
unsigned long map_size, desc_size;
efi_memory_desc_t *map;
efi_status_t status;
unsigned long nr_pages;
u64 max_addr = 0;
int i;
status = __get_map(&map, &map_size, &desc_size);
if (status != EFI_SUCCESS)
goto fail;
nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
again:
for (i = 0; i < map_size / desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map;
u64 start, end;
desc = (efi_memory_desc_t *)(m + (i * desc_size));
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
if ((start + size) > end || (start + size) > max)
continue;
if (end - size > max)
end = max;
if (round_down(end - size, align) < start)
continue;
start = round_down(end - size, align);
/*
* Don't allocate at 0x0. It will confuse code that
* checks pointers against NULL.
*/
if (start == 0x0)
continue;
if (start > max_addr)
max_addr = start;
}
if (!max_addr)
status = EFI_NOT_FOUND;
else {
status = efi_call_phys4(sys_table->boottime->allocate_pages,
EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
nr_pages, &max_addr);
if (status != EFI_SUCCESS) {
max = max_addr;
max_addr = 0;
goto again;
}
*addr = max_addr;
}
free_pool:
efi_call_phys1(sys_table->boottime->free_pool, map);
fail:
return status;
}
/*
* Allocate at the lowest possible address.
*/
static efi_status_t low_alloc(unsigned long size, unsigned long align,
unsigned long *addr)
{
unsigned long map_size, desc_size