genode/repos/base-fiasco/src/lib/base/ipc.cc
Norman Feske 88b358c5ef Unification of native_capability.h
This patch establishes the sole use of generic headers across all
kernels. The common 'native_capability.h' is based on the version of
base-sel4. All traditional L4 kernels and Linux use the same
implementation of the capability-lifetime management. On base-hw, NOVA,
Fiasco.OC, and seL4, custom implementations (based on their original
mechanisms) are used, with the potential to unify them further in the
future.

This change achieves binary compatibility of dynamically linked programs
across all kernels.

Furthermore, the patch introduces a Native_capability::print method,
which allows the easy output of the kernel-specific capability
representation using the base/log.h API.

Issue #1993
2016-07-11 13:07:37 +02:00

288 lines
7.6 KiB
C++

/*
* \brief IPC implementation for Fiasco
* \author Norman Feske
* \date 2006-06-13
*/
/*
* Copyright (C) 2006-2013 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 <base/printf.h>
#include <base/log.h>
#include <base/ipc.h>
#include <base/blocking.h>
/* base-internal includes */
#include <base/internal/ipc_server.h>
#include <base/internal/capability_space_tpl.h>
/* Fiasco includes */
namespace Fiasco {
#include <l4/sys/ipc.h>
#include <l4/sys/syscalls.h>
}
using namespace Genode;
class Msg_header
{
private:
/* kernel-defined message header */
Fiasco::l4_fpage_t rcv_fpage; /* unused */
Fiasco::l4_msgdope_t size_dope;
Fiasco::l4_msgdope_t send_dope;
public:
/*
* First two data words of message, used to transfer the local name of
* the invoked object (when a client calls a server) or the exception
* code (when the server replies), and the number of capability
* arguments. The kernel does not fetch these data words from memory
* but transfers them via the short-IPC registers.
*/
Fiasco::l4_umword_t protocol_word;
Fiasco::l4_umword_t num_caps;
private:
enum { MAX_CAPS_PER_MSG = Msgbuf_base::MAX_CAPS_PER_MSG };
Fiasco::l4_threadid_t _cap_tid [MAX_CAPS_PER_MSG];
unsigned long _cap_local_name [MAX_CAPS_PER_MSG];
size_t _num_msg_words(size_t num_data_words) const
{
size_t const caps_size = sizeof(_cap_tid) + sizeof(_cap_local_name);
/*
* Account for the transfer of the protocol word, capability count,
* and capability arguments in front of the payload.
*/
return 2 + caps_size/sizeof(Fiasco::l4_umword_t) + num_data_words;
}
public:
void *msg_start() { return &rcv_fpage; }
/**
* Load header fields according to send-message buffer
*/
void prepare_snd_msg(unsigned long protocol, Msgbuf_base const &snd_msg)
{
using namespace Fiasco;
protocol_word = protocol;
num_caps = min((unsigned)MAX_CAPS_PER_MSG, snd_msg.used_caps());
size_t const snd_words = snd_msg.data_size()/sizeof(l4_umword_t);
send_dope = L4_IPC_DOPE(_num_msg_words(snd_words), 0);
/* reset _cap_tid and _cap_local_name */
for (unsigned i = 0; i < MAX_CAPS_PER_MSG; i++) {
_cap_tid[i] = L4_INVALID_ID;
_cap_local_name[i] = 0;
}
for (unsigned i = 0; i < num_caps; i++) {
Native_capability const &cap = snd_msg.cap(i);
if (cap.valid()) {
Capability_space::Ipc_cap_data const cap_data =
Capability_space::ipc_cap_data(snd_msg.cap(i));
_cap_tid[i] = cap_data.dst;
_cap_local_name[i] = cap_data.rpc_obj_key.value();
}
}
}
/**
* Prepare header for receiving a message
*/
void prepare_rcv_msg(Msgbuf_base const &rcv_msg)
{
using namespace Fiasco;
size_t const rcv_max_words = rcv_msg.capacity()/sizeof(l4_umword_t);
size_dope = L4_IPC_DOPE(_num_msg_words(rcv_max_words), 0);
}
/**
* Copy received capability arguments into receive message buffer
*/
void extract_caps(Msgbuf_base &rcv_msg) const
{
for (unsigned i = 0; i < min((unsigned)MAX_CAPS_PER_MSG, num_caps); i++) {
Rpc_obj_key const rpc_obj_key(_cap_local_name[i]);
bool const cap_valid = !Fiasco::l4_is_invalid_id(_cap_tid[i]);
Native_capability cap;
if (cap_valid) {
cap = Capability_space::lookup(rpc_obj_key);
if (!cap.valid())
cap = Capability_space::import(_cap_tid[i], rpc_obj_key);
}
rcv_msg.insert(cap);
}
}
};
/****************
** IPC client **
****************/
Rpc_exception_code Genode::ipc_call(Native_capability dst,
Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg,
size_t)
{
using namespace Fiasco;
Capability_space::Ipc_cap_data const dst_data =
Capability_space::ipc_cap_data(dst);
Msg_header &snd_header = snd_msg.header<Msg_header>();
snd_header.prepare_snd_msg(dst_data.rpc_obj_key.value(), snd_msg);
Msg_header &rcv_header = rcv_msg.header<Msg_header>();
rcv_header.prepare_rcv_msg(rcv_msg);
l4_msgdope_t ipc_result;
l4_ipc_call(dst_data.dst,
snd_header.msg_start(),
snd_header.protocol_word,
snd_header.num_caps,
rcv_header.msg_start(),
&rcv_header.protocol_word,
&rcv_header.num_caps,
L4_IPC_NEVER, &ipc_result);
rcv_header.extract_caps(rcv_msg);
if (L4_IPC_IS_ERROR(ipc_result)) {
if (L4_IPC_ERROR(ipc_result) == L4_IPC_RECANCELED)
throw Genode::Blocking_canceled();
PERR("ipc_call error %lx", L4_IPC_ERROR(ipc_result));
throw Genode::Ipc_error();
}
return Rpc_exception_code(rcv_header.protocol_word);
}
/****************
** IPC server **
****************/
void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
{
using namespace Fiasco;
Msg_header &snd_header = snd_msg.header<Msg_header>();
snd_header.prepare_snd_msg(exc.value, snd_msg);
l4_msgdope_t result;
l4_ipc_send(Capability_space::ipc_cap_data(caller).dst,
snd_header.msg_start(),
snd_header.protocol_word,
snd_header.num_caps,
L4_IPC_SEND_TIMEOUT_0, &result);
if (L4_IPC_IS_ERROR(result))
PERR("ipc_send error %lx, ignored", L4_IPC_ERROR(result));
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg)
{
using namespace Fiasco;
l4_msgdope_t ipc_result;
bool need_to_wait = true;
Msg_header &snd_header = reply_msg.header<Msg_header>();
snd_header.prepare_snd_msg(exc.value, reply_msg);
request_msg.reset();
Msg_header &rcv_header = request_msg.header<Msg_header>();
rcv_header.prepare_rcv_msg(request_msg);
l4_threadid_t caller = L4_INVALID_ID;
if (last_caller.valid()) {
/*
* Use short IPC for reply if possible. This is the common case of
* returning an integer as RPC result.
*/
l4_ipc_reply_and_wait(Capability_space::ipc_cap_data(last_caller).dst,
snd_header.msg_start(),
snd_header.protocol_word,
snd_header.num_caps,
&caller, rcv_header.msg_start(),
&rcv_header.protocol_word,
&rcv_header.num_caps,
L4_IPC_SEND_TIMEOUT_0, &ipc_result);
/*
* The error condition could be a message cut (which we want to ignore
* on the server side) or a reply failure (for example, if the caller
* went dead during the call. In both cases, we do not reflect the
* error condition to the user but want to wait for the next proper
* incoming message.
*/
if (L4_IPC_IS_ERROR(ipc_result)) {
PERR("ipc_reply_and_wait error %lx", L4_IPC_ERROR(ipc_result));
} else {
need_to_wait = false;
}
}
while (need_to_wait) {
l4_ipc_wait(&caller, rcv_header.msg_start(),
&rcv_header.protocol_word,
&rcv_header.num_caps,
L4_IPC_NEVER, &ipc_result);
if (L4_IPC_IS_ERROR(ipc_result)) {
PERR("ipc_wait error %lx", L4_IPC_ERROR(ipc_result));
} else {
need_to_wait = false;
}
};
rcv_header.extract_caps(request_msg);
return Rpc_request(Capability_space::import(caller, Rpc_obj_key()),
rcv_header.protocol_word);
}
Ipc_server::Ipc_server()
:
Native_capability(Capability_space::import(Fiasco::l4_myself(), Rpc_obj_key()))
{ }
Ipc_server::~Ipc_server() { }