From fcd62729d4510b06338c1d186ae93cdf180b23fc Mon Sep 17 00:00:00 2001 From: Alexander Boettcher <alexander.boettcher@genode-labs.com> Date: Wed, 22 Aug 2012 12:10:04 +0200 Subject: [PATCH] NOVA: tunnel thread start parameters via state() The cpu_session interface fails to be virtualized by gdb_monitor because platform-nova uses an extended nova_cpu_session interface. The problem was that threads have been created directly at core without knowledge of gdb_monitor. This lead to the situation that gdb_monitor didn't know of all threads to be debugged. Tunnel the additional parameters required on base-nova through the state() call of the cpu_session interface before the thread actual is started. --- base-nova/include/base/pager.h | 6 ++- base-nova/include/base/thread_state.h | 10 +++- base-nova/include/cpu_session/client.h | 8 ---- .../nova_cpu_session/nova_cpu_session.h | 7 +-- base-nova/src/base/pager/pager.cc | 29 ++++++++---- base-nova/src/base/server/server.cc | 13 ++++-- base-nova/src/base/thread/thread_nova.cc | 15 ++++-- base-nova/src/core/cpu_session_extension.cc | 12 ----- .../src/core/include/cpu_session_component.h | 2 - base-nova/src/core/include/platform_thread.h | 8 +--- base-nova/src/core/platform_thread.cc | 46 ++++++++++++++----- 11 files changed, 92 insertions(+), 64 deletions(-) diff --git a/base-nova/include/base/pager.h b/base-nova/include/base/pager.h index 198f89093c..8454fec467 100644 --- a/base-nova/include/base/pager.h +++ b/base-nova/include/base/pager.h @@ -59,6 +59,7 @@ namespace Genode { struct { struct Thread_state thread; + addr_t sel_client_ec; bool valid; bool dead; } _state; @@ -164,7 +165,10 @@ namespace Genode { * Cancel blocking in a lock so that recall exception can take * place. */ - void cancel_blocking_client(); + void client_cancel_blocking(); + + uint8_t client_recall(); + void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; } }; diff --git a/base-nova/include/base/thread_state.h b/base-nova/include/base/thread_state.h index 8738cbc290..3ec23a9a95 100644 --- a/base-nova/include/base/thread_state.h +++ b/base-nova/include/base/thread_state.h @@ -20,7 +20,15 @@ namespace Genode { - struct Thread_state : public Cpu_state { }; + struct Thread_state : public Cpu_state + { + bool transfer; + bool is_vcpu; + addr_t sel_exc_base; + + Thread_state(bool trans = false) : Cpu_state(), transfer(trans), + is_vcpu(false), sel_exc_base(~0UL) {} + }; } #endif /* _INCLUDE__BASE__THREAD_STATE_H_ */ diff --git a/base-nova/include/cpu_session/client.h b/base-nova/include/cpu_session/client.h index a65ca609e5..9985451ce1 100644 --- a/base-nova/include/cpu_session/client.h +++ b/base-nova/include/cpu_session/client.h @@ -80,14 +80,6 @@ namespace Genode { Native_capability native_cap(Thread_capability cap) { return call<Rpc_native_cap>(cap); } - int start_exc_base_vcpu(Thread_capability thread, addr_t ip, - addr_t sp, addr_t exc_base, - bool vcpu = false) - { - return call<Rpc_start_exc_base_vcpu>(thread, ip, sp, - exc_base, vcpu); - } - private: Native_capability pause_sync(Thread_capability target) { diff --git a/base-nova/include/nova_cpu_session/nova_cpu_session.h b/base-nova/include/nova_cpu_session/nova_cpu_session.h index c5dd45e3fb..bb39532aef 100644 --- a/base-nova/include/nova_cpu_session/nova_cpu_session.h +++ b/base-nova/include/nova_cpu_session/nova_cpu_session.h @@ -23,9 +23,6 @@ namespace Genode { { virtual ~Nova_cpu_session() { } - virtual int - start_exc_base_vcpu(Thread_capability thread, addr_t ip, - addr_t sp, addr_t exc_base, bool vcpu) = 0; virtual Native_capability native_cap(Thread_capability cap) = 0; @@ -36,15 +33,13 @@ namespace Genode { ** RPC declaration ** *********************/ - GENODE_RPC(Rpc_start_exc_base_vcpu, int, start_exc_base_vcpu, - Thread_capability, addr_t, addr_t, addr_t, bool); GENODE_RPC(Rpc_native_cap, Native_capability, native_cap, Thread_capability); GENODE_RPC(Rpc_pause_sync, Native_capability, pause_sync, Thread_capability); GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_native_cap, - Rpc_start_exc_base_vcpu, Rpc_pause_sync); + Rpc_pause_sync); }; } diff --git a/base-nova/src/base/pager/pager.cc b/base-nova/src/base/pager/pager.cc index 21c649ef39..835850894a 100644 --- a/base-nova/src/base/pager/pager.cc +++ b/base-nova/src/base/pager/pager.cc @@ -44,18 +44,21 @@ void Pager_object::_page_fault_handler() int ret = obj->pager(ipc_pager); if (ret) { - PWRN("unresolvable page-fault at address 0x%lx, ip=0x%lx", - ipc_pager.fault_addr(), ipc_pager.fault_ip()); - if (!obj->submit_exception_signal()) { + PWRN("unresolvable page-fault at address 0x%lx, ip=0x%lx", + ipc_pager.fault_addr(), ipc_pager.fault_ip()); + /* revoke paging capability, let thread die in kernel */ Nova::revoke(Obj_crd(obj->exc_pt_sel() + PT_SEL_PAGE_FAULT, 0), true); obj->_state.dead = true; - } + } else + /* Somebody takes care don't die - just recall and block */ + obj->client_recall(); Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); utcb->set_msg_word(0); + utcb->mtd = 0; } ipc_pager.reply_and_wait_for_fault(); @@ -133,19 +136,27 @@ void Pager_object::_invoke_handler() void Pager_object::wake_up() { cancel_blocking(); } -void Pager_object::cancel_blocking_client() { + +void Pager_object::client_cancel_blocking() { uint8_t res = sm_ctrl(exc_pt_sel() + SM_SEL_EC_CLIENT, SEMAPHORE_UP); if (res != NOVA_OK) PWRN("cancel blocking failed"); } + +uint8_t Pager_object::client_recall() { + return ec_ctrl(_state.sel_client_ec); +} + + Pager_object::Pager_object(unsigned long badge) : Thread_base("pager", PF_HANDLER_STACK_SIZE), _badge(badge) { - _pt_cleanup = cap_selector_allocator()->alloc(); - _sm_state_notify = cap_selector_allocator()->alloc(); - _state.valid = false; - _state.dead = false; + _pt_cleanup = cap_selector_allocator()->alloc(); + _sm_state_notify = cap_selector_allocator()->alloc(); + _state.valid = false; + _state.dead = false; + _state.sel_client_ec = ~0UL; /* create portal for page-fault handler */ addr_t pd_sel = __core_pd_sel; diff --git a/base-nova/src/base/server/server.cc b/base-nova/src/base/server/server.cc index 023d560b2f..18d742c9c3 100644 --- a/base-nova/src/base/server/server.cc +++ b/base-nova/src/base/server/server.cc @@ -214,9 +214,13 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, throw Cpu_session::Thread_creation_failed(); addr_t thread_sp = (addr_t)&_context->stack[-4]; - Genode::Nova_cpu_connection cpu; - cpu.start_exc_base_vcpu(_thread_cap, 0, thread_sp, - _tid.exc_pt_sel); + + Thread_state state(true); + state.sel_exc_base = _tid.exc_pt_sel; + + if (env()->cpu_session()->state(_thread_cap, &state) || + env()->cpu_session()->start(_thread_cap, 0, thread_sp)) + throw Cpu_session::Thread_creation_failed(); request_event_portal(pager_cap, _tid.exc_pt_sel, Nova::PT_SEL_STARTUP); @@ -232,7 +236,10 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, * The native thread cap is required to attach new rpc objects * (to create portals bound to the ec) */ + Genode::Nova_cpu_connection cpu; Native_capability ec_cap = cpu.native_cap(_thread_cap); + if (!ec_cap.valid()) + throw Cpu_session::Thread_creation_failed(); _tid.ec_sel = ec_cap.local_name(); } diff --git a/base-nova/src/base/thread/thread_nova.cc b/base-nova/src/base/thread/thread_nova.cc index c2dc021576..889ebf260f 100644 --- a/base-nova/src/base/thread/thread_nova.cc +++ b/base-nova/src/base/thread/thread_nova.cc @@ -116,13 +116,20 @@ void Thread_base::start() /* create EC at core */ addr_t thread_sp = reinterpret_cast<addr_t>(&_context->stack[-4]); - Genode::Nova_cpu_connection cpu; - if (cpu.start_exc_base_vcpu(_thread_cap, (addr_t)_thread_start, - thread_sp, _tid.exc_pt_sel, _tid.is_vcpu)) + Thread_state state(true); + state.sel_exc_base = _tid.exc_pt_sel; + state.is_vcpu = _tid.is_vcpu; + + if (env()->cpu_session()->state(_thread_cap, &state) || + env()->cpu_session()->start(_thread_cap, (addr_t)_thread_start, + thread_sp)) throw Cpu_session::Thread_creation_failed(); - + /* request native EC thread cap */ + Genode::Nova_cpu_connection cpu; Native_capability ec_cap = cpu.native_cap(_thread_cap); + if (!ec_cap.valid()) + throw Cpu_session::Thread_creation_failed(); _tid.ec_sel = ec_cap.local_name(); using namespace Nova; diff --git a/base-nova/src/core/cpu_session_extension.cc b/base-nova/src/core/cpu_session_extension.cc index 094e6d28bd..7b56c9b95c 100644 --- a/base-nova/src/core/cpu_session_extension.cc +++ b/base-nova/src/core/cpu_session_extension.cc @@ -29,18 +29,6 @@ Cpu_session_component::native_cap(Thread_capability thread_cap) return thread->platform_thread()->native_cap(); } -int -Cpu_session_component::start_exc_base_vcpu(Thread_capability thread_cap, - addr_t ip, addr_t sp, - addr_t exc_base, bool vcpu) -{ - Cpu_thread_component *thread = _lookup_thread(thread_cap); - if (!thread) return -1; - - return thread->platform_thread()->start((void *)ip, (void *)sp, - exc_base, vcpu); -} - Native_capability Cpu_session_component::pause_sync(Thread_capability target_thread_cap) { diff --git a/base-nova/src/core/include/cpu_session_component.h b/base-nova/src/core/include/cpu_session_component.h index 24a9d10c76..774527daec 100644 --- a/base-nova/src/core/include/cpu_session_component.h +++ b/base-nova/src/core/include/cpu_session_component.h @@ -147,8 +147,6 @@ namespace Genode { ** NOVA specific extensions ** ***********************************/ - int start_exc_base_vcpu(Thread_capability, addr_t, - addr_t, addr_t, bool); Native_capability native_cap(Thread_capability); Native_capability pause_sync(Thread_capability); }; diff --git a/base-nova/src/core/include/platform_thread.h b/base-nova/src/core/include/platform_thread.h index 0c87aee258..c6c5d34c5f 100644 --- a/base-nova/src/core/include/platform_thread.h +++ b/base-nova/src/core/include/platform_thread.h @@ -35,6 +35,7 @@ namespace Genode { addr_t _sel_exc_base; unsigned _cpu_no; bool _is_main_thread; + bool _is_vcpu; addr_t _sel_ec() { return _id_base; } addr_t _sel_sc() { return _id_base + 1; } @@ -61,16 +62,11 @@ namespace Genode { * * \param ip instruction pointer to start at * \param sp stack pointer to use - * \param exc_base exception base of thread in caller - * protection domain - * \param vcpu If true it will run as vCPU, - * otherwise it will be a thread. * * \retval 0 successful * \retval -1 thread/vCPU could not be started */ - int start(void *ip, void *sp, addr_t exc_base = ~0UL, - bool vcpu = false); + int start(void *ip, void *sp); /** * Pause this thread diff --git a/base-nova/src/core/platform_thread.cc b/base-nova/src/core/platform_thread.cc index ed228e7124..4a3ef5d911 100644 --- a/base-nova/src/core/platform_thread.cc +++ b/base-nova/src/core/platform_thread.cc @@ -41,7 +41,7 @@ void Platform_thread::set_cpu(unsigned int cpu_no) } -int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) +int Platform_thread::start(void *ip, void *sp) { using namespace Nova; @@ -59,10 +59,10 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) _pager->initial_eip((addr_t)ip); if (!_is_main_thread) { addr_t initial_sp = reinterpret_cast<addr_t>(sp); - addr_t utcb = vcpu ? 0 : round_page(initial_sp); + addr_t utcb = _is_vcpu ? 0 : round_page(initial_sp); _pager->initial_esp(initial_sp); - if (exc_base == ~0UL) { + if (_sel_exc_base == ~0UL) { PERR("exception base not specified"); return -3; } @@ -86,10 +86,12 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) bool thread_global = ip; res = create_ec(_sel_ec(), _pd->pd_sel(), _cpu_no, utcb, - initial_sp, exc_base, thread_global); + initial_sp, _sel_exc_base, thread_global); if (res) PERR("creation of new thread failed %u", res); + _pager->client_set_ec(_sel_ec()); + return res ? -5 : 0; } @@ -175,13 +177,17 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) * becomes running immediately. */ _pd->assign_pd(pd_sel); + _pager->client_set_ec(_sel_ec()); /* Let the thread run */ res = create_sc(_sel_sc(), pd_sel, _sel_ec(), Qpd()); if (res) { - /* Reset pd cap since thread got not running and pd cap will - * be revoked during cleanup*/ + /** + * Reset pd cap since thread got not running and pd cap will + * be revoked during cleanup. + */ _pd->assign_pd(~0UL); + _pager->client_set_ec(~0UL); PERR("create_sc returned %d", res); goto cleanup_ec; @@ -215,7 +221,7 @@ Native_capability Platform_thread::pause() Native_capability notify_sm = _pager->notify_sm(); if (!notify_sm.valid()) return notify_sm; - if (Nova::ec_ctrl(_sel_ec()) != Nova::NOVA_OK) + if (_pager->client_recall() != Nova::NOVA_OK) return Native_capability::invalid_cap(); /* If the thread is blocked in the its own SM, get him out */ @@ -233,6 +239,7 @@ void Platform_thread::resume() if (res == NOVA_OK) return; if (!_pager) return; + /* Thread was paused beforehand and blocked in pager - wake up pager */ _pager->wake_up(); } @@ -242,9 +249,24 @@ int Platform_thread::state(Thread_state *state_dst) { if (!state_dst || !_pager) return -1; - int res = _pager->copy_thread_state(state_dst); + if (state_dst->transfer) { + /* Not permitted for main thread */ + if (_is_main_thread) return -2; + /* You can do it only once */ + if (_sel_exc_base != ~0UL) return -3; + /** + * _sel_exc_base exception base of thread in caller + * protection domain - not in Core ! + * _is_vcpu If true it will run as vCPU, + * otherwise it will be a thread. + */ + _sel_exc_base = state_dst->sel_exc_base; + _is_vcpu = state_dst->is_vcpu; - return res; + return 0; + } + + return _pager->copy_thread_state(state_dst); } @@ -252,7 +274,7 @@ void Platform_thread::cancel_blocking() { if (!_pager) return; - _pager->cancel_blocking_client(); + _pager->client_cancel_blocking(); } @@ -261,7 +283,7 @@ unsigned long Platform_thread::pager_object_badge() const { return ~0UL; } Platform_thread::Platform_thread(const char *name, unsigned, int thread_id) : _pd(0), _pager(0), _id_base(cap_selector_allocator()->alloc(1)), - _sel_exc_base(~0UL), _cpu_no(0), _is_main_thread(false) { } + _sel_exc_base(~0UL), _cpu_no(0), _is_main_thread(false), _is_vcpu(false) { } Platform_thread::~Platform_thread() @@ -273,7 +295,7 @@ Platform_thread::~Platform_thread() cap_selector_allocator()->free(_id_base, 1); /* free exc_base used by main thread */ - if (_sel_exc_base != ~0UL) { + if (_is_main_thread && _sel_exc_base != ~0UL) { revoke(Obj_crd(_sel_exc_base, NUM_INITIAL_PT_LOG2)); cap_selector_allocator()->free(_sel_exc_base, NUM_INITIAL_PT_LOG2);