Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/unaccepted_memory.c
26483 views
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
225