# HG changeset patch # Parent e8692b3b9cbc7942a9349505a0dbbe6e6ca29579 diff -r e8692b3b9cbc sys/boot/amd64/efi/bootinfo.c --- a/sys/boot/amd64/efi/bootinfo.c +++ b/sys/boot/amd64/efi/bootinfo.c @@ -266,6 +266,7 @@ bi_load_efi_data(struct preloaded_file * sz += mmsz; sz = (sz + 0xf) & ~0xf; pages = EFI_SIZE_TO_PAGES(sz + efisz); + pages <<= 1; /* For extra copy of the memory map for SetVirtualMap */ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, &addr); if (EFI_ERROR(status)) { @@ -288,12 +289,26 @@ bi_load_efi_data(struct preloaded_file * (long)status); return (EINVAL); } + /* + * Stash away a second copy of the memory map + * We'll overwrite it later for the call to SetVirtualMap() and + * then fixup the ST structure after we call ExitBootServices. + * We setup a simple direct map that the FreeBSD kernel uses + * so that we can call allowed routines in the ST structure + * once the system's VM is initialized. This does impose a + * limitation that the addresses be in the first 4 TB of + * space, but the brute-force PT4 that's setup in + * elf64_freebsd.c is only 512GB, so that's not such a big + * limit... though machines with that much RAM are here today. + */ + memcpy(mm + sz, mm, sz); efihdr->memory_size = sz; efihdr->descriptor_size = mmsz; efihdr->descriptor_version = mmver; file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, efihdr); + file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST); return (0); } @@ -362,7 +377,6 @@ bi_load(char *args, vm_offset_t *modulep file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); - file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST); bi_load_efi_data(kfp); diff -r e8692b3b9cbc sys/boot/amd64/efi/elf64_freebsd.c --- a/sys/boot/amd64/efi/elf64_freebsd.c +++ b/sys/boot/amd64/efi/elf64_freebsd.c @@ -79,6 +79,26 @@ static void (*trampoline)(uint64_t stack extern uintptr_t amd64_tramp; extern uint32_t amd64_tramp_size; +static int +cvt_ptr(void **ptr) +{ + EFI_STATUS status; + + status = RS->ConvertPointer(0, ptr); + if (EFI_ERROR(status)) { + printf("%s: ConvertPointer(%p) returned 0x%lx\n", __func__, + *ptr, (long)status); + return (EINVAL); + } + return (0); +} + +#define CVT_PTR(p) \ + do { \ + if (cvt_ptr(p) != 0) \ + return (EINVAL); \ + } while (0); \ + /* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such @@ -177,6 +197,56 @@ elf64_exec(struct preloaded_file *fp) dev_cleanup(); + /* + * Tell the UEFI of the new memory map. This memory map will be valid + * after we boot, but isn't quite valid yet. It is unclear if this will + * cause problems for poorly written UEFI BIOS or not. In theory, + * everybody is already doing this, so this should be good. + */ + mm = efihdr + efihdr->memory_size; + for (i = 0; i < efihdr->memory_size / efihdr->descriptor_size; i++) + mm[i].va = mm[i].pa | DMAP_MIN; + ST->SetVirtualAddressMap(efihdr->memory_size, efihdr->descriptor_size, + efihdr->descriptor_version, mm); + + /* + * Fixup ST, some things must be NULL'd out, others must have their + * pointers converted. We just set the CRC32 to 0 for ST since after + * we've called ExitBootServices, we can't call the CRC32 routine in + * boot services. But we can't know the new ST and RS tables until + * afterwards since we can't change them until we call + * SetVirtualAddressMap. We null out console access, per recommendation + * in the standard (UEFI 2.4.B Section 6.4 ExitBootServices) + */ + ST->Hdr.CRC32 = 0; /* Not sure how to calculate this */ + CVT_PTR((void **)&ST->FirmwareVendor); + ST->ConsoleInHandle = NULL; + ST->ConIn = NULL; + ST->ConsoleOutHandle = NULL; + ST->ConOut = NULL; + ST->StandardErrorHandle = NULL; + ST->StdErr = NULL; + CVT_PTR((void **)&ST->RuntimeServices); /* Must use RS-> after here */ + ST->BootServices = NULL; + CVT_PTR((void **)&ST->ConfigurationTable); + + /* + * Fix up runtime services. See note above for CRC32. + */ + RS->Hdr.CRC32 = 0; /* Not sure how to calculate this */ + CVT_PTR((void **)&RS->GetTime); + CVT_PTR((void **)&RS->SetTime); + CVT_PTR((void **)&RS->GetWakeupTime); + CVT_PTR((void **)&RS->SetWakeupTime); + // Skip virtual memory services -- on purpose + CVT_PTR((void **)&RS->GetVariable); + CVT_PTR((void **)&RS->GetNextVariableName); + CVT_PTR((void **)&RS->SetVariable); + CVT_PTR((void **)&RS->GetNextHighMonotonicCount); + CVT_PTR((void **)&RS->ResetSystem); + + /* WARNING: Do not call anything in ST/RS until the direct map is setup */ + trampoline(trampstack, x86_efi_copy_finish, kernend, modulep, PT4, ehdr->e_entry);