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
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2012-01-03 15:35:05 +01:00
|
|
|
* Copyright (C) 2010-2012 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
|
|
|
#include <base/cap_sel_alloc.h>
|
|
|
|
|
|
|
|
/* NOVA includes */
|
|
|
|
#include <nova/syscalls.h>
|
2012-08-08 14:23:13 +02:00
|
|
|
#include <nova_cpu_session/connection.h>
|
2012-08-03 10:56:08 +02:00
|
|
|
#include <nova/util.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
/***********************
|
|
|
|
** Server entrypoint **
|
|
|
|
***********************/
|
|
|
|
|
|
|
|
Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
|
|
|
|
{
|
|
|
|
using namespace Nova;
|
|
|
|
|
2012-08-02 12:20:00 +02:00
|
|
|
Untyped_capability ec_cap, ep_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 */
|
|
|
|
if (tid().ec_sel != ~0UL)
|
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
|
|
|
|
2012-07-30 10:56:07 +02:00
|
|
|
ep_cap = _cap_session->alloc(ec_cap, (addr_t)_activation_entry);
|
2012-06-19 15:54:41 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* add server object to object pool */
|
2012-08-02 12:20:00 +02:00
|
|
|
obj->cap(ep_cap);
|
2011-12-22 16:19:25 +01:00
|
|
|
insert(obj);
|
|
|
|
|
2012-08-02 12:20:00 +02:00
|
|
|
/* return entrypoint capability */
|
|
|
|
return ep_cap;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::_dissolve(Rpc_object_base *obj)
|
|
|
|
{
|
2012-07-30 12:09:25 +02:00
|
|
|
/* Avoid any incoming IPC early, keep local cap solely */
|
2012-08-08 14:23:13 +02:00
|
|
|
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), false);
|
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 */
|
|
|
|
remove(obj);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
_leave_server_object(obj);
|
|
|
|
|
|
|
|
/* wait until nobody is inside dispatch */
|
|
|
|
obj->lock();
|
|
|
|
|
2012-08-08 14:23:13 +02:00
|
|
|
/* De-announce object from cap_session */
|
2012-06-19 15:54:41 +02:00
|
|
|
_cap_session->free(obj->cap());
|
2012-08-08 14:23:13 +02:00
|
|
|
|
2012-07-30 12:09:25 +02:00
|
|
|
/* Revoke also local cap finally */
|
2012-08-08 14:23:13 +02:00
|
|
|
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true);
|
2012-06-19 15:54:41 +02:00
|
|
|
|
2012-07-30 12:09:25 +02:00
|
|
|
/* free cap selector */
|
|
|
|
cap_selector_allocator()->free(obj->cap().local_name(), 0);
|
2012-06-19 15:54:41 +02: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());
|
|
|
|
|
|
|
|
Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf);
|
2012-08-07 13:19:28 +02:00
|
|
|
ep->_rcv_buf.post_ipc(reinterpret_cast<Nova::Utcb *>(ep->utcb()));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/* destination of next reply */
|
2012-07-30 10:56:07 +02:00
|
|
|
srv.dst(Native_capability(id_pt));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
int opcode = 0;
|
|
|
|
|
|
|
|
srv >> IPC_WAIT >> opcode;
|
|
|
|
|
|
|
|
/* set default return value */
|
|
|
|
srv.ret(ERR_INVALID_OBJECT);
|
|
|
|
|
|
|
|
/* atomically lookup and lock referenced object */
|
|
|
|
{
|
|
|
|
Lock::Guard lock_guard(ep->_curr_obj_lock);
|
|
|
|
|
2012-07-12 09:36:20 +02:00
|
|
|
ep->_curr_obj = ep->obj_by_id(id_pt);
|
2012-08-08 14:23:13 +02:00
|
|
|
if (!ep->_curr_obj || !id_pt) {
|
2012-07-30 12:09:25 +02:00
|
|
|
/* Badge is used to suppress error message solely.
|
|
|
|
* It's non zero during cleanup call of an
|
|
|
|
* rpc_object_base object, see _leave_server_object.
|
|
|
|
*/
|
|
|
|
if (!srv.badge())
|
2012-08-08 14:23:13 +02:00
|
|
|
PERR("could not look up server object, "
|
2012-07-30 12:09:25 +02:00
|
|
|
" return from call id_pt=%lx",
|
|
|
|
id_pt);
|
|
|
|
ep->_curr_obj_lock.unlock();
|
|
|
|
srv << IPC_REPLY;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ep->_curr_obj->lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dispatch request */
|
|
|
|
try { srv.ret(ep->_curr_obj->dispatch(opcode, srv, srv)); }
|
|
|
|
catch (Blocking_canceled) { }
|
|
|
|
|
|
|
|
ep->_curr_obj->unlock();
|
|
|
|
ep->_curr_obj = 0;
|
|
|
|
|
|
|
|
ep->_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)ep->utcb());
|
|
|
|
srv << IPC_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Rpc_entrypoint::entry()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Thread entry is not used for activations on NOVA
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-19 15:54:41 +02:00
|
|
|
void Rpc_entrypoint::_leave_server_object(Rpc_object_base *obj)
|
|
|
|
{
|
2012-06-26 11:49:55 +02:00
|
|
|
{
|
|
|
|
Lock::Guard lock_guard(_curr_obj_lock);
|
|
|
|
|
|
|
|
if (obj == _curr_obj)
|
|
|
|
cancel_blocking();
|
|
|
|
}
|
|
|
|
|
2012-08-08 14:23:13 +02:00
|
|
|
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(
|
|
|
|
Thread_base::myself()->utcb());
|
2012-06-19 15:54:41 +02:00
|
|
|
/* don't call ourself */
|
|
|
|
if (utcb != reinterpret_cast<Nova::Utcb *>(&_context->utcb)) {
|
2012-07-30 12:09:25 +02:00
|
|
|
utcb->msg[0] = 0xdead;
|
|
|
|
utcb->set_msg_word(1);
|
2012-08-08 14:23:13 +02:00
|
|
|
if (uint8_t res = Nova::call(obj->cap().local_name()))
|
|
|
|
PERR("could not clean up entry point - %u", res);
|
2012-06-19 15:54:41 +02:00
|
|
|
}
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
* called, we grab the '_curr_obj_lock' on construction and release it
|
|
|
|
* here.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
|
|
|
_curr_obj_lock.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
|
|
|
const char *name, bool start_on_construction)
|
|
|
|
:
|
|
|
|
Thread_base(name, stack_size),
|
|
|
|
_curr_obj(0),
|
|
|
|
_curr_obj_lock(Lock::LOCKED),
|
|
|
|
_cap_session(cap_session)
|
|
|
|
{
|
2012-08-08 14:23:13 +02:00
|
|
|
/**
|
|
|
|
* Create thread if we aren't running in core.
|
|
|
|
*
|
|
|
|
* For core this code can't be performed since the sessions aren't
|
|
|
|
* setup in the early bootstrap phase of core. In core the thread
|
|
|
|
* is created 'manually'.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2012-08-08 14:23:13 +02:00
|
|
|
if (_tid.ec_sel == ~0UL) {
|
|
|
|
/* create new pager object and assign it to the new thread */
|
|
|
|
Pager_capability pager_cap =
|
|
|
|
env()->rm_session()->add_client(_thread_cap);
|
2012-08-02 12:20:00 +02:00
|
|
|
if (!pager_cap.valid())
|
|
|
|
throw Cpu_session::Thread_creation_failed();
|
|
|
|
|
|
|
|
if (env()->cpu_session()->set_pager(_thread_cap, pager_cap))
|
|
|
|
throw Cpu_session::Thread_creation_failed();
|
2012-08-08 14:23:13 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
|
|
|
Nova::PT_SEL_STARTUP);
|
|
|
|
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
|
|
|
Nova::PT_SEL_PAGE_FAULT);
|
2012-08-01 16:16:51 +02:00
|
|
|
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
|
|
|
Nova::SM_SEL_EC);
|
2012-08-24 10:25:24 +02:00
|
|
|
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
|
|
|
Nova::PT_SEL_RECALL);
|
2012-08-08 14:23:13 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Request native thread cap, _thread_cap only a token.
|
|
|
|
* The native thread cap is required to attach new rpc objects
|
|
|
|
* (to create portals bound to the ec)
|
|
|
|
*/
|
|
|
|
Native_capability ec_cap = cpu.native_cap(_thread_cap);
|
2012-07-30 10:56:07 +02:00
|
|
|
_tid.ec_sel = ec_cap.local_name();
|
2012-08-08 14:23:13 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-08 14:23:13 +02:00
|
|
|
_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)&_context->utcb);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
if (start_on_construction)
|
|
|
|
activate();
|
|
|
|
}
|