Commit d367cef0a7f0 ("x86/mm/pat: Fix boot crash when 1GB pages are not supported by the CPU") added checking for CPU support for 1G pages before using them. However, when support is not present, nothing is done to map the intermediate 1G regions and we go directly to the code that normally maps the remainder after 1G mappings have been done. This code can only handle mappings that fit inside a single PUD entry, but there is no check, and it instead silently produces a corrupted mapping to the end of the PUD entry, and no mapping beyond it, but still returns success. This bug is encountered on EFI machines in mixed mode (32-bit firmware with 64-bit kernel), with RAM beyond 2G. The EFI support code direct-maps all the RAM, so a memory range from below 1G to above 2G triggers the bug and results in no mapping above 2G, and an incorrect mapping in the 1G-2G range. If the kernel resides in the 1G-2G range, a firmware call does not return correctly, and if it resides above 2G, we end up passing addresses that are not mapped in the EFI pagetable. Fix this by mapping the 1G regions using 2M pages when 1G page support is not available. Signed-off-by: Arvind Sankar <nivedita@xxxxxxxxxxxx> --- arch/x86/mm/pat/set_memory.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index c4aedd00c1ba..d0b7b06253a5 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -1370,12 +1370,22 @@ static int populate_pud(struct cpa_data *cpa, unsigned long start, p4d_t *p4d, /* * Map everything starting from the Gb boundary, possibly with 1G pages */ - while (boot_cpu_has(X86_FEATURE_GBPAGES) && end - start >= PUD_SIZE) { - set_pud(pud, pud_mkhuge(pfn_pud(cpa->pfn, - canon_pgprot(pud_pgprot)))); + while (end - start >= PUD_SIZE) { + if (boot_cpu_has(X86_FEATURE_GBPAGES)) { + set_pud(pud, pud_mkhuge(pfn_pud(cpa->pfn, + canon_pgprot(pud_pgprot)))); + cpa->pfn += PUD_SIZE >> PAGE_SHIFT; + } else { + if (pud_none(*pud)) + if (alloc_pmd_page(pud)) + return -1; + if (populate_pmd(cpa, start, start + PUD_SIZE, + PUD_SIZE >> PAGE_SHIFT, + pud, pgprot) < 0) + return cur_pages; + } start += PUD_SIZE; - cpa->pfn += PUD_SIZE >> PAGE_SHIFT; cur_pages += PUD_SIZE >> PAGE_SHIFT; pud++; } -- 2.24.1