blob: 0821429a67df30a5727609b161dbb489f37a4005 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_TRIVIAL_ALLOCATOR_INCLUDE_LIB_TRIVIAL_ALLOCATOR_ZIRCON_H_
#define SRC_LIB_TRIVIAL_ALLOCATOR_INCLUDE_LIB_TRIVIAL_ALLOCATOR_ZIRCON_H_
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <zircon/syscalls.h>
#include <cassert>
#include <utility>
namespace trivial_allocator {
// trivial_allocator::ZirconVmar holds a zx::unowned_vmar and uses it to meet
// the Memory API for trivial_allocator::PageAllocator.
class ZirconVmar {
public:
// We use a sub-VMAR as a capability for each allocation so that once it's
// been sealed, its protections cannot be changed again. (It can still be
// unmapped and something else mapped in the same location.)
using Capability = zx::vmar;
ZirconVmar() = default;
ZirconVmar(const ZirconVmar&) = default;
explicit ZirconVmar(const zx::vmar& vmar) : vmar_(vmar) { assert(vmar_->is_valid()); }
ZirconVmar& operator=(const ZirconVmar&) = default;
const zx::vmar& vmar() const { return *vmar_; }
[[gnu::const]] size_t page_size() const { return zx_system_get_page_size(); }
[[nodiscard]] std::pair<void*, zx::vmar> Allocate(size_t size) {
assert(vmar_->is_valid());
zx::vmo vmo;
zx_status_t status = zx::vmo::create(size, 0, &vmo);
if (status == ZX_OK) {
zx::vmar sub_vmar;
uintptr_t vmar_address;
status = vmar_->allocate(ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE, 0, size, &sub_vmar,
&vmar_address);
if (status == ZX_OK) {
uintptr_t address;
status = sub_vmar.map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, size, &address);
if (status == ZX_OK) {
assert(address >= vmar_address);
return {reinterpret_cast<void*>(address), std::move(sub_vmar)};
}
}
}
return {};
}
void Deallocate(zx::vmar sub_vmar, void* ptr, size_t size) {
// Destruction of the VMAR object cleans up the mapping.
assert(sub_vmar.is_valid());
[[maybe_unused]] zx_status_t status = sub_vmar.destroy();
assert(status == ZX_OK);
}
// The VMAR handle is consumed here, so there will no longer be any way to
// "unseal" this allocation (that is, change page protections on the memory).
void Seal(zx::vmar sub_vmar, void* ptr, size_t size) {
assert(sub_vmar.is_valid());
[[maybe_unused]] zx_status_t status =
sub_vmar.protect(ZX_VM_PERM_READ, reinterpret_cast<uintptr_t>(ptr), size);
assert(status == ZX_OK);
}
private:
zx::unowned_vmar vmar_;
};
} // namespace trivial_allocator
#endif // SRC_LIB_TRIVIAL_ALLOCATOR_INCLUDE_LIB_TRIVIAL_ALLOCATOR_ZIRCON_H_