/* * \brief seL4 implementation of the IPC API * \author Norman Feske * \date 2014-10-14 */ /* * Copyright (C) 2014 Genode Labs GmbH * * 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 #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; } /** * 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); /* * Allocate and define receive selector */ if (!rcv_sel()) rcv_sel() = Capability_space::alloc_rcv_sel(); /* * 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) { /* * Extract Genode capabilities from seL4 IPC message */ dst_msg.reset(); size_t const num_caps = seL4_GetMR(MR_IDX_NUM_CAPS); size_t curr_sel4_cap_idx = 0; for (size_t i = 0; i < num_caps; i++) { Rpc_obj_key const rpc_obj_key(seL4_GetMR(MR_IDX_CAPS + 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() && seL4_MessageInfo_get_extraCaps(msg_info) == 0) { dst_msg.insert(Native_capability()); continue; } /* * RPC object key as contained in the message data is valid. */ unsigned const unwrapped = seL4_MessageInfo_get_capsUnwrapped(msg_info) & (1 << 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. */ unsigned long const arg_badge = seL4_CapData_Badge_get_Badge(seL4_GetBadge(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 = seL4_MessageInfo_get_extraCaps(msg_info); 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++; } /* * Extract message data payload */ size_t const num_msg_words = seL4_MessageInfo_get_length(msg_info); /* detect malformed message with too small header */ if (num_msg_words < MR_IDX_DATA) return; /* 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)); } /**************** ** 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"); } if (!rcv_sel()) rcv_sel() = Capability_space::alloc_rcv_sel(); seL4_MessageInfo_t const request_msg_info = new_seL4_message(snd_msg); unsigned const dst_sel = Capability_space::ipc_cap_data(dst).sel.value(); seL4_MessageInfo_t const reply_msg_info = seL4_Call(dst_sel, request_msg_info); decode_seL4_message(reply_msg_info, rcv_msg); return Rpc_exception_code(seL4_GetMR(MR_IDX_EXC_CODE)); } /**************** ** IPC server ** ****************/ void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc, Msgbuf_base &snd_msg) { ASSERT(false); } Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller, Rpc_exception_code exc, Msgbuf_base &reply_msg, Msgbuf_base &request_msg) { seL4_Word badge = 0; if (exc.value == Rpc_exception_code::INVALID_OBJECT) { seL4_MessageInfo_t const request_msg_info = seL4_Recv(Thread::myself()->native_thread().ep_sel, &badge); decode_seL4_message(request_msg_info, request_msg); } else { seL4_MessageInfo_t const reply_msg_info = new_seL4_message(reply_msg); seL4_SetMR(MR_IDX_EXC_CODE, exc.value); seL4_MessageInfo_t const request_msg_info = seL4_ReplyRecv(Thread::myself()->native_thread().ep_sel, reply_msg_info, &badge); decode_seL4_message(request_msg_info, 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() { }