blob: 8cac5283155ebf79d35762074f1559625fbb5204 [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
#include <stdint.h>
#include <ktl/algorithm.h>
#include <ktl/span.h>
#include <ktl/string_view.h>
// PhysHandoffPtr provides a "smart pointer" style API for pointers handed off
// from physboot to the kernel proper. A handoff pointer is only ever created
// in physboot by the HandoffPrep class. It's only ever dereferenced (or
// converted into a raw pointer) in the kernel proper.
// Lifetime issues for handoff data are complex. PhysHandoffPtr is always
// treated as a traditional "owning" smart pointer and is a move-only type.
// Ordinarily, handoff pointer objects will be left in place and only have raw
// pointers extracted from them for later use.
// PhysHandoffPtr has no destructor and the "owning" pointer dying doesn't have
// any direct effect. The lifetime of all handoff pointers is actually grouped
// in two buckets:
// * Permanent handoff data will be accessible in the kernel's virtual address
// space permanently. This data resides on pages that the PMM has been told
// are owned by kernel mappings.
// * Temporary handoff data must be consumed only during the handoff phase,
// roughly until the kernel becomes multi-threaded(*). This data resides on
// pages that the PMM may be told to reuse after handoff.
// (*) TODO( Currently permanent handoff data is not possible
// at all, since the kernel proper will consume all physical memory. Temporary
// data is in fact available until userboot, which is the last thing in kernel
// startup. In future, the cutoff when temporary handoff pointers become
// invalid will be somewhere after PMM setup TBD.
enum class PhysHandoffPtrLifetime { Permanent, Temporary };
// The handoff pointers can be encoded in two ways.
// TODO( Currently pointers are physical addresses residing
// inside the data ZBI. The kernel will access them via the physmap.
// Eventually they will be kernel virtual pointers into some kernel mapping
// (possibly the physmap or possibly dedicated mappings). The distinction here
// can probably go away and have only kernel virtual pointers be supported.
enum class PhysHandoffPtrEncoding {
PhysAddr, // Stored as uintptr_t, physical address.
KernelVirtualPtr, // Stored as T*, kernel virtual address.
// This is defined only in the kernel proper and not in physboot.
template <PhysHandoffPtrEncoding Encoding>
[[gnu::const]] void* PhysHandoffPtrImportPhysAddr(uintptr_t ptr);
template <typename T, PhysHandoffPtrEncoding Encoding>
struct PhysHandoffPtrTraits {};
template <typename T>
struct PhysHandoffPtrTraits<T, PhysHandoffPtrEncoding::PhysAddr> {
using ExportType = uintptr_t;
static T* Import(ExportType ptr) {
return static_cast<T*>(PhysHandoffPtrImportPhysAddr<PhysHandoffPtrEncoding::PhysAddr>(ptr));
#ifdef _KERNEL
template <typename T>
struct PhysHandoffPtrTraits<T, PhysHandoffPtrEncoding::KernelVirtualPtr> {
using ExportType = T*;
static_assert(sizeof(ExportType) == sizeof(uintptr_t));
static_assert(alignof(ExportType) == alignof(uintptr_t));
static T* Import(ExportType ptr) { return ptr; }
template <typename T, PhysHandoffPtrEncoding Encoding, PhysHandoffPtrLifetime Lifetime>
class PhysHandoffPtr {
constexpr PhysHandoffPtr() = default;
constexpr PhysHandoffPtr(const PhysHandoffPtr&) = delete;
constexpr PhysHandoffPtr(PhysHandoffPtr&& other) noexcept : ptr_(ktl::exchange(other.ptr_, {})) {}
constexpr PhysHandoffPtr& operator=(PhysHandoffPtr&& other) noexcept {
ptr_ = ktl::exchange(other.ptr_, {});
return *this;
#ifdef _KERNEL
// Handoff pointers can only be dereferenced in the kernel proper.
T* get() const { return Traits::Import(ptr_); }
T* release() { return Traits::Import(ktl::exchange(ptr_, {})); }
T& operator*() const { return *get(); }
T* operator->() const { return get(); }
#endif // _KERNEL
using Traits = PhysHandoffPtrTraits<T, Encoding>;
friend class HandoffPrep;
typename Traits::ExportType ptr_{};
// PhysHandoffSpan<T> is to ktl::span<T> as PhysHandoffPtr<T> is to T*.
// It has get() and release() methods that return ktl::span<T>.
template <typename T, PhysHandoffPtrEncoding Encoding, PhysHandoffPtrLifetime Lifetime>
class PhysHandoffSpan {
PhysHandoffSpan() = default;
PhysHandoffSpan(const PhysHandoffSpan&) = default;
PhysHandoffSpan(PhysHandoffPtr<T, Encoding, Lifetime> ptr, size_t size)
: ptr_(ptr), size_(size) {}
#ifdef _KERNEL
ktl::span<T> get() const { return {ptr_.get(), size_}; }
ktl::span<T> release() { return {ptr_.release(), size_}; }
friend class HandoffPrep;
PhysHandoffPtr<T, Encoding, Lifetime> ptr_;
size_t size_ = 0;
// PhysHandoffString is stored just the same as PhysHandoffSpan<const char>,
// but its get() and release() methods yield ktl::string_view.
template <PhysHandoffPtrEncoding Encoding, PhysHandoffPtrLifetime Lifetime>
class PhysHandoffString : public PhysHandoffSpan<const char, Encoding, Lifetime> {
using Base = PhysHandoffSpan<const char, Encoding, Lifetime>;
PhysHandoffString() = default;
PhysHandoffString(const PhysHandoffString&) = default;
#ifdef _KERNEL
ktl::string_view get() const {
ktl::span str = Base::get();
return {, str.size()};
ktl::string_view release() {
ktl::span str = Base::release();
return {, str.size()};
// Convenience aliases used in the PhysHandoff declaration.
template <typename T>
using PhysHandoffTemporaryPtr =
PhysHandoffPtr<T, PhysHandoffPtrEncoding::PhysAddr, PhysHandoffPtrLifetime::Temporary>;
template <typename T>
using PhysHandoffTemporarySpan =
PhysHandoffSpan<T, PhysHandoffPtrEncoding::PhysAddr, PhysHandoffPtrLifetime::Temporary>;
using PhysHandoffTemporaryString =
PhysHandoffString<PhysHandoffPtrEncoding::PhysAddr, PhysHandoffPtrLifetime::Temporary>;
// TODO( permanent handoff pointers are not yet available
// template <typename T>
// using PhysHandoffPermanentPtr =
// PhysHandoffPtr<T, PhysHandoffPtrEncoding::KernelVirtualPtr,
// PhysHandoffPtrLifetime::Permanent>;
// template <typename T>
// using PhysHandoffPermanentSpan =
// PhysHandoffSpan<T, PhysHandoffPtrEncoding::KernelVirtualPtr,
// PhysHandoffPtrLifetime::Permanent>;
// using PhysHandoffPermanentString =
// PhysHandoffString<PhysHandoffPtrEncoding::KernelVirtualPtr,
// PhysHandoffPtrLifetime::Permanent>;