blob: cc6fcbac777993b5a22129319f99d03adf1d1d67 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <fbl/ref_ptr.h>
#include <platform/pc/bootloader.h>
#include <platform/pc/efi.h>
#include <vm/vm_aspace.h>
namespace {
// Address space with EFI services mapped in 1:1.
fbl::RefPtr<VmAspace> efi_aspace;
// Switch into the given address space in a panic-handler friendly manner.
//
// In some contexts (such as panicking) the thread lock may already be
// held, in which case we avoid grabbing the lock again.
void PanicFriendlySwitchAspace(VmAspace* aspace) {
if (thread_lock.IsHeld()) {
vmm_set_active_aspace_locked(efi_aspace.get());
} else {
vmm_set_active_aspace(efi_aspace.get());
}
}
} // namespace
zx_status_t InitEfiServices() {
// Ensure EFI service details were passed in via the bootloader.
if (bootloader.efi_system_table == nullptr) {
return ZX_ERR_NOT_FOUND;
}
// Create a new address space.
efi_aspace = VmAspace::Create(VmAspace::TYPE_LOW_KERNEL, "uefi");
if (!efi_aspace) {
return ZX_ERR_NO_RESOURCES;
}
// Map in EFI services.
//
// We map the first 16 GiB of address space 1:1 from virt/phys.
//
// TODO: Be more precise about this. This gets the job done on the platforms
// we're working on right now, but is probably not entirely correct.
auto* desired_location = static_cast<void*>(0); // NOLINT
zx_status_t result = efi_aspace->AllocPhysical(
"1:1", 16 * 1024 * 1024 * 1024UL, &desired_location, PAGE_SIZE_SHIFT, 0,
VmAspace::VMM_FLAG_VALLOC_SPECIFIC,
ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE | ARCH_MMU_FLAG_PERM_EXECUTE);
if (result != ZX_OK) {
efi_aspace.reset();
return result;
}
return ZX_OK;
}
EfiServicesActivation TryActivateEfiServices() {
// Ensure we have EFI services available and it has been initialised.
if (efi_aspace == nullptr) {
return EfiServicesActivation::Null();
}
// Switch into the address space where EFI services have been mapped.
VmAspace* old_aspace = Thread::Current::Get()->aspace();
PanicFriendlySwitchAspace(efi_aspace.get());
// Return the services.
efi_system_table* sys = static_cast<efi_system_table*>(bootloader.efi_system_table);
return EfiServicesActivation(old_aspace, sys->RuntimeServices);
}
void EfiServicesActivation::reset() {
if (previous_aspace_ == nullptr) {
return;
}
// Restore the previous address space.
PanicFriendlySwitchAspace(previous_aspace_);
previous_aspace_ = nullptr;
}