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);