History

efi/x86: Prune invalid memory map entries and fix boot regression

Some machines, such as the Lenovo ThinkPad W541 with firmware GNET80WW (2.28), include memory map entries with phys_addr=0x0 and num_pages=0. These machines fail to boot after the following commit, commit 8e80632fb23f ("efi/esrt: Use efi_mem_reserve() and avoid a kmalloc()") Fix this by removing such bogus entries from the memory map. Furthermore, currently the log output for this case (with efi=debug) looks like: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[0x0000000000000000-0xffffffffffffffff] (0MB) This is clearly wrong, and also not as informative as it could be. This patch changes it so that if we find obviously invalid memory map entries, we print an error and skip those entries. It also detects the display of the address range calculation overflow, so the new output is: [ 0.000000] efi: [Firmware Bug]: Invalid EFI memory map entries: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[0x0000000000000000-0x0000000000000000] (invalid) It also detects memory map sizes that would overflow the physical address, for example phys_addr=0xfffffffffffff000 and num_pages=0x0200000000000001, and prints: [ 0.000000] efi: [Firmware Bug]: Invalid EFI memory map entries: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[phys_addr=0xfffffffffffff000-0x20ffffffffffffffff] (invalid) It then removes these entries from the memory map.

Peter Jones committed 9 years ago

Commit 0100a3e1 parent 20b1e222 files · +67 −0Browse files

Unified · Split

2 files
  1. arch/x86/platform/efi/efi.c +66 −0
  2. include/linux/efi.h +1 −0
arch/x86/platform/efi/efi.c+66−0
@@ -210,6 +210,70 @@ int __init efi_memblock_x86_reserve_range(void) · 209 unmodified lines
210210 return 0;
211211 }
212212
213+#define OVERFLOW_ADDR_SHIFT (64 - EFI_PAGE_SHIFT)
214+#define OVERFLOW_ADDR_MASK (U64_MAX << OVERFLOW_ADDR_SHIFT)
215+#define U64_HIGH_BIT (~(U64_MAX >> 1))
216+
217+static bool __init efi_memmap_entry_valid(const efi_memory_desc_t *md, int i)
218+{
219+ u64 end = (md->num_pages << EFI_PAGE_SHIFT) + md->phys_addr - 1;
220+ u64 end_hi = 0;
221+ char buf[64];
222+
223+ if (md->num_pages == 0) {
224+ end = 0;
225+ } else if (md->num_pages > EFI_PAGES_MAX ||
226+ EFI_PAGES_MAX - md->num_pages <
227+ (md->phys_addr >> EFI_PAGE_SHIFT)) {
228+ end_hi = (md->num_pages & OVERFLOW_ADDR_MASK)
229+ >> OVERFLOW_ADDR_SHIFT;
230+
231+ if ((md->phys_addr & U64_HIGH_BIT) && !(end & U64_HIGH_BIT))
232+ end_hi += 1;
233+ } else {
234+ return true;
235+ }
236+
237+ pr_warn_once(FW_BUG "Invalid EFI memory map entries:\n");
238+
239+ if (end_hi) {
240+ pr_warn("mem%02u: %s range=[0x%016llx-0x%llx%016llx] (invalid)\n",
241+ i, efi_md_typeattr_format(buf, sizeof(buf), md),
242+ md->phys_addr, end_hi, end);
243+ } else {
244+ pr_warn("mem%02u: %s range=[0x%016llx-0x%016llx] (invalid)\n",
245+ i, efi_md_typeattr_format(buf, sizeof(buf), md),
246+ md->phys_addr, end);
247+ }
248+ return false;
249+}
250+
251+static void __init efi_clean_memmap(void)
252+{
253+ efi_memory_desc_t *out = efi.memmap.map;
254+ const efi_memory_desc_t *in = out;
255+ const efi_memory_desc_t *end = efi.memmap.map_end;
256+ int i, n_removal;
257+
258+ for (i = n_removal = 0; in < end; i++) {
259+ if (efi_memmap_entry_valid(in, i)) {
260+ if (out != in)
261+ memcpy(out, in, efi.memmap.desc_size);
262+ out = (void *)out + efi.memmap.desc_size;
263+ } else {
264+ n_removal++;
265+ }
266+ in = (void *)in + efi.memmap.desc_size;
267+ }
268+
269+ if (n_removal > 0) {
270+ u64 size = efi.memmap.nr_map - n_removal;
271+
272+ pr_warn("Removing %d invalid memory map entries.\n", n_removal);
273+ efi_memmap_install(efi.memmap.phys_map, size);
274+ }
275+}
276+
213277 void __init efi_print_memmap(void)
214278 {
215279 efi_memory_desc_t *md;
@@ -472,6 +536,8 @@ void __init efi_init(void) · 256 unmodified lines
472536 }
473537 }
474538
539+ efi_clean_memmap();
540+
475541 if (efi_enabled(EFI_DBG))
476542 efi_print_memmap();
477543 }
include/linux/efi.h+1−0
@@ -103,6 +103,7 @@ typedef struct { · 102 unmodified lines
103103
104104 #define EFI_PAGE_SHIFT 12
105105 #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
106+#define EFI_PAGES_MAX (U64_MAX >> EFI_PAGE_SHIFT)
106107
107108 typedef struct {
108109 u32 type;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t