blob: 716c4bfcfe50ad48a131bfe11f149dd3a498b316 [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 <arch/interrupt.h>
#include <fbl/ref_ptr.h>
#include <ktl/optional.h>
#include <platform/pc/efi.h>
#include <vm/vm_aspace.h>
#include <ktl/enforce.h>
namespace {
// EFI system table physical address.
ktl::optional<uint64_t> gEfiSystemTable;
// 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) {
InterruptDisableGuard interrupt_guard;
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(uint64_t efi_system_table) {
ZX_ASSERT(!gEfiSystemTable);
gEfiSystemTable = efi_system_table;
// Create a new address space.
efi_aspace = VmAspace::Create(VmAspace::Type::LowKernel, "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();
}
ZX_DEBUG_ASSERT(gEfiSystemTable);
// 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 = reinterpret_cast<efi_system_table*>(*gEfiSystemTable);
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;
}