From d28a087d74e2d48517189dc234f4cfe254561877 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 10 Apr 2014 09:00:20 -0700 Subject: [PATCH] 3.14-stable patches added patches: x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch x86-pageattr-export-page-unmapping-interface.patch --- queue-3.14/series | 2 + ...tual-runtime-map-passing-more-robust.patch | 269 ++++++++++++++++++ ...attr-export-page-unmapping-interface.patch | 124 ++++++++ 3 files changed, 395 insertions(+) create mode 100644 queue-3.14/x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch create mode 100644 queue-3.14/x86-pageattr-export-page-unmapping-interface.patch diff --git a/queue-3.14/series b/queue-3.14/series index 28ea9241a12..e6345e1b159 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -1,2 +1,4 @@ revert-alsa-hda-increment-default-stream-numbers-for-amd-hdmi-controllers.patch selinux-correctly-label-proc-inodes-in-use-before-the-policy-is-loaded.patch +x86-pageattr-export-page-unmapping-interface.patch +x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch diff --git a/queue-3.14/x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch b/queue-3.14/x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch new file mode 100644 index 00000000000..5241a0faf85 --- /dev/null +++ b/queue-3.14/x86-efi-make-efi-virtual-runtime-map-passing-more-robust.patch @@ -0,0 +1,269 @@ +From b7b898ae0c0a82489511a1ce1b35f26215e6beb5 Mon Sep 17 00:00:00 2001 +From: Borislav Petkov +Date: Sat, 18 Jan 2014 12:48:17 +0100 +Subject: x86/efi: Make efi virtual runtime map passing more robust + +From: Borislav Petkov + +commit b7b898ae0c0a82489511a1ce1b35f26215e6beb5 upstream. + +Currently, running SetVirtualAddressMap() and passing the physical +address of the virtual map array was working only by a lucky coincidence +because the memory was present in the EFI page table too. Until Toshi +went and booted this on a big HP box - the krealloc() manner of resizing +the memmap we're doing did allocate from such physical addresses which +were not mapped anymore and boom: + +http://lkml.kernel.org/r/1386806463.1791.295.camel@misato.fc.hp.com + +One way to take care of that issue is to reimplement the krealloc thing +but with pages. We start with contiguous pages of order 1, i.e. 2 pages, +and when we deplete that memory (shouldn't happen all that often but you +know firmware) we realloc the next power-of-two pages. + +Having the pages, it is much more handy and easy to map them into the +EFI page table with the already existing mapping code which we're using +for building the virtual mappings. + +Thanks to Toshi Kani and Matt for the great debugging help. + +Reported-by: Toshi Kani +Signed-off-by: Borislav Petkov +Tested-by: Toshi Kani +Signed-off-by: Matt Fleming +Signed-off-by: Borislav Petkov +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/include/asm/efi.h | 3 - + arch/x86/platform/efi/efi.c | 99 ++++++++++++++++++++++++++++++++--------- + arch/x86/platform/efi/efi_32.c | 7 ++ + arch/x86/platform/efi/efi_64.c | 32 ++++++++++++- + 4 files changed, 115 insertions(+), 26 deletions(-) + +--- a/arch/x86/include/asm/efi.h ++++ b/arch/x86/include/asm/efi.h +@@ -130,7 +130,8 @@ extern void efi_memory_uc(u64 addr, unsi + extern void __init efi_map_region(efi_memory_desc_t *md); + extern void __init efi_map_region_fixed(efi_memory_desc_t *md); + extern void efi_sync_low_kernel_mappings(void); +-extern void efi_setup_page_tables(void); ++extern int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages); ++extern void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages); + extern void __init old_map_region(efi_memory_desc_t *md); + extern void __init runtime_code_page_mkexec(void); + extern void __init efi_runtime_mkexec(void); +--- a/arch/x86/platform/efi/efi.c ++++ b/arch/x86/platform/efi/efi.c +@@ -939,14 +939,36 @@ static void __init efi_map_regions_fixed + + } + ++static void *realloc_pages(void *old_memmap, int old_shift) ++{ ++ void *ret; ++ ++ ret = (void *)__get_free_pages(GFP_KERNEL, old_shift + 1); ++ if (!ret) ++ goto out; ++ ++ /* ++ * A first-time allocation doesn't have anything to copy. ++ */ ++ if (!old_memmap) ++ return ret; ++ ++ memcpy(ret, old_memmap, PAGE_SIZE << old_shift); ++ ++out: ++ free_pages((unsigned long)old_memmap, old_shift); ++ return ret; ++} ++ + /* +- * Map efi memory ranges for runtime serivce and update new_memmap with virtual +- * addresses. ++ * Map the efi memory ranges of the runtime services and update new_mmap with ++ * virtual addresses. + */ +-static void * __init efi_map_regions(int *count) ++static void * __init efi_map_regions(int *count, int *pg_shift) + { ++ void *p, *new_memmap = NULL; ++ unsigned long left = 0; + efi_memory_desc_t *md; +- void *p, *tmp, *new_memmap = NULL; + + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; +@@ -961,20 +983,23 @@ static void * __init efi_map_regions(int + efi_map_region(md); + get_systab_virt_addr(md); + +- tmp = krealloc(new_memmap, (*count + 1) * memmap.desc_size, +- GFP_KERNEL); +- if (!tmp) +- goto out; +- new_memmap = tmp; ++ if (left < memmap.desc_size) { ++ new_memmap = realloc_pages(new_memmap, *pg_shift); ++ if (!new_memmap) ++ return NULL; ++ ++ left += PAGE_SIZE << *pg_shift; ++ (*pg_shift)++; ++ } ++ + memcpy(new_memmap + (*count * memmap.desc_size), md, + memmap.desc_size); ++ ++ left -= memmap.desc_size; + (*count)++; + } + + return new_memmap; +-out: +- kfree(new_memmap); +- return NULL; + } + + /* +@@ -1000,9 +1025,9 @@ out: + */ + void __init efi_enter_virtual_mode(void) + { +- efi_status_t status; ++ int err, count = 0, pg_shift = 0; + void *new_memmap = NULL; +- int err, count = 0; ++ efi_status_t status; + + efi.systab = NULL; + +@@ -1019,20 +1044,24 @@ void __init efi_enter_virtual_mode(void) + efi_map_regions_fixed(); + } else { + efi_merge_regions(); +- new_memmap = efi_map_regions(&count); ++ new_memmap = efi_map_regions(&count, &pg_shift); + if (!new_memmap) { + pr_err("Error reallocating memory, EFI runtime non-functional!\n"); + return; + } +- } + +- err = save_runtime_map(); +- if (err) +- pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); ++ err = save_runtime_map(); ++ if (err) ++ pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); ++ } + + BUG_ON(!efi.systab); + +- efi_setup_page_tables(); ++ if (!efi_setup) { ++ if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) ++ return; ++ } ++ + efi_sync_low_kernel_mappings(); + + if (!efi_setup) { +@@ -1072,7 +1101,35 @@ void __init efi_enter_virtual_mode(void) + + efi_runtime_mkexec(); + +- kfree(new_memmap); ++ ++ /* ++ * We mapped the descriptor array into the EFI pagetable above but we're ++ * not unmapping it here. Here's why: ++ * ++ * We're copying select PGDs from the kernel page table to the EFI page ++ * table and when we do so and make changes to those PGDs like unmapping ++ * stuff from them, those changes appear in the kernel page table and we ++ * go boom. ++ * ++ * From setup_real_mode(): ++ * ++ * ... ++ * trampoline_pgd[0] = init_level4_pgt[pgd_index(__PAGE_OFFSET)].pgd; ++ * ++ * In this particular case, our allocation is in PGD 0 of the EFI page ++ * table but we've copied that PGD from PGD[272] of the EFI page table: ++ * ++ * pgd_index(__PAGE_OFFSET = 0xffff880000000000) = 272 ++ * ++ * where the direct memory mapping in kernel space is. ++ * ++ * new_memmap's VA comes from that direct mapping and thus clearing it, ++ * it would get cleared in the kernel page table too. ++ * ++ * efi_cleanup_page_tables(__pa(new_memmap), 1 << pg_shift); ++ */ ++ if (!efi_setup) ++ free_pages((unsigned long)new_memmap, pg_shift); + + /* clean DUMMY object */ + efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID, +--- a/arch/x86/platform/efi/efi_32.c ++++ b/arch/x86/platform/efi/efi_32.c +@@ -40,7 +40,12 @@ + static unsigned long efi_rt_eflags; + + void efi_sync_low_kernel_mappings(void) {} +-void efi_setup_page_tables(void) {} ++void __init efi_dump_pagetable(void) {} ++int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) ++{ ++ return 0; ++} ++void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) {} + + void __init efi_map_region(efi_memory_desc_t *md) + { +--- a/arch/x86/platform/efi/efi_64.c ++++ b/arch/x86/platform/efi/efi_64.c +@@ -137,12 +137,38 @@ void efi_sync_low_kernel_mappings(void) + sizeof(pgd_t) * num_pgds); + } + +-void efi_setup_page_tables(void) ++int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) + { ++ pgd_t *pgd; ++ ++ if (efi_enabled(EFI_OLD_MEMMAP)) ++ return 0; ++ + efi_scratch.efi_pgt = (pgd_t *)(unsigned long)real_mode_header->trampoline_pgd; ++ pgd = __va(efi_scratch.efi_pgt); ++ ++ /* ++ * It can happen that the physical address of new_memmap lands in memory ++ * which is not mapped in the EFI page table. Therefore we need to go ++ * and ident-map those pages containing the map before calling ++ * phys_efi_set_virtual_address_map(). ++ */ ++ if (kernel_map_pages_in_pgd(pgd, pa_memmap, pa_memmap, num_pages, _PAGE_NX)) { ++ pr_err("Error ident-mapping new memmap (0x%lx)!\n", pa_memmap); ++ return 1; ++ } ++ ++ efi_scratch.use_pgd = true; ++ ++ ++ return 0; ++} ++ ++void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) ++{ ++ pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); + +- if (!efi_enabled(EFI_OLD_MEMMAP)) +- efi_scratch.use_pgd = true; ++ kernel_unmap_pages_in_pgd(pgd, pa_memmap, num_pages); + } + + static void __init __map_region(efi_memory_desc_t *md, u64 va) diff --git a/queue-3.14/x86-pageattr-export-page-unmapping-interface.patch b/queue-3.14/x86-pageattr-export-page-unmapping-interface.patch new file mode 100644 index 00000000000..273cd03153e --- /dev/null +++ b/queue-3.14/x86-pageattr-export-page-unmapping-interface.patch @@ -0,0 +1,124 @@ +From 42a5477251f0e0f33ad5f6a95c48d685ec03191e Mon Sep 17 00:00:00 2001 +From: Borislav Petkov +Date: Sat, 18 Jan 2014 12:48:16 +0100 +Subject: x86, pageattr: Export page unmapping interface + +From: Borislav Petkov + +commit 42a5477251f0e0f33ad5f6a95c48d685ec03191e upstream. + +We will use it in efi so expose it. + +Signed-off-by: Borislav Petkov +Tested-by: Toshi Kani +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/include/asm/pgtable_types.h | 2 + + arch/x86/mm/pageattr.c | 44 ++++++++++++++++++++++++----------- + 2 files changed, 33 insertions(+), 13 deletions(-) + +--- a/arch/x86/include/asm/pgtable_types.h ++++ b/arch/x86/include/asm/pgtable_types.h +@@ -385,6 +385,8 @@ extern pte_t *lookup_address(unsigned lo + extern phys_addr_t slow_virt_to_phys(void *__address); + extern int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, + unsigned numpages, unsigned long page_flags); ++void kernel_unmap_pages_in_pgd(pgd_t *root, unsigned long address, ++ unsigned numpages); + #endif /* !__ASSEMBLY__ */ + + #endif /* _ASM_X86_PGTABLE_DEFS_H */ +--- a/arch/x86/mm/pageattr.c ++++ b/arch/x86/mm/pageattr.c +@@ -692,6 +692,18 @@ static bool try_to_free_pmd_page(pmd_t * + return true; + } + ++static bool try_to_free_pud_page(pud_t *pud) ++{ ++ int i; ++ ++ for (i = 0; i < PTRS_PER_PUD; i++) ++ if (!pud_none(pud[i])) ++ return false; ++ ++ free_page((unsigned long)pud); ++ return true; ++} ++ + static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end) + { + pte_t *pte = pte_offset_kernel(pmd, start); +@@ -805,6 +817,16 @@ static void unmap_pud_range(pgd_t *pgd, + */ + } + ++static void unmap_pgd_range(pgd_t *root, unsigned long addr, unsigned long end) ++{ ++ pgd_t *pgd_entry = root + pgd_index(addr); ++ ++ unmap_pud_range(pgd_entry, addr, end); ++ ++ if (try_to_free_pud_page((pud_t *)pgd_page_vaddr(*pgd_entry))) ++ pgd_clear(pgd_entry); ++} ++ + static int alloc_pte_page(pmd_t *pmd) + { + pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_NOTRACK); +@@ -999,9 +1021,8 @@ static int populate_pud(struct cpa_data + static int populate_pgd(struct cpa_data *cpa, unsigned long addr) + { + pgprot_t pgprot = __pgprot(_KERNPG_TABLE); +- bool allocd_pgd = false; +- pgd_t *pgd_entry; + pud_t *pud = NULL; /* shut up gcc */ ++ pgd_t *pgd_entry; + int ret; + + pgd_entry = cpa->pgd + pgd_index(addr); +@@ -1015,7 +1036,6 @@ static int populate_pgd(struct cpa_data + return -1; + + set_pgd(pgd_entry, __pgd(__pa(pud) | _KERNPG_TABLE)); +- allocd_pgd = true; + } + + pgprot_val(pgprot) &= ~pgprot_val(cpa->mask_clr); +@@ -1023,19 +1043,11 @@ static int populate_pgd(struct cpa_data + + ret = populate_pud(cpa, addr, pgd_entry, pgprot); + if (ret < 0) { +- unmap_pud_range(pgd_entry, addr, ++ unmap_pgd_range(cpa->pgd, addr, + addr + (cpa->numpages << PAGE_SHIFT)); +- +- if (allocd_pgd) { +- /* +- * If I allocated this PUD page, I can just as well +- * free it in this error path. +- */ +- pgd_clear(pgd_entry); +- free_page((unsigned long)pud); +- } + return ret; + } ++ + cpa->numpages = ret; + return 0; + } +@@ -1861,6 +1873,12 @@ out: + return retval; + } + ++void kernel_unmap_pages_in_pgd(pgd_t *root, unsigned long address, ++ unsigned numpages) ++{ ++ unmap_pgd_range(root, address, address + (numpages << PAGE_SHIFT)); ++} ++ + /* + * The testcases use internal knowledge of the implementation that shouldn't + * be exposed to the rest of the kernel. Include these directly here. -- 2.47.3