blob: 4c7d86c338782628e321d4e1d5dc63312426b390 [file]
// Copyright 2016 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
#ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_HANDLE_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_HANDLE_H_
#include <stdint.h>
#include <zircon/types.h>
#include <fbl/gparena.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include <ktl/atomic.h>
#include <ktl/move.h>
class Dispatcher;
class Handle;
constexpr uint32_t kHandleReservedBits = 2;
template <typename T>
class KernelHandle;
// HandleOwner wraps a Handle in a unique_ptr-like object that has single
// ownership of the Handle and deletes it whenever it falls out of scope.
class HandleOwner {
public:
HandleOwner() = default;
HandleOwner(decltype(nullptr)) : h_(nullptr) {}
explicit HandleOwner(Handle* h) : h_(h) {}
HandleOwner(const HandleOwner&) = delete;
HandleOwner& operator=(const HandleOwner&) = delete;
HandleOwner(HandleOwner&& other) : h_(other.release()) {}
HandleOwner& operator=(HandleOwner&& other) {
reset(other.release());
return *this;
}
~HandleOwner() { Destroy(); }
Handle* operator->() const { return h_; }
Handle* get() const { return h_; }
Handle* release() {
Handle* h = h_;
h_ = nullptr;
return h;
}
void reset(Handle* h) {
Destroy();
h_ = h;
}
void swap(HandleOwner& other) {
Handle* h = h_;
h_ = other.h_;
other.h_ = h;
}
explicit operator bool() const { return h_ != nullptr; }
private:
// Defined inline below.
inline void Destroy();
Handle* h_ = nullptr;
};
class HandleTableArena;
// A Handle is how a specific process refers to a specific Dispatcher.
class Handle final {
public:
// Returns the Dispatcher to which this instance points.
const fbl::RefPtr<Dispatcher>& dispatcher() const { return dispatcher_; }
// Returns the process that owns this instance. Used to guarantee
// that one process may not access a handle owned by a different process.
zx_koid_t process_id() const { return process_id_.load(ktl::memory_order_relaxed); }
// Sets the value returned by process_id().
void set_process_id(zx_koid_t pid);
// Returns the |rights| parameter that was provided when this instance
// was created.
uint32_t rights() const { return rights_; }
// Returns true if this handle has all of the desired rights bits set.
bool HasRights(zx_rights_t desired) const { return (rights_ & desired) == desired; }
// Returns a value that can be decoded by Handle::FromU32() to derive a
// pointer to this instance. ProcessDispatcher will XOR this with its
// |handle_rand_| to create the zx_handle_t value that user space sees.
uint32_t base_value() const { return base_value_; }
// To be called once during bring up.
static void Init();
// Maps an integer obtained by Handle::base_value() back to a Handle.
static Handle* FromU32(uint32_t value);
// Get the number of outstanding handles for a given dispatcher.
static uint32_t Count(const fbl::RefPtr<const Dispatcher>&);
// Should only be called by diagnostics.cpp.
struct diagnostics {
// Dumps internal details of the handle table using printf().
static void DumpTableInfo();
// Returns the number of outstanding handles.
static size_t OutstandingHandles();
};
// Handle should never be created by anything other than Make or Dup.
static HandleOwner Make(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights);
static HandleOwner Make(KernelHandle<Dispatcher> kernel_handle, zx_rights_t rights);
static HandleOwner Dup(Handle* source, zx_rights_t rights);
// Use a manually declared linked list node state instead of inheriting so that the early
// memory of the class can be used by the members we want to preserve, and our NodeState can
// be placed later.
using NodeState = fbl::DoublyLinkedListNodeState<Handle*>;
struct NodeListTraits {
static NodeState& node_state(Handle& h) { return h.node_state_; }
};
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(Handle);
// Called only by Make.
Handle(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights, uint32_t base_value);
// Called only by Dup.
Handle(Handle* rhs, zx_rights_t rights, uint32_t base_value);
// Private subroutines of Make and Dup.
static void* Alloc(const fbl::RefPtr<Dispatcher>&, const char* what, uint32_t* base_value);
static uint32_t GetNewBaseValue(void* addr);
// Handle should never be destroyed by anything other than Delete,
// which uses TearDown to do the actual destruction.
~Handle() = default;
void TearDown();
void Delete();
// NOTE! This can return an invalid address. It must be checked
// against the arena bounds before being cast to a Handle*.
static uintptr_t IndexToHandle(uint32_t index);
static uint32_t HandleToIndex(Handle* handle);
// Only HandleOwner is allowed to call Delete.
friend class HandleOwner;
// process_id_ is atomic because threads from different processes can
// access it concurrently, while holding different instances of
// handle_table_lock_.
ktl::atomic<zx_koid_t> process_id_;
fbl::RefPtr<Dispatcher> dispatcher_;
const zx_rights_t rights_;
const uint32_t base_value_;
// Up to here the members need to be preserved when handles are free'd to the arena. The
// PreserveSize is an 'approximation' of how large all the previous members are, but we make
// 'HandleTableArena' a friend so that it can statically validate that the chosen PreserveSize
// is correct. Any incorrect size will result in a compilation error!
static constexpr size_t PreserveSize = 24;
friend HandleTableArena;
NodeState node_state_;
};
class HandleTableArena {
private:
// Validate that all the fields we need to preserve fit within the preservation window.
static_assert(offsetof(Handle, process_id_) + sizeof(Handle::process_id_) <=
Handle::PreserveSize);
static_assert(offsetof(Handle, base_value_) + sizeof(Handle::base_value_) <=
Handle::PreserveSize);
static_assert(offsetof(Handle, dispatcher_) + sizeof(Handle::dispatcher_) <=
Handle::PreserveSize);
static fbl::GPArena<Handle::PreserveSize, sizeof(Handle)> arena_;
// Give the Handle access to its arena.
friend Handle;
};
// This can't be defined directly in the HandleOwner class definition
// because Handle is an incomplete type at that point.
inline void HandleOwner::Destroy() {
if (h_)
h_->Delete();
}
// A minimal wrapper around a Dispatcher which is owned by the kernel.
//
// Intended usage when creating new a Dispatcher object is:
// 1. Create a KernelHandle on the stack (cannot fail)
// 2. Move the RefPtr<Dispatcher> into the KernelHandle (cannot fail)
// 3. When ready to give the handle to a process, upgrade the KernelHandle
// to a full HandleOwner via UpgradeToHandleOwner() or
// user_out_handle::make() (can fail)
//
// This sequence ensures that the Dispatcher's on_zero_handles() method is
// called even if errors occur during or before HandleOwner creation, which
// is necessary to break circular references for some Dispatcher types.
//
// This class is thread-unsafe and must be externally synchronized if used
// across multiple threads.
template <typename T>
class KernelHandle {
public:
KernelHandle() = default;
explicit KernelHandle(fbl::RefPtr<T> dispatcher) : dispatcher_(ktl::move(dispatcher)) {}
~KernelHandle() { reset(); }
// Movable but not copyable since we own the underlying Dispatcher.
KernelHandle(const KernelHandle&) = delete;
KernelHandle& operator=(const KernelHandle&) = delete;
template <typename U>
KernelHandle(KernelHandle<U>&& other) : dispatcher_(ktl::move(other.dispatcher_)) {}
template <typename U>
KernelHandle& operator=(KernelHandle<U>&& other) {
reset(ktl::move(other.dispatcher_));
return *this;
}
void reset() { reset(fbl::RefPtr<T>()); }
template <typename U>
void reset(fbl::RefPtr<U> dispatcher) {
if (dispatcher_) {
dispatcher_->on_zero_handles();
}
dispatcher_ = ktl::move(dispatcher);
}
const fbl::RefPtr<T>& dispatcher() const { return dispatcher_; }
fbl::RefPtr<T> release() { return ktl::move(dispatcher_); }
private:
template <typename U>
friend class KernelHandle;
fbl::RefPtr<T> dispatcher_;
};
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_HANDLE_H_