Merge tag 'pull-work.unaligned' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / drivers / firmware / efi / libstub / unaccepted_memory.c
CommitLineData
745e3ed8
KS
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
887c4cf5 32 d = efi_memdesc_ptr(m, map->desc_size, i);
745e3ed8
KS
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
e7761d82 65 status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
745e3ed8
KS
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
5adfeaec 180void accept_memory(phys_addr_t start, unsigned long size)
745e3ed8
KS
181{
182 unsigned long range_start, range_end;
5adfeaec 183 phys_addr_t end = start + size;
745e3ed8
KS
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}