genode/repos/base/src/include/base/internal/capability_space_tpl.h
2019-01-07 12:25:44 +01:00

211 lines
5.2 KiB
C++

/*
* \brief Capability-space management for traditional L4 kernels and Linux
* \author Norman Feske
* \date 2016-06-15
*
* On traditional L4 kernels, a capability is represented by the thread ID
* of the invoked entrypoint thread and a globally unique RPC object key.
* On Linux, a capability is represented by a socket descriptor and an RPC
* object key. The thread ID respectively socket descriptor refer to the
* recipient of an RPC call (RPC destination).
*/
/*
* Copyright (C) 2016-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.
*/
#ifndef _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_
#define _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_
/* base includes */
#include <util/avl_tree.h>
#include <util/bit_allocator.h>
#include <base/lock.h>
#include <base/log.h>
#include <util/construct_at.h>
/* base-internal includes */
#include <base/internal/capability_space.h>
#include <base/internal/rpc_destination.h>
namespace Genode { template <unsigned, typename> class Capability_space_tpl; }
/**
* Platform-specific supplement to the generic 'Capability_space' interface
*/
namespace Genode { namespace Capability_space {
/**
* Information needed to transfer capability via the kernel's IPC mechanism
*/
struct Ipc_cap_data
{
Rpc_destination dst;
Rpc_obj_key rpc_obj_key;
Ipc_cap_data(Rpc_destination dst, Rpc_obj_key rpc_obj_key)
: dst(dst), rpc_obj_key(rpc_obj_key) { }
void print(Output &out) const { Genode::print(out, dst, ",", rpc_obj_key); }
};
/**
* Retrieve IPC data for given capability
*/
Ipc_cap_data ipc_cap_data(Native_capability const &cap);
Native_capability lookup(Rpc_obj_key);
Native_capability import(Rpc_destination, Rpc_obj_key);
} }
/**
* Capability space template
*
* The capability space of core and non-core components differ in two ways.
*
* First, core must keep track of all capabilities of the system. Hence, its
* capability space must be dimensioned larger.
*
* Second, core has to maintain the information about the PD session that
* was used to allocate the capability to prevent misbehaving clients from
* freeing capabilities allocated from another component. This information
* is part of the core-specific 'Native_capability::Data' structure.
*/
template <unsigned NUM_CAPS, typename CAP_DATA>
class Genode::Capability_space_tpl
{
private:
typedef CAP_DATA Data;
/**
* Supplement Native_capability::Data with the meta data needed to
* manage it in an AVL tree
*/
struct Tree_managed_data : Data, Avl_node<Tree_managed_data>
{
template <typename... ARGS>
Tree_managed_data(ARGS... args) : Data(args...) { }
Tree_managed_data() { }
bool higher(Tree_managed_data *data)
{
return data->rpc_obj_key().value() > rpc_obj_key().value();
}
Tree_managed_data *find_by_key(Rpc_obj_key key)
{
if (key.value() == rpc_obj_key().value()) return this;
Tree_managed_data *data =
this->child(key.value() > rpc_obj_key().value());
return data ? data->find_by_key(key) : nullptr;
}
};
Tree_managed_data _caps_data[NUM_CAPS];
Bit_allocator<NUM_CAPS> _alloc { };
Avl_tree<Tree_managed_data> _tree { };
Lock mutable _lock { };
/**
* Calculate index into _caps_data for capability data object
*/
unsigned _index(Data const &data) const
{
addr_t const offset = (addr_t)&data - (addr_t)_caps_data;
return offset / sizeof(_caps_data[0]);
}
Data *_lookup(Rpc_obj_key key) const
{
Lock::Guard guard(_lock);
if (!_tree.first())
return nullptr;
return _tree.first()->find_by_key(key);
}
public:
/**
* Create Genode capability
*
* The arguments are passed to the constructor of the
* 'Native_capability::Data' type.
*/
template <typename... ARGS>
Native_capability::Data &create_capability(ARGS... args)
{
Lock::Guard guard(_lock);
addr_t const index = _alloc.alloc();
construct_at<Tree_managed_data>(&_caps_data[index], args...);
if (_caps_data[index].rpc_obj_key().valid())
_tree.insert(&_caps_data[index]);
return _caps_data[index];
}
void dec_ref(Data &data)
{
Lock::Guard guard(_lock);
if (data.dec_ref() == 0) {
if (data.rpc_obj_key().valid())
_tree.remove(static_cast<Tree_managed_data *>(&data));
_alloc.free(_index(data));
data = Tree_managed_data();
}
}
void inc_ref(Data &data)
{
Lock::Guard guard(_lock);
if (data.inc_ref() == 255)
throw Native_capability::Reference_count_overflow();
}
Rpc_obj_key rpc_obj_key(Data const &data) const
{
return data.rpc_obj_key();
}
void print(Output &out, Data const &data) const
{
ipc_cap_data(data).print(out);
}
Capability_space::Ipc_cap_data ipc_cap_data(Data const &data) const
{
return { data.dst, data.rpc_obj_key() };
}
Native_capability lookup(Rpc_obj_key rpc_obj_key)
{
Native_capability::Data *data = _lookup(rpc_obj_key);
return data ? Native_capability(data) : Native_capability();
}
Native_capability import(Rpc_destination dst, Rpc_obj_key key)
{
return Native_capability(&create_capability(dst, key));
}
};
#endif /* _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_ */