/* * \brief NOVA-specific support code for the server-side RPC API * \author Norman Feske * \author Sebastian Sumpf * \author Alexander Boettcher * \date 2010-01-13 */ /* * Copyright (C) 2010-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include #include /* base-internal includes */ #include #include /* NOVA includes */ #include #include using namespace Genode; /*********************** ** Server entrypoint ** ***********************/ Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj) { using namespace Nova; /* don't manage RPC object twice */ if (obj->cap().valid()) { warning("attempt to manage RPC object twice"); return obj->cap(); } Untyped_capability ec_cap; /* _ec_sel is invalid until thread gets started */ if (native_thread().ec_sel != Native_thread::INVALID_INDEX) ec_cap = Capability_space::import(native_thread().ec_sel); else ec_cap = _thread_cap; Untyped_capability obj_cap = _alloc_rpc_cap(_pd_session, ec_cap, (addr_t)&_activation_entry); if (!obj_cap.valid()) return obj_cap; /* add server object to object pool */ obj->cap(obj_cap); insert(obj); /* return object capability managed by entrypoint thread */ return obj_cap; } void Rpc_entrypoint::_dissolve(Rpc_object_base *obj) { /* don't dissolve RPC object twice */ if (!obj->cap().valid()) return; /* de-announce object from cap_session */ _free_rpc_cap(_pd_session, obj->cap()); /* avoid any incoming IPC */ Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true); /* make sure nobody is able to find this object */ remove(obj); /* effectively invalidate the capability used before */ obj->cap(Untyped_capability()); /* * 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(Thread::myself()->utcb()); /* don't call ourself */ if (utcb == reinterpret_cast(this->utcb())) return; /* activate entrypoint now - otherwise cleanup call will block forever */ _delay_start.unlock(); /* 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())) error(utcb, " - could not clean up entry point of thread ", this->utcb(), " - res ", res); } static void reply(Nova::Utcb &utcb, Rpc_exception_code exc, Msgbuf_base &snd_msg) { copy_msgbuf_to_utcb(utcb, snd_msg, exc.value); Nova::reply(Thread::myself()->stack_top()); } void Rpc_entrypoint::_activation_entry() { /* retrieve portal id from eax/rdi */ #ifdef __x86_64__ addr_t id_pt; asm volatile ("" : "=D" (id_pt)); #else addr_t id_pt; asm volatile ("" : "=a" (id_pt)); #endif Rpc_entrypoint &ep = *static_cast(Thread::myself()); Nova::Utcb &utcb = *(Nova::Utcb *)Thread::myself()->utcb(); Receive_window &rcv_window = ep.native_thread().server_rcv_window; rcv_window.post_ipc(utcb); /* handle ill-formed message */ if (utcb.msg_words() < 2) { ep._rcv_buf.word(0) = ~0UL; /* invalid opcode */ } else { copy_utcb_to_msgbuf(utcb, rcv_window, ep._rcv_buf); } Ipc_unmarshaller unmarshaller(ep._rcv_buf); Rpc_opcode opcode(0); unmarshaller.extract(opcode); /* default return value */ Rpc_exception_code exc = Rpc_exception_code(Rpc_exception_code::INVALID_OBJECT); /* in case of a portal cleanup call we are done here - just reply */ if (ep._cap.local_name() == (long)id_pt) { if (!rcv_window.prepare_rcv_window(utcb)) warning("out of capability selectors for handling server requests"); ep._rcv_buf.reset(); reply(utcb, exc, ep._snd_buf); } { /* potentially delay start */ Lock::Guard lock_guard(ep._delay_start); } /* atomically lookup and lock referenced object */ auto lambda = [&] (Rpc_object_base *obj) { if (!obj) { error("could not look up server object, return from call id_pt=", id_pt); return; } /* dispatch request */ ep._snd_buf.reset(); try { exc = obj->dispatch(opcode, unmarshaller, ep._snd_buf); } catch (Blocking_canceled) { } }; ep.apply(id_pt, lambda); if (!rcv_window.prepare_rcv_window(*(Nova::Utcb *)ep.utcb())) warning("out of capability selectors for handling server requests"); ep._rcv_buf.reset(); reply(utcb, exc, ep._snd_buf); } 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 * 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 '_delay_start' lock on construction and release it * here. */ _delay_start.unlock(); } bool Rpc_entrypoint::is_myself() const { return (Thread::myself() == this); } Rpc_entrypoint::Rpc_entrypoint(Pd_session *pd_session, size_t stack_size, const char *name, bool start_on_construction, Affinity::Location location) : Thread(Cpu_session::Weight::DEFAULT_WEIGHT, name, stack_size, location), _delay_start(Lock::LOCKED), _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) { 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(); /* create cleanup portal */ _cap = _alloc_rpc_cap(_pd_session, Capability_space::import(native_thread().ec_sel), (addr_t)_activation_entry); if (!_cap.valid()) throw Cpu_session::Thread_creation_failed(); Receive_window &rcv_window = Thread::native_thread().server_rcv_window; /* prepare portal receive window of new thread */ if (!rcv_window.prepare_rcv_window(*(Nova::Utcb *)&_stack->utcb())) throw Cpu_session::Thread_creation_failed(); if (start_on_construction) activate(); } Rpc_entrypoint::~Rpc_entrypoint() { typedef Object_pool Pool; Pool::remove_all([&] (Rpc_object_base *obj) { warning("object pool not empty in ", __func__); _dissolve(obj); }); if (!_cap.valid()) return; _free_rpc_cap(_pd_session, _cap); }