diff options
Diffstat (limited to 'arch/x86/platform/efi/quirks.c')
-rw-r--r-- | arch/x86/platform/efi/quirks.c | 79 |
1 files changed, 62 insertions, 17 deletions
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 2d66db8f80f9..ed30e79347e8 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c | |||
@@ -131,6 +131,27 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) | |||
131 | EXPORT_SYMBOL_GPL(efi_query_variable_store); | 131 | EXPORT_SYMBOL_GPL(efi_query_variable_store); |
132 | 132 | ||
133 | /* | 133 | /* |
134 | * Helper function for efi_reserve_boot_services() to figure out if we | ||
135 | * can free regions in efi_free_boot_services(). | ||
136 | * | ||
137 | * Use this function to ensure we do not free regions owned by somebody | ||
138 | * else. We must only reserve (and then free) regions: | ||
139 | * | ||
140 | * - Not within any part of the kernel | ||
141 | * - Not the BIOS reserved area (E820_RESERVED, E820_NVS, etc) | ||
142 | */ | ||
143 | static bool can_free_region(u64 start, u64 size) | ||
144 | { | ||
145 | if (start + size > __pa_symbol(_text) && start <= __pa_symbol(_end)) | ||
146 | return false; | ||
147 | |||
148 | if (!e820_all_mapped(start, start+size, E820_RAM)) | ||
149 | return false; | ||
150 | |||
151 | return true; | ||
152 | } | ||
153 | |||
154 | /* | ||
134 | * The UEFI specification makes it clear that the operating system is free to do | 155 | * The UEFI specification makes it clear that the operating system is free to do |
135 | * whatever it wants with boot services code after ExitBootServices() has been | 156 | * whatever it wants with boot services code after ExitBootServices() has been |
136 | * called. Ignoring this recommendation a significant bunch of EFI implementations | 157 | * called. Ignoring this recommendation a significant bunch of EFI implementations |
@@ -147,26 +168,50 @@ void __init efi_reserve_boot_services(void) | |||
147 | efi_memory_desc_t *md = p; | 168 | efi_memory_desc_t *md = p; |
148 | u64 start = md->phys_addr; | 169 | u64 start = md->phys_addr; |
149 | u64 size = md->num_pages << EFI_PAGE_SHIFT; | 170 | u64 size = md->num_pages << EFI_PAGE_SHIFT; |
171 | bool already_reserved; | ||
150 | 172 | ||
151 | if (md->type != EFI_BOOT_SERVICES_CODE && | 173 | if (md->type != EFI_BOOT_SERVICES_CODE && |
152 | md->type != EFI_BOOT_SERVICES_DATA) | 174 | md->type != EFI_BOOT_SERVICES_DATA) |
153 | continue; | 175 | continue; |
154 | /* Only reserve where possible: | 176 | |
155 | * - Not within any already allocated areas | 177 | already_reserved = memblock_is_region_reserved(start, size); |
156 | * - Not over any memory area (really needed, if above?) | 178 | |
157 | * - Not within any part of the kernel | 179 | /* |
158 | * - Not the bios reserved area | 180 | * Because the following memblock_reserve() is paired |
159 | */ | 181 | * with free_bootmem_late() for this region in |
160 | if ((start + size > __pa_symbol(_text) | 182 | * efi_free_boot_services(), we must be extremely |
161 | && start <= __pa_symbol(_end)) || | 183 | * careful not to reserve, and subsequently free, |
162 | !e820_all_mapped(start, start+size, E820_RAM) || | 184 | * critical regions of memory (like the kernel image) or |
163 | memblock_is_region_reserved(start, size)) { | 185 | * those regions that somebody else has already |
164 | /* Could not reserve, skip it */ | 186 | * reserved. |
165 | md->num_pages = 0; | 187 | * |
166 | memblock_dbg("Could not reserve boot range [0x%010llx-0x%010llx]\n", | 188 | * A good example of a critical region that must not be |
167 | start, start+size-1); | 189 | * freed is page zero (first 4Kb of memory), which may |
168 | } else | 190 | * contain boot services code/data but is marked |
191 | * E820_RESERVED by trim_bios_range(). | ||
192 | */ | ||
193 | if (!already_reserved) { | ||
169 | memblock_reserve(start, size); | 194 | memblock_reserve(start, size); |
195 | |||
196 | /* | ||
197 | * If we are the first to reserve the region, no | ||
198 | * one else cares about it. We own it and can | ||
199 | * free it later. | ||
200 | */ | ||
201 | if (can_free_region(start, size)) | ||
202 | continue; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * We don't own the region. We must not free it. | ||
207 | * | ||
208 | * Setting this bit for a boot services region really | ||
209 | * doesn't make sense as far as the firmware is | ||
210 | * concerned, but it does provide us with a way to tag | ||
211 | * those regions that must not be paired with | ||
212 | * free_bootmem_late(). | ||
213 | */ | ||
214 | md->attribute |= EFI_MEMORY_RUNTIME; | ||
170 | } | 215 | } |
171 | } | 216 | } |
172 | 217 | ||
@@ -183,8 +228,8 @@ void __init efi_free_boot_services(void) | |||
183 | md->type != EFI_BOOT_SERVICES_DATA) | 228 | md->type != EFI_BOOT_SERVICES_DATA) |
184 | continue; | 229 | continue; |
185 | 230 | ||
186 | /* Could not reserve boot area */ | 231 | /* Do not free, someone else owns it: */ |
187 | if (!size) | 232 | if (md->attribute & EFI_MEMORY_RUNTIME) |
188 | continue; | 233 | continue; |
189 | 234 | ||
190 | free_bootmem_late(start, size); | 235 | free_bootmem_late(start, size); |