1745e3ed8SKirill A. Shutemov // SPDX-License-Identifier: GPL-2.0-only
2745e3ed8SKirill A. Shutemov
3745e3ed8SKirill A. Shutemov #include <linux/efi.h>
4745e3ed8SKirill A. Shutemov #include <asm/efi.h>
5745e3ed8SKirill A. Shutemov #include "efistub.h"
6745e3ed8SKirill A. Shutemov
7745e3ed8SKirill A. Shutemov struct efi_unaccepted_memory *unaccepted_table;
8745e3ed8SKirill A. Shutemov
allocate_unaccepted_bitmap(__u32 nr_desc,struct efi_boot_memmap * map)9745e3ed8SKirill A. Shutemov efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc,
10745e3ed8SKirill A. Shutemov struct efi_boot_memmap *map)
11745e3ed8SKirill A. Shutemov {
12745e3ed8SKirill A. Shutemov efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;
13745e3ed8SKirill A. Shutemov u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size;
14745e3ed8SKirill A. Shutemov efi_status_t status;
15745e3ed8SKirill A. Shutemov int i;
16745e3ed8SKirill A. Shutemov
17745e3ed8SKirill A. Shutemov /* Check if the table is already installed */
18745e3ed8SKirill A. Shutemov unaccepted_table = get_efi_config_table(unaccepted_table_guid);
19745e3ed8SKirill A. Shutemov if (unaccepted_table) {
20745e3ed8SKirill A. Shutemov if (unaccepted_table->version != 1) {
21745e3ed8SKirill A. Shutemov efi_err("Unknown version of unaccepted memory table\n");
22745e3ed8SKirill A. Shutemov return EFI_UNSUPPORTED;
23745e3ed8SKirill A. Shutemov }
24745e3ed8SKirill A. Shutemov return EFI_SUCCESS;
25745e3ed8SKirill A. Shutemov }
26745e3ed8SKirill A. Shutemov
27745e3ed8SKirill A. Shutemov /* Check if there's any unaccepted memory and find the max address */
28745e3ed8SKirill A. Shutemov for (i = 0; i < nr_desc; i++) {
29745e3ed8SKirill A. Shutemov efi_memory_desc_t *d;
30745e3ed8SKirill A. Shutemov unsigned long m = (unsigned long)map->map;
31745e3ed8SKirill A. Shutemov
32745e3ed8SKirill A. Shutemov d = efi_early_memdesc_ptr(m, map->desc_size, i);
33745e3ed8SKirill A. Shutemov if (d->type != EFI_UNACCEPTED_MEMORY)
34745e3ed8SKirill A. Shutemov continue;
35745e3ed8SKirill A. Shutemov
36745e3ed8SKirill A. Shutemov unaccepted_start = min(unaccepted_start, d->phys_addr);
37745e3ed8SKirill A. Shutemov unaccepted_end = max(unaccepted_end,
38745e3ed8SKirill A. Shutemov d->phys_addr + d->num_pages * PAGE_SIZE);
39745e3ed8SKirill A. Shutemov }
40745e3ed8SKirill A. Shutemov
41745e3ed8SKirill A. Shutemov if (unaccepted_start == ULLONG_MAX)
42745e3ed8SKirill A. Shutemov return EFI_SUCCESS;
43745e3ed8SKirill A. Shutemov
44745e3ed8SKirill A. Shutemov unaccepted_start = round_down(unaccepted_start,
45745e3ed8SKirill A. Shutemov EFI_UNACCEPTED_UNIT_SIZE);
46745e3ed8SKirill A. Shutemov unaccepted_end = round_up(unaccepted_end, EFI_UNACCEPTED_UNIT_SIZE);
47745e3ed8SKirill A. Shutemov
48745e3ed8SKirill A. Shutemov /*
49745e3ed8SKirill A. Shutemov * If unaccepted memory is present, allocate a bitmap to track what
50745e3ed8SKirill A. Shutemov * memory has to be accepted before access.
51745e3ed8SKirill A. Shutemov *
52745e3ed8SKirill A. Shutemov * One bit in the bitmap represents 2MiB in the address space:
53745e3ed8SKirill A. Shutemov * A 4k bitmap can track 64GiB of physical address space.
54745e3ed8SKirill A. Shutemov *
55745e3ed8SKirill A. Shutemov * In the worst case scenario -- a huge hole in the middle of the
56745e3ed8SKirill A. Shutemov * address space -- It needs 256MiB to handle 4PiB of the address
57745e3ed8SKirill A. Shutemov * space.
58745e3ed8SKirill A. Shutemov *
59745e3ed8SKirill A. Shutemov * The bitmap will be populated in setup_e820() according to the memory
60745e3ed8SKirill A. Shutemov * map after efi_exit_boot_services().
61745e3ed8SKirill A. Shutemov */
62745e3ed8SKirill A. Shutemov bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start,
63745e3ed8SKirill A. Shutemov EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE);
64745e3ed8SKirill A. Shutemov
65*e7761d82SArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
66745e3ed8SKirill A. Shutemov sizeof(*unaccepted_table) + bitmap_size,
67745e3ed8SKirill A. Shutemov (void **)&unaccepted_table);
68745e3ed8SKirill A. Shutemov if (status != EFI_SUCCESS) {
69745e3ed8SKirill A. Shutemov efi_err("Failed to allocate unaccepted memory config table\n");
70745e3ed8SKirill A. Shutemov return status;
71745e3ed8SKirill A. Shutemov }
72745e3ed8SKirill A. Shutemov
73745e3ed8SKirill A. Shutemov unaccepted_table->version = 1;
74745e3ed8SKirill A. Shutemov unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE;
75745e3ed8SKirill A. Shutemov unaccepted_table->phys_base = unaccepted_start;
76745e3ed8SKirill A. Shutemov unaccepted_table->size = bitmap_size;
77745e3ed8SKirill A. Shutemov memset(unaccepted_table->bitmap, 0, bitmap_size);
78745e3ed8SKirill A. Shutemov
79745e3ed8SKirill A. Shutemov status = efi_bs_call(install_configuration_table,
80745e3ed8SKirill A. Shutemov &unaccepted_table_guid, unaccepted_table);
81745e3ed8SKirill A. Shutemov if (status != EFI_SUCCESS) {
82745e3ed8SKirill A. Shutemov efi_bs_call(free_pool, unaccepted_table);
83745e3ed8SKirill A. Shutemov efi_err("Failed to install unaccepted memory config table!\n");
84745e3ed8SKirill A. Shutemov }
85745e3ed8SKirill A. Shutemov
86745e3ed8SKirill A. Shutemov return status;
87745e3ed8SKirill A. Shutemov }
88745e3ed8SKirill A. Shutemov
89745e3ed8SKirill A. Shutemov /*
90745e3ed8SKirill A. Shutemov * The accepted memory bitmap only works at unit_size granularity. Take
91745e3ed8SKirill A. Shutemov * unaligned start/end addresses and either:
92745e3ed8SKirill A. Shutemov * 1. Accepts the memory immediately and in its entirety
93745e3ed8SKirill A. Shutemov * 2. Accepts unaligned parts, and marks *some* aligned part unaccepted
94745e3ed8SKirill A. Shutemov *
95745e3ed8SKirill A. Shutemov * The function will never reach the bitmap_set() with zero bits to set.
96745e3ed8SKirill A. Shutemov */
process_unaccepted_memory(u64 start,u64 end)97745e3ed8SKirill A. Shutemov void process_unaccepted_memory(u64 start, u64 end)
98745e3ed8SKirill A. Shutemov {
99745e3ed8SKirill A. Shutemov u64 unit_size = unaccepted_table->unit_size;
100745e3ed8SKirill A. Shutemov u64 unit_mask = unaccepted_table->unit_size - 1;
101745e3ed8SKirill A. Shutemov u64 bitmap_size = unaccepted_table->size;
102745e3ed8SKirill A. Shutemov
103745e3ed8SKirill A. Shutemov /*
104745e3ed8SKirill A. Shutemov * Ensure that at least one bit will be set in the bitmap by
105745e3ed8SKirill A. Shutemov * immediately accepting all regions under 2*unit_size. This is
106745e3ed8SKirill A. Shutemov * imprecise and may immediately accept some areas that could
107745e3ed8SKirill A. Shutemov * have been represented in the bitmap. But, results in simpler
108745e3ed8SKirill A. Shutemov * code below
109745e3ed8SKirill A. Shutemov *
110745e3ed8SKirill A. Shutemov * Consider case like this (assuming unit_size == 2MB):
111745e3ed8SKirill A. Shutemov *
112745e3ed8SKirill A. Shutemov * | 4k | 2044k | 2048k |
113745e3ed8SKirill A. Shutemov * ^ 0x0 ^ 2MB ^ 4MB
114745e3ed8SKirill A. Shutemov *
115745e3ed8SKirill A. Shutemov * Only the first 4k has been accepted. The 0MB->2MB region can not be
116745e3ed8SKirill A. Shutemov * represented in the bitmap. The 2MB->4MB region can be represented in
117745e3ed8SKirill A. Shutemov * the bitmap. But, the 0MB->4MB region is <2*unit_size and will be
118745e3ed8SKirill A. Shutemov * immediately accepted in its entirety.
119745e3ed8SKirill A. Shutemov */
120745e3ed8SKirill A. Shutemov if (end - start < 2 * unit_size) {
121745e3ed8SKirill A. Shutemov arch_accept_memory(start, end);
122745e3ed8SKirill A. Shutemov return;
123745e3ed8SKirill A. Shutemov }
124745e3ed8SKirill A. Shutemov
125745e3ed8SKirill A. Shutemov /*
126745e3ed8SKirill A. Shutemov * No matter how the start and end are aligned, at least one unaccepted
127745e3ed8SKirill A. Shutemov * unit_size area will remain to be marked in the bitmap.
128745e3ed8SKirill A. Shutemov */
129745e3ed8SKirill A. Shutemov
130745e3ed8SKirill A. Shutemov /* Immediately accept a <unit_size piece at the start: */
131745e3ed8SKirill A. Shutemov if (start & unit_mask) {
132745e3ed8SKirill A. Shutemov arch_accept_memory(start, round_up(start, unit_size));
133745e3ed8SKirill A. Shutemov start = round_up(start, unit_size);
134745e3ed8SKirill A. Shutemov }
135745e3ed8SKirill A. Shutemov
136745e3ed8SKirill A. Shutemov /* Immediately accept a <unit_size piece at the end: */
137745e3ed8SKirill A. Shutemov if (end & unit_mask) {
138745e3ed8SKirill A. Shutemov arch_accept_memory(round_down(end, unit_size), end);
139745e3ed8SKirill A. Shutemov end = round_down(end, unit_size);
140745e3ed8SKirill A. Shutemov }
141745e3ed8SKirill A. Shutemov
142745e3ed8SKirill A. Shutemov /*
143745e3ed8SKirill A. Shutemov * Accept part of the range that before phys_base and cannot be recorded
144745e3ed8SKirill A. Shutemov * into the bitmap.
145745e3ed8SKirill A. Shutemov */
146745e3ed8SKirill A. Shutemov if (start < unaccepted_table->phys_base) {
147745e3ed8SKirill A. Shutemov arch_accept_memory(start,
148745e3ed8SKirill A. Shutemov min(unaccepted_table->phys_base, end));
149745e3ed8SKirill A. Shutemov start = unaccepted_table->phys_base;
150745e3ed8SKirill A. Shutemov }
151745e3ed8SKirill A. Shutemov
152745e3ed8SKirill A. Shutemov /* Nothing to record */
153745e3ed8SKirill A. Shutemov if (end < unaccepted_table->phys_base)
154745e3ed8SKirill A. Shutemov return;
155745e3ed8SKirill A. Shutemov
156745e3ed8SKirill A. Shutemov /* Translate to offsets from the beginning of the bitmap */
157745e3ed8SKirill A. Shutemov start -= unaccepted_table->phys_base;
158745e3ed8SKirill A. Shutemov end -= unaccepted_table->phys_base;
159745e3ed8SKirill A. Shutemov
160745e3ed8SKirill A. Shutemov /* Accept memory that doesn't fit into bitmap */
161745e3ed8SKirill A. Shutemov if (end > bitmap_size * unit_size * BITS_PER_BYTE) {
162745e3ed8SKirill A. Shutemov unsigned long phys_start, phys_end;
163745e3ed8SKirill A. Shutemov
164745e3ed8SKirill A. Shutemov phys_start = bitmap_size * unit_size * BITS_PER_BYTE +
165745e3ed8SKirill A. Shutemov unaccepted_table->phys_base;
166745e3ed8SKirill A. Shutemov phys_end = end + unaccepted_table->phys_base;
167745e3ed8SKirill A. Shutemov
168745e3ed8SKirill A. Shutemov arch_accept_memory(phys_start, phys_end);
169745e3ed8SKirill A. Shutemov end = bitmap_size * unit_size * BITS_PER_BYTE;
170745e3ed8SKirill A. Shutemov }
171745e3ed8SKirill A. Shutemov
172745e3ed8SKirill A. Shutemov /*
173745e3ed8SKirill A. Shutemov * 'start' and 'end' are now both unit_size-aligned.
174745e3ed8SKirill A. Shutemov * Record the range as being unaccepted:
175745e3ed8SKirill A. Shutemov */
176745e3ed8SKirill A. Shutemov bitmap_set(unaccepted_table->bitmap,
177745e3ed8SKirill A. Shutemov start / unit_size, (end - start) / unit_size);
178745e3ed8SKirill A. Shutemov }
179745e3ed8SKirill A. Shutemov
accept_memory(phys_addr_t start,phys_addr_t end)180745e3ed8SKirill A. Shutemov void accept_memory(phys_addr_t start, phys_addr_t end)
181745e3ed8SKirill A. Shutemov {
182745e3ed8SKirill A. Shutemov unsigned long range_start, range_end;
183745e3ed8SKirill A. Shutemov unsigned long bitmap_size;
184745e3ed8SKirill A. Shutemov u64 unit_size;
185745e3ed8SKirill A. Shutemov
186745e3ed8SKirill A. Shutemov if (!unaccepted_table)
187745e3ed8SKirill A. Shutemov return;
188745e3ed8SKirill A. Shutemov
189745e3ed8SKirill A. Shutemov unit_size = unaccepted_table->unit_size;
190745e3ed8SKirill A. Shutemov
191745e3ed8SKirill A. Shutemov /*
192745e3ed8SKirill A. Shutemov * Only care for the part of the range that is represented
193745e3ed8SKirill A. Shutemov * in the bitmap.
194745e3ed8SKirill A. Shutemov */
195745e3ed8SKirill A. Shutemov if (start < unaccepted_table->phys_base)
196745e3ed8SKirill A. Shutemov start = unaccepted_table->phys_base;
197745e3ed8SKirill A. Shutemov if (end < unaccepted_table->phys_base)
198745e3ed8SKirill A. Shutemov return;
199745e3ed8SKirill A. Shutemov
200745e3ed8SKirill A. Shutemov /* Translate to offsets from the beginning of the bitmap */
201745e3ed8SKirill A. Shutemov start -= unaccepted_table->phys_base;
202745e3ed8SKirill A. Shutemov end -= unaccepted_table->phys_base;
203745e3ed8SKirill A. Shutemov
204745e3ed8SKirill A. Shutemov /* Make sure not to overrun the bitmap */
205745e3ed8SKirill A. Shutemov if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE)
206745e3ed8SKirill A. Shutemov end = unaccepted_table->size * unit_size * BITS_PER_BYTE;
207745e3ed8SKirill A. Shutemov
208745e3ed8SKirill A. Shutemov range_start = start / unit_size;
209745e3ed8SKirill A. Shutemov bitmap_size = DIV_ROUND_UP(end, unit_size);
210745e3ed8SKirill A. Shutemov
211745e3ed8SKirill A. Shutemov for_each_set_bitrange_from(range_start, range_end,
212745e3ed8SKirill A. Shutemov unaccepted_table->bitmap, bitmap_size) {
213745e3ed8SKirill A. Shutemov unsigned long phys_start, phys_end;
214745e3ed8SKirill A. Shutemov
215745e3ed8SKirill A. Shutemov phys_start = range_start * unit_size + unaccepted_table->phys_base;
216745e3ed8SKirill A. Shutemov phys_end = range_end * unit_size + unaccepted_table->phys_base;
217745e3ed8SKirill A. Shutemov
218745e3ed8SKirill A. Shutemov arch_accept_memory(phys_start, phys_end);
219745e3ed8SKirill A. Shutemov bitmap_clear(unaccepted_table->bitmap,
220745e3ed8SKirill A. Shutemov range_start, range_end - range_start);
221745e3ed8SKirill A. Shutemov }
222745e3ed8SKirill A. Shutemov }
223