From f4531c91abbbc3d2e64f36665abec29308bbbc10 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Mon, 6 Mar 2017 09:34:31 +0000 Subject: [PATCH] loader.efi: fix recent UEFI-boot regression on physical machines This patch fixes my recent patch "loader.efi: reduce the size of the staging area if necessary", which causes EFI-boot failure on physical machines since Mar 2: on the host there is a 1MB LoaderData memory range, which splits the big Conventional Memory range into a small one (15MB) and a big one: the small one is too small to hold the staging area. We can actually use the LoaderData range safely, because when amd64_tramp -> efi_copy_finish() starts to run, we're almost at the very end of the efi loader code and we're going to "return" to the kernel entry, so we're pretty sure we won't access any loader data any more. For people who are interested in the details: please see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c22 PS, some people also reported the regression happened to FreeBSD VM running on Bhyve in EFI mode. This patch should resolve it too, though I don't have such a setup to test. Reviewed by: sephe Approved by: sephe (mentor) MFC after: 2 weeks Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D9904 --- sys/boot/efi/loader/copy.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/sys/boot/efi/loader/copy.c b/sys/boot/efi/loader/copy.c index 7a0ee8f2079e..cd54b9f6d9cb 100644 --- a/sys/boot/efi/loader/copy.c +++ b/sys/boot/efi/loader/copy.c @@ -53,7 +53,7 @@ efi_verify_staging_size(unsigned long *nr_pages) UINT32 dver; EFI_STATUS status; int i, ndesc; - unsigned long available_pages; + unsigned long available_pages = 0; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); @@ -70,7 +70,6 @@ efi_verify_staging_size(unsigned long *nr_pages) } ndesc = sz / dsz; - for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { start = p->PhysicalStart; @@ -81,20 +80,38 @@ efi_verify_staging_size(unsigned long *nr_pages) continue; if (p->Type != EfiConventionalMemory) - continue; + printf("Warning: wrong EFI memory type: %d\n", + p->Type); available_pages = p->NumberOfPages - ((KERNEL_PHYSICAL_BASE - start) >> EFI_PAGE_SHIFT); - - if (*nr_pages > available_pages) { - printf("staging area size is reduced: %ld -> %ld!\n", - *nr_pages, available_pages); - *nr_pages = available_pages; - } - break; } + if (available_pages == 0) { + printf("Can't find valid memory map for staging area!\n"); + goto out; + } + + for ( ; i < ndesc; + i++, p = NextMemoryDescriptor(p, dsz)) { + if (p->Type != EfiConventionalMemory && + p->Type != EfiLoaderData) + break; + + if (p->PhysicalStart != end) + break; + + end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE; + + available_pages += p->NumberOfPages; + } + + if (*nr_pages > available_pages) { + printf("Staging area's size is reduced: %ld -> %ld!\n", + *nr_pages, available_pages); + *nr_pages = available_pages; + } out: free(map); }