/* * \brief Paging-server framework * \author Norman Feske * \date 2006-04-28 */ /* * Copyright (C) 2006-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _CORE__INCLUDE__PAGER_H_ #define _CORE__INCLUDE__PAGER_H_ /* Genode includes */ #include #include #include #include #include #include /* NOVA includes */ #include /* core-local includes */ #include #include namespace Genode { typedef Cpu_session::Thread_creation_failed Invalid_thread; class Pager_entrypoint; class Pager_object; class Exception_handlers { private: template __attribute__((regparm(1))) static void _handler(Pager_object &); public: Exception_handlers(Pager_object &); template void register_handler(Pager_object &, Nova::Mtd, void (__attribute__((regparm(1)))*)(Pager_object &) = nullptr); }; class Pager_object : public Object_pool::Entry { private: unsigned long _badge; /* used for debugging */ /** * User-level signal handler registered for this pager object via * 'Cpu_session::exception_handler()'. */ Signal_context_capability _exception_sigh { }; /** * selectors for * - cleanup portal * - semaphore used by caller used to notify paused state * - semaphore used to block during page fault handling or pausing */ addr_t _selectors; addr_t _initial_esp = 0; addr_t _initial_eip = 0; addr_t _client_exc_pt_sel; Lock _state_lock { }; struct { struct Thread_state thread; addr_t sel_client_ec; enum { BLOCKED = 0x1U, DEAD = 0x2U, SINGLESTEP = 0x4U, SIGNAL_SM = 0x8U, DISSOLVED = 0x10U, SUBMIT_SIGNAL = 0x20U, BLOCKED_PAUSE_SM = 0x40U, }; uint8_t _status; bool modified; /* convenience function to access pause/recall state */ inline bool blocked() { return _status & BLOCKED;} inline void block() { _status |= BLOCKED; } inline void unblock() { _status &= ~BLOCKED; } inline bool blocked_pause_sm() { return _status & BLOCKED_PAUSE_SM;} inline void block_pause_sm() { _status |= BLOCKED_PAUSE_SM; } inline void unblock_pause_sm() { _status &= ~BLOCKED_PAUSE_SM; } inline void mark_dead() { _status |= DEAD; } inline bool is_dead() { return _status & DEAD; } inline bool singlestep() { return _status & SINGLESTEP; } inline void mark_signal_sm() { _status |= SIGNAL_SM; } inline bool has_signal_sm() { return _status & SIGNAL_SM; } inline void mark_dissolved() { _status |= DISSOLVED; } inline bool dissolved() { return _status & DISSOLVED; } inline bool to_submit() { return _status & SUBMIT_SIGNAL; } inline void submit_signal() { _status |= SUBMIT_SIGNAL; } inline void reset_submit() { _status &= ~SUBMIT_SIGNAL; } } _state { }; Cpu_session_capability _cpu_session_cap; Thread_capability _thread_cap; Affinity::Location const _location; Exception_handlers _exceptions; addr_t _pd_target; void _copy_state_from_utcb(Nova::Utcb const &utcb); void _copy_state_to_utcb(Nova::Utcb &utcb) const; uint8_t _unsynchronized_client_recall(bool get_state_and_block); addr_t sel_pt_cleanup() const { return _selectors; } addr_t sel_sm_block_pause() const { return _selectors + 1; } addr_t sel_sm_block_oom() const { return _selectors + 2; } addr_t sel_oom_portal() const { return _selectors + 3; } __attribute__((regparm(1))) static void _page_fault_handler(Pager_object &); __attribute__((regparm(1))) static void _startup_handler(Pager_object &); __attribute__((regparm(1))) static void _invoke_handler(Pager_object &); __attribute__((regparm(1))) static void _recall_handler(Pager_object &); __attribute__((regparm(3))) static void _oom_handler(addr_t, addr_t, addr_t); public: Pager_object(Cpu_session_capability cpu_session_cap, Thread_capability thread_cap, unsigned long badge, Affinity::Location location, Genode::Session_label const &, Cpu_session::Name const &); virtual ~Pager_object(); unsigned long badge() const { return _badge; } void reset_badge() { _badge = 0; } const char * client_thread() const; const char * client_pd() const; virtual int pager(Ipc_pager &ps) = 0; /** * Assign user-level exception handler for the pager object */ void exception_handler(Signal_context_capability sigh) { _exception_sigh = sigh; } Affinity::Location location() const { return _location; } /** * Assign PD selector to PD */ void assign_pd(addr_t pd_sel) { _pd_target = pd_sel; } addr_t pd_sel() const { return _pd_target; } void exception(uint8_t exit_id); /** * Return base of initial portal window */ addr_t exc_pt_sel_client() { return _client_exc_pt_sel; } /** * Set initial stack pointer used by the startup handler */ addr_t initial_esp() { return _initial_esp; } void initial_esp(addr_t esp) { _initial_esp = esp; } /** * Set initial instruction pointer used by the startup handler */ void initial_eip(addr_t eip) { _initial_eip = eip; } /** * Continue execution of pager object */ void wake_up(); /** * Notify exception handler about the occurrence of an exception */ bool submit_exception_signal() { if (!_exception_sigh.valid()) return false; _state.reset_submit(); Signal_transmitter transmitter(_exception_sigh); transmitter.submit(); return true; } /** * Copy thread state of recalled thread. */ bool copy_thread_state(Thread_state * state_dst) { Lock::Guard _state_lock_guard(_state_lock); if (!state_dst || !_state.blocked()) return false; *state_dst = _state.thread; return true; } /* * Copy thread state to recalled thread. */ bool copy_thread_state(Thread_state state_src) { Lock::Guard _state_lock_guard(_state_lock); if (!_state.blocked()) return false; _state.thread = state_src; _state.modified = true; return true; } /** * Cancel blocking in a lock so that recall exception can take * place. */ void client_cancel_blocking(); uint8_t client_recall(bool get_state_and_block); void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; } inline void single_step(bool on) { _state_lock.lock(); if (_state.is_dead() || !_state.blocked() || (on && (_state._status & _state.SINGLESTEP)) || (!on && !(_state._status & _state.SINGLESTEP))) { _state_lock.unlock(); return; } if (on) _state._status |= _state.SINGLESTEP; else _state._status &= ~_state.SINGLESTEP; _state_lock.unlock(); /* force client in exit and thereby apply single_step change */ client_recall(false); } /** * Return CPU session that was used to created the thread */ Cpu_session_capability cpu_session_cap() const { return _cpu_session_cap; } /** * Return thread capability * * This function enables the destructor of the thread's * address-space region map to kill the thread. */ Thread_capability thread_cap() const { return _thread_cap; } /** * Note in the thread state that an unresolved page * fault occurred. */ void unresolved_page_fault_occurred() { _state.thread.unresolved_page_fault = true; } /** * Make sure nobody is in the handler anymore by doing an IPC to a * local cap pointing to same serving thread (if not running in the * context of the serving thread). When the call returns * we know that nobody is handled by this object anymore, because * all remotely available portals had been revoked beforehand. */ void cleanup_call(); /** * Portal called by thread that causes a out of memory in kernel. */ addr_t get_oom_portal(); enum Policy { STOP = 1, UPGRADE_CORE_TO_DST = 2, UPGRADE_PREFER_SRC_TO_DST = 3, }; enum Oom { SEND = 1, REPLY = 2, SELF = 4, SRC_CORE_PD = ~0UL, SRC_PD_UNKNOWN = 0, NO_NOTIFICATION = 0 }; /** * Implements policy on how to react on out of memory in kernel. * * Used solely inside core. On Genode core creates all the out * of memory portals per EC. If the PD of a EC runs out of kernel * memory it causes a OOM portal traversal, which is handled * by the pager object of the causing thread. * * /param pd_sel PD selector from where to transfer kernel memory * resources. The PD of this pager_object is the * target PD. * /param pd debug feature - string of PD (transfer_from) * /param thread debug feature - string of EC (transfer_from) */ uint8_t handle_oom(addr_t pd_sel = SRC_CORE_PD, const char * pd = "core", const char * thread = "unknown", Policy = Policy::UPGRADE_CORE_TO_DST); static uint8_t handle_oom(addr_t pd_from, addr_t pd_to, char const * src_pd, char const * src_thread, Policy policy, addr_t sm_notify = NO_NOTIFICATION, char const * dst_pd = "unknown", char const * dst_thread = "unknown"); void print(Output &out) const; }; /** * A 'Pager_activation' processes one page fault of a 'Pager_object' at a time. */ class Pager_entrypoint; class Pager_activation_base: public Thread { private: Native_capability _cap; Pager_entrypoint *_ep; /* entry point to which the activation belongs */ /** * Lock used for blocking until '_cap' is initialized */ Lock _cap_valid; /* * Noncopyable */ Pager_activation_base(Pager_activation_base const &); Pager_activation_base &operator = (Pager_activation_base const &); public: /** * Constructor * * \param name name of the new thread * \param stack_size stack size of the new thread */ Pager_activation_base(char const * const name, size_t const stack_size); /** * Thread interface */ void entry() override; /** * Return capability to this activation * * This function should only be called from 'Pager_entrypoint' */ Native_capability cap() { /* ensure that the initialization of our 'Ipc_pager' is done */ if (!_cap.valid()) _cap_valid.lock(); return _cap; } }; /** * Paging entry point * * For a paging entry point can hold only one activation. So, paging is * strictly serialized for one entry point. */ class Pager_entrypoint : public Object_pool { public: /** * Constructor * * \param cap_factory factory for creating capabilities * for the pager objects managed by this * entry point */ Pager_entrypoint(Rpc_cap_factory &cap_factory); /** * Associate Pager_object with the entry point */ Pager_capability manage(Pager_object &) { return Pager_capability(); } /** * Dissolve Pager_object from entry point */ void dissolve(Pager_object &obj); }; template class Pager_activation : public Pager_activation_base { public: Pager_activation() : Pager_activation_base("pager", STACK_SIZE) { } }; } #endif /* _CORE__INCLUDE__PAGER_H_ */