genode/repos/base-foc/src/base/ipc/ipc.cc

379 lines
10 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Implementation of the IPC API for Fiasco.OC
* \author Stefan Kalkowski
* \author Norman Feske
2011-12-22 16:19:25 +01:00
* \date 2009-12-03
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2009-2013 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.
*/
/*
* 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>
#include <util/assert.h>
2011-12-22 16:19:25 +01:00
/* base-internal includes */
#include <base/internal/lock_helper.h> /* for 'thread_get_my_native_id()' */
#include <base/internal/ipc_server.h>
2011-12-22 16:19:25 +01:00
/* 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>
#include <l4/sys/kdebug.h>
}
using namespace Genode;
using namespace Fiasco;
/***************
** Utilities **
***************/
enum Debug { DEBUG_MSG = 1, HALT_ON_ERROR = 0 };
2011-12-22 16:19:25 +01:00
Fiasco.OC: introduce Cap_index (fixes #149, #112) This commit introduces a Cap_index class for Fiasco.OC's capabilities. A Cap_index is a combination of the global capability id, that is used by Genode to correctly identify a kernel-object, and a corresponding entry in a protection-domain's (kernel-)capability-space. The cap-indices are non-copyable, unique objects, that are held in a Cap_map. The Cap_map is used to re-find capabilities already present in the protection-domain, when a capability is received via IPC. The retrieval of capabilities effectively fixes issue #112, meaning the waste of capability-space entries. Because Cap_index objects are non-copyable (their address indicates the position in the capability-space of the pd), they are inappropriate to use as Native_capability. Therefore, Native_capability is implemented as a reference to Cap_index objects. This design seems to be a good pre-condition to implement smart-pointers for entries in the capability-space, and thereby closing existing leaks (please refer to issue #32). Cap_index, Cap_map, and the allocator for Cap_index objects are designed in a way, that it should be relatively easy to apply the same concept to NOVA also. By now, these classes are located in the `base-foc` repository, but they intentionally contain no Fiasco.OC specific elements. The previously explained changes had extensive impact on the whole Fiasco.OC platform implementation, due to various dependencies. The following things had to be changed: * The Thread object's startup and destruction routine is re-arranged, to enable another thread (that calls the Thread destructor) gaining the capability id of the thread's gate to remove it from the Cap_map, the thread's UTCB had to be made available to the caller, because there is the current location of that id. After having the UTCB available in the Thread object for that reason, the whole thread bootstrapping could be simplified. * In the course of changing the Native_capability's semantic, a new Cap_mapping class was introduced in core, that facilitates the establishment and destruction of capability mappings between core and it's client's, especially mappings related to Platform_thread and Platform_task, that are relevant to task and thread creation and destruction. Thereby, the destruction of threads had to be reworked, which effectively removed a bug (issue #149) where some threads weren't destroyed properly. * In the quick fix for issue #112, something similar to the Cap_map was introduced available in all processes. Moreover, some kind of a capability map already existed in core, to handle cap-session request properly. The introduction of the Cap_map unified both structures, so that the cap-session component code in core had to be reworked too. * The platform initialization code had to be changed sligthly due to the changes in Native_capability * The vcpu initialization in the L4Linux support library had to be adapted according to the already mentioned changes in the Thread object's bootstrap code.
2012-03-15 12:41:24 +01:00
static inline bool ipc_error(l4_msgtag_t tag, bool print)
2011-12-22 16:19:25 +01:00
{
int ipc_error = l4_ipc_error(tag, l4_utcb());
if (ipc_error) {
if (print) {
outstring("Ipc error: ");
outhex32(ipc_error);
outstring(" occurred!\n");
}
if (HALT_ON_ERROR)
enter_kdebug("Ipc error");
return true;
}
return false;
}
enum { 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;
};
2011-12-22 16:19:25 +01:00
/**
* Copy message registers from UTCB to destination message buffer
*
* \return protocol word (local name or exception code)
2011-12-22 16:19:25 +01:00
*/
static unsigned long extract_msg_from_utcb(l4_msgtag_t tag,
Receive_window &rcv_window,
Msgbuf_base &rcv_msg)
2011-12-22 16:19:25 +01:00
{
unsigned num_msg_words = l4_msgtag_words(tag);
l4_mword_t const *msg_words = (l4_mword_t const *)l4_utcb_mr();
/* each message has at least the protocol word and the capability count */
if (num_msg_words < 2)
return 0;
2011-12-22 16:19:25 +01:00
/* read badge / exception code from first message word */
unsigned long const protocol_word = *msg_words++;
/* read number of capability arguments from second message word */
unsigned long 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) {
outstring("unexpected end of message, capability info missing\n");
return 0;
2011-12-22 16:19:25 +01:00
}
/*
* 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) {
outstring("missing capability selector in message\n");
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)
outstring("receive message buffer too small\n");
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;
2011-12-22 16:19:25 +01:00
}
/**
* 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)
2011-12-22 16:19:25 +01:00
*/
static l4_msgtag_t copy_msgbuf_to_utcb(Msgbuf_base &snd_msg,
unsigned long protocol_word)
2011-12-22 16:19:25 +01:00
{
unsigned const num_data_words = snd_msg.data_size() / sizeof(l4_mword_t);
unsigned const num_caps = snd_msg.used_caps();
2011-12-22 16:19:25 +01:00
/* 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.dst())))
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.dst();
}
}
/*
* 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) {
outstring("receive message buffer too small\n");
2011-12-22 16:19:25 +01:00
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++) {
Native_capability const &cap = snd_msg.cap(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;
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++;
}
}
2011-12-22 16:19:25 +01:00
/* store message data into UTCB message registers */
for (unsigned i = 0; i < num_data_words; i++)
*msg_words++ = snd_msg.word(i);
2011-12-22 16:19:25 +01:00
return l4_msgtag(0, num_msg_words, num_cap_sel, 0);
}
/****************
** IPC client **
2011-12-22 16:19:25 +01:00
****************/
Rpc_exception_code Genode::ipc_call(Native_capability dst,
Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg,
size_t rcv_caps)
2011-12-22 16:19:25 +01:00
{
Receive_window rcv_window;
rcv_window.init();
rcv_msg.reset();
2011-12-22 16:19:25 +01:00
/* copy call message to the UTCBs message registers */
l4_msgtag_t const call_tag = copy_msgbuf_to_utcb(snd_msg, dst.local_name());
2011-12-22 16:19:25 +01:00
addr_t rcv_cap_sel = rcv_window.rcv_cap_sel_base();
for (int i = 0; i < Msgbuf_base::MAX_CAPS_PER_MSG; i++) {
2011-12-22 16:19:25 +01:00
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
rcv_cap_sel += L4_CAP_SIZE;
}
l4_msgtag_t const reply_tag =
l4_ipc_call(dst.dst(), l4_utcb(), call_tag, L4_IPC_NEVER);
2011-12-22 16:19:25 +01:00
if (l4_ipc_error(reply_tag, l4_utcb()) == L4_IPC_RECANCELED)
throw Genode::Blocking_canceled();
2011-12-22 16:19:25 +01:00
if (ipc_error(reply_tag, DEBUG_MSG))
throw Genode::Ipc_error();
2011-12-22 16:19:25 +01:00
return Rpc_exception_code(extract_msg_from_utcb(reply_tag, rcv_window, rcv_msg));
}
2011-12-22 16:19:25 +01:00
/****************
** IPC server **
2011-12-22 16:19:25 +01:00
****************/
static bool badge_matches_label(unsigned long badge, unsigned long label)
2011-12-22 16:19:25 +01:00
{
return badge == (label & (~0UL << 2));
2011-12-22 16:19:25 +01:00
}
void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
2011-12-22 16:19:25 +01:00
{
l4_msgtag_t tag = copy_msgbuf_to_utcb(snd_msg, exc.value);
2011-12-22 16:19:25 +01:00
tag = l4_ipc_send(L4_SYSF_REPLY, l4_utcb(), tag, L4_IPC_SEND_TIMEOUT_0);
2011-12-22 16:19:25 +01:00
ipc_error(tag, DEBUG_MSG);
2011-12-22 16:19:25 +01:00
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg)
2011-12-22 16:19:25 +01:00
{
Receive_window &rcv_window = Thread_base::myself()->native_thread().rcv_window;
for (;;) {
request_msg.reset();
2011-12-22 16:19:25 +01:00
/* prepare receive window in UTCB */
addr_t rcv_cap_sel = rcv_window.rcv_cap_sel_base();
for (int i = 0; i < Msgbuf_base::MAX_CAPS_PER_MSG; i++) {
2011-12-22 16:19:25 +01:00
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) {
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);
2011-12-22 16:19:25 +01:00
} else {
request_tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
}
2011-12-22 16:19:25 +01:00
if (ipc_error(request_tag, false))
continue;
/* 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)) {
outstring("badge does not match label, ignoring request\n");
continue;
2011-12-22 16:19:25 +01:00
}
return Rpc_request(Native_capability(), badge);
}
2011-12-22 16:19:25 +01:00
}
Ipc_server::Ipc_server()
:
Native_capability((Cap_index*)Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE])
{
Thread_base::myself()->native_thread().rcv_window.init();
}
Ipc_server::~Ipc_server() { }