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
This commit is contained in:
Norman Feske
2016-06-15 15:04:54 +02:00
parent d71f0a9606
commit 88b358c5ef
173 changed files with 2614 additions and 1333 deletions

View File

@ -11,12 +11,12 @@
* under the terms of the GNU General Public License version 2.
*/
#include <base/cap_map.h>
/* Genode includes */
#include <base/printf.h>
/* base-nova specific include */
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/cap_map.h>
using namespace Genode;

View File

@ -0,0 +1,78 @@
/*
* \brief Capability lifetime management
* \author Norman Feske
* \date 2015-05-06
*/
/*
* Copyright (C) 2015 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.
*/
#include <base/printf.h>
/* base-internal includes */
#include <base/internal/capability_data.h>
/* NOVA includes */
#include <nova/cap_map.h>
#include <nova/capability_space.h>
using namespace Genode;
Native_capability::Native_capability()
{
*this = Capability_space::import(Capability_space::INVALID_INDEX);
}
void Native_capability::_inc()
{
Cap_index idx(cap_map()->find(local_name()));
idx.inc();
}
void Native_capability::_dec()
{
Cap_index idx(cap_map()->find(local_name()));
idx.dec();
}
long Native_capability::local_name() const
{
if (valid())
return Capability_space::crd(*this).base();
else
return Capability_space::INVALID_INDEX;
}
bool Native_capability::valid() const
{
return _data != nullptr;
}
Native_capability::Raw Native_capability::raw() const
{
return { 0, 0, 0, 0 };
}
void Native_capability::print(Genode::Output &out) const
{
using Genode::print;
print(out, "cap<");
if (_data) {
print(out, local_name());
} else {
print(out, "invalid");
}
print(out, ">");
}

View File

@ -19,6 +19,9 @@
/* base-internal includes */
#include <base/internal/ipc.h>
/* NOVA includes */
#include <nova/cap_map.h>
using namespace Genode;
@ -43,7 +46,8 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
rcv_window.rcv_wnd(log2_max);
}
Nova::Utcb &utcb = *(Nova::Utcb *)Thread::myself()->utcb();
Thread * const myself = Thread::myself();
Nova::Utcb &utcb = *(Nova::Utcb *)myself->utcb();
/* the protocol value is unused as the badge is delivered by the kernel */
if (!copy_msgbuf_to_utcb(utcb, snd_msg, 0)) {
@ -51,8 +55,15 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
throw Ipc_error();
}
/*
* Determine manually defined selector for receiving the call result.
* See the comment in 'base-nova/include/nova/native_thread.h'.
*/
addr_t const manual_rcv_sel = myself ? myself->native_thread().client_rcv_sel
: Receive_window::INVALID_INDEX;
/* if we can't setup receive window, die in order to recognize the issue */
if (!rcv_window.prepare_rcv_window(utcb, dst.rcv_window()))
if (!rcv_window.prepare_rcv_window(utcb, manual_rcv_sel))
/* printf doesn't work here since for IPC also rcv_prepare* is used */
nova_die();
@ -64,7 +75,7 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
utcb.set_msg_word(0);
/* track potentially received caps and invalidate unused caps slots */
rcv_window.post_ipc(utcb, dst.rcv_window());
rcv_window.post_ipc(utcb, manual_rcv_sel);
if (res != Nova::NOVA_OK)
return Rpc_exception_code(Rpc_exception_code::INVALID_OBJECT);
@ -75,3 +86,112 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
return Rpc_exception_code(copy_utcb_to_msgbuf(utcb, rcv_window, rcv_msg));
}
/********************
** Receive_window **
********************/
void Receive_window::rcv_pt_sel(Native_capability &cap)
{
if (_rcv_pt_sel_cnt >= _rcv_pt_sel_max) {
cap = Native_capability();
return;
}
/* return only received or translated caps */
cap = Capability_space::import(_rcv_pt_sel[_rcv_pt_sel_cnt++].sel);
}
bool Receive_window::rcv_invalid() const
{
return _rcv_pt_base == Capability_space::INVALID_INDEX;
}
bool Receive_window::rcv_cleanup(bool keep, unsigned short const new_max)
{
/* mark used mapped capabilities as used to prevent freeing */
bool reinit = false;
for (unsigned i = 0; i < _rcv_pt_sel_cnt; i++) {
if (!_rcv_pt_sel[i].del)
continue;
/* should never happen */
if (_rcv_pt_sel[i].sel < _rcv_pt_base ||
(_rcv_pt_sel[i].sel >= _rcv_pt_base + MAX_CAP_ARGS))
nova_die();
_rcv_pt_cap_free [_rcv_pt_sel[i].sel - _rcv_pt_base] = USED_CAP;
reinit = true;
}
/* if old receive window was smaller, we need to re-init */
for (unsigned i = 0; !reinit && i < new_max; i++)
if (_rcv_pt_cap_free[i] == FREE_INVALID)
reinit = true;
_rcv_pt_sel_cnt = 0;
_rcv_pt_sel_max = 0;
/* we can keep the cap selectors if none was used */
if (keep && !reinit) {
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
/* revoke received caps which are unused */
if (_rcv_pt_cap_free[i] == UNUSED_CAP)
Nova::revoke(Nova::Obj_crd(_rcv_pt_base + i, 0), true);
/* free rest of indexes if new_max is smaller then last window */
if (i >= new_max && _rcv_pt_cap_free[i] == FREE_SEL)
cap_map()->remove(_rcv_pt_base + i, 0, false);
}
return false;
}
/* decrease ref count if valid selector */
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
if (_rcv_pt_cap_free[i] == FREE_INVALID)
continue;
cap_map()->remove(_rcv_pt_base + i, 0, _rcv_pt_cap_free[i] != FREE_SEL);
}
return true;
}
bool Receive_window::prepare_rcv_window(Nova::Utcb &utcb, addr_t rcv_window)
{
/* open maximal translate window */
utcb.crd_xlt = Nova::Obj_crd(0, ~0UL);
/* use receive window if specified */
if (rcv_window != INVALID_INDEX) {
/* cleanup if receive window already used */
if (!rcv_invalid()) rcv_cleanup(false);
_rcv_pt_base = rcv_window;
/* open receive window */
utcb.crd_rcv = Nova::Obj_crd(_rcv_pt_base, _rcv_wnd_log2);
return true;
}
/* allocate receive window if necessary, otherwise use old one */
if (rcv_invalid() || rcv_cleanup(true, 1U << _rcv_wnd_log2))
{
_rcv_pt_base = cap_map()->insert(_rcv_wnd_log2);
if (_rcv_pt_base == INVALID_INDEX) {
/* no mappings can be received */
utcb.crd_rcv = Nova::Obj_crd();
return false;
}
}
/* open receive window */
utcb.crd_rcv = Nova::Obj_crd(_rcv_pt_base, _rcv_wnd_log2);
return true;
}

View File

@ -23,7 +23,6 @@
#include <base/internal/ipc.h>
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/util.h>
#include <nova/native_thread.h>
@ -42,7 +41,7 @@ Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
/* _ec_sel is invalid until thread gets started */
if (native_thread().ec_sel != Native_thread::INVALID_INDEX)
ec_cap = Native_capability(native_thread().ec_sel);
ec_cap = Capability_space::import(native_thread().ec_sel);
else
ec_cap = _thread_cap;
@ -127,7 +126,7 @@ void Rpc_entrypoint::_activation_entry()
Rpc_entrypoint &ep = *static_cast<Rpc_entrypoint *>(Thread::myself());
Nova::Utcb &utcb = *(Nova::Utcb *)Thread::myself()->utcb();
Receive_window &rcv_window = ep.native_thread().rcv_window;
Receive_window &rcv_window = ep.native_thread().server_rcv_window;
rcv_window.post_ipc(utcb);
/* handle ill-formed message */
@ -146,7 +145,7 @@ void Rpc_entrypoint::_activation_entry()
Rpc_exception_code exc = Rpc_exception_code(Rpc_exception_code::INVALID_OBJECT);
/* in case of a portal cleanup call we are done here - just reply */
if (ep._cap.local_name() == id_pt) {
if (ep._cap.local_name() == (long)id_pt) {
if (!rcv_window.prepare_rcv_window(utcb))
PWRN("out of capability selectors for handling server requests");
@ -224,12 +223,13 @@ Rpc_entrypoint::Rpc_entrypoint(Pd_session *pd_session, size_t stack_size,
Thread::start();
/* create cleanup portal */
_cap = _alloc_rpc_cap(_pd_session, Native_capability(native_thread().ec_sel),
_cap = _alloc_rpc_cap(_pd_session,
Capability_space::import(native_thread().ec_sel),
(addr_t)_activation_entry);
if (!_cap.valid())
throw Cpu_session::Thread_creation_failed();
Receive_window &rcv_window = Thread::native_thread().rcv_window;
Receive_window &rcv_window = Thread::native_thread().server_rcv_window;
/* prepare portal receive window of new thread */
if (!rcv_window.prepare_rcv_window(*(Nova::Utcb *)&_stack->utcb()))

View File

@ -16,6 +16,9 @@
#include <base/signal.h>
#include <base/trace/events.h>
/* NOVA includes */
#include <nova/syscalls.h>
using namespace Genode;

View File

@ -26,8 +26,8 @@
#include <base/internal/stack_area.h>
#include <base/internal/native_utcb.h>
/* base-nova includes */
#include <base/cap_map.h>
/* NOVA includes */
#include <nova/cap_map.h>
using namespace Genode;

View File

@ -29,6 +29,8 @@
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/util.h>
#include <nova/cap_map.h>
#include <nova/capability_space.h>
using namespace Genode;
@ -80,7 +82,8 @@ void Thread::_init_platform_thread(size_t weight, Type type)
if (type == MAIN || type == REINITIALIZED_MAIN) {
_thread_cap = env()->parent()->main_thread_cap();
Genode::Native_capability pager_cap(Nova::PT_SEL_MAIN_PAGER);
Genode::Native_capability pager_cap =
Capability_space::import(Nova::PT_SEL_MAIN_PAGER);
native_thread().exc_pt_sel = 0;
native_thread().ec_sel = Nova::PT_SEL_MAIN_EC;