414 lines
11 KiB
C++
414 lines
11 KiB
C++
/*
|
|
* \brief Implementation of the IPC API for Fiasco.OC
|
|
* \author Stefan Kalkowski
|
|
* \author Norman Feske
|
|
* \date 2009-12-03
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-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.
|
|
*/
|
|
|
|
/*
|
|
* l4_msgtag_t (size == 1 mword) format:
|
|
*
|
|
* --------------------------------------------------------------
|
|
* | label | 4 Bit flags | 6 Bit items | 6 Bit word count |
|
|
* --------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/* Genode includes */
|
|
#include <base/blocking.h>
|
|
#include <base/ipc.h>
|
|
#include <base/ipc_msgbuf.h>
|
|
#include <base/thread.h>
|
|
|
|
/* base-internal includes */
|
|
#include <base/internal/lock_helper.h> /* for 'thread_get_my_native_id()' */
|
|
#include <base/internal/ipc_server.h>
|
|
#include <base/internal/native_utcb.h>
|
|
#include <base/internal/cap_map.h>
|
|
#include <base/internal/foc_assert.h>
|
|
|
|
/* Fiasco.OC includes */
|
|
namespace Fiasco {
|
|
#include <l4/sys/consts.h>
|
|
#include <l4/sys/ipc.h>
|
|
#include <l4/sys/types.h>
|
|
#include <l4/sys/utcb.h>
|
|
}
|
|
|
|
using namespace Genode;
|
|
using namespace Fiasco;
|
|
|
|
|
|
/***************
|
|
** Utilities **
|
|
***************/
|
|
|
|
enum Debug { DEBUG_MSG = 1 };
|
|
|
|
|
|
static inline bool ipc_error(l4_msgtag_t tag, bool print)
|
|
{
|
|
int ipc_error = l4_ipc_error(tag, l4_utcb());
|
|
if (ipc_error && print) raw("Ipc error: ", ipc_error, " occurred!");
|
|
return ipc_error;
|
|
}
|
|
|
|
|
|
static constexpr unsigned long INVALID_BADGE = ~0UL;
|
|
|
|
|
|
/**
|
|
* Representation of a capability during UTCB marshalling/unmarshalling
|
|
*/
|
|
struct Cap_info
|
|
{
|
|
bool valid = false;
|
|
unsigned long sel = 0;
|
|
unsigned long badge = 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* Copy message registers from UTCB to destination message buffer
|
|
*
|
|
* \return protocol word (local name or exception code)
|
|
*/
|
|
static unsigned long extract_msg_from_utcb(l4_msgtag_t tag,
|
|
Receive_window &rcv_window,
|
|
Msgbuf_base &rcv_msg)
|
|
{
|
|
unsigned num_msg_words = l4_msgtag_words(tag);
|
|
|
|
l4_umword_t const *msg_words = (l4_umword_t const *)l4_utcb_mr();
|
|
|
|
/* each message has at least the protocol word and the capability count */
|
|
if (num_msg_words < 2)
|
|
return 0;
|
|
|
|
/* read badge / exception code from first message word */
|
|
unsigned long const protocol_word = *msg_words++;
|
|
|
|
/* read number of capability arguments from second message word */
|
|
size_t const num_caps = min(*msg_words, Msgbuf_base::MAX_CAPS_PER_MSG);
|
|
msg_words++;
|
|
|
|
num_msg_words -= 2;
|
|
if (num_caps > 0 && num_msg_words < num_caps) {
|
|
raw("unexpected end of message, capability info missing");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Extract capabilities
|
|
*
|
|
* The badges are stored in the subsequent message registers. For each
|
|
* valid badge, we expect one capability selector to be present in the
|
|
* receive window. The content of the receive window is tracked via
|
|
* 'sel_idx'. If we encounter an invalid badge, the sender specified
|
|
* an invalid capabilty as argument.
|
|
*/
|
|
unsigned const num_cap_sel = l4_msgtag_items(tag);
|
|
|
|
Cap_info caps[num_caps];
|
|
|
|
for (unsigned i = 0, sel_idx = 0; i < num_caps; i++) {
|
|
|
|
unsigned long const badge = *msg_words++;
|
|
|
|
if (badge == INVALID_BADGE)
|
|
continue;
|
|
|
|
/* received a delegated capability */
|
|
if (sel_idx == num_cap_sel) {
|
|
raw("missing capability selector in message");
|
|
break;
|
|
}
|
|
|
|
caps[i].badge = badge;
|
|
caps[i].valid = true;
|
|
caps[i].sel = rcv_window.rcv_cap_sel(sel_idx++);
|
|
}
|
|
num_msg_words -= num_caps;
|
|
|
|
/* the remainder of the message contains the regular data payload */
|
|
if ((num_msg_words)*sizeof(l4_mword_t) > rcv_msg.capacity()) {
|
|
if (DEBUG_MSG)
|
|
raw("receive message buffer too small");
|
|
num_msg_words = rcv_msg.capacity()/sizeof(l4_mword_t);
|
|
}
|
|
|
|
/* read message payload beginning from the second UTCB message register */
|
|
l4_mword_t *dst = (l4_mword_t *)rcv_msg.data();
|
|
for (unsigned i = 0; i < num_msg_words; i++)
|
|
*dst++ = *msg_words++;
|
|
|
|
rcv_msg.data_size(sizeof(l4_mword_t)*num_msg_words);
|
|
|
|
/*
|
|
* Insert received capability selectors into cap map.
|
|
*
|
|
* Note that this operation pollutes the UTCB. Therefore we must perform
|
|
* it not before the entire message content is extracted.
|
|
*/
|
|
for (unsigned i = 0; i < num_caps; i++) {
|
|
if (caps[i].valid) {
|
|
rcv_msg.insert(Native_capability(cap_map().insert_map(caps[i].badge,
|
|
caps[i].sel)));
|
|
} else {
|
|
rcv_msg.insert(Native_capability());
|
|
}
|
|
}
|
|
|
|
return protocol_word;
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy message registers from message buffer to UTCB and create message tag.
|
|
*
|
|
* \param protocol_word badge of invoked object (when a client calls a server)
|
|
* or the exception code (when a server replies to a
|
|
* client)
|
|
*/
|
|
static l4_msgtag_t copy_msgbuf_to_utcb(Msgbuf_base &snd_msg,
|
|
unsigned long protocol_word)
|
|
{
|
|
|
|
unsigned const num_data_words = snd_msg.data_size() / sizeof(l4_mword_t);
|
|
unsigned const num_caps = snd_msg.used_caps();
|
|
|
|
/* validate capabilities present in the message buffer */
|
|
for (unsigned i = 0; i < num_caps; i++) {
|
|
|
|
Native_capability &cap = snd_msg.cap(i);
|
|
if (!cap.valid())
|
|
continue;
|
|
|
|
if (!l4_msgtag_label(l4_task_cap_valid(L4_BASE_TASK_CAP, cap.data()->kcap())))
|
|
cap = Native_capability();
|
|
}
|
|
|
|
/*
|
|
* Obtain capability info from message buffer
|
|
*
|
|
* This step must be performed prior any write operation to the UTCB
|
|
* because the 'Genode::Capability' operations may indirectly trigger
|
|
* system calls, which pollute the UTCB.
|
|
*/
|
|
Cap_info caps[num_caps];
|
|
for (unsigned i = 0; i < num_caps; i++) {
|
|
Native_capability const &cap = snd_msg.cap(i);
|
|
if (cap.valid()) {
|
|
caps[i].valid = true;
|
|
caps[i].badge = cap.local_name();
|
|
caps[i].sel = cap.data()->kcap();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The message consists of a protocol word, the capability count, one badge
|
|
* value per capability, and the data payload.
|
|
*/
|
|
unsigned const num_msg_words = 2 + num_caps + num_data_words;
|
|
|
|
if (num_msg_words > L4_UTCB_GENERIC_DATA_SIZE) {
|
|
raw("receive message buffer too small");
|
|
throw Ipc_error();
|
|
}
|
|
|
|
l4_mword_t *msg_words = (l4_mword_t *)l4_utcb_mr();
|
|
|
|
*msg_words++ = protocol_word;
|
|
*msg_words++ = num_caps;
|
|
|
|
unsigned num_cap_sel = 0;
|
|
|
|
for (unsigned i = 0; i < num_caps; i++) {
|
|
|
|
/* store badge as normal message word */
|
|
*msg_words++ = caps[i].valid ? caps[i].badge : INVALID_BADGE;
|
|
|
|
/* setup flexpage for valid capability to delegate */
|
|
if (caps[i].valid) {
|
|
unsigned const idx = num_msg_words + 2*num_cap_sel;
|
|
|
|
/* check bounds of 'l4_msg_regs_t::mr' */
|
|
if (idx + 1 >= L4_UTCB_GENERIC_DATA_SIZE)
|
|
break;
|
|
|
|
l4_utcb_mr()->mr[idx] = L4_ITEM_MAP/* | L4_ITEM_CONT*/;
|
|
l4_utcb_mr()->mr[idx + 1] = l4_obj_fpage(caps[i].sel,
|
|
0, L4_FPAGE_RWX).raw;
|
|
num_cap_sel++;
|
|
}
|
|
}
|
|
|
|
/* store message data into UTCB message registers */
|
|
for (unsigned i = 0; i < num_data_words; i++)
|
|
*msg_words++ = snd_msg.word(i);
|
|
|
|
return l4_msgtag(0, num_msg_words, num_cap_sel, 0);
|
|
}
|
|
|
|
|
|
/****************
|
|
** IPC client **
|
|
****************/
|
|
|
|
Rpc_exception_code Genode::ipc_call(Native_capability dst,
|
|
Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg,
|
|
size_t)
|
|
{
|
|
Receive_window rcv_window;
|
|
rcv_window.init();
|
|
rcv_msg.reset();
|
|
|
|
/* copy call message to the UTCBs message registers */
|
|
l4_msgtag_t const call_tag = copy_msgbuf_to_utcb(snd_msg, dst.local_name());
|
|
|
|
addr_t rcv_cap_sel = rcv_window.rcv_cap_sel_base();
|
|
for (size_t i = 0; i < Msgbuf_base::MAX_CAPS_PER_MSG; i++) {
|
|
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
|
|
rcv_cap_sel += L4_CAP_SIZE;
|
|
}
|
|
|
|
if (!dst.valid())
|
|
throw Genode::Ipc_error();
|
|
|
|
l4_msgtag_t const reply_tag =
|
|
l4_ipc_call(dst.data()->kcap(), l4_utcb(), call_tag, L4_IPC_NEVER);
|
|
|
|
if (l4_ipc_error(reply_tag, l4_utcb()) == L4_IPC_RECANCELED)
|
|
throw Genode::Blocking_canceled();
|
|
|
|
if (ipc_error(reply_tag, DEBUG_MSG))
|
|
throw Genode::Ipc_error();
|
|
|
|
return Rpc_exception_code(extract_msg_from_utcb(reply_tag, rcv_window, rcv_msg));
|
|
}
|
|
|
|
|
|
/****************
|
|
** IPC server **
|
|
****************/
|
|
|
|
static bool badge_matches_label(unsigned long badge, unsigned long label)
|
|
{
|
|
return badge == (label & (~0UL << 2));
|
|
}
|
|
|
|
|
|
void Genode::ipc_reply(Native_capability, Rpc_exception_code exc,
|
|
Msgbuf_base &snd_msg)
|
|
{
|
|
l4_msgtag_t tag = copy_msgbuf_to_utcb(snd_msg, exc.value);
|
|
|
|
tag = l4_ipc_send(L4_SYSF_REPLY, l4_utcb(), tag, L4_IPC_SEND_TIMEOUT_0);
|
|
|
|
ipc_error(tag, DEBUG_MSG);
|
|
}
|
|
|
|
|
|
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
|
|
Rpc_exception_code exc,
|
|
Msgbuf_base &reply_msg,
|
|
Msgbuf_base &request_msg,
|
|
Rpc_entrypoint::Native_context &)
|
|
{
|
|
Receive_window &rcv_window = Thread::myself()->native_thread().rcv_window;
|
|
|
|
bool need_to_wait = false;
|
|
|
|
for (;;) {
|
|
|
|
request_msg.reset();
|
|
|
|
/* prepare receive window in UTCB */
|
|
addr_t rcv_cap_sel = rcv_window.rcv_cap_sel_base();
|
|
for (size_t i = 0; i < Msgbuf_base::MAX_CAPS_PER_MSG; i++) {
|
|
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
|
|
rcv_cap_sel += L4_CAP_SIZE;
|
|
}
|
|
l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK;
|
|
|
|
l4_msgtag_t request_tag;
|
|
l4_umword_t label = 0; /* kernel-protected label of invoked capability */
|
|
|
|
if (exc.value != Rpc_exception_code::INVALID_OBJECT
|
|
&& !need_to_wait) {
|
|
|
|
l4_msgtag_t const reply_tag = copy_msgbuf_to_utcb(reply_msg, exc.value);
|
|
|
|
request_tag = l4_ipc_reply_and_wait(l4_utcb(), reply_tag, &label, L4_IPC_SEND_TIMEOUT_0);
|
|
} else {
|
|
request_tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
|
|
}
|
|
|
|
if (ipc_error(request_tag, false)) {
|
|
need_to_wait = true;
|
|
continue;
|
|
} else need_to_wait = false;
|
|
|
|
/* copy request message from the UTCBs message registers */
|
|
unsigned long const badge =
|
|
extract_msg_from_utcb(request_tag, rcv_window, request_msg);
|
|
|
|
/* ignore request if we detect a forged badge */
|
|
if (!badge_matches_label(badge, label)) {
|
|
raw("badge does not match label, ignoring request");
|
|
continue;
|
|
}
|
|
|
|
return Rpc_request(Native_capability(), badge);
|
|
}
|
|
}
|
|
|
|
|
|
Ipc_server::Ipc_server(Rpc_entrypoint::Native_context& native_context)
|
|
:
|
|
Native_capability((Cap_index*)Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE]),
|
|
_native_context(native_context)
|
|
{
|
|
Thread::myself()->native_thread().rcv_window.init();
|
|
}
|
|
|
|
|
|
Ipc_server::~Ipc_server() { }
|
|
|
|
|
|
/********************
|
|
** Receive_window **
|
|
********************/
|
|
|
|
Receive_window::~Receive_window()
|
|
{
|
|
if (_rcv_idx_base)
|
|
cap_idx_alloc().free(_rcv_idx_base, MAX_CAPS_PER_MSG);
|
|
}
|
|
|
|
|
|
void Receive_window::init()
|
|
{
|
|
_rcv_idx_base = cap_idx_alloc().alloc_range(MAX_CAPS_PER_MSG);
|
|
}
|
|
|
|
|
|
addr_t Receive_window::rcv_cap_sel_base()
|
|
{
|
|
return _rcv_idx_base->kcap();
|
|
}
|
|
|
|
|
|
addr_t Receive_window::rcv_cap_sel(unsigned i)
|
|
{
|
|
return rcv_cap_sel_base() + i*Fiasco::L4_CAP_SIZE;
|
|
}
|
|
|