From 2cde1d36c122b459bab5f282da57e9e770c29e2c Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Tue, 17 May 2016 16:13:56 +0200 Subject: [PATCH] base: changes for GDB monitor - add a new function 'binary_ready_hook_for_gdb()' in ldso. GDB can set a breakpoint at this function to know when ldso has loaded the binary into memory. - get the thread state from the NOVA kernel immediately on 'pause()' Fixes #1968 --- repos/base-nova/include/base/thread_state.h | 6 +- repos/base-nova/include/cpu_session/client.h | 68 ---------- repos/base-nova/include/cpu_thread/client.h | 82 ++++++++++++ repos/base-nova/include/nova/native_thread.h | 5 +- .../include/nova_cpu_session/connection.h | 40 ------ .../nova_cpu_session/nova_cpu_session.h | 44 ------- repos/base-nova/ports/nova.hash | 2 +- repos/base-nova/ports/nova.port | 2 +- .../src/core/cpu_session_extension.cc | 48 ------- .../src/core/include/cpu_session_component.h | 11 +- repos/base-nova/src/core/include/pager.h | 100 +++++++------- .../src/core/include/platform_thread.h | 6 +- repos/base-nova/src/core/pager.cc | 124 +++++++++++------- repos/base-nova/src/core/platform_thread.cc | 89 ++++++------- repos/base-nova/src/core/spec/x86_32/pager.cc | 46 +++++-- repos/base-nova/src/core/spec/x86_64/pager.cc | 72 +++++++--- repos/base-nova/src/core/target.inc | 1 - .../base-nova/src/lib/base/rpc_entrypoint.cc | 4 +- repos/base-nova/src/lib/base/thread_start.cc | 8 +- repos/base/include/base/thread_state_base.h | 7 +- repos/base/src/lib/ldso/debug.cc | 6 + repos/base/src/lib/ldso/include/debug.h | 5 + repos/base/src/lib/ldso/main.cc | 2 + repos/ports/include/vmm/vcpu_thread.h | 1 - 24 files changed, 371 insertions(+), 408 deletions(-) delete mode 100644 repos/base-nova/include/cpu_session/client.h create mode 100644 repos/base-nova/include/cpu_thread/client.h delete mode 100644 repos/base-nova/include/nova_cpu_session/connection.h delete mode 100644 repos/base-nova/include/nova_cpu_session/nova_cpu_session.h delete mode 100644 repos/base-nova/src/core/cpu_session_extension.cc diff --git a/repos/base-nova/include/base/thread_state.h b/repos/base-nova/include/base/thread_state.h index 2dde3f60cc..97fea51799 100644 --- a/repos/base-nova/include/base/thread_state.h +++ b/repos/base-nova/include/base/thread_state.h @@ -25,11 +25,9 @@ struct Genode::Thread_state : Thread_state_base { bool vcpu; addr_t sel_exc_base; + bool global_thread; - Thread_state() : vcpu(false), sel_exc_base(~0UL) { } - - Thread_state(bool vcpu, addr_t sel_exc_base) - : vcpu(vcpu), sel_exc_base(sel_exc_base) { } + Thread_state() : vcpu(false), sel_exc_base(~0UL), global_thread(true) { } }; #endif /* _INCLUDE__BASE__THREAD_STATE_H_ */ diff --git a/repos/base-nova/include/cpu_session/client.h b/repos/base-nova/include/cpu_session/client.h deleted file mode 100644 index ea198a8a8e..0000000000 --- a/repos/base-nova/include/cpu_session/client.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * \brief Client-side cpu session NOVA extension - * \author Alexander Boettcher - * \date 2012-07-27 - */ - -/* - * Copyright (C) 2012-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__CPU_SESSION__CLIENT_H_ -#define _INCLUDE__CPU_SESSION__CLIENT_H_ - -#include -#include - -#include - -#include - -namespace Genode { struct Cpu_session_client; } - - -struct Genode::Cpu_session_client : Rpc_client -{ - explicit Cpu_session_client(Cpu_session_capability session) - : Rpc_client(static_cap_cast(session)) { } - - Thread_capability - create_thread(Capability pd, Name const &name, - Affinity::Location affinity, Weight weight, addr_t utcb = 0) override { - return call(pd, name, affinity, weight, utcb); } - - void kill_thread(Thread_capability thread) override { - call(thread); } - - void exception_sigh(Signal_context_capability handler) override { - call(handler); } - - Affinity::Space affinity_space() const override { - return call(); } - - Dataspace_capability trace_control() override { - return call(); } - - int ref_account(Cpu_session_capability session) override { - return call(session); } - - int transfer_quota(Cpu_session_capability session, size_t amount) override { - return call(session, amount); } - - Quota quota() override { return call(); } - - Capability native_cpu() override { return call(); } - - private: - - Native_capability pause_sync(Thread_capability) { - return Native_capability(); } - - Native_capability single_step_sync(Thread_capability, bool) { - return Native_capability(); } -}; - -#endif /* _INCLUDE__CPU_SESSION__CLIENT_H_ */ diff --git a/repos/base-nova/include/cpu_thread/client.h b/repos/base-nova/include/cpu_thread/client.h new file mode 100644 index 0000000000..c233c4dd47 --- /dev/null +++ b/repos/base-nova/include/cpu_thread/client.h @@ -0,0 +1,82 @@ +/* + * \brief Client-side CPU thread interface + * \author Norman Feske + * \date 2016-05-10 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__CPU_THREAD__CLIENT_H_ +#define _INCLUDE__CPU_THREAD__CLIENT_H_ + +#include +#include + +namespace Genode { struct Cpu_thread_client; } + + +struct Genode::Cpu_thread_client : Rpc_client +{ + explicit Cpu_thread_client(Thread_capability cap) + : Rpc_client(cap) { } + + Dataspace_capability utcb() override { + return call(); } + + void start(addr_t ip, addr_t sp) override { + call(ip, sp); } + + void pause() override { + + for (;;) { + + call(); + + try { + /* check if the thread state is valid */ + state(); + /* the thread is paused */ + return; + } catch (State_access_failed) { + /* the thread is (most likely) running on a different CPU */ + } + } + } + + void resume() override { + call(); } + + void cancel_blocking() override { + call(); } + + Thread_state state() override { + return call(); } + + void state(Thread_state const &state) override { + call(state); } + + void exception_sigh(Signal_context_capability handler) override { + call(handler); } + + void single_step(bool enabled) override { + call(enabled); } + + void affinity(Affinity::Location location) override { + call(location); } + + unsigned trace_control_index() override { + return call(); } + + Dataspace_capability trace_buffer() override { + return call(); } + + Dataspace_capability trace_policy() override { + return call(); } +}; + +#endif /* _INCLUDE__CPU_THREAD__CLIENT_H_ */ diff --git a/repos/base-nova/include/nova/native_thread.h b/repos/base-nova/include/nova/native_thread.h index 3517a76195..83bf684666 100644 --- a/repos/base-nova/include/nova/native_thread.h +++ b/repos/base-nova/include/nova/native_thread.h @@ -31,6 +31,7 @@ struct Genode::Native_thread addr_t ec_sel; /* selector for execution context */ addr_t exc_pt_sel; /* base of event portal window */ bool vcpu; /* true if thread is a virtual CPU */ + addr_t initial_ip; /* initial IP of local thread */ /* receive window for capability selectors received at the server side */ Receive_window rcv_window; @@ -38,7 +39,9 @@ struct Genode::Native_thread Native_capability pager_cap; Native_thread() : ec_sel(INVALID_INDEX), - exc_pt_sel(INVALID_INDEX), vcpu(false) { } + exc_pt_sel(INVALID_INDEX), + vcpu(false), + initial_ip(0) { } }; #endif /* _INCLUDE__NOVA__NATIVE_THREAD_H_ */ diff --git a/repos/base-nova/include/nova_cpu_session/connection.h b/repos/base-nova/include/nova_cpu_session/connection.h deleted file mode 100644 index 28b7101477..0000000000 --- a/repos/base-nova/include/nova_cpu_session/connection.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * \brief Connection to NOVA specific CPU service - * \author Alexander Boettcher - * \date 2012-07-27 - */ - -/* - * Copyright (C) 2012-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__NOVA_CPU_SESSION__CONNECTION_H_ -#define _INCLUDE__NOVA_CPU_SESSION__CONNECTION_H_ - -#include -#include - -namespace Genode { - - struct Nova_cpu_connection : Connection, Cpu_session_client - { - /** - * Constructor - * - * \param label initial session label - * \param priority designated priority of all threads created - * with this CPU session - */ - Nova_cpu_connection(const char *label = "", long priority = DEFAULT_PRIORITY) - : - Connection( - session("priority=0x%lx, ram_quota=32K, label=\"%s\"", - priority, label)), - Cpu_session_client(cap()) { } - }; -} - -#endif /* _INCLUDE__NOVA_CPU_SESSION__CONNECTION_H_ */ 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 deleted file mode 100644 index f3cf22d46f..0000000000 --- a/repos/base-nova/include/nova_cpu_session/nova_cpu_session.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * \brief Cpu session interface extension for NOVA - * \author Alexander Boettcher - * \date 2012-07-27 - */ - -/* - * Copyright (C) 2012-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__NOVA_CPU_SESSION__NOVA_CPU_SESSION_H_ -#define _INCLUDE__NOVA_CPU_SESSION__NOVA_CPU_SESSION_H_ - -#include -#include - -namespace Genode { - - struct Nova_cpu_session : Cpu_session - { - virtual ~Nova_cpu_session() { } - - virtual Native_capability pause_sync(Thread_capability) = 0; - virtual Native_capability single_step_sync(Thread_capability, bool) = 0; - - - /********************* - ** RPC declaration ** - *********************/ - - 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, - Rpc_single_step_sync); - }; -} - -#endif /* _INCLUDE__NOVA_CPU_SESSION__NOVA_CPU_SESSION_H_ */ diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index a94fc0dfd0..da0118efec 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -5d57765b0abab82acc9d0725bbd30329905751e2 +b27175ffa51a7e3c19784f2789e5ab0fb37034f3 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index be5ab823c8..eeda9d8a0e 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git # r9 branch - use r9_debug for more verbose kernel messages URL(nova) := https://github.com/alex-ab/NOVA.git -REV(nova) := 886e0d94053ab8d45320eecc85213f9a3b797baf +REV(nova) := 7b1209d02d69adaf93adc312f291997f3a98ba81 DIR(nova) := src/kernel/nova PATCHES := $(wildcard $(REP_DIR)/patches/*.patch) diff --git a/repos/base-nova/src/core/cpu_session_extension.cc b/repos/base-nova/src/core/cpu_session_extension.cc deleted file mode 100644 index af09380ba9..0000000000 --- a/repos/base-nova/src/core/cpu_session_extension.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * \brief Core implementation of the CPU session interface extension - * \author Alexander Boettcher - * \date 2012-07-27 - */ - -/* - * Copyright (C) 2012-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include - -/* core-local includes */ -#include - -using namespace Genode; - - -Native_capability -Cpu_session_component::pause_sync(Thread_capability thread_cap) -{ - auto lambda = [] (Cpu_thread_component *thread) { - if (!thread) - return Native_capability(); - - return thread->platform_thread().pause(); - }; - return _thread_ep->apply(thread_cap, lambda); -} - - -Native_capability -Cpu_session_component::single_step_sync(Thread_capability thread_cap, bool enable) -{ - using namespace Genode; - - auto lambda = [enable] (Cpu_thread_component *thread) { - if (!thread) - return Native_capability(); - - return thread->platform_thread().single_step_sync(enable); - }; - return _thread_ep->apply(thread_cap, lambda); -} 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 0038c04370..5802e6aac2 100644 --- a/repos/base-nova/src/core/include/cpu_session_component.h +++ b/repos/base-nova/src/core/include/cpu_session_component.h @@ -22,7 +22,6 @@ #include #include #include -#include /* core includes */ #include @@ -37,7 +36,7 @@ namespace Genode { class Cpu_session_component; } -class Genode::Cpu_session_component : public Rpc_object +class Genode::Cpu_session_component : public Rpc_object { public: @@ -150,14 +149,6 @@ class Genode::Cpu_session_component : public Rpc_object Quota quota() override; Capability native_cpu() { return _native_cpu.cap(); } - - - /****************************** - ** NOVA specific extensions ** - ******************************/ - - Native_capability pause_sync(Thread_capability) override; - Native_capability single_step_sync(Thread_capability, bool) override; }; #endif /* _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ */ diff --git a/repos/base-nova/src/core/include/pager.h b/repos/base-nova/src/core/include/pager.h index 247ffbe8c0..87958f1c5a 100644 --- a/repos/base-nova/src/core/include/pager.h +++ b/repos/base-nova/src/core/include/pager.h @@ -73,21 +73,22 @@ namespace Genode { addr_t _client_exc_pt_sel; addr_t _client_exc_vcpu; + Lock _state_lock; + struct { struct Thread_state thread; addr_t sel_client_ec; enum { - BLOCKED = 0x1U, - DEAD = 0x2U, - SINGLESTEP = 0x4U, - NOTIFY_REQUEST = 0x8U, - SIGNAL_SM = 0x10U, - DISSOLVED = 0x20U, - SUBMIT_SIGNAL = 0x40U, - SKIP_EXCEPTION = 0x80U, + BLOCKED = 0x1U, + DEAD = 0x2U, + SINGLESTEP = 0x4U, + SIGNAL_SM = 0x8U, + DISSOLVED = 0x10U, + SUBMIT_SIGNAL = 0x20U, }; uint8_t _status; + bool modified; /* convenience function to access pause/recall state */ inline bool blocked() { return _status & BLOCKED;} @@ -99,10 +100,6 @@ namespace Genode { 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; } @@ -113,9 +110,6 @@ namespace Genode { 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; Cpu_session_capability _cpu_session_cap; @@ -124,12 +118,15 @@ namespace Genode { addr_t _pd; - void _copy_state(Nova::Utcb * utcb); + void _copy_state_from_utcb(Nova::Utcb * utcb); + void _copy_state_to_utcb(Nova::Utcb * utcb); - addr_t sel_pt_cleanup() const { return _selectors; } - addr_t sel_sm_notify() const { return _selectors + 1; } - addr_t sel_sm_block() const { return _selectors + 2; } - addr_t sel_oom_portal() const { return _selectors + 3; } + 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(addr_t pager_obj); @@ -222,25 +219,13 @@ namespace Genode { return reinterpret_cast(_invoke_handler); } - /** - * Return semaphore to block on until state of a recall is - * available. - */ - Native_capability notify_sm() - { - if (_state.blocked() || _state.is_dead()) - return Native_capability(); - - _state.notify_request(); - - return Native_capability(sel_sm_notify()); - } - /** * 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; @@ -249,40 +234,51 @@ namespace Genode { 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(); - void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; } + uint8_t client_recall(bool get_state_and_block); + void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; } - inline Native_capability single_step(bool on) + inline void single_step(bool on) { - if (_state.is_dead() || + _state_lock.lock(); + + if (_state.is_dead() || !_state.blocked() || (on && (_state._status & _state.SINGLESTEP)) || - (!on && !(_state._status & _state.SINGLESTEP))) - return Native_capability(); + (!on && !(_state._status & _state.SINGLESTEP))) { + _state_lock.unlock(); + return; + } 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(); + _state_lock.unlock(); /* 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()); + client_recall(false); } /** @@ -304,6 +300,8 @@ namespace Genode { */ void unresolved_page_fault_occurred() { + Lock::Guard _state_lock_guard(_state_lock); + _state.thread.unresolved_page_fault = true; } diff --git a/repos/base-nova/src/core/include/platform_thread.h b/repos/base-nova/src/core/include/platform_thread.h index a470342395..599ccab876 100644 --- a/repos/base-nova/src/core/include/platform_thread.h +++ b/repos/base-nova/src/core/include/platform_thread.h @@ -92,12 +92,12 @@ namespace Genode { /** * Pause this thread */ - Native_capability pause(); + void pause(); /** * Enable/disable single stepping */ - void single_step(bool) { } + void single_step(bool); /** * Resume this thread @@ -173,8 +173,6 @@ namespace Genode { if (main_thread) _features |= MAIN_THREAD; } - Native_capability single_step_sync(bool on); - /** * Set CPU quota of the thread to 'quota' */ diff --git a/repos/base-nova/src/core/pager.cc b/repos/base-nova/src/core/pager.cc index ed7efc4a19..e54898b897 100644 --- a/repos/base-nova/src/core/pager.cc +++ b/repos/base-nova/src/core/pager.cc @@ -90,12 +90,16 @@ void Pager_object::_page_fault_handler(addr_t pager_obj) if (!ret) ipc_pager.reply_and_wait_for_fault(); + obj->_state_lock.lock(); + obj->_state.thread.ip = ipc_pager.fault_ip(); obj->_state.thread.sp = 0; obj->_state.thread.trapno = PT_SEL_PAGE_FAULT; obj->_state.block(); + obj->_state_lock.unlock(); + char const * client = reinterpret_cast(obj->_badge); /* region manager fault - to be handled */ if (ret == 1) { @@ -107,7 +111,7 @@ void Pager_object::_page_fault_handler(addr_t pager_obj) utcb->mtd = 0; /* block the faulting thread until region manager is done */ - ipc_pager.reply_and_wait_for_fault(obj->sel_sm_block()); + ipc_pager.reply_and_wait_for_fault(obj->sel_sm_block_pause()); } /* unhandled case */ @@ -141,21 +145,14 @@ void Pager_object::exception(uint8_t exit_id) uint8_t res = 0xFF; addr_t mtd = 0; - if (_state.skip_requested()) { - _state.skip_reset(); - - utcb->set_msg_word(0); - utcb->mtd = 0; - reply(myself->stack_top()); - } + _state_lock.lock(); /* 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(); + res = _unsynchronized_client_recall(true); } if (res != NOVA_OK) { @@ -178,6 +175,8 @@ void Pager_object::exception(uint8_t exit_id) } } + _state_lock.unlock(); + utcb->set_msg_word(0); utcb->mtd = mtd; @@ -191,44 +190,36 @@ void Pager_object::_recall_handler(addr_t pager_obj) 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_lock.lock(); - obj->_state.thread.ip = utcb->ip; - obj->_state.thread.sp = utcb->sp; + if (obj->_state.modified) { + obj->_copy_state_to_utcb(utcb); + obj->_state.modified = false; + } else + utcb->mtd = 0; - obj->_state.thread.eflags = utcb->flags; - - /* thread becomes blocked */ - obj->_state.block(); + /* switch on/off single step */ + bool singlestep_state = obj->_state.thread.eflags & 0x100UL; + if (obj->_state.singlestep() && !singlestep_state) { + utcb->flags |= 0x100UL; + utcb->mtd |= Mtd::EFL; + } else if (!obj->_state.singlestep() && singlestep_state) { + utcb->flags &= ~0x100UL; + utcb->mtd |= Mtd::EFL; + } /* deliver signal if it was requested */ if (obj->_state.to_submit()) obj->submit_exception_signal(); - /* 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 { - 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 */ + + unsigned long sm = obj->_state.blocked() ? obj->sel_sm_block_pause() : 0; + + obj->_state_lock.unlock(); + utcb->set_msg_word(0); - reply(myself->stack_top(), obj->sel_sm_block()); + reply(myself->stack_top(), sm); } @@ -242,6 +233,14 @@ void Pager_object::_startup_handler(addr_t pager_obj) utcb->sp = obj->_initial_esp; utcb->mtd = Mtd::EIP | Mtd::ESP; + + if (obj->_state.singlestep()) { + utcb->flags = 0x100UL; + utcb->mtd |= Mtd::EFL; + } + + obj->_state.unblock(); + utcb->set_msg_word(0); reply(myself->stack_top()); @@ -363,12 +362,16 @@ void Pager_object::_invoke_handler(addr_t pager_obj) void Pager_object::wake_up() { + Lock::Guard _state_lock_guard(_state_lock); + if (!_state.blocked()) return; + _state.thread.exception = false; + _state.unblock(); - uint8_t res = sm_ctrl(sel_sm_block(), SEMAPHORE_UP); + uint8_t res = sm_ctrl(sel_sm_block_pause(), SEMAPHORE_UP); if (res != NOVA_OK) PWRN("canceling blocked client failed (thread sm)"); } @@ -389,9 +392,30 @@ void Pager_object::client_cancel_blocking() } -uint8_t Pager_object::client_recall() +uint8_t Pager_object::client_recall(bool get_state_and_block) { - return ec_ctrl(EC_RECALL, _state.sel_client_ec); + Lock::Guard _state_lock_guard(_state_lock); + return _unsynchronized_client_recall(get_state_and_block); +} + + +uint8_t Pager_object::_unsynchronized_client_recall(bool get_state_and_block) +{ + enum { STATE_REQUESTED = 1 }; + + uint8_t res = ec_ctrl(EC_RECALL, _state.sel_client_ec, + get_state_and_block ? STATE_REQUESTED : ~0UL); + + if (res != NOVA_OK) + return res; + + if (get_state_and_block) { + Utcb *utcb = reinterpret_cast(Thread::myself()->utcb()); + _copy_state_from_utcb(utcb); + _state.block(); + } + + return res; } @@ -522,7 +546,9 @@ Pager_object::Pager_object(Cpu_session_capability cpu_session_cap, addr_t pd_sel = __core_pd_sel; _state._status = 0; + _state.modified = false; _state.sel_client_ec = Native_thread::INVALID_INDEX; + _state.block(); if (Native_thread::INVALID_INDEX == _selectors || Native_thread::INVALID_INDEX == _client_exc_pt_sel) @@ -575,14 +601,14 @@ Pager_object::Pager_object(Cpu_session_capability cpu_session_cap, throw Region_map::Invalid_thread(); } - /* used to notify caller of as soon as pause succeeded */ - res = Nova::create_sm(sel_sm_notify(), pd_sel, 0); + /* semaphore used to block paged thread during recall */ + res = Nova::create_sm(sel_sm_block_pause(), pd_sel, 0); if (res != Nova::NOVA_OK) { throw Region_map::Invalid_thread(); } - /* semaphore used to block paged thread during page fault or recall */ - res = Nova::create_sm(sel_sm_block(), pd_sel, 0); + /* semaphore used to block paged thread during OOM memory revoke */ + res = Nova::create_sm(sel_sm_block_oom(), pd_sel, 0); if (res != Nova::NOVA_OK) { throw Region_map::Invalid_thread(); } @@ -662,7 +688,7 @@ uint8_t Pager_object::handle_oom(addr_t transfer_from, /* if nothing helps try to revoke memory */ enum { REMOTE_REVOKE = true, PD_SELF = true }; Mem_crd crd_all(0, ~0U, Rights(true, true, true)); - Nova::revoke(crd_all, PD_SELF, REMOTE_REVOKE, pd_sel(), sel_sm_block()); + Nova::revoke(crd_all, PD_SELF, REMOTE_REVOKE, pd_sel(), sel_sm_block_oom()); /* re-request current kernel quota usage of target pd */ addr_t limit_after = 0, usage_after = 0; @@ -737,7 +763,7 @@ void Pager_object::_oom_handler(addr_t pager_dst, addr_t pager_src, if (policy == STOP) { PERR("PD has insufficient kernel memory left - stop thread"); utcb->set_msg_word(0); - reply(myself->stack_top(), obj_dst->sel_sm_block()); + reply(myself->stack_top(), obj_dst->sel_sm_block_pause()); } char const * src_pd = "core"; @@ -792,7 +818,7 @@ void Pager_object::_oom_handler(addr_t pager_dst, addr_t pager_src, /* else: caller will get blocked until RCU period is over */ /* block caller in semaphore */ - reply(myself->stack_top(), obj_dst->sel_sm_block()); + reply(myself->stack_top(), obj_dst->sel_sm_block_oom()); } diff --git a/repos/base-nova/src/core/platform_thread.cc b/repos/base-nova/src/core/platform_thread.cc index df24a21845..233bf4264c 100644 --- a/repos/base-nova/src/core/platform_thread.cc +++ b/repos/base-nova/src/core/platform_thread.cc @@ -79,13 +79,10 @@ int Platform_thread::start(void *ip, void *sp) _pager->assign_pd(_pd->pd_sel()); - /* ip == 0 means that caller will use the thread as worker */ - bool thread_global = ip; - uint8_t res; do { res = create_ec(_sel_ec(), _pd->pd_sel(), _location.xpos(), - utcb, initial_sp, _sel_exc_base, thread_global); + utcb, initial_sp, _sel_exc_base, !worker()); if (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK != _pager->handle_oom()) { _pager->assign_pd(Native_thread::INVALID_INDEX); PERR("creation of new thread failed %u", res); @@ -93,9 +90,7 @@ int Platform_thread::start(void *ip, void *sp) } } while (res != Nova::NOVA_OK); - if (!thread_global) { - _features |= WORKER; - + if (worker()) { /* local/worker threads do not require a startup portal */ revoke(Obj_crd(_pager->exc_pt_sel_client() + PT_SEL_STARTUP, 0)); } @@ -201,23 +196,12 @@ int Platform_thread::start(void *ip, void *sp) } -Native_capability Platform_thread::pause() +void Platform_thread::pause() { - if (!_pager) return Native_capability(); + if (!_pager) + return; - Native_capability notify_sm = _pager->notify_sm(); - if (!notify_sm.valid()) return notify_sm; - - if (_pager->client_recall() != Nova::NOVA_OK) - return Native_capability(); - - /* If the thread is blocked in its own SM, get him out */ - cancel_blocking(); - - /* local thread may never get be canceled if it doesn't receive an IPC */ - if (worker()) return Native_capability(); - - return notify_sm; + _pager->client_recall(true); } @@ -256,37 +240,46 @@ Thread_state Platform_thread::state() if (_pager->copy_thread_state(&s)) return s; - if (worker()) { - s.sp = _pager->initial_esp(); - return s; - } - throw Cpu_thread::State_access_failed(); } void Platform_thread::state(Thread_state s) { - /* you can do it only once */ - if (_sel_exc_base != Native_thread::INVALID_INDEX) - throw Cpu_thread::State_access_failed(); + if (_sel_exc_base == Native_thread::INVALID_INDEX) { - /* - * s.sel_exc_base exception base of thread in caller - * protection domain - not in Core ! - * s.vcpu If true it will run as vCPU, - * otherwise it will be a thread. - */ - if (!main_thread()) - _sel_exc_base = s.sel_exc_base; + /* you can do it only once */ - if (!s.vcpu) - return; + /* + * s.sel_exc_base exception base of thread in caller + * protection domain - not in Core ! + * s.is_vcpu If true it will run as vCPU, + * otherwise it will be a thread. + */ + if (!main_thread()) + _sel_exc_base = s.sel_exc_base; - _features |= VCPU; + if (!s.global_thread) + _features |= WORKER; - if (main_thread() && _pager) - _pager->prepare_vCPU_portals(); + if (!s.vcpu) + return; + + _features |= VCPU; + + if (main_thread() && _pager) + _pager->prepare_vCPU_portals(); + + } else { + + if (!_pager) throw Cpu_thread::State_access_failed(); + + if (!_pager->copy_thread_state(s)) + throw Cpu_thread::State_access_failed(); + + /* the new state is transferred to the kernel by the recall handler */ + _pager->client_recall(false); + } } @@ -298,15 +291,11 @@ void Platform_thread::cancel_blocking() } -Native_capability Platform_thread::single_step_sync(bool on) +void Platform_thread::single_step(bool on) { - if (!_pager) return Native_capability(); + if (!_pager) return; - Native_capability cap = _pager->single_step(on); - - if (worker()) return Native_capability(); - - return cap; + _pager->single_step(on); } diff --git a/repos/base-nova/src/core/spec/x86_32/pager.cc b/repos/base-nova/src/core/spec/x86_32/pager.cc index 13f618532f..2d54fb34a3 100644 --- a/repos/base-nova/src/core/spec/x86_32/pager.cc +++ b/repos/base-nova/src/core/spec/x86_32/pager.cc @@ -19,16 +19,42 @@ using namespace Genode; -void Pager_object::_copy_state(Nova::Utcb * utcb) +void Pager_object::_copy_state_from_utcb(Nova::Utcb * utcb) { - _state.thread.ebp = utcb->bp; - _state.thread.eax = utcb->ax; - _state.thread.ebx = utcb->bx; - _state.thread.ecx = utcb->cx; - _state.thread.edx = utcb->dx; - _state.thread.esi = utcb->si; - _state.thread.edi = utcb->di; + _state.thread.eax = utcb->ax; + _state.thread.ecx = utcb->cx; + _state.thread.edx = utcb->dx; + _state.thread.ebx = utcb->bx; - _state.thread.gs = utcb->gs.sel; - _state.thread.fs = utcb->fs.sel; + _state.thread.ebp = utcb->bp; + _state.thread.esi = utcb->si; + _state.thread.edi = utcb->di; + + _state.thread.sp = utcb->sp; + _state.thread.ip = utcb->ip; + _state.thread.eflags = utcb->flags; + + _state.thread.exception = utcb->qual[0]; +} + +void Pager_object::_copy_state_to_utcb(Nova::Utcb * utcb) +{ + utcb->ax = _state.thread.eax; + utcb->cx = _state.thread.ecx; + utcb->dx = _state.thread.edx; + utcb->bx = _state.thread.ebx; + + utcb->bp = _state.thread.ebp; + utcb->si = _state.thread.esi; + utcb->di = _state.thread.edi; + + utcb->sp = _state.thread.sp; + utcb->ip = _state.thread.ip; + utcb->flags = _state.thread.eflags; + + utcb->mtd = Nova::Mtd::ACDB | + Nova::Mtd::EBSD | + Nova::Mtd::ESP | + Nova::Mtd::EIP | + Nova::Mtd::EFL; } diff --git a/repos/base-nova/src/core/spec/x86_64/pager.cc b/repos/base-nova/src/core/spec/x86_64/pager.cc index c268660ca0..b954669d25 100644 --- a/repos/base-nova/src/core/spec/x86_64/pager.cc +++ b/repos/base-nova/src/core/spec/x86_64/pager.cc @@ -19,24 +19,62 @@ using namespace Genode; -void Pager_object::_copy_state(Nova::Utcb * utcb) +void Pager_object::_copy_state_from_utcb(Nova::Utcb * utcb) { - _state.thread.rbp = utcb->bp; - _state.thread.rax = utcb->ax; - _state.thread.rbx = utcb->bx; - _state.thread.rcx = utcb->cx; - _state.thread.rdx = utcb->dx; - _state.thread.rsi = utcb->si; - _state.thread.rdi = utcb->di; + _state.thread.rax = utcb->ax; + _state.thread.rcx = utcb->cx; + _state.thread.rdx = utcb->dx; + _state.thread.rbx = utcb->bx; - _state.thread.r8 = utcb->r8; - _state.thread.r9 = utcb->r9; - _state.thread.r10 = utcb->r10; - _state.thread.r11 = utcb->r11; - _state.thread.r12 = utcb->r12; - _state.thread.r13 = utcb->r13; - _state.thread.r14 = utcb->r14; - _state.thread.r15 = utcb->r15; + _state.thread.rbp = utcb->bp; + _state.thread.rsi = utcb->si; + _state.thread.rdi = utcb->di; - _state.thread.ss = utcb->ss.sel; + _state.thread.r8 = utcb->r8; + _state.thread.r9 = utcb->r9; + _state.thread.r10 = utcb->r10; + _state.thread.r11 = utcb->r11; + _state.thread.r12 = utcb->r12; + _state.thread.r13 = utcb->r13; + _state.thread.r14 = utcb->r14; + _state.thread.r15 = utcb->r15; + + _state.thread.sp = utcb->sp; + _state.thread.ip = utcb->ip; + _state.thread.eflags = utcb->flags; + + _state.thread.exception = utcb->qual[0]; +} + + +void Pager_object::_copy_state_to_utcb(Nova::Utcb * utcb) +{ + utcb->ax = _state.thread.rax; + utcb->cx = _state.thread.rcx; + utcb->dx = _state.thread.rdx; + utcb->bx = _state.thread.rbx; + + utcb->bp = _state.thread.rbp; + utcb->si = _state.thread.rsi; + utcb->di = _state.thread.rdi; + + utcb->r8 = _state.thread.r8; + utcb->r9 = _state.thread.r9; + utcb->r10 = _state.thread.r10; + utcb->r11 = _state.thread.r11; + utcb->r12 = _state.thread.r12; + utcb->r13 = _state.thread.r13; + utcb->r14 = _state.thread.r14; + utcb->r15 = _state.thread.r15; + + utcb->sp = _state.thread.sp; + utcb->ip = _state.thread.ip; + utcb->flags = _state.thread.eflags; + + utcb->mtd = Nova::Mtd::ACDB | + Nova::Mtd::EBSD | + Nova::Mtd::R8_R15 | + Nova::Mtd::EIP | + Nova::Mtd::ESP | + Nova::Mtd::EFL; } diff --git a/repos/base-nova/src/core/target.inc b/repos/base-nova/src/core/target.inc index 02ddda7186..3b35d62c0b 100644 --- a/repos/base-nova/src/core/target.inc +++ b/repos/base-nova/src/core/target.inc @@ -9,7 +9,6 @@ SRC_CC = stack_area.cc \ core_region_map.cc \ core_rpc_cap_alloc.cc \ cpu_session_component.cc \ - cpu_session_extension.cc \ cpu_session_support.cc \ cpu_thread_component.cc \ dataspace_component.cc \ diff --git a/repos/base-nova/src/lib/base/rpc_entrypoint.cc b/repos/base-nova/src/lib/base/rpc_entrypoint.cc index ad4f786293..8c79254acd 100644 --- a/repos/base-nova/src/lib/base/rpc_entrypoint.cc +++ b/repos/base-nova/src/lib/base/rpc_entrypoint.cc @@ -215,8 +215,10 @@ Rpc_entrypoint::Rpc_entrypoint(Pd_session *pd_session, size_t stack_size, _pd_session(*pd_session) { /* set magic value evaluated by thread_nova.cc to start a local thread */ - if (native_thread().ec_sel == Native_thread::INVALID_INDEX) + if (native_thread().ec_sel == Native_thread::INVALID_INDEX) { native_thread().ec_sel = Native_thread::INVALID_INDEX - 1; + native_thread().initial_ip = (addr_t)&_activation_entry; + } /* required to create a 'local' EC */ Thread::start(); diff --git a/repos/base-nova/src/lib/base/thread_start.cc b/repos/base-nova/src/lib/base/thread_start.cc index 31c0703563..ffcb6de30b 100644 --- a/repos/base-nova/src/lib/base/thread_start.cc +++ b/repos/base-nova/src/lib/base/thread_start.cc @@ -29,7 +29,6 @@ /* NOVA includes */ #include #include -#include using namespace Genode; @@ -159,11 +158,12 @@ void Thread::start() /* create EC at core */ Thread_state state; - state.sel_exc_base = native_thread().exc_pt_sel; - state.vcpu = native_thread().vcpu; + state.sel_exc_base = native_thread().exc_pt_sel; + state.vcpu = native_thread().vcpu; + state.global_thread = global; /* local thread have no start instruction pointer - set via portal entry */ - addr_t thread_ip = global ? reinterpret_cast(_thread_start) : 0; + addr_t thread_ip = global ? reinterpret_cast(_thread_start) : native_thread().initial_ip; Cpu_thread_client cpu_thread(_thread_cap); try { cpu_thread.state(state); } diff --git a/repos/base/include/base/thread_state_base.h b/repos/base/include/base/thread_state_base.h index f863119b39..690a82169c 100644 --- a/repos/base/include/base/thread_state_base.h +++ b/repos/base/include/base/thread_state_base.h @@ -22,10 +22,11 @@ namespace Genode { struct Thread_state_base; } struct Genode::Thread_state_base : Cpu_state { - bool unresolved_page_fault; + bool unresolved_page_fault = false; + bool exception = false; - Thread_state_base() : unresolved_page_fault(false) { }; - Thread_state_base(Cpu_state &c) : Cpu_state(c), unresolved_page_fault(false) { }; + Thread_state_base() { }; + Thread_state_base(Cpu_state &c) : Cpu_state(c) { }; }; #endif /* _INCLUDE__BASE__THREAD_STATE_BASE_H_ */ diff --git a/repos/base/src/lib/ldso/debug.cc b/repos/base/src/lib/ldso/debug.cc index 7bc6e36d02..8a4e0ec093 100644 --- a/repos/base/src/lib/ldso/debug.cc +++ b/repos/base/src/lib/ldso/debug.cc @@ -14,6 +14,12 @@ #include #include +/* + * GDB can set a breakpoint at this function to find out when ldso has loaded + * the binary into memory. + */ +void binary_ready_hook_for_gdb() { } + /** * C-break function for GDB */ diff --git a/repos/base/src/lib/ldso/include/debug.h b/repos/base/src/lib/ldso/include/debug.h index c315962190..9b3b954e92 100644 --- a/repos/base/src/lib/ldso/include/debug.h +++ b/repos/base/src/lib/ldso/include/debug.h @@ -27,6 +27,11 @@ namespace Linker { void dump_link_map(Object *o); } +/* + * GDB can set a breakpoint at this function to find out when ldso has loaded + * the binary into memory. + */ +void binary_ready_hook_for_gdb(); /** * LIBC debug support diff --git a/repos/base/src/lib/ldso/main.cc b/repos/base/src/lib/ldso/main.cc index ae9dbc4429..8c799e22bb 100644 --- a/repos/base/src/lib/ldso/main.cc +++ b/repos/base/src/lib/ldso/main.cc @@ -580,6 +580,8 @@ void Component::construct(Genode::Env &env) Link_map::dump(); + binary_ready_hook_for_gdb(); + /* start binary */ binary->call_entry_point(env); } diff --git a/repos/ports/include/vmm/vcpu_thread.h b/repos/ports/include/vmm/vcpu_thread.h index a04096d7d2..23d78d5dba 100644 --- a/repos/ports/include/vmm/vcpu_thread.h +++ b/repos/ports/include/vmm/vcpu_thread.h @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include