loader.efi: refresh size in GetMemoryMap retry loop

If ExitBootServices fails due to a changed efi_mapkey then GetMemoryMap
must be called again. In this case it is also possible for the memory
map to grow, so repeat the initial GetMemoryMap call to fetch the new
size.

Also roll bi_add_efi_data_and_exit into bi_load_efi_data as there's no
need for it to be a separate function.

PR:		202455
Reported by:	Berislav Purgar <bpurgar@gmail.com>
Tested by:	Berislav Purgar <bpurgar@gmail.com>
Reviewed by:	kib
MFC after:	1 week
MFC with:	r292338
Relnotes:	Yes
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D4621
This commit is contained in:
Ed Maste 2015-12-20 16:07:09 +00:00
parent b1f9559ae4
commit 09e4ee9af3

View File

@ -233,44 +233,6 @@ bi_copymodules(vm_offset_t addr)
return(addr);
}
static int
bi_add_efi_data_and_exit(struct preloaded_file *kfp,
struct efi_map_header *efihdr, size_t efisz, EFI_MEMORY_DESCRIPTOR *mm,
UINTN sz)
{
UINTN efi_mapkey;
UINTN mmsz;
UINT32 mmver;
EFI_STATUS status;
UINTN retry;
/*
* It is possible that the first call to ExitBootServices may change
* the map key. Fetch a new map key and retry ExitBootServices in that
* case.
*/
for (retry = 2; retry > 0; retry--) {
status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
if (EFI_ERROR(status)) {
printf("%s: GetMemoryMap error %lu\n", __func__,
(unsigned long)(status & ~EFI_ERROR_MASK));
return (EINVAL);
}
status = BS->ExitBootServices(IH, efi_mapkey);
if (EFI_ERROR(status) == 0) {
efihdr->memory_size = sz;
efihdr->descriptor_size = mmsz;
efihdr->descriptor_version = mmver;
file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
efihdr);
return (0);
}
}
printf("ExitBootServices error %lu\n",
(unsigned long)(status & ~EFI_ERROR_MASK));
return (EINVAL);
}
static int
bi_load_efi_data(struct preloaded_file *kfp)
{
@ -279,7 +241,7 @@ bi_load_efi_data(struct preloaded_file *kfp)
EFI_STATUS status;
size_t efisz;
UINTN efi_mapkey;
UINTN mmsz, pages, sz;
UINTN mmsz, pages, retry, sz;
UINT32 mmver;
struct efi_map_header *efihdr;
@ -304,37 +266,63 @@ bi_load_efi_data(struct preloaded_file *kfp)
efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
/*
* Allocate enough pages to hold the bootinfo block and the memory
* map EFI will return to us. The memory map has an unknown size,
* so we have to determine that first. Note that the AllocatePages
* call can itself modify the memory map, so we have to take that
* into account as well. The changes to the memory map are caused
* by splitting a range of free memory into two (AFAICT), so that
* one is marked as being loader data.
* It is possible that the first call to ExitBootServices may change
* the map key. Fetch a new map key and retry ExitBootServices in that
* case.
*/
sz = 0;
BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
sz += mmsz;
sz = (sz + 0xf) & ~0xf;
pages = EFI_SIZE_TO_PAGES(sz + efisz);
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages,
&addr);
if (EFI_ERROR(status)) {
printf("%s: AllocatePages error %lu\n", __func__,
(unsigned long)(status & ~EFI_ERROR_MASK));
return (ENOMEM);
for (retry = 2; retry > 0; retry--) {
/*
* Allocate enough pages to hold the bootinfo block and the
* memory map EFI will return to us. The memory map has an
* unknown size, so we have to determine that first. Note that
* the AllocatePages call can itself modify the memory map, so
* we have to take that into account as well. The changes to
* the memory map are caused by splitting a range of free
* memory into two (AFAICT), so that one is marked as being
* loader data.
*/
sz = 0;
BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
sz += mmsz;
sz = (sz + 0xf) & ~0xf;
pages = EFI_SIZE_TO_PAGES(sz + efisz);
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
pages, &addr);
if (EFI_ERROR(status)) {
printf("%s: AllocatePages error %lu\n", __func__,
(unsigned long)(status & ~EFI_ERROR_MASK));
return (ENOMEM);
}
/*
* Read the memory map and stash it after bootinfo. Align the
* memory map on a 16-byte boundary (the bootinfo block is page
* aligned).
*/
efihdr = (struct efi_map_header *)addr;
mm = (void *)((uint8_t *)efihdr + efisz);
sz = (EFI_PAGE_SIZE * pages) - efisz;
status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
if (EFI_ERROR(status)) {
printf("%s: GetMemoryMap error %lu\n", __func__,
(unsigned long)(status & ~EFI_ERROR_MASK));
return (EINVAL);
}
status = BS->ExitBootServices(IH, efi_mapkey);
if (EFI_ERROR(status) == 0) {
efihdr->memory_size = sz;
efihdr->descriptor_size = mmsz;
efihdr->descriptor_version = mmver;
file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
efihdr);
return (0);
}
BS->FreePages(addr, pages);
}
/*
* Read the memory map and stash it after bootinfo. Align the
* memory map on a 16-byte boundary (the bootinfo block is page
* aligned).
*/
efihdr = (struct efi_map_header *)addr;
mm = (void *)((uint8_t *)efihdr + efisz);
sz = (EFI_PAGE_SIZE * pages) - efisz;
return (bi_add_efi_data_and_exit(kfp, efihdr, efisz, mm, sz));
printf("ExitBootServices error %lu\n",
(unsigned long)(status & ~EFI_ERROR_MASK));
return (EINVAL);
}
/*