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 33fdc6d38dSArd Biesheuvel void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) 34fdc6d38dSArd Biesheuvel { 35fdc6d38dSArd Biesheuvel if (flags & EFI_MEMMAP_MEMBLOCK) { 36fdc6d38dSArd Biesheuvel if (slab_is_available()) 37fdc6d38dSArd Biesheuvel memblock_free_late(phys, size); 38fdc6d38dSArd Biesheuvel else 39fdc6d38dSArd Biesheuvel memblock_phys_free(phys, size); 40fdc6d38dSArd Biesheuvel } else if (flags & EFI_MEMMAP_SLAB) { 41fdc6d38dSArd Biesheuvel struct page *p = pfn_to_page(PHYS_PFN(phys)); 42fdc6d38dSArd Biesheuvel unsigned int order = get_order(size); 43fdc6d38dSArd Biesheuvel 44fdc6d38dSArd Biesheuvel free_pages((unsigned long) page_address(p), order); 45fdc6d38dSArd Biesheuvel } 46fdc6d38dSArd Biesheuvel } 47fdc6d38dSArd Biesheuvel 48fdc6d38dSArd Biesheuvel /** 49fdc6d38dSArd Biesheuvel * efi_memmap_alloc - Allocate memory for the EFI memory map 50fdc6d38dSArd Biesheuvel * @num_entries: Number of entries in the allocated map. 51fdc6d38dSArd Biesheuvel * @data: efi memmap installation parameters 52fdc6d38dSArd Biesheuvel * 53fdc6d38dSArd Biesheuvel * Depending on whether mm_init() has already been invoked or not, 54fdc6d38dSArd Biesheuvel * either memblock or "normal" page allocation is used. 55fdc6d38dSArd Biesheuvel * 56fdc6d38dSArd Biesheuvel * Returns zero on success, a negative error code on failure. 57fdc6d38dSArd Biesheuvel */ 58fdc6d38dSArd Biesheuvel int __init efi_memmap_alloc(unsigned int num_entries, 59fdc6d38dSArd Biesheuvel struct efi_memory_map_data *data) 60fdc6d38dSArd Biesheuvel { 61fdc6d38dSArd Biesheuvel /* Expect allocation parameters are zero initialized */ 62fdc6d38dSArd Biesheuvel WARN_ON(data->phys_map || data->size); 63fdc6d38dSArd Biesheuvel 64fdc6d38dSArd Biesheuvel data->size = num_entries * efi.memmap.desc_size; 65fdc6d38dSArd Biesheuvel data->desc_version = efi.memmap.desc_version; 66fdc6d38dSArd Biesheuvel data->desc_size = efi.memmap.desc_size; 67fdc6d38dSArd Biesheuvel data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); 68fdc6d38dSArd Biesheuvel data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; 69fdc6d38dSArd Biesheuvel 70fdc6d38dSArd Biesheuvel if (slab_is_available()) { 71fdc6d38dSArd Biesheuvel data->flags |= EFI_MEMMAP_SLAB; 72fdc6d38dSArd Biesheuvel data->phys_map = __efi_memmap_alloc_late(data->size); 73fdc6d38dSArd Biesheuvel } else { 74fdc6d38dSArd Biesheuvel data->flags |= EFI_MEMMAP_MEMBLOCK; 75fdc6d38dSArd Biesheuvel data->phys_map = __efi_memmap_alloc_early(data->size); 76fdc6d38dSArd Biesheuvel } 77fdc6d38dSArd Biesheuvel 78fdc6d38dSArd Biesheuvel if (!data->phys_map) 79fdc6d38dSArd Biesheuvel return -ENOMEM; 80fdc6d38dSArd Biesheuvel return 0; 81fdc6d38dSArd Biesheuvel } 82fdc6d38dSArd Biesheuvel 83fdc6d38dSArd Biesheuvel /** 84fdc6d38dSArd Biesheuvel * efi_memmap_install - Install a new EFI memory map in efi.memmap 85fdc6d38dSArd Biesheuvel * @ctx: map allocation parameters (address, size, flags) 86fdc6d38dSArd Biesheuvel * 87fdc6d38dSArd Biesheuvel * Unlike efi_memmap_init_*(), this function does not allow the caller 88fdc6d38dSArd Biesheuvel * to switch from early to late mappings. It simply uses the existing 89fdc6d38dSArd Biesheuvel * mapping function and installs the new memmap. 90fdc6d38dSArd Biesheuvel * 91fdc6d38dSArd Biesheuvel * Returns zero on success, a negative error code on failure. 92fdc6d38dSArd Biesheuvel */ 93fdc6d38dSArd Biesheuvel int __init efi_memmap_install(struct efi_memory_map_data *data) 94fdc6d38dSArd Biesheuvel { 95fdc6d38dSArd Biesheuvel efi_memmap_unmap(); 96fdc6d38dSArd Biesheuvel 97*d85e3e34SArd Biesheuvel if (efi_enabled(EFI_PARAVIRT)) 98*d85e3e34SArd Biesheuvel return 0; 99*d85e3e34SArd Biesheuvel 100fdc6d38dSArd Biesheuvel return __efi_memmap_init(data); 101fdc6d38dSArd Biesheuvel } 102fdc6d38dSArd Biesheuvel 103fdc6d38dSArd Biesheuvel /** 104fdc6d38dSArd Biesheuvel * efi_memmap_split_count - Count number of additional EFI memmap entries 105fdc6d38dSArd Biesheuvel * @md: EFI memory descriptor to split 106fdc6d38dSArd Biesheuvel * @range: Address range (start, end) to split around 107fdc6d38dSArd Biesheuvel * 108fdc6d38dSArd Biesheuvel * Returns the number of additional EFI memmap entries required to 109fdc6d38dSArd Biesheuvel * accommodate @range. 110fdc6d38dSArd Biesheuvel */ 111fdc6d38dSArd Biesheuvel int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) 112fdc6d38dSArd Biesheuvel { 113fdc6d38dSArd Biesheuvel u64 m_start, m_end; 114fdc6d38dSArd Biesheuvel u64 start, end; 115fdc6d38dSArd Biesheuvel int count = 0; 116fdc6d38dSArd Biesheuvel 117fdc6d38dSArd Biesheuvel start = md->phys_addr; 118fdc6d38dSArd Biesheuvel end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; 119fdc6d38dSArd Biesheuvel 120fdc6d38dSArd Biesheuvel /* modifying range */ 121fdc6d38dSArd Biesheuvel m_start = range->start; 122fdc6d38dSArd Biesheuvel m_end = range->end; 123fdc6d38dSArd Biesheuvel 124fdc6d38dSArd Biesheuvel if (m_start <= start) { 125fdc6d38dSArd Biesheuvel /* split into 2 parts */ 126fdc6d38dSArd Biesheuvel if (start < m_end && m_end < end) 127fdc6d38dSArd Biesheuvel count++; 128fdc6d38dSArd Biesheuvel } 129fdc6d38dSArd Biesheuvel 130fdc6d38dSArd Biesheuvel if (start < m_start && m_start < end) { 131fdc6d38dSArd Biesheuvel /* split into 3 parts */ 132fdc6d38dSArd Biesheuvel if (m_end < end) 133fdc6d38dSArd Biesheuvel count += 2; 134fdc6d38dSArd Biesheuvel /* split into 2 parts */ 135fdc6d38dSArd Biesheuvel if (end <= m_end) 136fdc6d38dSArd Biesheuvel count++; 137fdc6d38dSArd Biesheuvel } 138fdc6d38dSArd Biesheuvel 139fdc6d38dSArd Biesheuvel return count; 140fdc6d38dSArd Biesheuvel } 141fdc6d38dSArd Biesheuvel 142fdc6d38dSArd Biesheuvel /** 143fdc6d38dSArd Biesheuvel * efi_memmap_insert - Insert a memory region in an EFI memmap 144fdc6d38dSArd Biesheuvel * @old_memmap: The existing EFI memory map structure 145fdc6d38dSArd Biesheuvel * @buf: Address of buffer to store new map 146fdc6d38dSArd Biesheuvel * @mem: Memory map entry to insert 147fdc6d38dSArd Biesheuvel * 148fdc6d38dSArd Biesheuvel * It is suggested that you call efi_memmap_split_count() first 149fdc6d38dSArd Biesheuvel * to see how large @buf needs to be. 150fdc6d38dSArd Biesheuvel */ 151fdc6d38dSArd Biesheuvel void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, 152fdc6d38dSArd Biesheuvel struct efi_mem_range *mem) 153fdc6d38dSArd Biesheuvel { 154fdc6d38dSArd Biesheuvel u64 m_start, m_end, m_attr; 155fdc6d38dSArd Biesheuvel efi_memory_desc_t *md; 156fdc6d38dSArd Biesheuvel u64 start, end; 157fdc6d38dSArd Biesheuvel void *old, *new; 158fdc6d38dSArd Biesheuvel 159fdc6d38dSArd Biesheuvel /* modifying range */ 160fdc6d38dSArd Biesheuvel m_start = mem->range.start; 161fdc6d38dSArd Biesheuvel m_end = mem->range.end; 162fdc6d38dSArd Biesheuvel m_attr = mem->attribute; 163fdc6d38dSArd Biesheuvel 164fdc6d38dSArd Biesheuvel /* 165fdc6d38dSArd Biesheuvel * The EFI memory map deals with regions in EFI_PAGE_SIZE 166fdc6d38dSArd Biesheuvel * units. Ensure that the region described by 'mem' is aligned 167fdc6d38dSArd Biesheuvel * correctly. 168fdc6d38dSArd Biesheuvel */ 169fdc6d38dSArd Biesheuvel if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || 170fdc6d38dSArd Biesheuvel !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { 171fdc6d38dSArd Biesheuvel WARN_ON(1); 172fdc6d38dSArd Biesheuvel return; 173fdc6d38dSArd Biesheuvel } 174fdc6d38dSArd Biesheuvel 175fdc6d38dSArd Biesheuvel for (old = old_memmap->map, new = buf; 176fdc6d38dSArd Biesheuvel old < old_memmap->map_end; 177fdc6d38dSArd Biesheuvel old += old_memmap->desc_size, new += old_memmap->desc_size) { 178fdc6d38dSArd Biesheuvel 179fdc6d38dSArd Biesheuvel /* copy original EFI memory descriptor */ 180fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 181fdc6d38dSArd Biesheuvel md = new; 182fdc6d38dSArd Biesheuvel start = md->phys_addr; 183fdc6d38dSArd Biesheuvel end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 184fdc6d38dSArd Biesheuvel 185fdc6d38dSArd Biesheuvel if (m_start <= start && end <= m_end) 186fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 187fdc6d38dSArd Biesheuvel 188fdc6d38dSArd Biesheuvel if (m_start <= start && 189fdc6d38dSArd Biesheuvel (start < m_end && m_end < end)) { 190fdc6d38dSArd Biesheuvel /* first part */ 191fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 192fdc6d38dSArd Biesheuvel md->num_pages = (m_end - md->phys_addr + 1) >> 193fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 194fdc6d38dSArd Biesheuvel /* latter part */ 195fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 196fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 197fdc6d38dSArd Biesheuvel md = new; 198fdc6d38dSArd Biesheuvel md->phys_addr = m_end + 1; 199fdc6d38dSArd Biesheuvel md->num_pages = (end - md->phys_addr + 1) >> 200fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 201fdc6d38dSArd Biesheuvel } 202fdc6d38dSArd Biesheuvel 203fdc6d38dSArd Biesheuvel if ((start < m_start && m_start < end) && m_end < end) { 204fdc6d38dSArd Biesheuvel /* first part */ 205fdc6d38dSArd Biesheuvel md->num_pages = (m_start - md->phys_addr) >> 206fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 207fdc6d38dSArd Biesheuvel /* middle part */ 208fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 209fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 210fdc6d38dSArd Biesheuvel md = new; 211fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 212fdc6d38dSArd Biesheuvel md->phys_addr = m_start; 213fdc6d38dSArd Biesheuvel md->num_pages = (m_end - m_start + 1) >> 214fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 215fdc6d38dSArd Biesheuvel /* last part */ 216fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 217fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 218fdc6d38dSArd Biesheuvel md = new; 219fdc6d38dSArd Biesheuvel md->phys_addr = m_end + 1; 220fdc6d38dSArd Biesheuvel md->num_pages = (end - m_end) >> 221fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 222fdc6d38dSArd Biesheuvel } 223fdc6d38dSArd Biesheuvel 224fdc6d38dSArd Biesheuvel if ((start < m_start && m_start < end) && 225fdc6d38dSArd Biesheuvel (end <= m_end)) { 226fdc6d38dSArd Biesheuvel /* first part */ 227fdc6d38dSArd Biesheuvel md->num_pages = (m_start - md->phys_addr) >> 228fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 229fdc6d38dSArd Biesheuvel /* latter part */ 230fdc6d38dSArd Biesheuvel new += old_memmap->desc_size; 231fdc6d38dSArd Biesheuvel memcpy(new, old, old_memmap->desc_size); 232fdc6d38dSArd Biesheuvel md = new; 233fdc6d38dSArd Biesheuvel md->phys_addr = m_start; 234fdc6d38dSArd Biesheuvel md->num_pages = (end - md->phys_addr + 1) >> 235fdc6d38dSArd Biesheuvel EFI_PAGE_SHIFT; 236fdc6d38dSArd Biesheuvel md->attribute |= m_attr; 237fdc6d38dSArd Biesheuvel } 238fdc6d38dSArd Biesheuvel } 239fdc6d38dSArd Biesheuvel } 240