blob: 3aac729950d5aa679f77cee040f4af9aed70ce50 [file] [log] [blame]
// Copyright 2020 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 <align.h>
#include <lib/acpi_lite.h>
#include <lib/acpi_lite/zircon.h>
#include <lib/page/size.h>
#include <zircon/compiler.h>
#include <kernel/range_check.h>
#include <ktl/limits.h>
#include <vm/physmap.h>
#include <vm/vm_aspace.h>
#include <vm/vm_object_physical.h>
namespace {
// AcpiParser requires a ZirconPhysmemReader instance that outlives
// it. We share a single global instance for all AcpiParser instances.
acpi_lite::ZirconPhysmemReader g_physmem_reader;
} // anonymous namespace
namespace acpi_lite {
zx::result<const void *> ZirconPhysmemReader::PhysToPtr(uintptr_t phys, size_t length) {
// We don't support the 0 physical address or 0-length ranges.
if (length == 0 || phys == 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
// Get the last byte of the specified range, ensuring we don't wrap around the address
// space.
if (phys > ktl::numeric_limits<uintptr_t>::max() - length) {
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
// Convert to a page aligned base and size.
const paddr_t paddr_base = RoundDownPageSize(phys);
const size_t size = RoundUpPageSize(phys + length - 1) - paddr_base;
Guard<Mutex> guard{&lock_};
// Search existing mappings.
ArchVmAspace &arch_aspace = VmAspace::kernel_aspace()->arch_aspace();
for (auto &mapping : mappings_) {
paddr_t map_paddr = 0;
[[maybe_unused]] uint mmu_flags = 0;
zx_status_t status = arch_aspace.Query(mapping.mapping->base(), &map_paddr, &mmu_flags);
if (status != ZX_OK) {
return zx::error{status};
}
DEBUG_ASSERT((mmu_flags & ARCH_MMU_FLAG_PERM_READ) != 0);
if (InRange(paddr_base, size, map_paddr, map_paddr + mapping.mapping->size())) {
uintptr_t offset = phys - map_paddr;
return zx::ok(reinterpret_cast<const void *>(mapping.mapping->base() + offset));
}
}
// Need to create a new mapping to cover this range.
fbl::AllocChecker ac;
ktl::unique_ptr<Mapping> pl = ktl::unique_ptr<Mapping>(new (&ac) Mapping());
if (!ac.check()) {
return zx::error{ZX_ERR_NO_MEMORY};
}
fbl::RefPtr<VmObjectPhysical> vmo;
zx_status_t status = VmObjectPhysical::Create(paddr_base, size, &vmo);
if (status != ZX_OK) {
return zx::error{status};
}
zx::result<VmAddressRegion::MapResult> map_result =
VmAspace::kernel_aspace()->RootVmar()->CreateVmMapping(
0, size, 0, VMAR_FLAG_CAN_MAP_READ, ktl::move(vmo), 0, ARCH_MMU_FLAG_PERM_READ, "acpi");
if (map_result.is_error()) {
return map_result.take_error();
}
status = map_result->mapping->MapRange(0, size, true, false);
if (status != ZX_OK) {
map_result->mapping->Destroy();
return zx::error(status);
}
pl->mapping = map_result->mapping;
mappings_.push_front(ktl::move(pl));
uintptr_t offset = phys - paddr_base;
return zx::ok(reinterpret_cast<const void *>(map_result->base + offset));
}
// Create a new AcpiParser, starting at the given Root System Description Pointer (RSDP).
zx::result<AcpiParser> AcpiParserInit(zx_paddr_t rsdp_pa) {
return AcpiParser::Init(g_physmem_reader, rsdp_pa);
}
} // namespace acpi_lite