EFIRT: SetVirtualAddressMap with 1:1 mapping after exiting boot services
This fixes a problem encountered on the Lenovo Thinkpad X220/Yoga 11e where runtime services would try to inexplicably jump to other parts of memory where it shouldn't be when attempting to enumerate EFI vars, causing a panic. The virtual mapping is enabled by default and can be disabled by setting efi_disable_vmap in loader.conf(5). Reviewed by: kib (earlier version) MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D14677
This commit is contained in:
parent
a95659f75f
commit
63ee68c220
@ -236,17 +236,48 @@ bi_copymodules(vm_offset_t addr)
|
||||
return(addr);
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver)
|
||||
{
|
||||
EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap;
|
||||
EFI_STATUS ret;
|
||||
int curr, ndesc, nset;
|
||||
|
||||
nset = 0;
|
||||
desc = mm;
|
||||
ndesc = sz / mmsz;
|
||||
vmap = malloc(sz);
|
||||
if (vmap == NULL)
|
||||
/* This isn't really an EFI error case, but pretend it is */
|
||||
return (EFI_OUT_OF_RESOURCES);
|
||||
viter = vmap;
|
||||
for (curr = 0; curr < ndesc;
|
||||
curr++, desc = NextMemoryDescriptor(desc, mmsz)) {
|
||||
if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
|
||||
++nset;
|
||||
desc->VirtualStart = desc->PhysicalStart;
|
||||
*viter = *desc;
|
||||
viter = NextMemoryDescriptor(viter, mmsz);
|
||||
}
|
||||
}
|
||||
ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap);
|
||||
free(vmap);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
bi_load_efi_data(struct preloaded_file *kfp)
|
||||
{
|
||||
EFI_MEMORY_DESCRIPTOR *mm;
|
||||
EFI_PHYSICAL_ADDRESS addr;
|
||||
EFI_STATUS status;
|
||||
const char *efi_novmap;
|
||||
size_t efisz;
|
||||
UINTN efi_mapkey;
|
||||
UINTN mmsz, pages, retry, sz;
|
||||
UINT32 mmver;
|
||||
struct efi_map_header *efihdr;
|
||||
bool do_vmap;
|
||||
|
||||
#if defined(__amd64__) || defined(__aarch64__)
|
||||
struct efi_fb efifb;
|
||||
@ -266,6 +297,11 @@ bi_load_efi_data(struct preloaded_file *kfp)
|
||||
}
|
||||
#endif
|
||||
|
||||
do_vmap = true;
|
||||
efi_novmap = getenv("efi_disable_vmap");
|
||||
if (efi_novmap != NULL)
|
||||
do_vmap = strcasecmp(efi_novmap, "YES") != 0;
|
||||
|
||||
efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
|
||||
|
||||
/*
|
||||
@ -321,6 +357,13 @@ bi_load_efi_data(struct preloaded_file *kfp)
|
||||
}
|
||||
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;
|
||||
|
@ -165,7 +165,7 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
|
||||
descsz)) {
|
||||
if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
|
||||
continue;
|
||||
if (p->md_virt != NULL) {
|
||||
if (p->md_virt != NULL && (uint64_t)p->md_virt != p->md_phys) {
|
||||
if (bootverbose)
|
||||
printf("EFI Runtime entry %d is mapped\n", i);
|
||||
goto fail;
|
||||
|
@ -169,7 +169,7 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
|
||||
descsz)) {
|
||||
if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
|
||||
continue;
|
||||
if (p->md_virt != NULL) {
|
||||
if (p->md_virt != NULL && (uint64_t)p->md_virt != p->md_phys) {
|
||||
if (bootverbose)
|
||||
printf("EFI Runtime entry %d is mapped\n", i);
|
||||
goto fail;
|
||||
|
Loading…
Reference in New Issue
Block a user