Skip to content

Commit 3e8fa26

Browse files
Matt FlemingIngo Molnar
Matt Fleming
authored and
Ingo Molnar
committed
x86/efi: Fix oops caused by incorrect set_memory_uc() usage
Calling __pa() with an ioremap'd address is invalid. If we encounter an efi_memory_desc_t without EFI_MEMORY_WB set in ->attribute we currently call set_memory_uc(), which in turn calls __pa() on a potentially ioremap'd address. On CONFIG_X86_32 this results in the following oops: BUG: unable to handle kernel paging request at f7f22280 IP: [<c10257b9>] reserve_ram_pages_type+0x89/0x210 *pdpt = 0000000001978001 *pde = 0000000001ffb067 *pte = 0000000000000000 Oops: 0000 [#1] PREEMPT SMP Modules linked in: Pid: 0, comm: swapper Not tainted 3.0.0-acpi-efi-0805 #3 EIP: 0060:[<c10257b9>] EFLAGS: 00010202 CPU: 0 EIP is at reserve_ram_pages_type+0x89/0x210 EAX: 0070e280 EBX: 38714000 ECX: f7814000 EDX: 00000000 ESI: 00000000 EDI: 38715000 EBP: c189fef0 ESP: c189fea8 DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 Process swapper (pid: 0, ti=c189e000 task=c18bbe60 task.ti=c189e000) Stack: 80000200 ff108000 00000000 c189ff00 00038714 00000000 00000000 c189fed0 c104f8ca 00038714 00000000 00038715 00000000 00000000 00038715 00000000 00000010 38715000 c189ff48 c1025aff 38715000 00000000 00000010 00000000 Call Trace: [<c104f8ca>] ? page_is_ram+0x1a/0x40 [<c1025aff>] reserve_memtype+0xdf/0x2f0 [<c1024dc9>] set_memory_uc+0x49/0xa0 [<c19334d0>] efi_enter_virtual_mode+0x1c2/0x3aa [<c19216d4>] start_kernel+0x291/0x2f2 [<c19211c7>] ? loglevel+0x1b/0x1b [<c19210bf>] i386_start_kernel+0xbf/0xc8 The only time we can call set_memory_uc() for a memory region is when it is part of the direct kernel mapping. For the case where we ioremap a memory region we must leave it alone. This patch reimplements the fix from e8c7106 ("x86, efi: Calling __pa() with an ioremap()ed address is invalid") which was reverted in e1ad783 because it caused a regression on some MacBooks (they hung at boot). The regression was caused because the commit only marked EFI_RUNTIME_SERVICES_DATA as E820_RESERVED_EFI, when it should have marked all regions that have the EFI_MEMORY_RUNTIME attribute. Despite first impressions, it's not possible to use ioremap_cache() to map all cached memory regions on CONFIG_X86_64 because of the way that the memory map might be configured as detailed in the following bug report, https://bugzilla.redhat.com/show_bug.cgi?id=748516 e.g. some of the EFI memory regions *need* to be mapped as part of the direct kernel mapping. Signed-off-by: Matt Fleming <[email protected]> Cc: Matthew Garrett <[email protected]> Cc: Zhang Rui <[email protected]> Cc: Huang Ying <[email protected]> Cc: Keith Packard <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andrew Morton <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 876ee61 commit 3e8fa26

File tree

3 files changed

+26
-15
lines changed

3 files changed

+26
-15
lines changed

arch/x86/include/asm/efi.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);
3535
#define efi_call_virt6(f, a1, a2, a3, a4, a5, a6) \
3636
efi_call_virt(f, a1, a2, a3, a4, a5, a6)
3737

38-
#define efi_ioremap(addr, size, type) ioremap_cache(addr, size)
38+
#define efi_ioremap(addr, size, type, attr) ioremap_cache(addr, size)
3939

4040
#else /* !CONFIG_X86_32 */
4141

@@ -89,7 +89,7 @@ extern u64 efi_call6(void *fp, u64 arg1, u64 arg2, u64 arg3,
8989
(u64)(a3), (u64)(a4), (u64)(a5), (u64)(a6))
9090

9191
extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
92-
u32 type);
92+
u32 type, u64 attribute);
9393

9494
#endif /* CONFIG_X86_32 */
9595

@@ -98,6 +98,7 @@ extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
9898
extern int efi_memblock_x86_reserve_range(void);
9999
extern void efi_call_phys_prelog(void);
100100
extern void efi_call_phys_epilog(void);
101+
extern void efi_memory_uc(u64 addr, unsigned long size);
101102

102103
#ifndef CONFIG_EFI
103104
/*

arch/x86/platform/efi/efi.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,16 @@ void __iomem *efi_lookup_mapped_addr(u64 phys_addr)
810810
return NULL;
811811
}
812812

813+
void efi_memory_uc(u64 addr, unsigned long size)
814+
{
815+
unsigned long page_shift = 1UL << EFI_PAGE_SHIFT;
816+
u64 npages;
817+
818+
npages = round_up(size, page_shift) / page_shift;
819+
memrange_efi_to_native(&addr, &npages);
820+
set_memory_uc(addr, npages);
821+
}
822+
813823
/*
814824
* This function will switch the EFI runtime services to virtual mode.
815825
* Essentially, look through the EFI memmap and map every region that
@@ -823,7 +833,7 @@ void __init efi_enter_virtual_mode(void)
823833
efi_memory_desc_t *md, *prev_md = NULL;
824834
efi_status_t status;
825835
unsigned long size;
826-
u64 end, systab, addr, npages, end_pfn;
836+
u64 end, systab, end_pfn;
827837
void *p, *va, *new_memmap = NULL;
828838
int count = 0;
829839

@@ -879,10 +889,14 @@ void __init efi_enter_virtual_mode(void)
879889
end_pfn = PFN_UP(end);
880890
if (end_pfn <= max_low_pfn_mapped
881891
|| (end_pfn > (1UL << (32 - PAGE_SHIFT))
882-
&& end_pfn <= max_pfn_mapped))
892+
&& end_pfn <= max_pfn_mapped)) {
883893
va = __va(md->phys_addr);
884-
else
885-
va = efi_ioremap(md->phys_addr, size, md->type);
894+
895+
if (!(md->attribute & EFI_MEMORY_WB))
896+
efi_memory_uc((u64)(unsigned long)va, size);
897+
} else
898+
va = efi_ioremap(md->phys_addr, size,
899+
md->type, md->attribute);
886900

887901
md->virt_addr = (u64) (unsigned long) va;
888902

@@ -892,13 +906,6 @@ void __init efi_enter_virtual_mode(void)
892906
continue;
893907
}
894908

895-
if (!(md->attribute & EFI_MEMORY_WB)) {
896-
addr = md->virt_addr;
897-
npages = md->num_pages;
898-
memrange_efi_to_native(&addr, &npages);
899-
set_memory_uc(addr, npages);
900-
}
901-
902909
systab = (u64) (unsigned long) efi_phys.systab;
903910
if (md->phys_addr <= systab && systab < end) {
904911
systab += md->virt_addr - md->phys_addr;

arch/x86/platform/efi/efi_64.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void __init efi_call_phys_epilog(void)
8282
}
8383

8484
void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,
85-
u32 type)
85+
u32 type, u64 attribute)
8686
{
8787
unsigned long last_map_pfn;
8888

@@ -92,8 +92,11 @@ void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,
9292
last_map_pfn = init_memory_mapping(phys_addr, phys_addr + size);
9393
if ((last_map_pfn << PAGE_SHIFT) < phys_addr + size) {
9494
unsigned long top = last_map_pfn << PAGE_SHIFT;
95-
efi_ioremap(top, size - (top - phys_addr), type);
95+
efi_ioremap(top, size - (top - phys_addr), type, attribute);
9696
}
9797

98+
if (!(attribute & EFI_MEMORY_WB))
99+
efi_memory_uc((u64)(unsigned long)__va(phys_addr), size);
100+
98101
return (void __iomem *)__va(phys_addr);
99102
}

0 commit comments

Comments
 (0)