blob: 45657efda8809579bf798a2575f15c9d4ed0cd5f [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
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_RESTRICTED_STATE_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_RESTRICTED_STATE_H_
#include <lib/user_copy/internal.h>
#include <lib/zx/result.h>
#include <zircon/syscalls-next.h>
#include <arch/exception.h>
#include <arch/regs.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include <ktl/unique_ptr.h>
// Per thread state to support restricted mode.
// Intentionally kept simple to keep the amount of kernel/thread.h dependencies to a minimum.
class VmObjectPaged;
class VmMapping;
// Must provide a definition of struct ArchSavedNormalState
#include <arch/restricted.h>
// Encapsulates a thread's restricted mode state.
//
// Note everything in this class should be accessed only by the thread that it belongs to,
// since there is no internal locking for efficiency reasons.
class RestrictedState {
public:
static zx::result<ktl::unique_ptr<RestrictedState>> Create();
~RestrictedState();
DISALLOW_COPY_ASSIGN_AND_MOVE(RestrictedState);
bool in_restricted() const { return in_restricted_; }
uintptr_t vector_ptr() const { return vector_ptr_; }
uintptr_t context() const { return context_; }
bool in_thread_exceptions_enabled() const { return in_thread_exceptions_enabled_; }
const ArchSavedNormalState& arch_normal_state() const { return arch_; }
ArchSavedNormalState& arch_normal_state() { return arch_; }
template <typename T>
T* state_ptr_as() const {
static_assert(internal::is_copy_allowed<T>::value);
return reinterpret_cast<T*>(state_mapping_ptr_);
}
zx_restricted_state_t* state_ptr() const { return state_ptr_as<zx_restricted_state_t>(); }
fbl::RefPtr<VmObjectPaged> vmo() const;
void set_in_restricted(bool r) { in_restricted_ = r; }
void set_vector_ptr(uintptr_t v) { vector_ptr_ = v; }
void set_context(uintptr_t c) { context_ = c; }
void set_in_thread_exceptions_enabled(bool enable) { in_thread_exceptions_enabled_ = enable; }
// Each arch must fill out the following routines prefixed with Arch:
//
// Prior to entering restricted mode, ask the arch layer to validate the saved register state is
// valid. Return ZX_OK if valid.
// Possible invalid states: program counter outside of user space, invalid processor flags, etc.
static zx_status_t ArchValidateStatePreRestrictedEntry(const zx_restricted_state_t& state);
// Just prior to entering restricted mode, give the arch layer a chance to save any state it
// may need for the return trip back to normal mode into the ArchSavedNormalState state argument.
// For example, the GS/FS base is saved here for x86.
static void ArchSaveStatePreRestrictedEntry(ArchSavedNormalState& state);
// Use an architcturally-specific mechanism to directly enter user space in restricted mode.
// Does not return.
[[noreturn]] static void ArchEnterRestricted(const zx_restricted_state_t& state);
// Having just exited from restricted mode via a syscall, save the necessary restricted mode
// state from a pointer to the syscall state saved by the exception handler.
static void ArchSaveRestrictedSyscallState(zx_restricted_state_t& state,
const syscall_regs_t& regs);
// Having just exited from restricted mode via an interrupt, save the necessary restricted mode
// state from a pointer to the interrupt frame state saved by the exception handler.
static void ArchSaveRestrictedIframeState(zx_restricted_state_t& state, const iframe_t& frame);
// Having exited from restricted mode via a synchronous exception, save the necessary
// restricted mode state.
static void ArchSaveRestrictedExceptionState(zx_restricted_state_t& state);
// Update the exception context so that we will return to normal mode to allow normal
// mode to handle a restricted exception.
static void ArchRedirectRestrictedExceptionToNormal(const ArchSavedNormalState& arch_state,
uintptr_t vector_table, uintptr_t context);
// Enter normal mode at the address pointed to by vector_table with arguments code and context
// in an architecturally specific register in an architecturally specific way.
[[noreturn]] static void ArchEnterFull(const ArchSavedNormalState& arch_state,
uintptr_t vector_table, uintptr_t context, uint64_t code);
// Dump the architecturally specific state out of the restricted mode state
static void ArchDump(const zx_restricted_state_t& state);
private:
RestrictedState(fbl::RefPtr<VmObjectPaged> state_vmo, fbl::RefPtr<VmMapping> state_mapping);
bool in_restricted_ = false;
bool in_thread_exceptions_enabled_ = false;
uintptr_t vector_ptr_ = 0;
uintptr_t context_ = 0;
// Ref pointer to a vmo holding the restricted state and a kernel mapping of the first page.
const fbl::RefPtr<VmObjectPaged> state_vmo_;
const fbl::RefPtr<VmMapping> state_mapping_;
// Cached pointer to the mapping, to avoid needing to deref the mapping on every access.
void* const state_mapping_ptr_ = nullptr;
// Arch specific part of the save state
ArchSavedNormalState arch_;
};
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_RESTRICTED_STATE_H_