Index: stand/efi/loader/bootinfo.c =================================================================== --- stand/efi/loader/bootinfo.c +++ stand/efi/loader/bootinfo.c @@ -287,12 +287,12 @@ bi_load_efi_data(struct preloaded_file *kfp) { EFI_MEMORY_DESCRIPTOR *mm; - EFI_PHYSICAL_ADDRESS addr; + EFI_PHYSICAL_ADDRESS addr = 0; EFI_STATUS status; const char *efi_novmap; size_t efisz; UINTN efi_mapkey; - UINTN mmsz, pages, retry, sz; + UINTN mmsz, pages, retry, gmm_retries, sz; UINT32 mmver; struct efi_map_header *efihdr; bool do_vmap; @@ -342,39 +342,61 @@ * 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. + * memory into two, 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__, - EFI_ERROR_CODE(status)); - return (ENOMEM); + mm = NULL; + + for (gmm_retries = 10; gmm_retries > 0; gmm_retries--) { + status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver); + if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) { + printf("%s: GetMemoryMap error %lu\n", __func__, + EFI_ERROR_CODE(status)); + return (EINVAL); + } + + if (!EFI_ERROR(status)) + break; + + if (status == EFI_BUFFER_TOO_SMALL) { + if (addr != 0) + BS->FreePages(addr, pages); + + 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__, + EFI_ERROR_CODE(status)); + 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 *)(uintptr_t)addr; + mm = (void *)((uint8_t *)efihdr + efisz); + sz = (EFI_PAGE_SIZE * pages) - efisz; + + } } - /* - * 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 *)(uintptr_t)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__, - EFI_ERROR_CODE(status)); + if (EFI_ERROR(status) || gmm_retries == 0) { + printf("%s: GetMemoryMap error: %lu\n", __func__, + EFI_ERROR_CODE(status)); + if (addr != 0) + BS->FreePages(addr, pages); return (EINVAL); } + status = BS->ExitBootServices(IH, efi_mapkey); - if (EFI_ERROR(status) == 0) { + if (!EFI_ERROR(status)) { /* * This may be disabled by setting efi_disable_vmap in * loader.conf(5). By default we will setup the virtual