Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  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_early_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 */
 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 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
180void accept_memory(phys_addr_t start, phys_addr_t end)
181{
182	unsigned long range_start, range_end;
183	unsigned long bitmap_size;
184	u64 unit_size;
185
186	if (!unaccepted_table)
187		return;
188
189	unit_size = unaccepted_table->unit_size;
190
191	/*
192	 * Only care for the part of the range that is represented
193	 * in the bitmap.
194	 */
195	if (start < unaccepted_table->phys_base)
196		start = unaccepted_table->phys_base;
197	if (end < unaccepted_table->phys_base)
198		return;
199
200	/* Translate to offsets from the beginning of the bitmap */
201	start -= unaccepted_table->phys_base;
202	end -= unaccepted_table->phys_base;
203
204	/* Make sure not to overrun the bitmap */
205	if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE)
206		end = unaccepted_table->size * unit_size * BITS_PER_BYTE;
207
208	range_start = start / unit_size;
209	bitmap_size = DIV_ROUND_UP(end, unit_size);
210
211	for_each_set_bitrange_from(range_start, range_end,
212				   unaccepted_table->bitmap, bitmap_size) {
213		unsigned long phys_start, phys_end;
214
215		phys_start = range_start * unit_size + unaccepted_table->phys_base;
216		phys_end = range_end * unit_size + unaccepted_table->phys_base;
217
218		arch_accept_memory(phys_start, phys_end);
219		bitmap_clear(unaccepted_table->bitmap,
220			     range_start, range_end - range_start);
221	}
222}