From a31378476dbefa338e171f3841649b22899823a5 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 5 Sep 2014 17:00:31 +0200 Subject: [PATCH] nova: use in core one pager per CPU Fixes #1394 --- repos/base-nova/include/32bit/nova/syscalls.h | 6 +- repos/base-nova/include/64bit/nova/syscalls.h | 6 +- repos/base-nova/include/base/ipc_msgbuf.h | 2 + repos/base-nova/include/base/ipc_pager.h | 2 +- repos/base-nova/include/base/pager.h | 238 +++++-- repos/base-nova/include/cpu_session/client.h | 17 +- .../base-nova/include/nova/syscall-generic.h | 20 +- repos/base-nova/include/nova/util.h | 46 +- .../nova_cpu_session/nova_cpu_session.h | 6 +- repos/base-nova/ports/nova.hash | 2 +- repos/base-nova/ports/nova.port | 2 +- repos/base-nova/src/base/ipc/pager.cc | 4 +- repos/base-nova/src/base/pager/pager.cc | 580 ++++++++++++------ repos/base-nova/src/base/server/server.cc | 4 +- .../src/core/cpu_session_extension.cc | 13 +- .../src/core/include/cpu_session_component.h | 1 + .../src/core/include/platform_thread.h | 2 +- repos/base-nova/src/core/platform_thread.cc | 23 +- repos/base-nova/src/core/thread_start.cc | 6 +- repos/base/run/thread.run | 17 +- repos/base/src/test/thread/main.cc | 61 ++ 21 files changed, 750 insertions(+), 308 deletions(-) diff --git a/repos/base-nova/include/32bit/nova/syscalls.h b/repos/base-nova/include/32bit/nova/syscalls.h index bfd06c7bc..b80d4fea1 100644 --- a/repos/base-nova/include/32bit/nova/syscalls.h +++ b/repos/base-nova/include/32bit/nova/syscalls.h @@ -180,11 +180,13 @@ namespace Nova { ALWAYS_INLINE __attribute__((noreturn)) - inline void reply(void *next_sp) + inline void reply(void *next_sp, unsigned sm = 0) { + mword_t reg = eax(NOVA_REPLY, 0, sm); + asm volatile ("sysenter;" : - : "a" (NOVA_REPLY), "c" (next_sp) + : "a" (reg), "c" (next_sp) : "memory"); __builtin_unreachable(); } diff --git a/repos/base-nova/include/64bit/nova/syscalls.h b/repos/base-nova/include/64bit/nova/syscalls.h index f54032ccd..134f6201e 100644 --- a/repos/base-nova/include/64bit/nova/syscalls.h +++ b/repos/base-nova/include/64bit/nova/syscalls.h @@ -142,12 +142,14 @@ namespace Nova { ALWAYS_INLINE __attribute__((noreturn)) - inline void reply(void *next_sp) + inline void reply(void *next_sp, unsigned long sm = 0) { + mword_t syscall = rdi(NOVA_REPLY, 0, sm); + asm volatile ("mov %1, %%rsp;" "syscall;" : - : "D" (NOVA_REPLY), "ir" (next_sp) + : "D" (syscall), "ir" (next_sp) : "memory"); __builtin_unreachable(); } diff --git a/repos/base-nova/include/base/ipc_msgbuf.h b/repos/base-nova/include/base/ipc_msgbuf.h index 87c7365a3..ea59ed9ce 100644 --- a/repos/base-nova/include/base/ipc_msgbuf.h +++ b/repos/base-nova/include/base/ipc_msgbuf.h @@ -394,6 +394,8 @@ namespace Genode { */ if (rcv_window != INVALID_INDEX) _rcv_pt_base = INVALID_INDEX; + + utcb->crd_rcv = 0; } }; diff --git a/repos/base-nova/include/base/ipc_pager.h b/repos/base-nova/include/base/ipc_pager.h index 9658b93bb..9deffb8cc 100644 --- a/repos/base-nova/include/base/ipc_pager.h +++ b/repos/base-nova/include/base/ipc_pager.h @@ -103,7 +103,7 @@ namespace Genode { /** * Answer current page fault */ - void reply_and_wait_for_fault(); + void reply_and_wait_for_fault(unsigned sm = 0); /** * Request instruction pointer of current fault diff --git a/repos/base-nova/include/base/pager.h b/repos/base-nova/include/base/pager.h index 4e1c5d18a..07ce11abd 100644 --- a/repos/base-nova/include/base/pager.h +++ b/repos/base-nova/include/base/pager.h @@ -24,17 +24,28 @@ namespace Genode { class Pager_entrypoint; + class Pager_object; - /* - * On NOVA, each pager object is an EC that corresponds to one user thread. - */ - class Pager_object : public Object_pool::Entry, - Thread_base + class Exception_handlers { private: - void entry() { } - void start() { } + template + __attribute__((regparm(1))) static void _handler(addr_t); + + public: + + Exception_handlers(Pager_object *); + + template + void register_handler(Pager_object *, Nova::Mtd, + void (__attribute__((regparm(1)))*)(addr_t) = nullptr); + }; + + + class Pager_object : public Object_pool::Entry + { + private: unsigned long _badge; /* used for debugging */ @@ -45,9 +56,12 @@ namespace Genode { Signal_context_capability _exception_sigh; /** - * Portal selector for object cleanup/destruction + * 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 _pt_cleanup; + addr_t _selectors; addr_t _initial_esp; addr_t _initial_eip; @@ -59,58 +73,77 @@ namespace Genode { struct Thread_state thread; addr_t sel_client_ec; enum { - VALID = 0x1U, - DEAD = 0x2U, - SINGLESTEP = 0x4U, - CLIENT_CANCEL = 0x8U, - SIGNAL_SM = 0x10U, + BLOCKED = 0x1U, + DEAD = 0x2U, + SINGLESTEP = 0x4U, + NOTIFY_REQUEST = 0x8U, + SIGNAL_SM = 0x10U, + DISSOLVED = 0x20U, + SUBMIT_SIGNAL = 0x40U, + SKIP_EXCEPTION = 0x80U, }; uint8_t _status; /* convenience function to access pause/recall state */ - inline bool is_valid() { return _status & VALID; } - inline void mark_valid() { _status |= VALID; } - inline void mark_invalid() { _status &= ~VALID; } - - inline bool is_client_cancel() { return _status & CLIENT_CANCEL; } - inline void mark_client_cancel() { _status |= CLIENT_CANCEL; } - inline void unmark_client_cancel() { _status &= ~CLIENT_CANCEL; } + inline bool blocked() { return _status & BLOCKED;} + inline void block() { _status |= BLOCKED; } + inline void unblock() { _status &= ~BLOCKED; } inline void mark_dead() { _status |= DEAD; } inline bool is_dead() { return _status & DEAD; } inline bool singlestep() { return _status & SINGLESTEP; } + inline void notify_request() { _status |= NOTIFY_REQUEST; } + inline bool notify_requested() { return _status & NOTIFY_REQUEST; } + inline void notify_cancel() { _status &= ~NOTIFY_REQUEST; } + 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; } + + inline bool skip_requested() { return _status & SKIP_EXCEPTION; } + inline void skip_request() { _status |= SKIP_EXCEPTION; } + inline void skip_reset() { _status &= ~SKIP_EXCEPTION; } } _state; - Thread_capability _thread_cap; + Thread_capability _thread_cap; + Exception_handlers _exceptions; void _copy_state(Nova::Utcb * utcb); - /** - * Semaphore selector to synchronize pause/state/resume operations - */ - addr_t sm_state_notify() { return _pt_cleanup + 1; } - - static void _page_fault_handler(); - static void _startup_handler(); - static void _invoke_handler(); - static void _recall_handler(); + addr_t sel_pt_cleanup() { return _selectors; } + addr_t sel_sm_notify() { return _selectors + 1; } + addr_t sel_sm_block() { return _selectors + 2; } __attribute__((regparm(1))) - static void _exception_handler(addr_t portal_id); + static void _page_fault_handler(addr_t pager_obj); - static Nova::Utcb * _check_handler(Thread_base *&, Pager_object *&); + __attribute__((regparm(1))) + static void _startup_handler(addr_t pager_obj); + + __attribute__((regparm(1))) + static void _invoke_handler(addr_t pager_obj); + + __attribute__((regparm(1))) + static void _recall_handler(addr_t pager_obj); public: + const Affinity::Location location; + Pager_object(unsigned long badge, Affinity::Location location); virtual ~Pager_object(); unsigned long badge() const { return _badge; } + void reset_badge() { _badge = 0; } virtual int pager(Ipc_pager &ps) = 0; @@ -122,15 +155,11 @@ namespace Genode { _exception_sigh = sigh; } - /** - * Return base of initial portal window - */ - addr_t ec_sel() { return _tid.ec_sel; } + void exception(uint8_t exit_id); /** * Return base of initial portal window */ - addr_t exc_pt_sel() { return _tid.exc_pt_sel; } addr_t exc_pt_sel_client() { return _client_exc_pt_sel; } addr_t exc_pt_vcpu() { return _client_exc_vcpu; } @@ -157,6 +186,8 @@ namespace Genode { { if (!_exception_sigh.valid()) return false; + _state.reset_submit(); + Signal_transmitter transmitter(_exception_sigh); transmitter.submit(); @@ -177,10 +208,12 @@ namespace Genode { */ Native_capability notify_sm() { - if (_state.is_valid() || _state.is_dead()) + if (_state.blocked() || _state.is_dead()) return Native_capability(); - return Native_capability(sm_state_notify()); + _state.notify_request(); + + return Native_capability(sel_sm_notify()); } /** @@ -188,7 +221,7 @@ namespace Genode { */ bool copy_thread_state(Thread_state * state_dst) { - if (!state_dst || !_state.is_valid()) + if (!state_dst || !_state.blocked()) return false; *state_dst = _state.thread; @@ -205,12 +238,31 @@ namespace Genode { uint8_t client_recall(); void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; } - inline void single_step(bool on) + inline Native_capability single_step(bool on) { + if (_state.is_dead() || + (on && (_state._status & _state.SINGLESTEP)) || + (!on && !(_state._status & _state.SINGLESTEP))) + return Native_capability(); + if (on) _state._status |= _state.SINGLESTEP; else _state._status &= ~_state.SINGLESTEP; + + /* we want to be notified if state change is done */ + _state.notify_request(); + /* the first single step exit ignore when switching it on */ + if (on && _state.blocked()) + _state.skip_request(); + + /* force client in exit and thereby apply single_step change */ + client_recall(); + /* single_step mode changes don't apply if blocked - wake up */ + if (_state.blocked()) + wake_up(); + + return Native_capability(sel_sm_notify()); } /** @@ -220,7 +272,7 @@ namespace Genode { Thread_capability thread_cap() { return _thread_cap; } const void thread_cap(Thread_capability cap) { _thread_cap = cap; } - /* + /** * Note in the thread state that an unresolved page * fault occurred. */ @@ -244,53 +296,109 @@ namespace Genode { void prepare_vCPU_portals() { _client_exc_vcpu = cap_map()->insert(Nova::NUM_INITIAL_VCPU_PT_LOG2); + } + }; - Nova::Utcb *utcb = reinterpret_cast(Thread_base::utcb()); + /** + * A 'Pager_activation' processes one page fault of a 'Pager_object' at a time. + */ + class Pager_entrypoint; + class Pager_activation_base: public Thread_base + { + private: - utcb->crd_rcv = Nova::Obj_crd(_client_exc_vcpu, Nova::NUM_INITIAL_VCPU_PT_LOG2); + Native_capability _cap; + Pager_entrypoint *_ep; /* entry point to which the + activation belongs */ + /** + * Lock used for blocking until '_cap' is initialized + */ + Lock _cap_valid; + + 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); + + /** + * Set entry point, which the activation serves + * + * This function is only called by the 'Pager_entrypoint' + * constructor. + */ + void ep(Pager_entrypoint *ep) { _ep = ep; } + + /** + * Thread interface + */ + void entry(); + + /** + * 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; } }; /** - * Dummy pager activation + * Paging entry point * - * Because on NOVA each pager object can be invoked separately, - * there is no central pager activation. - */ - class Pager_activation_base { }; - - - template - class Pager_activation : public Pager_activation_base - { }; - - - /** - * Dummy pager entrypoint + * 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 { private: - Cap_session *_cap_session; + Pager_activation_base *_activation; + Cap_session *_cap_session; public: - Pager_entrypoint(Cap_session *cap_session, - Pager_activation_base *a = 0) - : _cap_session(cap_session) { } + /** + * Constructor + * + * \param cap_session Cap_session for creating capabilities + * for the pager objects managed by this + * entry point + * \param a initial activation + */ + Pager_entrypoint(Cap_session *cap_session, Pager_activation_base *a = 0); /** - * Return capability for 'Pager_object' + * Associate Pager_object with the entry point */ Pager_capability manage(Pager_object *obj); /** - * Dissolve 'Pager_object' from entry point + * 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 /* _INCLUDE__BASE__PAGER_H_ */ diff --git a/repos/base-nova/include/cpu_session/client.h b/repos/base-nova/include/cpu_session/client.h index dad4e5425..77c819b97 100644 --- a/repos/base-nova/include/cpu_session/client.h +++ b/repos/base-nova/include/cpu_session/client.h @@ -67,8 +67,14 @@ namespace Genode { void exception_handler(Thread_capability thread, Signal_context_capability handler) { call(thread, handler); } - void single_step(Thread_capability thread, bool enable) { - call(thread, enable); } + void single_step(Thread_capability thread, bool enable) + { + Native_capability block = call(thread, enable); + if (!block.valid()) + return; + + Nova::sm_ctrl(block.local_name(), Nova::SEMAPHORE_DOWN); + } Affinity::Space affinity_space() const { return call(); } @@ -100,8 +106,11 @@ namespace Genode { private: - Native_capability pause_sync(Thread_capability target) { - return Native_capability::invalid_cap(); } + Native_capability pause_sync(Thread_capability) { + return Native_capability(); } + + Native_capability single_step_sync(Thread_capability, bool) { + return Native_capability(); } }; } diff --git a/repos/base-nova/include/nova/syscall-generic.h b/repos/base-nova/include/nova/syscall-generic.h index 21f8fcc2b..26b40daf4 100644 --- a/repos/base-nova/include/nova/syscall-generic.h +++ b/repos/base-nova/include/nova/syscall-generic.h @@ -129,16 +129,24 @@ namespace Nova { unsigned cpus() const { unsigned cpu_num = 0; - const char * cpu_desc = - reinterpret_cast(this) + cpu_desc_offset; - for (unsigned i = 0; i < cpu_max(); i++) { - if ((*cpu_desc) & 0x1) cpu_num++; - cpu_desc += cpu_desc_size; - } + for (unsigned i = 0; i < cpu_max(); i++) + if (is_cpu_enabled(i)) + cpu_num++; return cpu_num; } + + bool is_cpu_enabled(unsigned i) const { + if (i >= cpu_max()) + return false; + + const char * cpu_desc = reinterpret_cast(this) + + cpu_desc_offset + i * cpu_desc_size; + + return (*cpu_desc) & 0x1; + } + } __attribute__((packed)); diff --git a/repos/base-nova/include/nova/util.h b/repos/base-nova/include/nova/util.h index 28c0c1597..49df5fcfc 100644 --- a/repos/base-nova/include/nova/util.h +++ b/repos/base-nova/include/nova/util.h @@ -56,17 +56,22 @@ inline void request_event_portal(Genode::Native_capability const &cap, inline void request_native_ec_cap(Genode::Native_capability const &cap, - Genode::addr_t sel, unsigned no_pager_cap = 0) { - request_event_portal(cap, sel , ~0UL, no_pager_cap); } + Genode::addr_t const sel, + unsigned const no_pager_cap = 0) +{ + request_event_portal(cap, sel , ~0UL, no_pager_cap); +} inline void request_signal_sm_cap(Genode::Native_capability const &cap, - Genode::addr_t sel) { - request_event_portal(cap, sel, ~0UL - 1, 0); } + Genode::addr_t const sel) +{ + request_event_portal(cap, sel, ~0UL - 1, 0); +} inline void delegate_vcpu_portals(Genode::Native_capability const &cap, - Genode::addr_t sel) + Genode::addr_t const sel) { Genode::Thread_base * myself = Genode::Thread_base::myself(); Nova::Utcb *utcb = reinterpret_cast(myself->utcb()); @@ -74,14 +79,33 @@ inline void delegate_vcpu_portals(Genode::Native_capability const &cap, /* save original receive window */ Nova::Crd orig_crd = utcb->crd_rcv; - Nova::Obj_crd obj_crd(sel, Nova::NUM_INITIAL_VCPU_PT_LOG2); - utcb->crd_rcv = Nova::Obj_crd(); - utcb->set_msg_word(0); - Genode::uint8_t res = utcb->append_item(obj_crd, 0); - (void)res; - res = Nova::call(cap.local_name()); + Genode::uint8_t res = Nova::NOVA_OK; + enum { + TRANSLATE = true, THIS_PD = false, NON_GUEST = false, HOTSPOT = 0, + TRANSFER_ITEMS = 1U << (Nova::NUM_INITIAL_VCPU_PT_LOG2 - 1) + }; + + /* prepare translation items for every portal separately */ + for (unsigned half = 0; !res && half < 2; half++) { + /* translate half of portals - due to size constraints on 64bit */ + utcb->msg[0] = half; + utcb->set_msg_word(1); + /* add one translate item per portal */ + for (unsigned i = 0; !res && i < TRANSFER_ITEMS; i++) { + Nova::Obj_crd obj_crd(sel + half * TRANSFER_ITEMS + i, 0); + + if (!utcb->append_item(obj_crd, HOTSPOT, THIS_PD, NON_GUEST, + TRANSLATE)) + res = 0xff; + } + if (res != Nova::NOVA_OK) + break; + + /* trigger the translation */ + res = Nova::call(cap.local_name()); + } /* restore original receive window */ utcb->crd_rcv = orig_crd; diff --git a/repos/base-nova/include/nova_cpu_session/nova_cpu_session.h b/repos/base-nova/include/nova_cpu_session/nova_cpu_session.h index d77e72375..f3cf22d46 100644 --- a/repos/base-nova/include/nova_cpu_session/nova_cpu_session.h +++ b/repos/base-nova/include/nova_cpu_session/nova_cpu_session.h @@ -24,6 +24,7 @@ namespace Genode { virtual ~Nova_cpu_session() { } virtual Native_capability pause_sync(Thread_capability) = 0; + virtual Native_capability single_step_sync(Thread_capability, bool) = 0; /********************* @@ -32,8 +33,11 @@ namespace Genode { GENODE_RPC(Rpc_pause_sync, Native_capability, pause_sync, Thread_capability); + GENODE_RPC(Rpc_single_step_sync, Native_capability, single_step_sync, + Thread_capability, bool); - GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_pause_sync); + GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_pause_sync, + Rpc_single_step_sync); }; } diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index dd0dbba33..960bd4d84 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -0c75755a7d208d1f114b25becd129c760d57f456 +6425874c3de23cd3cd5ab08a9273b60aebbd1b80 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index ed1ae4c50..4cc00b035 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git URL(nova) := https://github.com/alex-ab/NOVA.git # r8 branch -REV(nova) := 41868231ae25678e1f2e344e40fbdbd0fd13e8ee +REV(nova) := d82533c18bdae96c036c900c7adf431ec7efc377 DIR(nova) := src/kernel/nova PATCHES := $(wildcard $(REP_DIR)/patches/*.patch) diff --git a/repos/base-nova/src/base/ipc/pager.cc b/repos/base-nova/src/base/ipc/pager.cc index 3826b1ff4..fd1a8df3d 100644 --- a/repos/base-nova/src/base/ipc/pager.cc +++ b/repos/base-nova/src/base/ipc/pager.cc @@ -64,7 +64,7 @@ void Ipc_pager::set_reply_mapping(Mapping m) } -void Ipc_pager::reply_and_wait_for_fault() +void Ipc_pager::reply_and_wait_for_fault(unsigned sm) { - Nova::reply(Thread_base::myself()->stack_top()); + Nova::reply(Thread_base::myself()->stack_top(), sm); } diff --git a/repos/base-nova/src/base/pager/pager.cc b/repos/base-nova/src/base/pager/pager.cc index e21b75aab..e8682dde1 100644 --- a/repos/base-nova/src/base/pager/pager.cc +++ b/repos/base-nova/src/base/pager/pager.cc @@ -17,111 +17,156 @@ #include #include +#include + +#include + /* NOVA includes */ #include +#include /* map_local */ using namespace Genode; using namespace Nova; -enum { PF_HANDLER_STACK_SIZE = 2 * sizeof(addr_t) * 1024 }; extern Genode::addr_t __core_pd_sel; - -Utcb * Pager_object::_check_handler(Thread_base *&myself, Pager_object *&obj) +static Nova::Hip * kernel_hip() { - Utcb * utcb; - myself = Thread_base::myself(); - obj = static_cast(myself); + /** + * Initial value of esp register, saved by the crt0 startup code. + * This value contains the address of the hypervisor information page. + */ + extern addr_t __initial_sp; + return reinterpret_cast(__initial_sp); +} - if (!myself || !obj) goto dead; +/* pager activation threads storage and handling - one thread per CPU */ +enum { PAGER_CPUS = 128, PAGER_STACK_SIZE = 2*4096 }; - utcb = reinterpret_cast(myself->utcb()); - if (!utcb) goto dead; +static char pager_activation_mem[sizeof (Pager_activation) * PAGER_CPUS]; +static Pager_activation_base * pager_threads[PAGER_CPUS]; - return utcb; - dead: +static unsigned which_cpu(Pager_activation_base * pager) +{ + Pager_activation_base * start = reinterpret_cast(&pager_activation_mem); + Pager_activation_base * end = start + PAGER_CPUS; - PERR("unexpected exception-fault for non-existing pager object," - " going to sleep forever"); + if (start <= pager && pager < end) { + /* pager of one of the non boot CPUs */ + unsigned cpu_id = pager - start; + return cpu_id; + } - if (obj) obj->_state.mark_dead(); - sleep_forever(); + /* pager of boot CPU */ + return Affinity::Location().xpos(); } -void Pager_object::_page_fault_handler() +void Pager_object::_page_fault_handler(addr_t pager_obj) { Ipc_pager ipc_pager; ipc_pager.wait_for_fault(); - Thread_base *myself; - Pager_object *obj; - Utcb *utcb = _check_handler(myself, obj); + Thread_base * myself = Thread_base::myself(); + Pager_object * obj = reinterpret_cast(pager_obj); + Utcb * utcb = reinterpret_cast(myself->utcb()); + Pager_activation_base * pager_thread = static_cast(myself); + /* lookup fault address and decide what to do */ int ret = obj->pager(ipc_pager); - if (ret) { - if (obj->client_recall() != Nova::NOVA_OK) { - char client_name[Context::NAME_LEN]; - myself->name(client_name, sizeof(client_name)); + /* don't open receive window for pager threads */ + if (utcb->crd_rcv.value()) + nova_die(); - PWRN("unresolvable page fault since recall failed, '%s' " - "address=0x%lx ip=0x%lx", client_name, ipc_pager.fault_addr(), - ipc_pager.fault_ip()); + /* good case - found a valid region which is mappable */ + if (!ret) + ipc_pager.reply_and_wait_for_fault(); - Native_capability pager_obj = obj->Object_pool::Entry::cap(); - revoke(pager_obj.dst(), true); + obj->_state.thread.ip = ipc_pager.fault_ip(); + obj->_state.thread.sp = 0; + obj->_state.thread.trapno = PT_SEL_PAGE_FAULT; - revoke(Obj_crd(obj->exc_pt_sel_client(), NUM_INITIAL_PT_LOG2), false); + obj->_state.block(); - obj->_state.mark_dead(); - } - - if (ret == 1) { - char client_name[Context::NAME_LEN]; - myself->name(client_name, sizeof(client_name)); - - PDBG("unhandled page fault, '%s' address=0x%lx ip=0x%lx", - client_name, ipc_pager.fault_addr(), ipc_pager.fault_ip()); - } + char const * client = reinterpret_cast(obj->_badge); + /* region manager fault - to be handled */ + if (ret == 1) { + PDBG("page fault, thread '%s', cpu %u, ip=%lx, fault address=0x%lx", + client, which_cpu(pager_thread), ipc_pager.fault_ip(), + ipc_pager.fault_addr()); utcb->set_msg_word(0); utcb->mtd = 0; + + /* block the faulting thread until region manager is done */ + ipc_pager.reply_and_wait_for_fault(obj->sel_sm_block()); } + /* unhandled case */ + obj->_state.mark_dead(); + + PWRN("unresolvable page fault, thread '%s', cpu %u, ip=%lx, " + "fault address=0x%lx ret=%u", client, which_cpu(pager_thread), + ipc_pager.fault_ip(), ipc_pager.fault_addr(), ret); + + Native_capability pager_cap = obj->Object_pool::Entry::cap(); + revoke(pager_cap.dst()); + + revoke(Obj_crd(obj->exc_pt_sel_client(), NUM_INITIAL_PT_LOG2)); + + utcb->set_msg_word(0); + utcb->mtd = 0; ipc_pager.reply_and_wait_for_fault(); } -void Pager_object::_exception_handler(addr_t portal_id) +void Pager_object::exception(uint8_t exit_id) { - Thread_base *myself; - Pager_object *obj; - Utcb *utcb = _check_handler(myself, obj); + Thread_base *myself = Thread_base::myself(); + Utcb * utcb = reinterpret_cast(myself->utcb()); + Pager_activation_base * pager_thread = static_cast(myself); + + if (exit_id > PT_SEL_PARENT || !pager_thread) + nova_die(); + addr_t fault_ip = utcb->ip; uint8_t res = 0xFF; addr_t mtd = 0; - if (obj->submit_exception_signal()) - res = obj->client_recall(); + if (_state.skip_requested()) { + _state.skip_reset(); + + utcb->set_msg_word(0); + utcb->mtd = 0; + reply(myself->stack_top()); + } + + /* remember exception type for cpu_session()->state() calls */ + _state.thread.trapno = exit_id; + _state.thread.ip = fault_ip; + + if (_exception_sigh.valid()) { + _state.submit_signal(); + res = client_recall(); + } if (res != NOVA_OK) { - char client_name[Context::NAME_LEN]; - myself->name(client_name, sizeof(client_name)); + /* nobody handles this exception - so thread will be stopped finally */ + _state.mark_dead(); - PWRN("unresolvable exception at ip 0x%lx, exception portal 0x%lx, %s, " - "'%s'", fault_ip, - portal_id, res == 0xFF ? "no signal handler" : - res == NOVA_OK ? "" : "recall failed", - client_name); + char const * client = reinterpret_cast(_badge); + PWRN("unresolvable exception %u, thread '%s', cpu %u, ip=0x%lx, %s", + exit_id, client, which_cpu(pager_thread), fault_ip, + res == 0xFF ? "no signal handler" : + (res == NOVA_OK ? "" : "recall failed")); - Nova::revoke(Obj_crd(portal_id, 0)); - obj->_state.mark_dead(); + Nova::revoke(Obj_crd(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2)); enum { TRAP_BREAKPOINT = 3 }; - if ((portal_id & 0x1f) == TRAP_BREAKPOINT) { + if (exit_id == TRAP_BREAKPOINT) { utcb->ip = fault_ip - 1; mtd = Mtd::EIP; } @@ -134,55 +179,58 @@ void Pager_object::_exception_handler(addr_t portal_id) } -void Pager_object::_recall_handler() +void Pager_object::_recall_handler(addr_t pager_obj) { - Thread_base *myself; - Pager_object *obj; - Utcb *utcb = _check_handler(myself, obj); + Thread_base * myself = Thread_base::myself(); + Pager_object * obj = reinterpret_cast(pager_obj); + Utcb * utcb = reinterpret_cast(myself->utcb()); + /* save state - can be requested via cpu_session->state */ obj->_copy_state(utcb); obj->_state.thread.ip = utcb->ip; obj->_state.thread.sp = utcb->sp; obj->_state.thread.eflags = utcb->flags; - obj->_state.thread.trapno = PT_SEL_RECALL; - obj->_state.mark_valid(); + /* thread becomes blocked */ + obj->_state.block(); - if (obj->_state.is_client_cancel()) - if (sm_ctrl(obj->sm_state_notify(), SEMAPHORE_UP) != NOVA_OK) - PWRN("notify failed"); + /* deliver signal if it was requested */ + if (obj->_state.to_submit()) + obj->submit_exception_signal(); - do { - if (sm_ctrl(obj->exc_pt_sel() + SM_SEL_EC, SEMAPHORE_DOWNZERO) != NOVA_OK) - PWRN("blocking recall handler failed"); - } while (obj->_state.is_client_cancel()); - - obj->_state.mark_invalid(); + /* notify callers of cpu_session()->pause that the state is now valid */ + if (obj->_state.notify_requested()) { + obj->_state.notify_cancel(); + if (sm_ctrl(obj->sel_sm_notify(), SEMAPHORE_UP) != NOVA_OK) + PWRN("paused notification failed"); + } + /* switch on/off single step */ bool singlestep_state = obj->_state.thread.eflags & 0x100UL; if (obj->_state.singlestep() && !singlestep_state) { utcb->flags = obj->_state.thread.eflags | 0x100UL; utcb->mtd = Nova::Mtd(Mtd::EFL).value(); - } else + } else { if (!obj->_state.singlestep() && singlestep_state) { utcb->flags = obj->_state.thread.eflags & ~0x100UL; utcb->mtd = Nova::Mtd(Mtd::EFL).value(); } else utcb->mtd = 0; + } + /* block until cpu_session()->resume() respectively wake_up() call */ utcb->set_msg_word(0); - - reply(myself->stack_top()); + reply(myself->stack_top(), obj->sel_sm_block()); } -void Pager_object::_startup_handler() +void Pager_object::_startup_handler(addr_t pager_obj) { - Thread_base *myself; - Pager_object *obj; - Utcb *utcb = _check_handler(myself, obj); + Thread_base *myself = Thread_base::myself(); + Pager_object * obj = reinterpret_cast(pager_obj); + Utcb * utcb = reinterpret_cast(myself->utcb()); utcb->ip = obj->_initial_eip; utcb->sp = obj->_initial_esp; @@ -194,22 +242,55 @@ void Pager_object::_startup_handler() } -void Pager_object::_invoke_handler() +void Pager_object::_invoke_handler(addr_t pager_obj) { - Thread_base *myself; - Pager_object *obj; - Utcb *utcb = _check_handler(myself, obj); + Thread_base *myself = Thread_base::myself(); + Pager_object * obj = reinterpret_cast(pager_obj); + Utcb * utcb = reinterpret_cast(myself->utcb()); - /* if protocol is violated ignore request and close receive window */ - if (utcb->msg_words() != 2) { - utcb->crd_rcv = Obj_crd(); - reply(myself->stack_top()); - } + /* receive window must be closed - otherwise implementation bug */ + if (utcb->crd_rcv.value()) + nova_die(); - /* send single portal as reply */ addr_t const event = utcb->msg[0]; addr_t const logcount = utcb->msg[1]; + /* check for translated vCPU portals */ + unsigned const items_count = 1U << (Nova::NUM_INITIAL_VCPU_PT_LOG2 - 1); + + if ((obj->_client_exc_vcpu != Native_thread::INVALID_INDEX) && + (utcb->msg_items() == items_count) && + (utcb->msg_words() == 1 && (event == 0UL || event == 1UL))) { + /* check all translated item and remap if valid */ + for (unsigned i = 0; i < items_count; i++) { + Nova::Utcb::Item * item = utcb->get_item(i); + + if (!item) + break; + + Nova::Crd cap(item->crd); + + if (cap.is_null() || item->is_del()) + continue; + + /** + * Remap portal to dense packed region - required for vCPU running + * in separate PD (non-colocated case) + */ + Obj_crd snd(cap.base(), 0); + Obj_crd rcv(obj->_client_exc_vcpu + event * items_count + i, 0); + if (map_local(utcb, snd, rcv)) + PWRN("could not remap vCPU portal 0x%x", i); + } + } + + /* if protocol is violated ignore request */ + if (utcb->msg_words() != 2) { + utcb->mtd = 0; + utcb->set_msg_word(0); + reply(myself->stack_top()); + } + utcb->mtd = 0; utcb->set_msg_word(0); @@ -276,19 +357,19 @@ void Pager_object::_invoke_handler() void Pager_object::wake_up() { - _state.unmark_client_cancel(); + if (!_state.blocked()) + return; - cancel_blocking(); + _state.unblock(); + + uint8_t res = sm_ctrl(sel_sm_block(), SEMAPHORE_UP); + if (res != NOVA_OK) + PWRN("canceling blocked client failed (thread sm)"); } void Pager_object::client_cancel_blocking() { - if (_state.is_client_cancel()) - return; - - _state.mark_client_cancel(); - uint8_t res = sm_ctrl(exc_pt_sel_client() + SM_SEL_EC, SEMAPHORE_UP); if (res != NOVA_OK) PWRN("canceling blocked client failed (thread sm)"); @@ -310,111 +391,155 @@ uint8_t Pager_object::client_recall() void Pager_object::cleanup_call() { + _state.mark_dissolved(); + /* revoke all portals handling the client. */ revoke(Obj_crd(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2)); - Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); - if (reinterpret_cast(this->utcb()) == utcb) return; - - /* if pager is blocked wake him up */ - wake_up(); + /* if we are paused or waiting for a page fault nothing is in-flight */ + if (_state.blocked()) + return; + Utcb *utcb = reinterpret_cast(Thread_base::myself()->utcb()); utcb->set_msg_word(0); - if (uint8_t res = call(_pt_cleanup)) - PERR("%8p - cleanup call to pager (%8p) failed res=%d", - utcb, this->utcb(), res); + utcb->mtd = 0; + if (uint8_t res = call(sel_pt_cleanup())) + PERR("%8p - cleanup call to pager failed res=%d", utcb, res); } static uint8_t create_portal(addr_t pt, addr_t pd, addr_t ec, Mtd mtd, - addr_t eip) + addr_t eip, addr_t localname) { uint8_t res = create_pt(pt, pd, ec, mtd, eip); + if (res != NOVA_OK) + return res; + + res = pt_ctrl(pt, localname); if (res == NOVA_OK) revoke(Obj_crd(pt, 0, Obj_crd::RIGHT_PT_CTRL)); + else + revoke(Obj_crd(pt, 0)); return res; } +/************************ + ** Exception handlers ** + ************************/ + +template +void Exception_handlers::register_handler(Pager_object *obj, Mtd mtd, + void (* __attribute__((regparm(1))) func)(addr_t)) +{ + unsigned use_cpu = obj->location.xpos(); + if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) + throw Rm_session::Invalid_thread(); + + addr_t const ec_sel = pager_threads[use_cpu]->tid().ec_sel; + + /* compiler generates instance of exception entry if not specified */ + addr_t entry = func ? (addr_t)func : (addr_t)(&_handler); + uint8_t res = create_portal(obj->exc_pt_sel_client() + EV, + __core_pd_sel, ec_sel, mtd, entry, + reinterpret_cast(obj)); + if (res != Nova::NOVA_OK) + throw Rm_session::Invalid_thread(); +} + + +template +void Exception_handlers::_handler(addr_t obj) +{ + Pager_object * pager_obj = reinterpret_cast(obj); + pager_obj->exception(EV); +} + + +Exception_handlers::Exception_handlers(Pager_object *obj) +{ + register_handler<0>(obj, Mtd(Mtd::EIP)); + register_handler<1>(obj, Mtd(Mtd::EIP)); + register_handler<2>(obj, Mtd(Mtd::EIP)); + register_handler<3>(obj, Mtd(Mtd::EIP)); + register_handler<4>(obj, Mtd(Mtd::EIP)); + register_handler<5>(obj, Mtd(Mtd::EIP)); + register_handler<6>(obj, Mtd(Mtd::EIP)); + register_handler<7>(obj, Mtd(Mtd::EIP)); + register_handler<8>(obj, Mtd(Mtd::EIP)); + register_handler<9>(obj, Mtd(Mtd::EIP)); + register_handler<10>(obj, Mtd(Mtd::EIP)); + register_handler<11>(obj, Mtd(Mtd::EIP)); + register_handler<12>(obj, Mtd(Mtd::EIP)); + register_handler<13>(obj, Mtd(Mtd::EIP)); + + register_handler<15>(obj, Mtd(Mtd::EIP)); + register_handler<16>(obj, Mtd(Mtd::EIP)); + register_handler<17>(obj, Mtd(Mtd::EIP)); + register_handler<18>(obj, Mtd(Mtd::EIP)); + register_handler<19>(obj, Mtd(Mtd::EIP)); + register_handler<20>(obj, Mtd(Mtd::EIP)); + register_handler<21>(obj, Mtd(Mtd::EIP)); + register_handler<22>(obj, Mtd(Mtd::EIP)); + register_handler<23>(obj, Mtd(Mtd::EIP)); + register_handler<24>(obj, Mtd(Mtd::EIP)); + register_handler<25>(obj, Mtd(Mtd::EIP)); +} + + +/****************** + ** Pager object ** + ******************/ + + Pager_object::Pager_object(unsigned long badge, Affinity::Location location) : - Thread_base(0, "pager:", PF_HANDLER_STACK_SIZE), - _badge(reinterpret_cast(_context->name + 6)), - _client_exc_vcpu(Native_thread::INVALID_INDEX) + _badge(badge), + _selectors(cap_map()->insert(2)), + _client_exc_pt_sel(cap_map()->insert(NUM_INITIAL_PT_LOG2)), + _client_exc_vcpu(Native_thread::INVALID_INDEX), + _exceptions(this), + location(location) { - class Create_exception_pt_failed { }; uint8_t res; - /* construct pager name out of client name */ - strncpy(_context->name + 6, reinterpret_cast(badge), - sizeof(_context->name) - 6); - addr_t pd_sel = __core_pd_sel; - _pt_cleanup = cap_map()->insert(1); - _client_exc_pt_sel = cap_map()->insert(NUM_INITIAL_PT_LOG2); _state._status = 0; _state.sel_client_ec = Native_thread::INVALID_INDEX; - if (_pt_cleanup == Native_thread::INVALID_INDEX || - _client_exc_pt_sel == Native_thread::INVALID_INDEX) - throw Create_exception_pt_failed(); + if (Native_thread::INVALID_INDEX == _selectors || + Native_thread::INVALID_INDEX == _client_exc_pt_sel) + throw Rm_session::Invalid_thread(); - /* tell thread starting code on which CPU to let run the pager */ - reinterpret_cast(stack_base())[0] = location; - - /* creates local EC */ - Thread_base::start(); - - /* create portal for exception handlers 0x0 - 0xd */ - for (unsigned i = 0; i < PT_SEL_PAGE_FAULT; i++) { - res = create_portal(exc_pt_sel_client() + i, pd_sel, _tid.ec_sel, - Mtd(Mtd::EIP), (addr_t)_exception_handler); - if (res) { - PERR("could not create exception portal, error = %u\n", res); - throw Create_exception_pt_failed(); - } + /* ypos information not supported by now */ + if (location.ypos()) { + PWRN("Unsupported location %ux%u", location.xpos(), location.ypos()); + throw Rm_session::Invalid_thread(); } - /* create portal for page-fault handler */ - res = create_portal(exc_pt_sel_client() + PT_SEL_PAGE_FAULT, pd_sel, _tid.ec_sel, - Mtd(Mtd::QUAL | Mtd::EIP), (mword_t)_page_fault_handler); - if (res) { - PERR("could not create page-fault portal, error = %u\n", res); - class Create_page_fault_pt_failed { }; - throw Create_page_fault_pt_failed(); - } + /* place Pager_object on specified CPU by selecting proper pager thread */ + unsigned use_cpu = location.xpos(); + if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) + throw Rm_session::Invalid_thread(); - /* create portal for exception handlers 0xf - 0x19 */ - for (unsigned i = PT_SEL_PAGE_FAULT + 1; i < PT_SEL_PARENT; i++) { - res = create_portal(exc_pt_sel_client() + i, pd_sel, _tid.ec_sel, - Mtd(Mtd::EIP), (addr_t)_exception_handler); - if (res) { - PERR("could not create exception portal, error = %u\n", res); - throw Create_exception_pt_failed(); - } - } + addr_t ec_sel = pager_threads[use_cpu]->tid().ec_sel; - /* create portal for startup handler */ - res = create_portal(exc_pt_sel_client() + PT_SEL_STARTUP, pd_sel, _tid.ec_sel, - Mtd(Mtd::ESP | Mtd::EIP), (mword_t)_startup_handler); - if (res) { - PERR("could not create startup portal, error = %u\n", - res); - class Create_startup_pt_failed { }; - throw Create_startup_pt_failed(); - } + /* create portal for page-fault handler - 14 */ + _exceptions.register_handler<14>(this, Mtd::QUAL | Mtd::EIP, + _page_fault_handler); - /* create portal for recall handler */ - Mtd mtd(Mtd::ESP | Mtd::EIP | Mtd::ACDB | Mtd::EFL | Mtd::EBSD | Mtd::FSGS); - res = create_portal(exc_pt_sel_client() + PT_SEL_RECALL, pd_sel, _tid.ec_sel, - mtd, (addr_t)_recall_handler); - if (res) { - PERR("could not create recall portal, error = %u\n", res); - class Create_recall_pt_failed { }; - throw Create_recall_pt_failed(); - } + /* create portal for startup handler - 26 */ + Mtd const mtd_startup(Mtd::ESP | Mtd::EIP); + _exceptions.register_handler(this, mtd_startup, + _startup_handler); + + /* create portal for recall handler - 31 */ + Mtd const mtd_recall(Mtd::ESP | Mtd::EIP | Mtd::ACDB | Mtd::EFL | + Mtd::EBSD | Mtd::FSGS); + _exceptions.register_handler(this, mtd_recall, + _recall_handler); /* * Create semaphore required for Genode locking. It can be later on @@ -422,40 +547,41 @@ Pager_object::Pager_object(unsigned long badge, Affinity::Location location) */ res = Nova::create_sm(exc_pt_sel_client() + SM_SEL_EC, pd_sel, 0); if (res != Nova::NOVA_OK) { - class Create_state_notifiy_sm_failed { }; - throw Create_state_notifiy_sm_failed(); + throw Rm_session::Invalid_thread(); } /* create portal for final cleanup call used during destruction */ - res = create_portal(_pt_cleanup, pd_sel, _tid.ec_sel, Mtd(0), - reinterpret_cast(_invoke_handler)); - if (res) { + res = create_portal(sel_pt_cleanup(), pd_sel, ec_sel, Mtd(0), + reinterpret_cast(_invoke_handler), + reinterpret_cast(this)); + if (res != Nova::NOVA_OK) { PERR("could not create pager cleanup portal, error = %u\n", res); - class Create_cleanup_pt_failed { }; - throw Create_cleanup_pt_failed(); + throw Rm_session::Invalid_thread(); } - res = Nova::create_sm(sm_state_notify(), pd_sel, 0); + /* used to notify caller of as soon as pause succeeded */ + res = Nova::create_sm(sel_sm_notify(), pd_sel, 0); if (res != Nova::NOVA_OK) { - class Create_state_notifiy_sm_failed { }; - throw Create_state_notifiy_sm_failed(); + throw Rm_session::Invalid_thread(); + } + + /* semaphore used to block paged thread during page fault or recall */ + res = Nova::create_sm(sel_sm_block(), pd_sel, 0); + if (res != Nova::NOVA_OK) { + throw Rm_session::Invalid_thread(); } } Pager_object::~Pager_object() { - /* if pager is blocked wake him up */ - sm_ctrl(sm_state_notify(), SEMAPHORE_UP); - revoke(Obj_crd(sm_state_notify(), 0)); + /* sanity check that object got dissolved already - otherwise bug */ + if (!_state.dissolved()) + nova_die(); - /* take care nobody is handled anymore by this object */ - cleanup_call(); - - /* revoke portal used for the cleanup call */ - revoke(Obj_crd(_pt_cleanup, 0)); - - cap_map()->remove(_pt_cleanup, 1, false); + /* revoke portal used for the cleanup call and sm cap for blocking state */ + revoke(Obj_crd(_selectors, 2)); + cap_map()->remove(_selectors, 2, false); cap_map()->remove(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2, false); if (_client_exc_vcpu == Native_thread::INVALID_INDEX) @@ -467,14 +593,86 @@ Pager_object::~Pager_object() } +/********************** + ** Pager activation ** + **********************/ + +Pager_activation_base::Pager_activation_base(const char *name, size_t stack_size) +: + Thread_base(0, name, stack_size), + _cap(Native_capability()), _ep(0), _cap_valid(Lock::LOCKED) +{ + /* tell thread starting code on which CPU to let run the pager */ + reinterpret_cast(stack_base())[0] = Affinity::Location(which_cpu(this), 0); + + /* creates local EC */ + Thread_base::start(); + + reinterpret_cast(Thread_base::utcb())->crd_xlt = Obj_crd(0, ~0UL); +} + + +void Pager_activation_base::entry() { } + + +/********************** + ** Pager entrypoint ** + **********************/ + + +Pager_entrypoint::Pager_entrypoint(Cap_session *cap_session, + Pager_activation_base *a) +: _activation(a), _cap_session(cap_session) +{ + /* sanity check space for pager threads */ + if (kernel_hip()->cpu_max() > PAGER_CPUS) { + PERR("kernel supports more CPUs (%u) than Genode (%u)", + kernel_hip()->cpu_max(), PAGER_CPUS); + nova_die(); + } + + /* determine boot cpu */ + unsigned master_cpu = boot_cpu(); + + /* detect enabled CPUs and create per CPU a pager thread */ + typedef Pager_activation Pager; + Pager * pager_of_cpu = reinterpret_cast(&pager_activation_mem); + + for (unsigned i = 0; i < kernel_hip()->cpu_max(); i++, pager_of_cpu++) { + if (i == master_cpu) { + pager_threads[master_cpu] = a; + a->ep(this); + continue; + } + + if (!kernel_hip()->is_cpu_enabled(i)) + continue; + + pager_threads[i] = pager_of_cpu; + construct_at(pager_threads[i]); + pager_threads[i]->ep(this); + } +} + + Pager_capability Pager_entrypoint::manage(Pager_object *obj) { + /* let handle pager_object of pager thread on same CPU */ + unsigned use_cpu = obj->location.xpos(); + if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) { + PWRN("invalid CPU parameter used in pager object"); + return Pager_capability(); + } + Native_capability pager_thread_cap(pager_threads[use_cpu]->tid().ec_sel); + /* request creation of portal bind to pager thread */ - Native_capability pager_thread_cap(obj->ec_sel()); Native_capability cap_session = _cap_session->alloc(pager_thread_cap, obj->handler_address()); - /* disable PT_CTRL feature */ + if (NOVA_OK != pt_ctrl(cap_session.local_name(), reinterpret_cast(obj))) + nova_die(); + + /* disable the feature for security reasons now */ revoke(Obj_crd(cap_session.local_name(), 0, Obj_crd::RIGHT_PT_CTRL)); /* add server object to object pool */ @@ -490,14 +688,12 @@ Pager_capability Pager_entrypoint::manage(Pager_object *obj) void Pager_entrypoint::dissolve(Pager_object *obj) { Native_capability pager_obj = obj->Object_pool::Entry::cap(); - /* cleanup at cap session */ _cap_session->free(pager_obj); - - /* cleanup locally */ + /* revoke cap selector locally */ revoke(pager_obj.dst(), true); - + /* remove object from pool */ remove_locked(obj); + /* take care that no faults are in-flight */ obj->cleanup_call(); } - diff --git a/repos/base-nova/src/base/server/server.cc b/repos/base-nova/src/base/server/server.cc index b36797fc0..dc6b0254a 100644 --- a/repos/base-nova/src/base/server/server.cc +++ b/repos/base-nova/src/base/server/server.cc @@ -125,11 +125,9 @@ void Rpc_entrypoint::_activation_entry() ep->_snd_buf.snd_reset(); /* prepare ipc server object (copying utcb content to message buffer */ - Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf); - ep->_rcv_buf.post_ipc(reinterpret_cast(ep->utcb())); - int opcode = 0; + Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf); srv >> IPC_WAIT >> opcode; /* set default return value */ diff --git a/repos/base-nova/src/core/cpu_session_extension.cc b/repos/base-nova/src/core/cpu_session_extension.cc index 09925c585..2ab01d4fa 100644 --- a/repos/base-nova/src/core/cpu_session_extension.cc +++ b/repos/base-nova/src/core/cpu_session_extension.cc @@ -26,21 +26,24 @@ Cpu_session_component::pause_sync(Thread_capability thread_cap) Object_pool::Guard thread(_thread_ep->lookup_and_lock(thread_cap)); if (!thread || !thread->platform_thread()) - return Native_capability::invalid_cap(); + return Native_capability(); return thread->platform_thread()->pause(); } -void -Cpu_session_component::single_step(Thread_capability thread_cap, bool enable) +Native_capability +Cpu_session_component::single_step_sync(Thread_capability thread_cap, bool enable) { using namespace Genode; Object_pool::Guard thread(_thread_ep->lookup_and_lock(thread_cap)); if (!thread || !thread->platform_thread()) - return; + return Native_capability(); - thread->platform_thread()->single_step(enable); + return thread->platform_thread()->single_step(enable); } + + +void Cpu_session_component::single_step(Thread_capability, bool) { return; } diff --git a/repos/base-nova/src/core/include/cpu_session_component.h b/repos/base-nova/src/core/include/cpu_session_component.h index 5b99bbaed..c11c42dfd 100644 --- a/repos/base-nova/src/core/include/cpu_session_component.h +++ b/repos/base-nova/src/core/include/cpu_session_component.h @@ -231,6 +231,7 @@ namespace Genode { ******************************/ Native_capability pause_sync(Thread_capability); + Native_capability single_step_sync(Thread_capability, bool); }; } diff --git a/repos/base-nova/src/core/include/platform_thread.h b/repos/base-nova/src/core/include/platform_thread.h index e707219ba..a79eb9623 100644 --- a/repos/base-nova/src/core/include/platform_thread.h +++ b/repos/base-nova/src/core/include/platform_thread.h @@ -163,7 +163,7 @@ namespace Genode { if (main_thread) _features |= MAIN_THREAD; } - void single_step(bool on); + Native_capability single_step(bool on); }; } diff --git a/repos/base-nova/src/core/platform_thread.cc b/repos/base-nova/src/core/platform_thread.cc index fc1143a7d..e48a126a2 100644 --- a/repos/base-nova/src/core/platform_thread.cc +++ b/repos/base-nova/src/core/platform_thread.cc @@ -108,15 +108,14 @@ int Platform_thread::start(void *ip, void *sp) pd_utcb = Native_config::context_area_virtual_base() + Native_config::context_virtual_size() - get_page_size(); - addr_t remap_src[] = { _pd->parent_pt_sel(), - _pager->Object_pool::Entry::cap().local_name() }; + addr_t remap_src[] = { _pd->parent_pt_sel(), _pager->Object_pool::Entry::cap().local_name() }; addr_t remap_dst[] = { PT_SEL_PARENT, PT_SEL_MAIN_PAGER }; /* remap exception portals for first thread */ for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) { if (map_local((Utcb *)Thread_base::myself()->utcb(), - Obj_crd(remap_src[i], 0), - Obj_crd(_sel_exc_base + remap_dst[i], 0))) + Obj_crd(remap_src[i], 0), + Obj_crd(_sel_exc_base + remap_dst[i], 0))) return -6; } } @@ -272,11 +271,15 @@ void Platform_thread::cancel_blocking() } -void Platform_thread::single_step(bool on) +Native_capability Platform_thread::single_step(bool on) { - if (!_pager) return; + if (!_pager) return Native_capability(); - _pager->single_step(on); + Native_capability cap = _pager->single_step(on); + + if (is_worker()) return Native_capability(); + + return cap; } @@ -305,6 +308,12 @@ Platform_thread::Platform_thread(const char *name, unsigned prio, int thread_id) Platform_thread::~Platform_thread() { + if (_pager) { + /* reset pager and badge used for debug output */ + _pager->reset_badge(); + _pager = 0; + } + using namespace Nova; /* free ec and sc caps */ diff --git a/repos/base-nova/src/core/thread_start.cc b/repos/base-nova/src/core/thread_start.cc index 2de55ef7d..d8578f05c 100644 --- a/repos/base-nova/src/core/thread_start.cc +++ b/repos/base-nova/src/core/thread_start.cc @@ -93,8 +93,8 @@ void Thread_base::start() addr_t pd_sel = Platform_pd::pd_core_sel(); /* - * In core, the affinity location was write to the stack base by the server - * code. So, thry to read the value from there. + * In core, the affinity location has been written to the stack base by + * the server or pager code. So - read the value from there. */ Affinity::Location location = reinterpret_cast(stack_base())[0]; @@ -106,7 +106,7 @@ void Thread_base::start() uint8_t res = create_ec(_tid.ec_sel, pd_sel, location.xpos(), utcb, sp, _tid.exc_pt_sel, LOCAL_THREAD); if (res != NOVA_OK) { - PERR("create_ec returned %d", res); + PERR("create_ec returned %d cpu=%u", res, location.xpos()); throw Cpu_session::Thread_creation_failed(); } diff --git a/repos/base/run/thread.run b/repos/base/run/thread.run index eeb072a94..022b0067b 100644 --- a/repos/base/run/thread.run +++ b/repos/base/run/thread.run @@ -22,4 +22,19 @@ build_boot_image "core init test-thread" append qemu_args "-nographic -m 64" -run_genode_until {child "test-thread" exited with exit value 0.*\n} 20 +run_genode_until {child "test-thread" exited with exit value .*\n} 20 + +# determine error code of child exit +set exit_code [regexp -inline {child "test-thread" exited with exit value .*\n} $output] +set exit_code [regexp -inline {[-+]?[0-9]+} $exit_code] + +# good case +if {$exit_code eq 0} { + return +} +# no pause/resume support for Fiasco and Pistachio - they may return a error +if {[expr [have_spec fiasco] || [have_spec pistachio]] && $exit_code eq -11} { return } +# no puase/resume support for Linux - it may return a error +if {[have_spec linux] && $exit_code eq -10} { return } + +exit -1 diff --git a/repos/base/src/test/thread/main.cc b/repos/base/src/test/thread/main.cc index 74f6e846d..4d3958a10 100644 --- a/repos/base/src/test/thread/main.cc +++ b/repos/base/src/test/thread/main.cc @@ -185,6 +185,66 @@ static void test_cpu_session() } +struct Pause_helper : Thread<0x1000> +{ + volatile unsigned loop = 0; + volatile bool beep = false; + + Pause_helper(const char * name, Cpu_session * cpu) + : Thread<0x1000>(name, cpu) { } + + void entry() + { + while (1) { + /** + * Don't use printf here, since this thread becomes "paused". + * If it is holding the lock of the printf backend being paused, + * all other threads of this task trying to do printf will + * block - looks like a deadlock. + */ +// printf("stop me if you can\n"); + loop ++; + if (beep) { + PINF("beep"); + beep = false; + } + } + } +}; + +static void test_pause_resume() +{ + Pause_helper thread("pause", env()->cpu_session()); + thread.start(); + + while (thread.loop < 1) { } + + Thread_state state; + + printf("--- pausing ---\n"); + env()->cpu_session()->pause(thread.cap()); + unsigned loop_paused = thread.loop; + printf("--- paused ---\n"); + + printf("--- reading thread state ---\n"); + try { + state = env()->cpu_session()->state(thread.cap()); + } catch (Cpu_session::State_access_failed) { + throw -10; + } + if (loop_paused != thread.loop) + throw -11; + + thread.beep = true; + printf("--- resuming thread ---\n"); + env()->cpu_session()->resume(thread.cap()); + + while (thread.loop == loop_paused) { } + + printf("--- thread resumed ---\n"); +} + + int main() { printf("--- thread test started ---\n"); @@ -194,6 +254,7 @@ int main() test_stack_alignment(); test_main_thread(); test_cpu_session(); + test_pause_resume(); } catch (int error) { return error; }