1fdc6d38dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2fdc6d38dSArd Biesheuvel /* 3fdc6d38dSArd Biesheuvel * Common EFI memory map functions. 4fdc6d38dSArd Biesheuvel */ 5fdc6d38dSArd Biesheuvel 6fdc6d38dSArd Biesheuvel #define pr_fmt(fmt) "efi: " fmt 7fdc6d38dSArd Biesheuvel 8fdc6d38dSArd Biesheuvel #include <linux/init.h> 9fdc6d38dSArd Biesheuvel #include <linux/kernel.h> 10fdc6d38dSArd Biesheuvel #include <linux/efi.h> 11fdc6d38dSArd Biesheuvel #include <linux/io.h> 12fdc6d38dSArd Biesheuvel #include <asm/early_ioremap.h> 13fdc6d38dSArd Biesheuvel #include <asm/efi.h> 14fdc6d38dSArd Biesheuvel #include <linux/memblock.h> 15fdc6d38dSArd Biesheuvel #include <linux/slab.h> 16fdc6d38dSArd Biesheuvel 17fdc6d38dSArd Biesheuvel static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) 18fdc6d38dSArd Biesheuvel { 19fdc6d38dSArd Biesheuvel return memblock_phys_alloc(size, SMP_CACHE_BYTES); 20fdc6d38dSArd Biesheuvel } 21fdc6d38dSArd Biesheuvel 22fdc6d38dSArd Biesheuvel static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) 23fdc6d38dSArd Biesheuvel { 24fdc6d38dSArd Biesheuvel unsigned int order = get_order(size); 25fdc6d38dSArd Biesheuvel struct page *p = alloc_pages(GFP_KERNEL, order); 26fdc6d38dSArd Biesheuvel 27fdc6d38dSArd Biesheuvel if (!p) 28fdc6d38dSArd Biesheuvel return 0; 29fdc6d38dSArd Biesheuvel 30fdc6d38dSArd Biesheuvel return PFN_PHYS(page_to_pfn(p)); 31fdc6d38dSArd Biesheuvel } 32fdc6d38dSArd Biesheuvel 33*37aee82cSArd Biesheuvel static 34fdc6d38dSArd Biesheuvel void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) 35fdc6d38dSArd Biesheuvel { 36fdc6d38dSArd Biesheuvel if (flags & EFI_MEMMAP_MEMBLOCK) { 37fdc6d38dSArd Biesheuvel if (slab_is_available()) 38fdc6d38dSArd Biesheuvel memblock_free_late(phys, size); 39fdc6d38dSArd Biesheuvel else 40fdc6d38dSArd Biesheuvel memblock_phys_free(phys, size); 41fdc6d38dSArd Biesheuvel } else if (flags & EFI_MEMMAP_SLAB) { 42fdc6d38dSArd Biesheuvel struct page *p = pfn_to_page(PHYS_PFN(phys)); 43fdc6d38dSArd Biesheuvel unsigned int order = get_order(size); 44fdc6d38dSArd Biesheuvel 45fdc6d38dSArd Biesheuvel free_pages((unsigned long) page_address(p), order); 46fdc6d38dSArd Biesheuvel } 47fdc6d38dSArd Biesheuvel } 48fdc6d38dSArd Biesheuvel 49fdc6d38dSArd Biesheuvel /** 50fdc6d38dSArd Biesheuvel * efi_memmap_alloc - Allocate memory for the EFI memory map 51fdc6d38dSArd Biesheuvel * @num_entries: Number of entries in the allocated map. 52fdc6d38dSArd Biesheuvel * @data: efi memmap installation parameters 53fdc6d38dSArd Biesheuvel * 54fdc6d38dSArd Biesheuvel * Depending on whether mm_init() has already been invoked or not, 55fdc6d38dSArd Biesheuvel * either memblock or "normal" page allocation is used. 56fdc6d38dSArd Biesheuvel * 57fdc6d38dSArd Biesheuvel * Returns zero on success, a negative error code on failure. 58fdc6d38dSArd Biesheuvel */ 59fdc6d38dSArd Biesheuvel int __init efi_memmap_alloc(unsigned int num_entries, 60fdc6d38dSArd Biesheuvel struct efi_memory_map_data *data) 61fdc6d38dSArd Biesheuvel { 62fdc6d38dSArd Biesheuvel /* Expect allocation parameters are zero initialized */ 63fdc6d38dSArd Biesheuvel WARN_ON(data->phys_map || data->size); 64fdc6d38dSArd Biesheuvel 65fdc6d38dSArd Biesheuvel data->size = num_entries * efi.memmap.desc_size; 66fdc6d38dSArd Biesheuvel data->desc_version = efi.memmap.desc_version; 67fdc6d38dSArd Biesheuvel data->desc_size = efi.memmap.desc_size; 68fdc6d38dSArd Biesheuvel data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); 69fdc6d38dSArd Biesheuvel data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; 70fdc6d38dSArd Biesheuvel 71fdc6d38dSArd Biesheuvel if (slab_is_available()) { 72fdc6d38dSArd Biesheuvel data->flags |= EFI_MEMMAP_SLAB; 73fdc6d38dSArd Biesheuvel data->phys_map = __efi_memmap_alloc_late(data->size); 74fdc6d38dSArd Biesheuvel } else { 75fdc6d38dSArd Biesheuvel data->flags |= EFI_MEMMAP_MEMBLOCK; 76fdc6d38dSArd Biesheuvel data->phys_map = __efi_memmap_alloc_early(data->size); 77fdc6d38dSArd Biesheuvel } 78fdc6d38dSArd Biesheuvel 79fdc6d38dSArd Biesheuvel if (!data->phys_map) 80fdc6d38dSArd Biesheuvel return -ENOMEM; 81fdc6d38dSArd Biesheuvel return 0; 82fdc6d38dSArd Biesheuvel } 83fdc6d38dSArd Biesheuvel 84fdc6d38dSArd Biesheuvel /** 85fdc6d38dSArd Biesheuvel * efi_memmap_install - Install a new EFI memory map in efi.memmap 8692faa821SZhu Wang * @data: efi memmap installation parameters 87fdc6d38dSArd Biesheuvel * 88fdc6d38dSArd Biesheuvel * Unlike efi_memmap_init_*(), this function does not allow the caller 89fdc6d38dSArd Biesheuvel * to switch from early to late mappings. It simply uses the existing 90fdc6d38dSArd Biesheuvel * mapping function and installs the new memmap. 91fdc6d38dSArd Biesheuvel * 92fdc6d38dSArd Biesheuvel * Returns zero on success, a negative error code on failure. 93fdc6d38dSArd Biesheuvel */ 94fdc6d38dSArd Biesheuvel int __init efi_memmap_install(struct efi_memory_map_data *data) 95fdc6d38dSArd Biesheuvel { 9675dde792SArd Biesheuvel unsigned long size = efi.memmap.desc_size * efi.memmap.nr_map; 9775dde792SArd Biesheuvel unsigned long flags = efi.memmap.flags; 9875dde792SArd Biesheuvel u64 phys = efi.memmap.phys_map; 9975dde792SArd Biesheuvel int ret; 10075dde792SArd Biesheuvel 101fdc6d38dSArd Biesheuvel efi_memmap_unmap(); 102fdc6d38dSArd Biesheuvel 103d85e3e34SArd Biesheuvel if (efi_enabled(EFI_PARAVIRT)) 104d85e3e34SArd Biesheuvel return 0; 105d85e3e34SArd Biesheuvel 10675dde792SArd Biesheuvel ret = __efi_memmap_init(data); 10775dde792SArd Biesheuvel if (ret) 10875dde792SArd Biesheuvel return ret; 10975dde792SArd Biesheuvel 11075dde792SArd Biesheuvel __efi_memmap_free(phys, size, flags); 11175dde792SArd Biesheuvel return 0; 112fdc6d38dSArd Biesheuvel } 113fdc6d38dSArd Biesheuvel 114fdc6d38dSArd Biesheuvel /** 115fdc6d38dSArd Biesheuvel * efi_memmap_split_count - Count number of additional EFI memmap entries 116fdc6d38dSArd Biesheuvel * @md: EFI memory descriptor to split 117fdc6d38dSArd Biesheuvel * @range: Address range (start, end) to split around 118fdc6d38dSArd Biesheuvel * 119fdc6d38dSArd Biesheuvel * Returns the number of additional EFI memmap entries required to 120fdc6d38dSArd Biesheuvel * accommodate @range. 121fdc6d38dSArd Biesheuvel */ 122fdc6d38dSArd Biesheuvel int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) 123fdc6d38dSArd Biesheuvel { 124fdc6d38dSArd Biesheuvel u64 m_start, m_end; 125fdc6d38dSArd Biesheuvel u64 start, end; 126fdc6d38dSArd Biesheuvel int count = 0; 127fdc6d38dSArd Biesheuvel 128fdc6d38dSArd Biesheuvel start = md->phys_addr; 129fdc6d38dSArd Biesheuvel end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; 130fdc6d38dSArd Biesheuvel 131fdc6d38dSArd Biesheuvel /* modifying range */ 132fdc6d38dSArd Biesheuvel m_start = range->start; 133fdc6d38dSArd Biesheuvel m_end = range->end; 134fdc6d38dSArd Biesheuvel 135fdc6d38dSArd Biesheuvel if (m_start <= start) { 136fdc6d38dSArd Biesheuvel /* split into 2 parts */ 137fdc6d38dSArd Biesheuvel if (start < m_end && m_end < end) 138fdc6d38dSArd Biesheuvel count++; 139fdc6d38dSArd Biesheuvel } 140fdc6d38dSArd Biesheuvel 141fdc6d38dSArd Biesheuvel if (start < m_start && m_start < end) { 142fdc6d38dSArd Biesheuvel /* split into 3 parts */ 143fdc6d38dSArd Biesheuvel if (m_end < end) 144fdc6d38dSArd Biesheuvel count += 2; 145fdc6d38dSArd Biesheuvel /* split into 2 parts */ 146fdc6d38dSArd Biesheuvel if (end <= m_end) 147fdc6d38dSArd Biesheuvel count++; 148fdc6d38dSArd Biesheuvel } 149fdc6d38dSArd Biesheuvel 150fdc6d38dSArd Biesheuvel return count; 151fdc6d38dSArd Biesheuvel } 152fdc6d38dSArd Biesheuvel 153fdc6d38dSArd Biesheuvel /** 154fdc6d38dSArd Biesheuvel * efi_memmap_insert - Insert a memory region in an EFI memmap 155fdc6d38dSArd Biesheuvel * @old_memmap: The existing EFI memory map structure 156fdc6d38dSArd Biesheuvel * @buf: Address of buffer to store new map 157fdc6d38dSArd Biesheuvel * @mem: Memory map entry to insert 158fdc6d38dSArd Biesheuvel * 159fdc6d38dSArd Biesheuvel * It is suggested that you call efi_memmap_split_count() first 160fdc6d38dSArd Biesheuvel * to see how large @buf needs to be. 161fdc6d38dSArd Biesheuvel */ 162fdc6d38dSArd Biesheuvel void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, 163fdc6d38dSArd Biesheuvel struct efi_mem_range *mem) 164fdc6d38dSArd Biesheuvel { 165fdc6d38dSArd Biesheuvel u64 m_start, m_end, m_attr; 166fdc6d38dSArd Biesheuvel efi_memory_desc_t *md; 167fdc6d38dSArd Biesheuvel u64 start, end; 168fdc6d38dSArd Biesheuvel void *old, *new; 169fdc6d38dSArd Biesheuvel 170fdc6d38dSArd Biesheuvel /* modifying range */ 171fdc6d38dSArd Biesheuvel m_start = mem->range.start; 172fdc6d38dSArd Biesheuvel m_end = mem->range.end; 173fdc6d38dSArd Biesheuvel m_attr = mem->attribute; 174fdc6d38dSArd Biesheuvel 175fdc6d38dSArd Biesheuvel /* 176fdc6d38dSArd Biesheuvel * The EFI memory map deals with regions in EFI_PAGE_SIZE 177fdc6d38dSArd Biesheuvel * units. Ensure that the region described by 'mem' is aligned 178fdc6d38dSArd Biesheuvel * correctly. 179fdc6d38dSArd Biesheuvel */ 180fdc6d38dSArd Biesheuvel if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || 181fdc6d38dSArd Biesheuvel !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { 182fdc6d38dSArd Biesheuvel WARN_ON(1); 183fdc6d38dSArd Biesheuvel return; 184fdc6d38dSArd Biesheuvel } 185fdc6d38dSArd Biesheuvel 186fdc6d38dSArd Biesheuvel for (old = old_memmap->map, new = buf; 187fdc6d38dSArd Biesheuvel old < old_memmap->map_end; 188fdc6d38dSArd Biesheuvel old += old_memmap->desc_size, new += old_memmap->desc_size) { 189fdc6d38dSArd Biesheuvel 190fdc6d38dSArd Biesheuvel /* copy original EFI memory descriptor */ 191fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 192fdc6d38dSArd Biesheuvel md = new; 193fdc6d38dSArd Biesheuvel start = md->phys_addr; 194fdc6d38dSArd Biesheuvel end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 195fdc6d38dSArd Biesheuvel 196fdc6d38dSArd Biesheuvel if (m_start <= start && end <= m_end) 197fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 198fdc6d38dSArd Biesheuvel 199fdc6d38dSArd Biesheuvel if (m_start <= start && 200fdc6d38dSArd Biesheuvel (start < m_end && m_end < end)) { 201fdc6d38dSArd Biesheuvel /* first part */ 202fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 203fdc6d38dSArd Biesheuvel md->num_pages = (m_end - md->phys_addr + 1) >> 204fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 205fdc6d38dSArd Biesheuvel /* latter part */ 206fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 207fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 208fdc6d38dSArd Biesheuvel md = new; 209fdc6d38dSArd Biesheuvel md->phys_addr = m_end + 1; 210fdc6d38dSArd Biesheuvel md->num_pages = (end - md->phys_addr + 1) >> 211fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 212fdc6d38dSArd Biesheuvel } 213fdc6d38dSArd Biesheuvel 214fdc6d38dSArd Biesheuvel if ((start < m_start && m_start < end) && m_end < end) { 215fdc6d38dSArd Biesheuvel /* first part */ 216fdc6d38dSArd Biesheuvel md->num_pages = (m_start - md->phys_addr) >> 217fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 218fdc6d38dSArd Biesheuvel /* middle part */ 219fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 220fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 221fdc6d38dSArd Biesheuvel md = new; 222fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 223fdc6d38dSArd Biesheuvel md->phys_addr = m_start; 224fdc6d38dSArd Biesheuvel md->num_pages = (m_end - m_start + 1) >> 225fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 226fdc6d38dSArd Biesheuvel /* last part */ 227fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 228fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 229fdc6d38dSArd Biesheuvel md = new; 230fdc6d38dSArd Biesheuvel md->phys_addr = m_end + 1; 231fdc6d38dSArd Biesheuvel md->num_pages = (end - m_end) >> 232fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 233fdc6d38dSArd Biesheuvel } 234fdc6d38dSArd Biesheuvel 235fdc6d38dSArd Biesheuvel if ((start < m_start && m_start < end) && 236fdc6d38dSArd Biesheuvel (end <= m_end)) { 237fdc6d38dSArd Biesheuvel /* first part */ 238fdc6d38dSArd Biesheuvel md->num_pages = (m_start - md->phys_addr) >> 239fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 240fdc6d38dSArd Biesheuvel /* latter part */ 241fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 242fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 243fdc6d38dSArd Biesheuvel md = new; 244fdc6d38dSArd Biesheuvel md->phys_addr = m_start; 245fdc6d38dSArd Biesheuvel md->num_pages = (end - md->phys_addr + 1) >> 246fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 247fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 248fdc6d38dSArd Biesheuvel } 249fdc6d38dSArd Biesheuvel } 250fdc6d38dSArd Biesheuvel } 251