Skip to content

Commit 684019d

Browse files
committed
Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI updates from Ingo Molnar: "The main changes in this cycle were: - Allocate the E820 buffer before doing the GetMemoryMap/ExitBootServices dance so we don't run out of space - Clear EFI boot services mappings when freeing the memory - Harden efivars against callers that invoke it on non-EFI boots - Reduce the number of memblock reservations resulting from extensive use of the new efi_mem_reserve_persistent() API - Other assorted fixes and cleanups" * 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/efi: Don't unmap EFI boot services code/data regions for EFI_OLD_MEMMAP and EFI_MIXED_MODE efi: Reduce the amount of memblock reservations for persistent allocations efi: Permit multiple entries in persistent memreserve data structure efi/libstub: Disable some warnings for x86{,_64} x86/efi: Move efi_<reserve/free>_boot_services() to arch/x86 x86/efi: Unmap EFI boot services code/data regions from efi_pgd x86/mm/pageattr: Introduce helper function to unmap EFI boot services efi/fdt: Simplify the get_fdt() flow efi/fdt: Indentation fix firmware/efi: Add NULL pointer checks in efivars API functions
2 parents 792bf4d + 1debf09 commit 684019d

File tree

12 files changed

+242
-64
lines changed

12 files changed

+242
-64
lines changed

arch/x86/include/asm/efi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ extern int __init efi_reuse_config(u64 tables, int nr_tables);
141141
extern void efi_delete_dummy_variable(void);
142142
extern void efi_switch_mm(struct mm_struct *mm);
143143
extern void efi_recover_from_page_fault(unsigned long phys_addr);
144+
extern void efi_free_boot_services(void);
145+
extern void efi_reserve_boot_services(void);
144146

145147
struct efi_setup_data {
146148
u64 fw_vendor;

arch/x86/include/asm/pgtable_types.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,12 @@ extern pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
564564
unsigned int *level);
565565
extern pmd_t *lookup_pmd_address(unsigned long address);
566566
extern phys_addr_t slow_virt_to_phys(void *__address);
567-
extern int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
568-
unsigned numpages, unsigned long page_flags);
567+
extern int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn,
568+
unsigned long address,
569+
unsigned numpages,
570+
unsigned long page_flags);
571+
extern int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address,
572+
unsigned long numpages);
569573
#endif /* !__ASSEMBLY__ */
570574

571575
#endif /* _ASM_X86_PGTABLE_DEFS_H */

arch/x86/mm/pageattr.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,8 +2346,8 @@ bool kernel_page_present(struct page *page)
23462346

23472347
#endif /* CONFIG_DEBUG_PAGEALLOC */
23482348

2349-
int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
2350-
unsigned numpages, unsigned long page_flags)
2349+
int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
2350+
unsigned numpages, unsigned long page_flags)
23512351
{
23522352
int retval = -EINVAL;
23532353

@@ -2361,6 +2361,8 @@ int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
23612361
.flags = 0,
23622362
};
23632363

2364+
WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
2365+
23642366
if (!(__supported_pte_mask & _PAGE_NX))
23652367
goto out;
23662368

@@ -2382,6 +2384,40 @@ int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
23822384
return retval;
23832385
}
23842386

2387+
/*
2388+
* __flush_tlb_all() flushes mappings only on current CPU and hence this
2389+
* function shouldn't be used in an SMP environment. Presently, it's used only
2390+
* during boot (way before smp_init()) by EFI subsystem and hence is ok.
2391+
*/
2392+
int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address,
2393+
unsigned long numpages)
2394+
{
2395+
int retval;
2396+
2397+
/*
2398+
* The typical sequence for unmapping is to find a pte through
2399+
* lookup_address_in_pgd() (ideally, it should never return NULL because
2400+
* the address is already mapped) and change it's protections. As pfn is
2401+
* the *target* of a mapping, it's not useful while unmapping.
2402+
*/
2403+
struct cpa_data cpa = {
2404+
.vaddr = &address,
2405+
.pfn = 0,
2406+
.pgd = pgd,
2407+
.numpages = numpages,
2408+
.mask_set = __pgprot(0),
2409+
.mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW),
2410+
.flags = 0,
2411+
};
2412+
2413+
WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
2414+
2415+
retval = __change_page_attr_set_clr(&cpa, 0);
2416+
__flush_tlb_all();
2417+
2418+
return retval;
2419+
}
2420+
23852421
/*
23862422
* The testcases use internal knowledge of the implementation that shouldn't
23872423
* be exposed to the rest of the kernel. Include these directly here.

arch/x86/platform/efi/efi.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,8 @@ static void __init __efi_enter_virtual_mode(void)
993993
panic("EFI call to SetVirtualAddressMap() failed!");
994994
}
995995

996+
efi_free_boot_services();
997+
996998
/*
997999
* Now that EFI is in virtual mode, update the function
9981000
* pointers in the runtime service table to the new virtual addresses.

arch/x86/platform/efi/quirks.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,40 @@ void __init efi_reserve_boot_services(void)
369369
}
370370
}
371371

372+
/*
373+
* Apart from having VA mappings for EFI boot services code/data regions,
374+
* (duplicate) 1:1 mappings were also created as a quirk for buggy firmware. So,
375+
* unmap both 1:1 and VA mappings.
376+
*/
377+
static void __init efi_unmap_pages(efi_memory_desc_t *md)
378+
{
379+
pgd_t *pgd = efi_mm.pgd;
380+
u64 pa = md->phys_addr;
381+
u64 va = md->virt_addr;
382+
383+
/*
384+
* To Do: Remove this check after adding functionality to unmap EFI boot
385+
* services code/data regions from direct mapping area because
386+
* "efi=old_map" maps EFI regions in swapper_pg_dir.
387+
*/
388+
if (efi_enabled(EFI_OLD_MEMMAP))
389+
return;
390+
391+
/*
392+
* EFI mixed mode has all RAM mapped to access arguments while making
393+
* EFI runtime calls, hence don't unmap EFI boot services code/data
394+
* regions.
395+
*/
396+
if (!efi_is_native())
397+
return;
398+
399+
if (kernel_unmap_pages_in_pgd(pgd, pa, md->num_pages))
400+
pr_err("Failed to unmap 1:1 mapping for 0x%llx\n", pa);
401+
402+
if (kernel_unmap_pages_in_pgd(pgd, va, md->num_pages))
403+
pr_err("Failed to unmap VA mapping for 0x%llx\n", va);
404+
}
405+
372406
void __init efi_free_boot_services(void)
373407
{
374408
phys_addr_t new_phys, new_size;
@@ -393,6 +427,13 @@ void __init efi_free_boot_services(void)
393427
continue;
394428
}
395429

430+
/*
431+
* Before calling set_virtual_address_map(), EFI boot services
432+
* code/data regions were mapped as a quirk for buggy firmware.
433+
* Unmap them from efi_pgd before freeing them up.
434+
*/
435+
efi_unmap_pages(md);
436+
396437
/*
397438
* Nasty quirk: if all sub-1MB memory is used for boot
398439
* services, we can get here without having allocated the

drivers/firmware/efi/efi.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -602,21 +602,33 @@ int __init efi_apply_persistent_mem_reservations(void)
602602

603603
while (prsv) {
604604
struct linux_efi_memreserve *rsv;
605-
606-
/* reserve the entry itself */
607-
memblock_reserve(prsv, sizeof(*rsv));
608-
609-
rsv = early_memremap(prsv, sizeof(*rsv));
610-
if (rsv == NULL) {
605+
u8 *p;
606+
int i;
607+
608+
/*
609+
* Just map a full page: that is what we will get
610+
* anyway, and it permits us to map the entire entry
611+
* before knowing its size.
612+
*/
613+
p = early_memremap(ALIGN_DOWN(prsv, PAGE_SIZE),
614+
PAGE_SIZE);
615+
if (p == NULL) {
611616
pr_err("Could not map UEFI memreserve entry!\n");
612617
return -ENOMEM;
613618
}
614619

615-
if (rsv->size)
616-
memblock_reserve(rsv->base, rsv->size);
620+
rsv = (void *)(p + prsv % PAGE_SIZE);
621+
622+
/* reserve the entry itself */
623+
memblock_reserve(prsv, EFI_MEMRESERVE_SIZE(rsv->size));
624+
625+
for (i = 0; i < atomic_read(&rsv->count); i++) {
626+
memblock_reserve(rsv->entry[i].base,
627+
rsv->entry[i].size);
628+
}
617629

618630
prsv = rsv->next;
619-
early_memunmap(rsv, sizeof(*rsv));
631+
early_memunmap(p, PAGE_SIZE);
620632
}
621633
}
622634

@@ -985,7 +997,8 @@ static int __init efi_memreserve_map_root(void)
985997
int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
986998
{
987999
struct linux_efi_memreserve *rsv;
988-
int rc;
1000+
unsigned long prsv;
1001+
int rc, index;
9891002

9901003
if (efi_memreserve_root == (void *)ULONG_MAX)
9911004
return -ENODEV;
@@ -996,12 +1009,27 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
9961009
return rc;
9971010
}
9981011

999-
rsv = kmalloc(sizeof(*rsv), GFP_ATOMIC);
1012+
/* first try to find a slot in an existing linked list entry */
1013+
for (prsv = efi_memreserve_root->next; prsv; prsv = rsv->next) {
1014+
rsv = __va(prsv);
1015+
index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
1016+
if (index < rsv->size) {
1017+
rsv->entry[index].base = addr;
1018+
rsv->entry[index].size = size;
1019+
1020+
return 0;
1021+
}
1022+
}
1023+
1024+
/* no slot found - allocate a new linked list entry */
1025+
rsv = (struct linux_efi_memreserve *)__get_free_page(GFP_ATOMIC);
10001026
if (!rsv)
10011027
return -ENOMEM;
10021028

1003-
rsv->base = addr;
1004-
rsv->size = size;
1029+
rsv->size = EFI_MEMRESERVE_COUNT(PAGE_SIZE);
1030+
atomic_set(&rsv->count, 1);
1031+
rsv->entry[0].base = addr;
1032+
rsv->entry[0].size = size;
10051033

10061034
spin_lock(&efi_mem_reserve_persistent_lock);
10071035
rsv->next = efi_memreserve_root->next;

drivers/firmware/efi/libstub/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ cflags-$(CONFIG_X86_32) := -march=i386
99
cflags-$(CONFIG_X86_64) := -mcmodel=small
1010
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
1111
-fPIC -fno-strict-aliasing -mno-red-zone \
12-
-mno-mmx -mno-sse -fshort-wchar
12+
-mno-mmx -mno-sse -fshort-wchar \
13+
-Wno-pointer-sign \
14+
$(call cc-disable-warning, address-of-packed-member) \
15+
$(call cc-disable-warning, gnu)
1316

1417
# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
1518
# disable the stackleak plugin

drivers/firmware/efi/libstub/arm-stub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg)
8686
}
8787

8888
rsv->next = 0;
89-
rsv->base = 0;
9089
rsv->size = 0;
90+
atomic_set(&rsv->count, 0);
9191

9292
status = efi_call_early(install_configuration_table,
9393
&memreserve_table_guid,

drivers/firmware/efi/libstub/fdt.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -370,22 +370,24 @@ void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
370370
{
371371
efi_guid_t fdt_guid = DEVICE_TREE_GUID;
372372
efi_config_table_t *tables;
373-
void *fdt;
374373
int i;
375374

376-
tables = (efi_config_table_t *) sys_table->tables;
377-
fdt = NULL;
375+
tables = (efi_config_table_t *)sys_table->tables;
378376

379-
for (i = 0; i < sys_table->nr_tables; i++)
380-
if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) {
381-
fdt = (void *) tables[i].table;
382-
if (fdt_check_header(fdt) != 0) {
383-
pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
384-
return NULL;
385-
}
386-
*fdt_size = fdt_totalsize(fdt);
387-
break;
388-
}
377+
for (i = 0; i < sys_table->nr_tables; i++) {
378+
void *fdt;
379+
380+
if (efi_guidcmp(tables[i].guid, fdt_guid) != 0)
381+
continue;
382+
383+
fdt = (void *)tables[i].table;
384+
if (fdt_check_header(fdt) != 0) {
385+
pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
386+
return NULL;
387+
}
388+
*fdt_size = fdt_totalsize(fdt);
389+
return fdt;
390+
}
389391

390-
return fdt;
392+
return NULL;
391393
}

0 commit comments

Comments
 (0)