blob: daf0bbf0cd4b407df150e561c4a21e181da3675d [file] [log] [blame]
// Copyright 2023 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 "kernel/restricted_state.h"
#include <lib/fit/defer.h>
#include <trace.h>
#include <vm/vm_address_region.h>
#include <vm/vm_object_paged.h>
#define LOCAL_TRACE 0
static constexpr size_t kStateVmoSize = PAGE_SIZE;
zx::result<ktl::unique_ptr<RestrictedState>> RestrictedState::Create() {
// Create a VMO.
static constexpr uint32_t kVmoOptions = 0;
static constexpr uint32_t kPmmAllocFlags = PMM_ALLOC_FLAG_ANY | PMM_ALLOC_FLAG_CAN_WAIT;
fbl::RefPtr<VmObjectPaged> state_vmo;
zx_status_t status =
VmObjectPaged::Create(kPmmAllocFlags, kVmoOptions, kStateVmoSize, &state_vmo);
if (status != ZX_OK) {
return zx::error_result(status);
}
DEBUG_ASSERT(state_vmo->is_paged());
DEBUG_ASSERT(!state_vmo->is_resizable());
DEBUG_ASSERT(!state_vmo->is_discardable());
DEBUG_ASSERT(!state_vmo->is_user_pager_backed());
DEBUG_ASSERT(state_vmo->GetMappingCachePolicy() == ZX_CACHE_POLICY_CACHED);
// Commit and pin the VMO.
status = state_vmo->CommitRangePinned(0, kStateVmoSize, /*write=*/true);
if (status != ZX_OK) {
return zx::error_result(status);
}
auto unpin = fit::defer([&]() { state_vmo->Unpin(0, kStateVmoSize); });
// Create a mapping of this VMO in the kernel aspace.
fbl::RefPtr<VmAddressRegion> kernel_vmar =
VmAspace::kernel_aspace()->RootVmar()->as_vm_address_region();
zx::result<VmAddressRegion::MapResult> state_mapping_result = kernel_vmar->CreateVmMapping(
0, kStateVmoSize, 0, 0, state_vmo, 0, ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
"restricted state");
if (state_mapping_result.is_error()) {
return state_mapping_result.take_error();
}
auto unmap = fit::defer([&]() { state_mapping_result->mapping->Destroy(); });
LTRACEF("%s mapping at %#" PRIxPTR "\n", "", state_mapping_result->base);
// Eagerly fault in all the pages so we don't demand fault the mapping.
status = state_mapping_result->mapping->MapRange(0, kStateVmoSize, true);
if (status != ZX_OK) {
return zx::error_result(status);
}
fbl::AllocChecker ac;
ktl::unique_ptr<RestrictedState> rs(
new (&ac) RestrictedState(std::move(state_vmo), std::move(state_mapping_result->mapping)));
if (!ac.check()) {
return zx::error_result(ZX_ERR_NO_MEMORY);
}
unmap.cancel();
unpin.cancel();
return zx::ok(ktl::move(rs));
}
RestrictedState::RestrictedState(fbl::RefPtr<VmObjectPaged> state_vmo,
fbl::RefPtr<VmMapping> state_mapping)
: state_vmo_(ktl::move(state_vmo)),
state_mapping_(ktl::move(state_mapping)),
state_mapping_ptr_(reinterpret_cast<void*>(state_mapping_->base_locking())) {
DEBUG_ASSERT(is_kernel_address(reinterpret_cast<vaddr_t>(state_mapping_ptr_)));
}
RestrictedState::~RestrictedState() {
zx_status_t status = state_mapping_->Destroy();
DEBUG_ASSERT(status == ZX_OK);
state_vmo_->Unpin(0, state_vmo_->size());
}
fbl::RefPtr<VmObjectPaged> RestrictedState::vmo() const { return state_vmo_; }