18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/efi.h>
48c2ecf20Sopenharmony_ci#include <asm/efi.h>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "efistub.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/**
98c2ecf20Sopenharmony_ci * efi_get_memory_map() - get memory map
108c2ecf20Sopenharmony_ci * @map:		pointer to memory map pointer to which to assign the
118c2ecf20Sopenharmony_ci *			newly allocated memory map
128c2ecf20Sopenharmony_ci * @install_cfg_tbl:	whether or not to install the boot memory map as a
138c2ecf20Sopenharmony_ci *			configuration table
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Retrieve the UEFI memory map. The allocated memory leaves room for
168c2ecf20Sopenharmony_ci * up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Return:	status code
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ciefi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
218c2ecf20Sopenharmony_ci				bool install_cfg_tbl)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	int memtype = install_cfg_tbl ? EFI_ACPI_RECLAIM_MEMORY
248c2ecf20Sopenharmony_ci				      : EFI_LOADER_DATA;
258c2ecf20Sopenharmony_ci	efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
268c2ecf20Sopenharmony_ci	struct efi_boot_memmap *m, tmp;
278c2ecf20Sopenharmony_ci	efi_status_t status;
288c2ecf20Sopenharmony_ci	unsigned long size;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	tmp.map_size = 0;
318c2ecf20Sopenharmony_ci	status = efi_bs_call(get_memory_map, &tmp.map_size, NULL, &tmp.map_key,
328c2ecf20Sopenharmony_ci			     &tmp.desc_size, &tmp.desc_ver);
338c2ecf20Sopenharmony_ci	if (status != EFI_BUFFER_TOO_SMALL)
348c2ecf20Sopenharmony_ci		return EFI_LOAD_ERROR;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	size = tmp.map_size + tmp.desc_size * EFI_MMAP_NR_SLACK_SLOTS;
378c2ecf20Sopenharmony_ci	status = efi_bs_call(allocate_pool, memtype, sizeof(*m) + size,
388c2ecf20Sopenharmony_ci			     (void **)&m);
398c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
408c2ecf20Sopenharmony_ci		return status;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (install_cfg_tbl) {
438c2ecf20Sopenharmony_ci		/*
448c2ecf20Sopenharmony_ci		 * Installing a configuration table might allocate memory, and
458c2ecf20Sopenharmony_ci		 * this may modify the memory map. This means we should install
468c2ecf20Sopenharmony_ci		 * the configuration table first, and re-install or delete it
478c2ecf20Sopenharmony_ci		 * as needed.
488c2ecf20Sopenharmony_ci		 */
498c2ecf20Sopenharmony_ci		status = efi_bs_call(install_configuration_table, &tbl_guid, m);
508c2ecf20Sopenharmony_ci		if (status != EFI_SUCCESS)
518c2ecf20Sopenharmony_ci			goto free_map;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	m->buff_size = m->map_size = size;
558c2ecf20Sopenharmony_ci	status = efi_bs_call(get_memory_map, &m->map_size, m->map, &m->map_key,
568c2ecf20Sopenharmony_ci			     &m->desc_size, &m->desc_ver);
578c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
588c2ecf20Sopenharmony_ci		goto uninstall_table;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	*map = m;
618c2ecf20Sopenharmony_ci	return EFI_SUCCESS;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciuninstall_table:
648c2ecf20Sopenharmony_ci	if (install_cfg_tbl)
658c2ecf20Sopenharmony_ci		efi_bs_call(install_configuration_table, &tbl_guid, NULL);
668c2ecf20Sopenharmony_cifree_map:
678c2ecf20Sopenharmony_ci	efi_bs_call(free_pool, m);
688c2ecf20Sopenharmony_ci	return status;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/**
728c2ecf20Sopenharmony_ci * efi_allocate_pages() - Allocate memory pages
738c2ecf20Sopenharmony_ci * @size:	minimum number of bytes to allocate
748c2ecf20Sopenharmony_ci * @addr:	On return the address of the first allocated page. The first
758c2ecf20Sopenharmony_ci *		allocated page has alignment EFI_ALLOC_ALIGN which is an
768c2ecf20Sopenharmony_ci *		architecture dependent multiple of the page size.
778c2ecf20Sopenharmony_ci * @max:	the address that the last allocated memory page shall not
788c2ecf20Sopenharmony_ci *		exceed
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
818c2ecf20Sopenharmony_ci * to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address
828c2ecf20Sopenharmony_ci * given by @max.
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * Return:	status code
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_ciefi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
878c2ecf20Sopenharmony_ci				unsigned long max)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	efi_physical_addr_t alloc_addr;
908c2ecf20Sopenharmony_ci	efi_status_t status;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
938c2ecf20Sopenharmony_ci		return efi_allocate_pages_aligned(size, addr, max,
948c2ecf20Sopenharmony_ci						  EFI_ALLOC_ALIGN);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
978c2ecf20Sopenharmony_ci	status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
988c2ecf20Sopenharmony_ci			     EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
998c2ecf20Sopenharmony_ci			     &alloc_addr);
1008c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
1018c2ecf20Sopenharmony_ci		return status;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	*addr = alloc_addr;
1048c2ecf20Sopenharmony_ci	return EFI_SUCCESS;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * efi_free() - free memory pages
1098c2ecf20Sopenharmony_ci * @size:	size of the memory area to free in bytes
1108c2ecf20Sopenharmony_ci * @addr:	start of the memory area to free (must be EFI_PAGE_SIZE
1118c2ecf20Sopenharmony_ci *		aligned)
1128c2ecf20Sopenharmony_ci *
1138c2ecf20Sopenharmony_ci * @size is rounded up to a multiple of EFI_ALLOC_ALIGN which is an
1148c2ecf20Sopenharmony_ci * architecture specific multiple of EFI_PAGE_SIZE. So this function should
1158c2ecf20Sopenharmony_ci * only be used to return pages allocated with efi_allocate_pages() or
1168c2ecf20Sopenharmony_ci * efi_low_alloc_above().
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_civoid efi_free(unsigned long size, unsigned long addr)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	unsigned long nr_pages;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!size)
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
1268c2ecf20Sopenharmony_ci	efi_bs_call(free_pages, addr, nr_pages);
1278c2ecf20Sopenharmony_ci}
128