/* * \brief seL4 implementation of the IPC API * \author Norman Feske * \date 2014-10-14 */ /* * Copyright (C) 2014-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 #include #include #include /* base-internal includes */ #include #include #include /* seL4 includes */ #include using namespace Genode; /** * Message-register definitions */ enum { MR_IDX_EXC_CODE = 0, MR_IDX_NUM_CAPS = 1, MR_IDX_CAPS = 2, MR_IDX_DATA = MR_IDX_CAPS + Msgbuf_base::MAX_CAPS_PER_MSG, }; /** * Return reference to receive selector of the calling thread */ static unsigned &rcv_sel() { /* * When the function is called at the very early initialization phase, we * cannot access Thread::myself()->native_thread() because the * Thread object of the main thread does not exist yet. During this * phase, we return a reference to the 'main_rcv_sel' variable. */ if (Thread::myself()) { return Thread::myself()->native_thread().rcv_sel; } static unsigned main_rcv_sel = Capability_space::alloc_rcv_sel(); return main_rcv_sel; } /***************************** ** Startup library support ** *****************************/ void prepare_reinit_main_thread() { /** * Reset selector to invalid, so that a new fresh will be allocated. * The IPC buffer of the thread must be configured to point to the * receive selector which is done by Capability_space::alloc_rcv_sel(), * which finally calls seL4_SetCapReceivePath(); */ rcv_sel() = 0; } /** * Convert Genode::Msgbuf_base content into seL4 message * * \param msg source message buffer */ static seL4_MessageInfo_t new_seL4_message(Msgbuf_base const &msg) { /* * Supply capabilities to kernel IPC message */ seL4_SetMR(MR_IDX_NUM_CAPS, msg.used_caps()); size_t sel4_sel_cnt = 0; for (size_t i = 0; i < msg.used_caps(); i++) { Native_capability const &cap = msg.cap(i); if (cap.valid()) { Capability_space::Ipc_cap_data const ipc_cap_data = Capability_space::ipc_cap_data(cap); seL4_SetMR(MR_IDX_CAPS + i, ipc_cap_data.rpc_obj_key.value()); seL4_SetCap(sel4_sel_cnt++, ipc_cap_data.sel.value()); } else { seL4_SetMR(MR_IDX_CAPS + i, Rpc_obj_key::INVALID); } } /* * Pad unused capability slots with invalid capabilities to avoid * leakage of any information that happens to be in the IPC buffer. */ for (size_t i = msg.used_caps(); i < Msgbuf_base::MAX_CAPS_PER_MSG; i++) seL4_SetMR(MR_IDX_CAPS + i, Rpc_obj_key::INVALID); /* * Supply data payload */ size_t const num_data_mwords = align_natural(msg.data_size()) / sizeof(umword_t); umword_t const *src = (umword_t const *)msg.data(); for (size_t i = 0; i < num_data_mwords; i++) seL4_SetMR(MR_IDX_DATA + i, *src++); seL4_MessageInfo_t const msg_info = seL4_MessageInfo_new(0, 0, sel4_sel_cnt, MR_IDX_DATA + num_data_mwords); return msg_info; } /** * Convert seL4 message into Genode::Msgbuf_base */ static void decode_seL4_message(seL4_MessageInfo_t const &msg_info, Msgbuf_base &dst_msg) { /** * Read all required data from seL4 IPC message * * You must not use any Genode primitives which may corrupt the IPCBuffer * during this step, e.g. Lock or RPC for output !!! */ size_t const num_caps = min(seL4_GetMR(MR_IDX_NUM_CAPS), (addr_t)Msgbuf_base::MAX_CAPS_PER_MSG); uint32_t const caps_extra = seL4_MessageInfo_get_extraCaps(msg_info); uint32_t const caps_unwrapped = seL4_MessageInfo_get_capsUnwrapped(msg_info); uint32_t const num_msg_words = seL4_MessageInfo_get_length(msg_info); Rpc_obj_key rpc_obj_keys[Msgbuf_base::MAX_CAPS_PER_MSG]; unsigned long arg_badges[Msgbuf_base::MAX_CAPS_PER_MSG]; for (size_t i = 0; i < num_caps; i++) { rpc_obj_keys[i] = Rpc_obj_key(seL4_GetMR(MR_IDX_CAPS + i)); if (!rpc_obj_keys[i].valid()) /* * If rpc_obj_key is invalid, avoid calling * seL4_CapData_Badge_get_Badge. It may trigger a assertion if * the lowest bit is set by the garbage badge value we got. */ arg_badges[i] = Rpc_obj_key::INVALID; else arg_badges[i] = seL4_GetBadge(i); } /** * Extract message data payload */ /* detect malformed message with too small header */ if (num_msg_words >= MR_IDX_DATA) { /* copy data payload */ size_t const max_words = dst_msg.capacity()/sizeof(umword_t); size_t const num_data_words = min(num_msg_words - MR_IDX_DATA, max_words); umword_t *dst = (umword_t *)dst_msg.data(); for (size_t i = 0; i < num_data_words; i++) *dst++ = seL4_GetMR(MR_IDX_DATA + i); dst_msg.data_size(num_data_words*sizeof(umword_t)); } /** * Now we got all data from the IPCBuffer, we may use Native_capability */ /** * Construct Genode capabilities from read seL4 IPC message stored in * rpc_opj_keys and arg_badges. */ size_t curr_sel4_cap_idx = 0; for (size_t i = 0; i < num_caps; i++) { Rpc_obj_key const rpc_obj_key = rpc_obj_keys[i]; /* * Detect passing of invalid capabilities as arguments * * The second condition of the check handles the case where a non-RPC * object capability as passed as RPC argument as done by the * 'Cap_session::alloc' RPC function. Here, the entrypoint capability * is not an RPC-object capability but a raw seL4 endpoint selector. * * XXX Technically, a message may contain one invalid capability * followed by a valid one. This check would still wrongly regard * the first capability as a valid one. A better approach would * be to introduce another state to Rpc_obj_key, which would * denote a valid capability that is not an RPC-object capability. * Hence it is meaningless as a key. */ if (!rpc_obj_key.valid() && caps_extra == 0) { dst_msg.insert(Native_capability()); continue; } /* * RPC object key as contained in the message data is valid. */ bool const unwrapped = caps_unwrapped & (1U << curr_sel4_cap_idx); /* distinguish unwrapped from delegated cap */ if (unwrapped) { /* * Received unwrapped capability * * This means that the capability argument belongs to our endpoint. * So it is already present within the capability space. */ ASSERT(curr_sel4_cap_idx < Msgbuf_base::MAX_CAPS_PER_MSG); unsigned long const arg_badge = arg_badges[curr_sel4_cap_idx]; if (arg_badge != rpc_obj_key.value()) { warning("argument badge (", arg_badge, ") != RPC object key (", rpc_obj_key.value(), ")"); } Native_capability arg_cap = Capability_space::lookup(rpc_obj_key); dst_msg.insert(arg_cap); } else { /* * Received delegated capability * * We have either received a capability that is foreign to us, * or an alias for a capability that we already posses. The * latter can happen in the following circumstances: * * - We forwarded a selector that was created by another * component. We cannot re-identify such a capability when * handed back because seL4's badge mechanism works only for * capabilities belonging to the IPC destination endpoint. * * - We received a selector on the IPC reply path, where seL4's * badge mechanism is not in effect. */ bool const delegated = caps_extra; ASSERT(delegated); Native_capability arg_cap = Capability_space::lookup(rpc_obj_key); if (arg_cap.valid()) { /* * Discard the received selector and keep using the already * present one. * * XXX We'd need to find out if both the received and the * looked-up selector refer to the same endpoint. * Unfortunaltely, seL4 lacks such a comparison operation. */ Capability_space::reset_sel(rcv_sel()); dst_msg.insert(arg_cap); } else { Capability_space::Ipc_cap_data const ipc_cap_data(rpc_obj_key, rcv_sel()); dst_msg.insert(Capability_space::import(ipc_cap_data)); /* * Since we keep using the received selector, we need to * allocate a fresh one for the next incoming delegation. */ rcv_sel() = Capability_space::alloc_rcv_sel(); } } curr_sel4_cap_idx++; } } /**************** ** IPC client ** ****************/ Rpc_exception_code Genode::ipc_call(Native_capability dst, Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg, size_t) { if (!dst.valid()) { error("Trying to invoke an invalid capability, stop."); kernel_debugger_panic("IPC destination is invalid"); } /* allocate and define receive selector */ if (!rcv_sel()) rcv_sel() = Capability_space::alloc_rcv_sel(); rcv_msg.reset(); unsigned const dst_sel = Capability_space::ipc_cap_data(dst).sel.value(); /** * Do not use Genode primitives after this point until the return which may * alter the content of the IPCBuffer, e.g. Lock or RPC. */ seL4_MessageInfo_t const request = new_seL4_message(snd_msg); seL4_MessageInfo_t const reply_msg_info = seL4_Call(dst_sel, request); Rpc_exception_code const exc_code(seL4_GetMR(MR_IDX_EXC_CODE)); decode_seL4_message(reply_msg_info, rcv_msg); return exc_code; } /**************** ** IPC server ** ****************/ void Genode::ipc_reply(Native_capability, Rpc_exception_code exc, Msgbuf_base &snd_msg) { /* allocate and define receive selector */ if (!rcv_sel()) rcv_sel() = Capability_space::alloc_rcv_sel(); /** * Do not use Genode primitives after this point until the return which may * alter the content of the IPCBuffer, e.g. Lock or RPC. */ /* called when entrypoint thread leaves entry loop and exits */ seL4_MessageInfo_t const reply_msg_info = new_seL4_message(snd_msg); seL4_SetMR(MR_IDX_EXC_CODE, exc.value); seL4_Reply(reply_msg_info); } Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &, Rpc_exception_code exc, Msgbuf_base &reply_msg, Msgbuf_base &request_msg) { /* allocate and define receive selector */ if (!rcv_sel()) rcv_sel() = Capability_space::alloc_rcv_sel(); seL4_CPtr const dest = Thread::myself()->native_thread().ep_sel; seL4_Word badge = 0; if (exc.value == Rpc_exception_code::INVALID_OBJECT) reply_msg.reset(); request_msg.reset(); /** * Do not use Genode primitives after this point until the return which may * alter the content of the IPCBuffer, e.g. Lock or RPC. */ seL4_MessageInfo_t const reply_msg_info = new_seL4_message(reply_msg); seL4_SetMR(MR_IDX_EXC_CODE, exc.value); seL4_MessageInfo_t const req = seL4_ReplyRecv(dest, reply_msg_info, &badge); decode_seL4_message(req, request_msg); return Rpc_request(Native_capability(), badge); } Ipc_server::Ipc_server() : Native_capability(Capability_space::create_ep_cap(*Thread::myself())) { } Ipc_server::~Ipc_server() { }