brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 757dbe7 Raw
224 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3#include <linux/efi.h>4#include <asm/efi.h>5#include "efistub.h"6 7struct efi_unaccepted_memory *unaccepted_table;8 9efi_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 what50	 * 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 the56	 * address space -- It needs 256MiB to handle 4PiB of the address57	 * space.58	 *59	 * The bitmap will be populated in setup_e820() according to the memory60	 * 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.  Take91 * unaligned start/end addresses and either:92 *  1. Accepts the memory immediately and in its entirety93 *  2. Accepts unaligned parts, and marks *some* aligned part unaccepted94 *95 * The function will never reach the bitmap_set() with zero bits to set.96 */97void 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 by105	 * immediately accepting all regions under 2*unit_size.  This is106	 * imprecise and may immediately accept some areas that could107	 * have been represented in the bitmap.  But, results in simpler108	 * code below109	 *110	 * Consider case like this (assuming unit_size == 2MB):111	 *112	 * | 4k | 2044k |    2048k   |113	 * ^ 0x0        ^ 2MB        ^ 4MB114	 *115	 * Only the first 4k has been accepted. The 0MB->2MB region can not be116	 * represented in the bitmap. The 2MB->4MB region can be represented in117	 * the bitmap. But, the 0MB->4MB region is <2*unit_size and will be118	 * 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 unaccepted127	 * 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 recorded144	 * 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 180void 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 represented194	 * 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