2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief NOVA-specific support code for the server-side RPC API
|
|
|
|
* \author Norman Feske
|
|
|
|
* \author Sebastian Sumpf
|
2012-08-01 16:16:51 +02:00
|
|
|
* \author Alexander Boettcher
|
2011-12-22 16:19:25 +01:00
|
|
|
* \date 2010-01-13
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-01-05 22:09:51 +01:00
|
|
|
* Copyright (C) 2010-2015 Genode Labs GmbH
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* 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 <base/printf.h>
|
|
|
|
#include <base/rpc_server.h>
|
2012-08-08 14:23:13 +02:00
|
|
|
#include <base/env.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/* NOVA includes */
|
|
|
|
#include <nova/syscalls.h>
|
2012-08-03 10:56:08 +02:00
|
|
|
#include <nova/util.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
using namespace Genode;
|
|
|
|
|
2013-01-11 23:10:21 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/***********************
|
|
|
|
** Server entrypoint **
|
|
|
|
***********************/
|
|
|
|
|
|
|
|
Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
|
|
|
|
{
|
|
|
|
using namespace Nova;
|
|
|
|
|
2016-01-19 20:24:22 +01:00
|
|
|
Untyped_capability ec_cap;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-08 14:23:13 +02:00
|
|
|
/* _ec_sel is invalid until thread gets started */
|
2012-12-12 12:06:54 +01:00
|
|
|
if (tid().ec_sel != Native_thread::INVALID_INDEX)
|
2012-07-30 10:56:07 +02:00
|
|
|
ec_cap = Native_capability(tid().ec_sel);
|
2012-08-08 14:23:13 +02:00
|
|
|
else
|
2012-07-30 10:56:07 +02:00
|
|
|
ec_cap = _thread_cap;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2016-01-19 20:24:22 +01:00
|
|
|
Untyped_capability obj_cap = _alloc_rpc_cap(_pd_session, ec_cap,
|
|
|
|
(addr_t)&_activation_entry);
|
2015-01-05 22:09:51 +01:00
|
|
|
if (!obj_cap.valid())
|
|
|
|
return obj_cap;
|
2012-06-19 15:54:41 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* add server object to object pool */
|
2015-01-05 22:09:51 +01:00
|
|
|
obj->cap(obj_cap);
|
2011-12-22 16:19:25 +01:00
|
|
|
insert(obj);
|
|
|
|
|
2015-01-05 22:09:51 +01:00
|
|
|
/* return object capability managed by entrypoint thread */
|
|
|
|
return obj_cap;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::_dissolve(Rpc_object_base *obj)
|
|
|
|
{
|
2012-12-12 12:06:54 +01:00
|
|
|
/* de-announce object from cap_session */
|
2016-01-19 20:24:22 +01:00
|
|
|
_free_rpc_cap(_pd_session, obj->cap());
|
2012-12-12 12:06:54 +01:00
|
|
|
|
|
|
|
/* avoid any incoming IPC */
|
|
|
|
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true);
|
2012-06-19 15:54:41 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* make sure nobody is able to find this object */
|
2015-08-10 13:34:16 +02:00
|
|
|
remove(obj);
|
2015-11-12 15:21:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The activation may execute a blocking operation in a dispatch function.
|
|
|
|
* Before resolving the corresponding object, we need to ensure that it is
|
|
|
|
* no longer used by an activation. Therefore, we to need cancel an
|
|
|
|
* eventually blocking operation and let the activation leave the context
|
|
|
|
* of the object.
|
|
|
|
*/
|
|
|
|
using namespace Nova;
|
|
|
|
|
|
|
|
Utcb *utcb = reinterpret_cast<Utcb *>(Thread_base::myself()->utcb());
|
|
|
|
/* don't call ourself */
|
|
|
|
if (utcb == reinterpret_cast<Utcb *>(this->utcb()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Required outside of core. E.g. launchpad needs it to forcefully kill
|
|
|
|
* a client which blocks on a session opening request where the service
|
|
|
|
* is not up yet.
|
|
|
|
*/
|
|
|
|
cancel_blocking();
|
|
|
|
|
2015-11-12 20:15:54 +01:00
|
|
|
/* activate entrypoint now - otherwise cleanup call will block forever */
|
|
|
|
_delay_start.unlock();
|
|
|
|
|
2015-11-12 15:21:23 +01:00
|
|
|
/* make a IPC to ensure that cap() identifier is not used anymore */
|
|
|
|
utcb->msg[0] = 0xdead;
|
|
|
|
utcb->set_msg_word(1);
|
|
|
|
if (uint8_t res = call(_cap.local_name()))
|
|
|
|
PERR("%8p - could not clean up entry point of thread 0x%p - res %u",
|
|
|
|
utcb, this->utcb(), res);
|
2012-06-19 15:54:41 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-11-12 15:21:23 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
void Rpc_entrypoint::_activation_entry()
|
|
|
|
{
|
2012-08-02 12:20:00 +02:00
|
|
|
/* retrieve portal id from eax/rdi */
|
2012-06-06 10:19:48 +02:00
|
|
|
#ifdef __x86_64__
|
2012-06-19 15:54:41 +02:00
|
|
|
addr_t id_pt; asm volatile ("" : "=D" (id_pt));
|
2012-06-06 10:19:48 +02:00
|
|
|
#else
|
2012-06-19 15:54:41 +02:00
|
|
|
addr_t id_pt; asm volatile ("" : "=a" (id_pt));
|
2012-06-06 10:19:48 +02:00
|
|
|
#endif
|
2012-08-01 16:16:51 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
Rpc_entrypoint *ep = static_cast<Rpc_entrypoint *>(Thread_base::myself());
|
|
|
|
|
2013-09-11 10:45:23 +02:00
|
|
|
/* required to decrease ref count of capability used during last reply */
|
|
|
|
ep->_snd_buf.snd_reset();
|
|
|
|
|
2012-12-14 10:03:55 +01:00
|
|
|
/* prepare ipc server object (copying utcb content to message buffer */
|
2011-12-22 16:19:25 +01:00
|
|
|
int opcode = 0;
|
|
|
|
|
2014-09-05 17:00:31 +02:00
|
|
|
Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf);
|
2011-12-22 16:19:25 +01:00
|
|
|
srv >> IPC_WAIT >> opcode;
|
|
|
|
|
|
|
|
/* set default return value */
|
2015-03-16 15:24:03 +01:00
|
|
|
srv.ret(Ipc_client::ERR_INVALID_OBJECT);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-11-12 20:15:54 +01:00
|
|
|
/* in case of a portal cleanup call we are done here - just reply */
|
|
|
|
if (ep->_cap.local_name() == id_pt) {
|
|
|
|
if (!ep->_rcv_buf.prepare_rcv_window((Nova::Utcb *)ep->utcb()))
|
|
|
|
PWRN("out of capability selectors for handling server requests");
|
|
|
|
srv << IPC_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
/* potentially delay start */
|
|
|
|
Lock::Guard lock_guard(ep->_delay_start);
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* atomically lookup and lock referenced object */
|
2015-08-10 13:34:16 +02:00
|
|
|
auto lambda = [&] (Rpc_object_base *obj) {
|
|
|
|
if (!obj) {
|
2015-11-12 20:15:54 +01:00
|
|
|
PERR("could not look up server object, return from call id_pt=%lx",
|
|
|
|
id_pt);
|
2015-08-10 13:34:16 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-01-17 17:09:09 +01:00
|
|
|
/* dispatch request */
|
2015-08-10 13:34:16 +02:00
|
|
|
try { srv.ret(obj->dispatch(opcode, srv, srv)); }
|
2013-01-17 17:09:09 +01:00
|
|
|
catch (Blocking_canceled) { }
|
2015-08-10 13:34:16 +02:00
|
|
|
};
|
|
|
|
ep->apply(id_pt, lambda);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-09-06 11:03:12 +02:00
|
|
|
if (!ep->_rcv_buf.prepare_rcv_window((Nova::Utcb *)ep->utcb()))
|
2015-07-14 16:19:14 +02:00
|
|
|
PWRN("out of capability selectors for handling server requests");
|
2013-01-17 17:09:09 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
srv << IPC_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::entry()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Thread entry is not used for activations on NOVA
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::_block_until_cap_valid() { }
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::activate()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In contrast to a normal thread, a server activation is created at
|
2012-08-08 14:23:13 +02:00
|
|
|
* construction time. However, it executes no code because processing
|
|
|
|
* time is always provided by the caller of the server activation. To
|
|
|
|
* delay the processing of requests until the 'activate' function is
|
2012-12-14 10:03:55 +01:00
|
|
|
* called, we grab the '_delay_start' lock on construction and release it
|
2012-08-08 14:23:13 +02:00
|
|
|
* here.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2012-12-14 10:03:55 +01:00
|
|
|
_delay_start.unlock();
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-19 20:24:22 +01:00
|
|
|
Rpc_entrypoint::Rpc_entrypoint(Pd_session *pd_session, size_t stack_size,
|
2013-07-19 09:24:13 +02:00
|
|
|
const char *name, bool start_on_construction,
|
2013-08-07 22:16:58 +02:00
|
|
|
Affinity::Location location)
|
2011-12-22 16:19:25 +01:00
|
|
|
:
|
2015-03-27 14:05:55 +01:00
|
|
|
Thread_base(Cpu_session::DEFAULT_WEIGHT, name, stack_size),
|
2012-12-14 10:03:55 +01:00
|
|
|
_delay_start(Lock::LOCKED),
|
2016-01-19 20:24:22 +01:00
|
|
|
_pd_session(*pd_session)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2013-07-22 09:14:40 +02:00
|
|
|
/* when not running in core set the affinity via cpu session */
|
2012-12-12 12:06:54 +01:00
|
|
|
if (_tid.ec_sel == Native_thread::INVALID_INDEX) {
|
2012-08-02 12:20:00 +02:00
|
|
|
|
2013-07-22 09:14:40 +02:00
|
|
|
/* place new thread on the specified CPU */
|
2013-08-07 22:16:58 +02:00
|
|
|
if (location.valid())
|
2014-04-03 14:18:52 +02:00
|
|
|
_cpu_session->affinity(_thread_cap, location);
|
2013-07-19 09:24:13 +02:00
|
|
|
|
2013-07-22 09:14:40 +02:00
|
|
|
/* magic value evaluated by thread_nova.cc to start a local thread */
|
|
|
|
_tid.ec_sel = Native_thread::INVALID_INDEX - 1;
|
2013-07-19 09:24:13 +02:00
|
|
|
} else {
|
2013-07-22 09:14:40 +02:00
|
|
|
/* tell affinity CPU in 'core' via stack */
|
2014-01-17 17:22:32 +01:00
|
|
|
reinterpret_cast<Affinity::Location *>(stack_base())[0] = location;
|
2013-07-08 09:06:26 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-07-22 09:14:40 +02:00
|
|
|
/* required to create a 'local' EC */
|
|
|
|
Thread_base::start();
|
|
|
|
|
2012-12-12 12:06:54 +01:00
|
|
|
/* create cleanup portal */
|
2016-01-19 20:24:22 +01:00
|
|
|
_cap = _alloc_rpc_cap(_pd_session, Native_capability(_tid.ec_sel),
|
|
|
|
(addr_t)_activation_entry);
|
2012-12-12 12:06:54 +01:00
|
|
|
if (!_cap.valid())
|
|
|
|
throw Cpu_session::Thread_creation_failed();
|
|
|
|
|
|
|
|
/* prepare portal receive window of new thread */
|
2013-09-06 11:03:12 +02:00
|
|
|
if (!_rcv_buf.prepare_rcv_window((Nova::Utcb *)&_context->utcb))
|
|
|
|
throw Cpu_session::Thread_creation_failed();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
if (start_on_construction)
|
|
|
|
activate();
|
|
|
|
}
|
2012-11-15 12:49:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
Rpc_entrypoint::~Rpc_entrypoint()
|
|
|
|
{
|
|
|
|
typedef Object_pool<Rpc_object_base> Pool;
|
|
|
|
|
2015-08-10 13:34:16 +02:00
|
|
|
Pool::remove_all([&] (Rpc_object_base *obj) {
|
2012-11-15 12:49:08 +01:00
|
|
|
PWRN("Object pool not empty in %s", __func__);
|
2015-08-10 13:34:16 +02:00
|
|
|
_dissolve(obj);
|
|
|
|
});
|
2012-12-12 12:06:54 +01:00
|
|
|
|
|
|
|
if (!_cap.valid())
|
|
|
|
return;
|
|
|
|
|
2016-01-19 20:24:22 +01:00
|
|
|
_free_rpc_cap(_pd_session, _cap);
|
2012-11-15 12:49:08 +01:00
|
|
|
}
|