genode/repos/base-sel4/src/lib/base/ipc.cc

401 lines
11 KiB
C++
Raw Normal View History

2014-10-15 14:48:45 +02:00
/*
* \brief seL4 implementation of the IPC API
* \author Norman Feske
* \date 2014-10-14
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
2014-10-15 14:48:45 +02:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2014-10-15 14:48:45 +02:00
*/
/* Genode includes */
#include <base/blocking.h>
#include <base/ipc.h>
#include <base/log.h>
2015-05-10 19:51:10 +02:00
#include <base/thread.h>
2014-10-15 14:48:45 +02:00
#include <util/misc_math.h>
2015-05-10 19:51:10 +02:00
/* base-internal includes */
#include <base/internal/capability_space_sel4.h>
#include <base/internal/kernel_debugger.h>
#include <base/internal/ipc_server.h>
2015-05-10 19:51:10 +02:00
/* seL4 includes */
sel4: update to version 2.1 This patch updates seL4 from the experimental branch of one year ago to the master branch of version 2.1. The transition has the following implications. In contrast to the experimental branch, the master branch has no way to manually define the allocation of kernel objects within untyped memory ranges. Instead, the kernel maintains a built-in allocation policy. This policy rules out the deallocation of once-used parts of untyped memory. The only way to reuse memory is to revoke the entire untyped memory range. Consequently, we cannot share a large untyped memory range for kernel objects of different protection domains. In order to reuse memory at a reasonably fine granularity, we need to split the initial untyped memory ranges into small chunks that can be individually revoked. Those chunks are called "untyped pages". An untyped page is a 4 KiB untyped memory region. The bootstrapping of core has to employ a two-stage allocation approach now. For creating the initial kernel objects for core, which remain static during the entire lifetime of the system, kernel objects are created directly out of the initial untyped memory regions as reported by the kernel. The so-called "initial untyped pool" keeps track of the consumption of those untyped memory ranges by mimicking the kernel's internal allocation policy. Kernel objects created this way can be of any size. For example the phys CNode, which is used to store page-frame capabilities is 16 MiB in size. Also, core's CSpace uses a relatively large CNode. After the initial setup phase, all remaining untyped memory is turned into untyped pages. From this point on, new created kernel objects cannot exceed 4 KiB in size because one kernel object cannot span multiple untyped memory regions. The capability selectors for untyped pages are organized similarly to those of page-frame capabilities. There is a new 2nd-level CNode (UNTYPED_CORE_CNODE) that is dimensioned according to the maximum amount of physical memory (1M entries, each entry representing 4 KiB). The CNode is organized such that an index into the CNode directly corresponds to the physical frame number of the underlying memory. This way, we can easily determine a untyped page selector for any physical addresses, i.e., for revoking the kernel objects allocated at a specific physical page. The downside is the need for another 16 MiB chunk of meta data. Also, we need to keep in mind that this approach won't scale to 64-bit systems. We will eventually need to replace the PHYS_CORE_CNODE and UNTYPED_CORE_CNODE by CNode hierarchies to model a sparsely populated CNode. The size constrain of kernel objects has the immediate implication that the VM CSpaces of protection domains must be organized via several levels of CNodes. I.e., as the top-level CNode of core has a size of 2^12, the remaining 20 PD-specific CSpace address bits are organized as a 2nd-level 2^4 padding CNode, a 3rd-level 2^8 CNode, and several 4th-level 2^8 leaf CNodes. The latter contain the actual selectors for the page tables and page-table entries of the respective PD. As another slight difference from the experimental branch, the master branch requires the explicit assignment of page directories to an ASID pool. Besides the adjustment to the new seL4 version, the patch introduces a dedicated type for capability selectors. Previously, we just used to represent them as unsigned integer values, which became increasingly confusing. The new type 'Cap_sel' is a PD-local capability selector. The type 'Cnode_index' is an index into a CNode (which is not generally not the entire CSpace of the PD). Fixes #1887
2016-02-03 14:50:44 +01:00
#include <sel4/sel4.h>
2014-10-15 14:48:45 +02:00
using namespace Genode;
2015-05-11 08:43:43 +02:00
/**
* Message-register definitions
*/
enum {
MR_IDX_EXC_CODE = 0,
MR_IDX_NUM_CAPS = 1,
MR_IDX_CAPS = 2,
2015-05-11 08:43:43 +02:00
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;
}
2015-05-11 08:43:43 +02:00
/**
* Convert Genode::Msgbuf_base content into seL4 message
*
* \param msg source message buffer
2015-05-11 08:43:43 +02:00
*/
static seL4_MessageInfo_t new_seL4_message(Msgbuf_base const &msg)
2015-05-11 08:43:43 +02:00
{
/*
* 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);
2015-05-11 08:43:43 +02:00
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: update to version 2.1 This patch updates seL4 from the experimental branch of one year ago to the master branch of version 2.1. The transition has the following implications. In contrast to the experimental branch, the master branch has no way to manually define the allocation of kernel objects within untyped memory ranges. Instead, the kernel maintains a built-in allocation policy. This policy rules out the deallocation of once-used parts of untyped memory. The only way to reuse memory is to revoke the entire untyped memory range. Consequently, we cannot share a large untyped memory range for kernel objects of different protection domains. In order to reuse memory at a reasonably fine granularity, we need to split the initial untyped memory ranges into small chunks that can be individually revoked. Those chunks are called "untyped pages". An untyped page is a 4 KiB untyped memory region. The bootstrapping of core has to employ a two-stage allocation approach now. For creating the initial kernel objects for core, which remain static during the entire lifetime of the system, kernel objects are created directly out of the initial untyped memory regions as reported by the kernel. The so-called "initial untyped pool" keeps track of the consumption of those untyped memory ranges by mimicking the kernel's internal allocation policy. Kernel objects created this way can be of any size. For example the phys CNode, which is used to store page-frame capabilities is 16 MiB in size. Also, core's CSpace uses a relatively large CNode. After the initial setup phase, all remaining untyped memory is turned into untyped pages. From this point on, new created kernel objects cannot exceed 4 KiB in size because one kernel object cannot span multiple untyped memory regions. The capability selectors for untyped pages are organized similarly to those of page-frame capabilities. There is a new 2nd-level CNode (UNTYPED_CORE_CNODE) that is dimensioned according to the maximum amount of physical memory (1M entries, each entry representing 4 KiB). The CNode is organized such that an index into the CNode directly corresponds to the physical frame number of the underlying memory. This way, we can easily determine a untyped page selector for any physical addresses, i.e., for revoking the kernel objects allocated at a specific physical page. The downside is the need for another 16 MiB chunk of meta data. Also, we need to keep in mind that this approach won't scale to 64-bit systems. We will eventually need to replace the PHYS_CORE_CNODE and UNTYPED_CORE_CNODE by CNode hierarchies to model a sparsely populated CNode. The size constrain of kernel objects has the immediate implication that the VM CSpaces of protection domains must be organized via several levels of CNodes. I.e., as the top-level CNode of core has a size of 2^12, the remaining 20 PD-specific CSpace address bits are organized as a 2nd-level 2^4 padding CNode, a 3rd-level 2^8 CNode, and several 4th-level 2^8 leaf CNodes. The latter contain the actual selectors for the page tables and page-table entries of the respective PD. As another slight difference from the experimental branch, the master branch requires the explicit assignment of page directories to an ASID pool. Besides the adjustment to the new seL4 version, the patch introduces a dedicated type for capability selectors. Previously, we just used to represent them as unsigned integer values, which became increasingly confusing. The new type 'Cap_sel' is a PD-local capability selector. The type 'Cnode_index' is an index into a CNode (which is not generally not the entire CSpace of the PD). Fixes #1887
2016-02-03 14:50:44 +01:00
seL4_SetCap(sel4_sel_cnt++, ipc_cap_data.sel.value());
2015-05-11 08:43:43 +02:00
} 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);
2015-05-11 08:43:43 +02:00
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,
2015-05-11 08:43:43 +02:00
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 !!!
*/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
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.
2015-05-11 08:43:43 +02:00
*/
size_t curr_sel4_cap_idx = 0;
2015-05-11 08:43:43 +02:00
for (size_t i = 0; i < num_caps; i++) {
Rpc_obj_key const rpc_obj_key = rpc_obj_keys[i];
2015-05-11 08:43:43 +02:00
/*
* 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());
2015-05-11 08:43:43 +02:00
continue;
}
/*
* RPC object key as contained in the message data is valid.
*/
bool const unwrapped = caps_unwrapped & (1U << curr_sel4_cap_idx);
2015-05-11 08:43:43 +02:00
/* 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];
2015-05-11 08:43:43 +02:00
if (arg_badge != rpc_obj_key.value()) {
warning("argument badge (", arg_badge, ") != RPC object key (",
rpc_obj_key.value(), ")");
2015-05-11 08:43:43 +02:00
}
Native_capability arg_cap = Capability_space::lookup(rpc_obj_key);
dst_msg.insert(arg_cap);
2015-05-11 08:43:43 +02:00
} 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;
2015-05-11 08:43:43 +02:00
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());
2015-05-11 08:43:43 +02:00
dst_msg.insert(arg_cap);
2015-05-11 08:43:43 +02:00
} else {
Capability_space::Ipc_cap_data const
ipc_cap_data(rpc_obj_key, rcv_sel());
2015-05-11 08:43:43 +02:00
dst_msg.insert(Capability_space::import(ipc_cap_data));
2015-05-11 08:43:43 +02:00
/*
* 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();
2015-05-11 08:43:43 +02:00
}
}
curr_sel4_cap_idx++;
}
2014-10-15 14:48:45 +02:00
}
/****************
** IPC client **
2014-10-15 14:48:45 +02:00
****************/
Rpc_exception_code Genode::ipc_call(Native_capability dst,
Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg,
size_t)
2014-10-15 14:48:45 +02:00
{
if (!dst.valid()) {
error("Trying to invoke an invalid capability, stop.");
2015-05-11 08:43:43 +02:00
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();
2015-05-11 08:43:43 +02:00
unsigned const dst_sel = Capability_space::ipc_cap_data(dst).sel.value();
2015-05-11 08:43:43 +02:00
/**
* 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));
2015-05-11 08:43:43 +02:00
decode_seL4_message(reply_msg_info, rcv_msg);
2014-10-15 14:48:45 +02:00
return exc_code;
}
2014-10-15 14:48:45 +02:00
/****************
** IPC server **
2014-10-15 14:48:45 +02:00
****************/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
void Genode::ipc_reply(Native_capability, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
2014-10-15 14:48:45 +02:00
{
/* 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.
*/
2016-06-29 16:23:52 +02:00
/* 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);
2014-10-15 14:48:45 +02:00
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg)
2014-10-15 14:48:45 +02:00
{
/* 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;
2015-05-11 08:43:43 +02:00
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);
2015-05-11 08:43:43 +02:00
decode_seL4_message(req, request_msg);
2015-05-11 08:43:43 +02:00
return Rpc_request(Native_capability(), badge);
2014-10-15 14:48:45 +02:00
}
Ipc_server::Ipc_server()
2014-10-15 14:48:45 +02:00
:
Native_capability(Capability_space::create_ep_cap(*Thread::myself()))
{ }
Ipc_server::~Ipc_server() { }