162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common EFI memory map functions. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "efi: " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/efi.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <asm/early_ioremap.h> 1362306a36Sopenharmony_ci#include <asm/efi.h> 1462306a36Sopenharmony_ci#include <linux/memblock.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci return memblock_phys_alloc(size, SMP_CACHE_BYTES); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci unsigned int order = get_order(size); 2562306a36Sopenharmony_ci struct page *p = alloc_pages(GFP_KERNEL, order); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (!p) 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return PFN_PHYS(page_to_pfn(p)); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci if (flags & EFI_MEMMAP_MEMBLOCK) { 3662306a36Sopenharmony_ci if (slab_is_available()) 3762306a36Sopenharmony_ci memblock_free_late(phys, size); 3862306a36Sopenharmony_ci else 3962306a36Sopenharmony_ci memblock_phys_free(phys, size); 4062306a36Sopenharmony_ci } else if (flags & EFI_MEMMAP_SLAB) { 4162306a36Sopenharmony_ci struct page *p = pfn_to_page(PHYS_PFN(phys)); 4262306a36Sopenharmony_ci unsigned int order = get_order(size); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci free_pages((unsigned long) page_address(p), order); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * efi_memmap_alloc - Allocate memory for the EFI memory map 5062306a36Sopenharmony_ci * @num_entries: Number of entries in the allocated map. 5162306a36Sopenharmony_ci * @data: efi memmap installation parameters 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Depending on whether mm_init() has already been invoked or not, 5462306a36Sopenharmony_ci * either memblock or "normal" page allocation is used. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * Returns zero on success, a negative error code on failure. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ciint __init efi_memmap_alloc(unsigned int num_entries, 5962306a36Sopenharmony_ci struct efi_memory_map_data *data) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* Expect allocation parameters are zero initialized */ 6262306a36Sopenharmony_ci WARN_ON(data->phys_map || data->size); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci data->size = num_entries * efi.memmap.desc_size; 6562306a36Sopenharmony_ci data->desc_version = efi.memmap.desc_version; 6662306a36Sopenharmony_ci data->desc_size = efi.memmap.desc_size; 6762306a36Sopenharmony_ci data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); 6862306a36Sopenharmony_ci data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (slab_is_available()) { 7162306a36Sopenharmony_ci data->flags |= EFI_MEMMAP_SLAB; 7262306a36Sopenharmony_ci data->phys_map = __efi_memmap_alloc_late(data->size); 7362306a36Sopenharmony_ci } else { 7462306a36Sopenharmony_ci data->flags |= EFI_MEMMAP_MEMBLOCK; 7562306a36Sopenharmony_ci data->phys_map = __efi_memmap_alloc_early(data->size); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!data->phys_map) 7962306a36Sopenharmony_ci return -ENOMEM; 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * efi_memmap_install - Install a new EFI memory map in efi.memmap 8562306a36Sopenharmony_ci * @data: efi memmap installation parameters 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Unlike efi_memmap_init_*(), this function does not allow the caller 8862306a36Sopenharmony_ci * to switch from early to late mappings. It simply uses the existing 8962306a36Sopenharmony_ci * mapping function and installs the new memmap. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Returns zero on success, a negative error code on failure. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ciint __init efi_memmap_install(struct efi_memory_map_data *data) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci efi_memmap_unmap(); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (efi_enabled(EFI_PARAVIRT)) 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return __efi_memmap_init(data); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * efi_memmap_split_count - Count number of additional EFI memmap entries 10562306a36Sopenharmony_ci * @md: EFI memory descriptor to split 10662306a36Sopenharmony_ci * @range: Address range (start, end) to split around 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Returns the number of additional EFI memmap entries required to 10962306a36Sopenharmony_ci * accommodate @range. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ciint __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u64 m_start, m_end; 11462306a36Sopenharmony_ci u64 start, end; 11562306a36Sopenharmony_ci int count = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci start = md->phys_addr; 11862306a36Sopenharmony_ci end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* modifying range */ 12162306a36Sopenharmony_ci m_start = range->start; 12262306a36Sopenharmony_ci m_end = range->end; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (m_start <= start) { 12562306a36Sopenharmony_ci /* split into 2 parts */ 12662306a36Sopenharmony_ci if (start < m_end && m_end < end) 12762306a36Sopenharmony_ci count++; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (start < m_start && m_start < end) { 13162306a36Sopenharmony_ci /* split into 3 parts */ 13262306a36Sopenharmony_ci if (m_end < end) 13362306a36Sopenharmony_ci count += 2; 13462306a36Sopenharmony_ci /* split into 2 parts */ 13562306a36Sopenharmony_ci if (end <= m_end) 13662306a36Sopenharmony_ci count++; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return count; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/** 14362306a36Sopenharmony_ci * efi_memmap_insert - Insert a memory region in an EFI memmap 14462306a36Sopenharmony_ci * @old_memmap: The existing EFI memory map structure 14562306a36Sopenharmony_ci * @buf: Address of buffer to store new map 14662306a36Sopenharmony_ci * @mem: Memory map entry to insert 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * It is suggested that you call efi_memmap_split_count() first 14962306a36Sopenharmony_ci * to see how large @buf needs to be. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_civoid __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, 15262306a36Sopenharmony_ci struct efi_mem_range *mem) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u64 m_start, m_end, m_attr; 15562306a36Sopenharmony_ci efi_memory_desc_t *md; 15662306a36Sopenharmony_ci u64 start, end; 15762306a36Sopenharmony_ci void *old, *new; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* modifying range */ 16062306a36Sopenharmony_ci m_start = mem->range.start; 16162306a36Sopenharmony_ci m_end = mem->range.end; 16262306a36Sopenharmony_ci m_attr = mem->attribute; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * The EFI memory map deals with regions in EFI_PAGE_SIZE 16662306a36Sopenharmony_ci * units. Ensure that the region described by 'mem' is aligned 16762306a36Sopenharmony_ci * correctly. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || 17062306a36Sopenharmony_ci !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { 17162306a36Sopenharmony_ci WARN_ON(1); 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (old = old_memmap->map, new = buf; 17662306a36Sopenharmony_ci old < old_memmap->map_end; 17762306a36Sopenharmony_ci old += old_memmap->desc_size, new += old_memmap->desc_size) { 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* copy original EFI memory descriptor */ 18062306a36Sopenharmony_ci memcpy(new, old, old_memmap->desc_size); 18162306a36Sopenharmony_ci md = new; 18262306a36Sopenharmony_ci start = md->phys_addr; 18362306a36Sopenharmony_ci end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (m_start <= start && end <= m_end) 18662306a36Sopenharmony_ci md->attribute |= m_attr; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (m_start <= start && 18962306a36Sopenharmony_ci (start < m_end && m_end < end)) { 19062306a36Sopenharmony_ci /* first part */ 19162306a36Sopenharmony_ci md->attribute |= m_attr; 19262306a36Sopenharmony_ci md->num_pages = (m_end - md->phys_addr + 1) >> 19362306a36Sopenharmony_ci EFI_PAGE_SHIFT; 19462306a36Sopenharmony_ci /* latter part */ 19562306a36Sopenharmony_ci new += old_memmap->desc_size; 19662306a36Sopenharmony_ci memcpy(new, old, old_memmap->desc_size); 19762306a36Sopenharmony_ci md = new; 19862306a36Sopenharmony_ci md->phys_addr = m_end + 1; 19962306a36Sopenharmony_ci md->num_pages = (end - md->phys_addr + 1) >> 20062306a36Sopenharmony_ci EFI_PAGE_SHIFT; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if ((start < m_start && m_start < end) && m_end < end) { 20462306a36Sopenharmony_ci /* first part */ 20562306a36Sopenharmony_ci md->num_pages = (m_start - md->phys_addr) >> 20662306a36Sopenharmony_ci EFI_PAGE_SHIFT; 20762306a36Sopenharmony_ci /* middle part */ 20862306a36Sopenharmony_ci new += old_memmap->desc_size; 20962306a36Sopenharmony_ci memcpy(new, old, old_memmap->desc_size); 21062306a36Sopenharmony_ci md = new; 21162306a36Sopenharmony_ci md->attribute |= m_attr; 21262306a36Sopenharmony_ci md->phys_addr = m_start; 21362306a36Sopenharmony_ci md->num_pages = (m_end - m_start + 1) >> 21462306a36Sopenharmony_ci EFI_PAGE_SHIFT; 21562306a36Sopenharmony_ci /* last part */ 21662306a36Sopenharmony_ci new += old_memmap->desc_size; 21762306a36Sopenharmony_ci memcpy(new, old, old_memmap->desc_size); 21862306a36Sopenharmony_ci md = new; 21962306a36Sopenharmony_ci md->phys_addr = m_end + 1; 22062306a36Sopenharmony_ci md->num_pages = (end - m_end) >> 22162306a36Sopenharmony_ci EFI_PAGE_SHIFT; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if ((start < m_start && m_start < end) && 22562306a36Sopenharmony_ci (end <= m_end)) { 22662306a36Sopenharmony_ci /* first part */ 22762306a36Sopenharmony_ci md->num_pages = (m_start - md->phys_addr) >> 22862306a36Sopenharmony_ci EFI_PAGE_SHIFT; 22962306a36Sopenharmony_ci /* latter part */ 23062306a36Sopenharmony_ci new += old_memmap->desc_size; 23162306a36Sopenharmony_ci memcpy(new, old, old_memmap->desc_size); 23262306a36Sopenharmony_ci md = new; 23362306a36Sopenharmony_ci md->phys_addr = m_start; 23462306a36Sopenharmony_ci md->num_pages = (end - md->phys_addr + 1) >> 23562306a36Sopenharmony_ci EFI_PAGE_SHIFT; 23662306a36Sopenharmony_ci md->attribute |= m_attr; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci} 240