1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/efi.h> 4 #include <asm/efi.h> 5 #include "efistub.h" 6 7 struct efi_unaccepted_memory *unaccepted_table; 8 9 efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, 10 struct efi_boot_memmap *map) 11 { 12 efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID; 13 u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size; 14 efi_status_t status; 15 int i; 16 17 /* Check if the table is already installed */ 18 unaccepted_table = get_efi_config_table(unaccepted_table_guid); 19 if (unaccepted_table) { 20 if (unaccepted_table->version != 1) { 21 efi_err("Unknown version of unaccepted memory table\n"); 22 return EFI_UNSUPPORTED; 23 } 24 return EFI_SUCCESS; 25 } 26 27 /* Check if there's any unaccepted memory and find the max address */ 28 for (i = 0; i < nr_desc; i++) { 29 efi_memory_desc_t *d; 30 unsigned long m = (unsigned long)map->map; 31 32 d = efi_memdesc_ptr(m, map->desc_size, i); 33 if (d->type != EFI_UNACCEPTED_MEMORY) 34 continue; 35 36 unaccepted_start = min(unaccepted_start, d->phys_addr); 37 unaccepted_end = max(unaccepted_end, 38 d->phys_addr + d->num_pages * PAGE_SIZE); 39 } 40 41 if (unaccepted_start == ULLONG_MAX) 42 return EFI_SUCCESS; 43 44 unaccepted_start = round_down(unaccepted_start, 45 EFI_UNACCEPTED_UNIT_SIZE); 46 unaccepted_end = round_up(unaccepted_end, EFI_UNACCEPTED_UNIT_SIZE); 47 48 /* 49 * If unaccepted memory is present, allocate a bitmap to track what 50 * memory has to be accepted before access. 51 * 52 * One bit in the bitmap represents 2MiB in the address space: 53 * A 4k bitmap can track 64GiB of physical address space. 54 * 55 * In the worst case scenario -- a huge hole in the middle of the 56 * address space -- It needs 256MiB to handle 4PiB of the address 57 * space. 58 * 59 * The bitmap will be populated in setup_e820() according to the memory 60 * map after efi_exit_boot_services(). 61 */ 62 bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start, 63 EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE); 64 65 status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, 66 sizeof(*unaccepted_table) + bitmap_size, 67 (void **)&unaccepted_table); 68 if (status != EFI_SUCCESS) { 69 efi_err("Failed to allocate unaccepted memory config table\n"); 70 return status; 71 } 72 73 unaccepted_table->version = 1; 74 unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE; 75 unaccepted_table->phys_base = unaccepted_start; 76 unaccepted_table->size = bitmap_size; 77 memset(unaccepted_table->bitmap, 0, bitmap_size); 78 79 status = efi_bs_call(install_configuration_table, 80 &unaccepted_table_guid, unaccepted_table); 81 if (status != EFI_SUCCESS) { 82 efi_bs_call(free_pool, unaccepted_table); 83 efi_err("Failed to install unaccepted memory config table!\n"); 84 } 85 86 return status; 87 } 88 89 /* 90 * The accepted memory bitmap only works at unit_size granularity. Take 91 * unaligned start/end addresses and either: 92 * 1. Accepts the memory immediately and in its entirety 93 * 2. Accepts unaligned parts, and marks *some* aligned part unaccepted 94 * 95 * The function will never reach the bitmap_set() with zero bits to set. 96 */ 97 void process_unaccepted_memory(u64 start, u64 end) 98 { 99 u64 unit_size = unaccepted_table->unit_size; 100 u64 unit_mask = unaccepted_table->unit_size - 1; 101 u64 bitmap_size = unaccepted_table->size; 102 103 /* 104 * Ensure that at least one bit will be set in the bitmap by 105 * immediately accepting all regions under 2*unit_size. This is 106 * imprecise and may immediately accept some areas that could 107 * have been represented in the bitmap. But, results in simpler 108 * code below 109 * 110 * Consider case like this (assuming unit_size == 2MB): 111 * 112 * | 4k | 2044k | 2048k | 113 * ^ 0x0 ^ 2MB ^ 4MB 114 * 115 * Only the first 4k has been accepted. The 0MB->2MB region can not be 116 * represented in the bitmap. The 2MB->4MB region can be represented in 117 * the bitmap. But, the 0MB->4MB region is <2*unit_size and will be 118 * immediately accepted in its entirety. 119 */ 120 if (end - start < 2 * unit_size) { 121 arch_accept_memory(start, end); 122 return; 123 } 124 125 /* 126 * No matter how the start and end are aligned, at least one unaccepted 127 * unit_size area will remain to be marked in the bitmap. 128 */ 129 130 /* Immediately accept a <unit_size piece at the start: */ 131 if (start & unit_mask) { 132 arch_accept_memory(start, round_up(start, unit_size)); 133 start = round_up(start, unit_size); 134 } 135 136 /* Immediately accept a <unit_size piece at the end: */ 137 if (end & unit_mask) { 138 arch_accept_memory(round_down(end, unit_size), end); 139 end = round_down(end, unit_size); 140 } 141 142 /* 143 * Accept part of the range that before phys_base and cannot be recorded 144 * into the bitmap. 145 */ 146 if (start < unaccepted_table->phys_base) { 147 arch_accept_memory(start, 148 min(unaccepted_table->phys_base, end)); 149 start = unaccepted_table->phys_base; 150 } 151 152 /* Nothing to record */ 153 if (end < unaccepted_table->phys_base) 154 return; 155 156 /* Translate to offsets from the beginning of the bitmap */ 157 start -= unaccepted_table->phys_base; 158 end -= unaccepted_table->phys_base; 159 160 /* Accept memory that doesn't fit into bitmap */ 161 if (end > bitmap_size * unit_size * BITS_PER_BYTE) { 162 unsigned long phys_start, phys_end; 163 164 phys_start = bitmap_size * unit_size * BITS_PER_BYTE + 165 unaccepted_table->phys_base; 166 phys_end = end + unaccepted_table->phys_base; 167 168 arch_accept_memory(phys_start, phys_end); 169 end = bitmap_size * unit_size * BITS_PER_BYTE; 170 } 171 172 /* 173 * 'start' and 'end' are now both unit_size-aligned. 174 * Record the range as being unaccepted: 175 */ 176 bitmap_set(unaccepted_table->bitmap, 177 start / unit_size, (end - start) / unit_size); 178 } 179 180 void accept_memory(phys_addr_t start, unsigned long size) 181 { 182 unsigned long range_start, range_end; 183 phys_addr_t end = start + size; 184 unsigned long bitmap_size; 185 u64 unit_size; 186 187 if (!unaccepted_table) 188 return; 189 190 unit_size = unaccepted_table->unit_size; 191 192 /* 193 * Only care for the part of the range that is represented 194 * in the bitmap. 195 */ 196 if (start < unaccepted_table->phys_base) 197 start = unaccepted_table->phys_base; 198 if (end < unaccepted_table->phys_base) 199 return; 200 201 /* Translate to offsets from the beginning of the bitmap */ 202 start -= unaccepted_table->phys_base; 203 end -= unaccepted_table->phys_base; 204 205 /* Make sure not to overrun the bitmap */ 206 if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE) 207 end = unaccepted_table->size * unit_size * BITS_PER_BYTE; 208 209 range_start = start / unit_size; 210 bitmap_size = DIV_ROUND_UP(end, unit_size); 211 212 for_each_set_bitrange_from(range_start, range_end, 213 unaccepted_table->bitmap, bitmap_size) { 214 unsigned long phys_start, phys_end; 215 216 phys_start = range_start * unit_size + unaccepted_table->phys_base; 217 phys_end = range_end * unit_size + unaccepted_table->phys_base; 218 219 arch_accept_memory(phys_start, phys_end); 220 bitmap_clear(unaccepted_table->bitmap, 221 range_start, range_end - range_start); 222 } 223 } 224