Index: stand/efi/loader/bootinfo.c =================================================================== --- stand/efi/loader/bootinfo.c +++ stand/efi/loader/bootinfo.c @@ -287,7 +287,7 @@ 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; @@ -323,35 +323,46 @@ efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; /* - * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with + * Assign size of EFI_MEMORY_DESCRIPTOR to keep compatible with * u-boot which doesn't fill this value when buffer for memory * descriptors is too small (eg. 0 to obtain memory map size) */ mmsz = sizeof(EFI_MEMORY_DESCRIPTOR); /* - * 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. + * 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, so that one is marked as being loader + * data. */ - 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 = 0; + + for (retry = 10; retry > 0; retry--) { + status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver); + if (!EFI_ERROR(status)) + break; + + if (status != EFI_BUFFER_TOO_SMALL) { + printf("%s: GetMemoryMap error %lu\n", __func__, + EFI_ERROR_CODE(status)); + return (EINVAL); + } + + if (addr != 0) + BS->FreePages(addr, pages); + + /* Add 10 descriptors to the size to allow for + * fragmentation caused by calling AllocatePages */ + sz += (10 * mmsz); sz = (sz + 0xf) & ~0xf; pages = EFI_SIZE_TO_PAGES(sz + efisz); status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, - pages, &addr); + pages, &addr); if (EFI_ERROR(status)) { printf("%s: AllocatePages error %lu\n", __func__, EFI_ERROR_CODE(status)); @@ -366,33 +377,38 @@ 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)); - return (EINVAL); - } - status = BS->ExitBootServices(IH, efi_mapkey); - if (EFI_ERROR(status) == 0) { - /* - * This may be disabled by setting efi_disable_vmap in - * loader.conf(5). By default we will setup the virtual - * map entries. - */ - if (do_vmap) - efi_do_vmap(mm, sz, mmsz, mmver); - efihdr->memory_size = sz; - efihdr->descriptor_size = mmsz; - efihdr->descriptor_version = mmver; - file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, - efihdr); - return (0); - } + if (EFI_ERROR(status) || retry == 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)) { + printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); BS->FreePages(addr, pages); + return (EINVAL); } - printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); - return (EINVAL); + + /* + * This may be disabled by setting efi_disable_vmap in + * loader.conf(5). By default we will setup the virtual + * map entries. + */ + + if (do_vmap) + efi_do_vmap(mm, sz, mmsz, mmver); + efihdr->memory_size = sz; + efihdr->descriptor_size = mmsz; + efihdr->descriptor_version = mmver; + file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, + efihdr); + + return (0); } /* Index: stand/efi/loader/copy.c =================================================================== --- stand/efi/loader/copy.c +++ stand/efi/loader/copy.c @@ -95,24 +95,41 @@ efi_verify_staging_size(unsigned long *nr_pages) { UINTN sz; - EFI_MEMORY_DESCRIPTOR *map, *p; + EFI_MEMORY_DESCRIPTOR *map = NULL, *p; EFI_PHYSICAL_ADDRESS start, end; - UINTN key, dsz; + UINTN key, dsz, retry; UINT32 dver; EFI_STATUS status; int i, ndesc; unsigned long available_pages = 0; sz = 0; - status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); - if (status != EFI_BUFFER_TOO_SMALL) { - printf("Can't determine memory map size\n"); - return; + + for (retry = 10; retry > 0; retry--) { + status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); + if (!EFI_ERROR(status)) + break; + + if (status != EFI_BUFFER_TOO_SMALL) { + printf("Can't read memory map: %lu\n", + EFI_ERROR_CODE(status)); + goto out; + } + + if (map != NULL) + free(map); + + /* Allocate 10 descriptors more than the size reported, + * to allow for any fragmentation caused by calling + * malloc */ + map = malloc(sz + (10 * sizeof(EFI_MEMORY_DESCRIPTOR))); + if (map == NULL) { + printf("Unable to allocate memory\n"); + goto out; + } } - map = malloc(sz); - status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); - if (EFI_ERROR(status)) { + if (EFI_ERROR(status) || retry == 0) { printf("Can't read memory map\n"); goto out; }