1 // SPDX-License-Identifier: GPL-2.0-only
7 struct efi_unaccepted_memory
*unaccepted_table
;
9 efi_status_t
allocate_unaccepted_bitmap(__u32 nr_desc
,
10 struct efi_boot_memmap
*map
)
12 efi_guid_t unaccepted_table_guid
= LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID
;
13 u64 unaccepted_start
= ULLONG_MAX
, unaccepted_end
= 0, bitmap_size
;
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
;
27 /* Check if there's any unaccepted memory and find the max address */
28 for (i
= 0; i
< nr_desc
; i
++) {
30 unsigned long m
= (unsigned long)map
->map
;
32 d
= efi_memdesc_ptr(m
, map
->desc_size
, i
);
33 if (d
->type
!= EFI_UNACCEPTED_MEMORY
)
36 unaccepted_start
= min(unaccepted_start
, d
->phys_addr
);
37 unaccepted_end
= max(unaccepted_end
,
38 d
->phys_addr
+ d
->num_pages
* PAGE_SIZE
);
41 if (unaccepted_start
== ULLONG_MAX
)
44 unaccepted_start
= round_down(unaccepted_start
,
45 EFI_UNACCEPTED_UNIT_SIZE
);
46 unaccepted_end
= round_up(unaccepted_end
, EFI_UNACCEPTED_UNIT_SIZE
);
49 * If unaccepted memory is present, allocate a bitmap to track what
50 * memory has to be accepted before access.
52 * One bit in the bitmap represents 2MiB in the address space:
53 * A 4k bitmap can track 64GiB of physical address space.
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
59 * The bitmap will be populated in setup_e820() according to the memory
60 * map after efi_exit_boot_services().
62 bitmap_size
= DIV_ROUND_UP(unaccepted_end
- unaccepted_start
,
63 EFI_UNACCEPTED_UNIT_SIZE
* BITS_PER_BYTE
);
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");
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
);
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");
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
95 * The function will never reach the bitmap_set() with zero bits to set.
97 void process_unaccepted_memory(u64 start
, u64 end
)
99 u64 unit_size
= unaccepted_table
->unit_size
;
100 u64 unit_mask
= unaccepted_table
->unit_size
- 1;
101 u64 bitmap_size
= unaccepted_table
->size
;
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
110 * Consider case like this (assuming unit_size == 2MB):
112 * | 4k | 2044k | 2048k |
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.
120 if (end
- start
< 2 * unit_size
) {
121 arch_accept_memory(start
, end
);
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.
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
);
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
);
143 * Accept part of the range that before phys_base and cannot be recorded
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
;
152 /* Nothing to record */
153 if (end
< unaccepted_table
->phys_base
)
156 /* Translate to offsets from the beginning of the bitmap */
157 start
-= unaccepted_table
->phys_base
;
158 end
-= unaccepted_table
->phys_base
;
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
;
164 phys_start
= bitmap_size
* unit_size
* BITS_PER_BYTE
+
165 unaccepted_table
->phys_base
;
166 phys_end
= end
+ unaccepted_table
->phys_base
;
168 arch_accept_memory(phys_start
, phys_end
);
169 end
= bitmap_size
* unit_size
* BITS_PER_BYTE
;
173 * 'start' and 'end' are now both unit_size-aligned.
174 * Record the range as being unaccepted:
176 bitmap_set(unaccepted_table
->bitmap
,
177 start
/ unit_size
, (end
- start
) / unit_size
);
180 void accept_memory(phys_addr_t start
, unsigned long size
)
182 unsigned long range_start
, range_end
;
183 phys_addr_t end
= start
+ size
;
184 unsigned long bitmap_size
;
187 if (!unaccepted_table
)
190 unit_size
= unaccepted_table
->unit_size
;
193 * Only care for the part of the range that is represented
196 if (start
< unaccepted_table
->phys_base
)
197 start
= unaccepted_table
->phys_base
;
198 if (end
< unaccepted_table
->phys_base
)
201 /* Translate to offsets from the beginning of the bitmap */
202 start
-= unaccepted_table
->phys_base
;
203 end
-= unaccepted_table
->phys_base
;
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
;
209 range_start
= start
/ unit_size
;
210 bitmap_size
= DIV_ROUND_UP(end
, unit_size
);
212 for_each_set_bitrange_from(range_start
, range_end
,
213 unaccepted_table
->bitmap
, bitmap_size
) {
214 unsigned long phys_start
, phys_end
;
216 phys_start
= range_start
* unit_size
+ unaccepted_table
->phys_base
;
217 phys_end
= range_end
* unit_size
+ unaccepted_table
->phys_base
;
219 arch_accept_memory(phys_start
, phys_end
);
220 bitmap_clear(unaccepted_table
->bitmap
,
221 range_start
, range_end
- range_start
);