noux: Noux_connection::context_area_rm_session()

This enables a forked process to update the capability of its
context area RM session.

ref #989
This commit is contained in:
Martin Stein 2014-01-28 14:07:36 +01:00 committed by Norman Feske
parent f7149623ca
commit c2af646ad8
7 changed files with 334 additions and 250 deletions

View File

@ -48,6 +48,11 @@ namespace Noux {
{
return call<Rpc_next_open_fd>(start_fd);
}
Rm_session_capability lookup_rm_session(addr_t const addr)
{
return call<Rpc_lookup_rm_session>(addr);
}
};
}

View File

@ -17,6 +17,7 @@
#include <base/stdint.h>
#include <session/session.h>
#include <dataspace/capability.h>
#include <rm_session/capability.h>
#define NOUX_DECL_SYSCALL_NAME(name) \
case SYSCALL_##name: return #name;
@ -33,6 +34,13 @@ namespace Noux {
virtual Dataspace_capability sysio_dataspace() = 0;
/**
* Return leaf RM session that covers a given address
*
* \param addr address that is covered by the requested RM session
*/
virtual Rm_session_capability lookup_rm_session(addr_t const addr) = 0;
enum Syscall {
SYSCALL_WRITE,
SYSCALL_READ,
@ -154,10 +162,13 @@ namespace Noux {
*********************/
GENODE_RPC(Rpc_sysio_dataspace, Dataspace_capability, sysio_dataspace);
GENODE_RPC(Rpc_lookup_rm_session, Rm_session_capability,
lookup_rm_session, addr_t);
GENODE_RPC(Rpc_syscall, bool, syscall, Syscall);
GENODE_RPC(Rpc_next_open_fd, int, next_open_fd, int);
GENODE_RPC_INTERFACE(Rpc_sysio_dataspace, Rpc_syscall, Rpc_next_open_fd);
GENODE_RPC_INTERFACE(Rpc_sysio_dataspace, Rpc_lookup_rm_session,
Rpc_syscall, Rpc_next_open_fd);
};
}

View File

@ -86,6 +86,16 @@ class Noux_connection
_sysio = _obtain_sysio();
}
/**
* Return the capability of the local context-area RM session
*/
Genode::Rm_session_capability context_area_rm_session()
{
using namespace Genode;
addr_t const addr = Native_config::context_area_virtual_base();
return _connection.lookup_rm_session(addr);
}
Noux::Session *session() { return &_connection; }
Noux::Sysio *sysio() { return _sysio; }
};

View File

@ -425,6 +425,11 @@ namespace Noux {
return _sysio_ds.cap();
}
Rm_session_capability lookup_rm_session(addr_t const addr)
{
return _resources.rm.lookup_rm_session(addr);
}
bool syscall(Syscall sc);
int next_open_fd(int start_fd)

View File

@ -103,6 +103,17 @@ namespace Noux {
* \param len length of source buffer in bytes
*/
virtual void poke(addr_t dst_offset, void const *src, size_t len) = 0;
/**
* Return leaf RM session that covers a given address
*
* \param addr address that is covered by the requested RM session
*/
virtual Rm_session_capability lookup_rm_session(addr_t const addr)
{
/* by default a dataspace is no sub RM, so return invalid */
return Rm_session_capability();
}
};

View File

@ -85,6 +85,12 @@ namespace Noux {
}
_sub_rm->poke(dst_offset, src, len);
}
Rm_session_capability lookup_rm_session(addr_t const addr)
{
/* the dataspace is a sub RM, so traverse into it */
return _sub_rm->lookup_rm_session(addr);
}
};

View File

@ -1,6 +1,7 @@
/*
* \brief RM session implementation used by Noux processes
* \author Norman Feske
* \author Martin Stein
* \date 2012-02-22
*
* The custom RM implementation is used for recording all RM regions attached
@ -22,298 +23,333 @@
#include <rm_session/connection.h>
#include <base/rpc_server.h>
namespace Noux {
namespace Noux
{
static bool verbose_attach = false;
class Rm_session_component : public Rpc_object<Rm_session>
{
private:
/**
* Server sided back-end of an RM session of a Noux process
*/
class Rm_session_component;
}
/**
* Record of an attached dataspace
*/
struct Region : List<Region>::Element, Dataspace_user
{
Rm_session_component &rm;
Dataspace_capability ds;
size_t size;
off_t offset;
addr_t local_addr;
class Noux::Rm_session_component : public Rpc_object<Rm_session>
{
private:
Region(Rm_session_component &rm,
Dataspace_capability ds, size_t size,
off_t offset, addr_t local_addr)
:
rm(rm), ds(ds), size(size), offset(offset),
local_addr(local_addr)
{ }
/**
* Record of an attached dataspace
*/
struct Region : List<Region>::Element, Dataspace_user
{
Rm_session_component &rm;
Dataspace_capability ds;
size_t size;
off_t offset;
addr_t local_addr;
/**
* Return true if region contains specified address
*/
bool contains(addr_t addr) const
{
return (addr >= local_addr)
&& (addr < local_addr + size);
}
Region *next_region()
{
return List<Region>::Element::next();
}
inline void dissolve(Dataspace_info &ds);
};
Lock _region_lock;
List<Region> _regions;
Region *_lookup_region_by_addr(addr_t local_addr)
{
Region *curr = _regions.first();
for (; curr; curr = curr->next_region()) {
if (curr->contains(local_addr))
return curr;
}
return 0;
}
/**
* Wrapped RM session at core
*/
Rm_connection _rm;
Dataspace_registry &_ds_registry;
public:
Rm_session_component(Dataspace_registry &ds_registry,
addr_t start = ~0UL, size_t size = 0)
Region(Rm_session_component &rm,
Dataspace_capability ds, size_t size,
off_t offset, addr_t local_addr)
:
_rm(start, size), _ds_registry(ds_registry)
rm(rm), ds(ds), size(size), offset(offset),
local_addr(local_addr)
{ }
~Rm_session_component()
{
Region *curr;
while ((curr = _regions.first()))
detach(curr->local_addr);
}
/**
* Replay attachments onto specified RM session
*
* \param dst_ram backing store used for allocating the
* the copies of RAM dataspaces
* \param ds_registry dataspace registry used for keeping track
* of newly created dataspaces
* \param ep entrypoint used to serve the RPC interface
* of forked managed dataspaces
* Return true if region contains specified address
*/
void replay(Ram_session_capability dst_ram,
Rm_session_capability dst_rm,
Dataspace_registry &ds_registry,
Rpc_entrypoint &ep)
bool contains(addr_t addr) const
{
Lock::Guard guard(_region_lock);
for (Region *curr = _regions.first(); curr; curr = curr->next_region()) {
Dataspace_capability ds;
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(curr->ds));
if (info) {
ds = info->fork(dst_ram, ds_registry, ep);
/*
* XXX We could detect dataspaces that are attached
* more than once. For now, we create a new fork
* for each attachment.
*/
} else {
PERR("replay: missing ds_info for dataspace at addr 0x%lx",
curr->local_addr);
/*
* If the dataspace is not a RAM dataspace, assume that
* it's a ROM dataspace.
*
* XXX Handle ROM dataspaces explicitly. For once, we
* need to make sure that they remain available
* until the child process exits even if the parent
* process exits earlier. Furthermore, we would
* like to detect unexpected dataspaces.
*/
ds = curr->ds;
}
if (!ds.valid()) {
PERR("replay: Error while forking dataspace");
continue;
}
Rm_session_client(dst_rm).attach(ds, curr->size,
curr->offset,
true,
curr->local_addr);
}
return (addr >= local_addr) && (addr < local_addr + size);
}
void poke(addr_t dst_addr, void const *src, size_t len)
Region *next_region()
{
Dataspace_capability ds_cap;
addr_t local_addr;
return List<Region>::Element::next();
}
{
Lock::Guard guard(_region_lock);
inline void dissolve(Dataspace_info &ds);
};
Region *region = _lookup_region_by_addr(dst_addr);
if (!region) {
PERR("poke: no region at 0x%lx", dst_addr);
return;
}
Lock _region_lock;
List<Region> _regions;
Region *_lookup_region_by_addr(addr_t local_addr)
{
Region *curr = _regions.first();
for (; curr; curr = curr->next_region()) {
if (curr->contains(local_addr))
return curr;
}
return 0;
}
/**
* Wrapped RM session at core
*/
Rm_connection _rm;
Dataspace_registry &_ds_registry;
public:
/**
* Constructor
*/
Rm_session_component(Dataspace_registry & ds_registry,
addr_t start = ~0UL, size_t size = 0)
:
_rm(start, size), _ds_registry(ds_registry)
{ }
/**
* Destructor
*/
~Rm_session_component()
{
Region *curr;
while ((curr = _regions.first()))
detach(curr->local_addr);
}
/**
* Return leaf RM session that covers a given address
*
* \param addr address that is covered by the requested RM session
*/
Rm_session_capability lookup_rm_session(addr_t const addr)
{
/* if there's no region that could be a sub RM then we're a leaf */
Region * const region = _lookup_region_by_addr(addr);
if (!region) { return cap(); }
/* if there is no info for the region it can't be a sub RM */
Dataspace_capability ds_cap = region->ds;
typedef Object_pool<Dataspace_info>::Guard Info_guard;
Info_guard info(_ds_registry.lookup_info(ds_cap));
if (!info) { return cap(); }
/* ask the dataspace info for an appropriate sub RM */
addr_t const region_base = region->local_addr;
addr_t const region_off = region->offset;
addr_t const sub_addr = addr - region_base + region_off;
Rm_session_capability sub_rm = info->lookup_rm_session(sub_addr);
/* if the result is invalid the dataspace is no sub RM */
if (!sub_rm.valid()) { return cap(); }
return sub_rm;
}
/**
* Replay attachments onto specified RM session
*
* \param dst_ram backing store used for allocating the
* the copies of RAM dataspaces
* \param ds_registry dataspace registry used for keeping track
* of newly created dataspaces
* \param ep entrypoint used to serve the RPC interface
* of forked managed dataspaces
*/
void replay(Ram_session_capability dst_ram,
Rm_session_capability dst_rm,
Dataspace_registry &ds_registry,
Rpc_entrypoint &ep)
{
Lock::Guard guard(_region_lock);
for (Region *curr = _regions.first(); curr; curr = curr->next_region()) {
Dataspace_capability ds;
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(curr->ds));
if (info) {
ds = info->fork(dst_ram, ds_registry, ep);
/*
* Test if start and end address occupied by the object
* type refers to the same region.
* XXX We could detect dataspaces that are attached
* more than once. For now, we create a new fork
* for each attachment.
*/
if (region != _lookup_region_by_addr(dst_addr + len - 1)) {
PERR("attempt to write beyond region boundary");
return;
}
if (region->offset) {
PERR("poke: writing to region with offset is not supported");
return;
}
} else {
ds_cap = region->ds;
local_addr = region->local_addr;
PERR("replay: missing ds_info for dataspace at addr 0x%lx",
curr->local_addr);
/*
* If the dataspace is not a RAM dataspace, assume that
* it's a ROM dataspace.
*
* XXX Handle ROM dataspaces explicitly. For once, we
* need to make sure that they remain available
* until the child process exits even if the parent
* process exits earlier. Furthermore, we would
* like to detect unexpected dataspaces.
*/
ds = curr->ds;
}
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(ds_cap));
if (!info) {
PERR("attempt to write to unknown dataspace type");
for (;;);
if (!ds.valid()) {
PERR("replay: Error while forking dataspace");
continue;
}
Rm_session_client(dst_rm).attach(ds, curr->size,
curr->offset,
true,
curr->local_addr);
}
}
void poke(addr_t dst_addr, void const *src, size_t len)
{
Dataspace_capability ds_cap;
addr_t local_addr;
{
Lock::Guard guard(_region_lock);
Region *region = _lookup_region_by_addr(dst_addr);
if (!region) {
PERR("poke: no region at 0x%lx", dst_addr);
return;
}
info->poke(dst_addr - local_addr, src, len);
}
/**************************
** RM session interface **
**************************/
Local_addr attach(Dataspace_capability ds,
size_t size = 0, off_t offset = 0,
bool use_local_addr = false,
Local_addr local_addr = (addr_t)0,
bool executable = false)
{
/*
* Rm_session subtracts offset from size if size is 0
* Test if start and end address occupied by the object
* type refers to the same region.
*/
if (size == 0)
size = Dataspace_client(ds).size() - offset;
local_addr = _rm.attach(ds, size, offset,
use_local_addr, local_addr,
executable);
Region *region = new (env()->heap())
Region(*this, ds, size, offset, local_addr);
/* register region as user of RAM dataspaces */
{
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(ds));
if (info) {
info->register_user(*region);
} else {
if (verbose_attach) {
PWRN("Trying to attach unknown dataspace type");
PWRN(" ds_info@%p at 0x%lx size=%zd offset=0x%lx",
info.object(), (long)local_addr,
Dataspace_client(ds).size(), (long)offset);
}
}
if (region != _lookup_region_by_addr(dst_addr + len - 1)) {
PERR("attempt to write beyond region boundary");
return;
}
/*
* Record attachment for later replay (needed during
* fork)
*/
if (region->offset) {
PERR("poke: writing to region with offset is not supported");
return;
}
ds_cap = region->ds;
local_addr = region->local_addr;
}
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(ds_cap));
if (!info) {
PERR("attempt to write to unknown dataspace type");
for (;;);
return;
}
info->poke(dst_addr - local_addr, src, len);
}
/**************************
** RM session interface **
**************************/
Local_addr attach(Dataspace_capability ds,
size_t size = 0, off_t offset = 0,
bool use_local_addr = false,
Local_addr local_addr = (addr_t)0,
bool executable = false)
{
/*
* Rm_session subtracts offset from size if size is 0
*/
if (size == 0) size = Dataspace_client(ds).size() - offset;
local_addr = _rm.attach(ds, size, offset, use_local_addr,
local_addr, executable);
Region * region = new (env()->heap())
Region(*this, ds, size, offset, local_addr);
/* register region as user of RAM dataspaces */
{
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(ds));
if (info) {
info->register_user(*region);
} else {
if (verbose_attach) {
PWRN("Trying to attach unknown dataspace type");
PWRN(" ds_info@%p at 0x%lx size=%zd offset=0x%lx",
info.object(), (long)local_addr,
Dataspace_client(ds).size(), (long)offset);
}
}
}
/*
* Record attachment for later replay (needed during
* fork)
*/
Lock::Guard guard(_region_lock);
_regions.insert(region);
return local_addr;
}
void detach(Local_addr local_addr)
{
Region * region = 0;
{
Lock::Guard guard(_region_lock);
_regions.insert(region);
return local_addr;
}
void detach(Local_addr local_addr)
{
Region * region = 0;
{
Lock::Guard guard(_region_lock);
region = _lookup_region_by_addr(local_addr);
if (!region) {
PWRN("Attempt to detach unknown region at 0x%p",
(void *)local_addr);
return;
}
_regions.remove(region);
region = _lookup_region_by_addr(local_addr);
if (!region) {
PWRN("Attempt to detach unknown region at 0x%p",
(void *)local_addr);
return;
}
{
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(region->ds));
if (info)
info->unregister_user(*region);
}
destroy(env()->heap(), region);
_rm.detach(local_addr);
_regions.remove(region);
}
Pager_capability add_client(Thread_capability thread)
{
return _rm.add_client(thread);
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(region->ds));
if (info) info->unregister_user(*region);
}
void remove_client(Pager_capability pager)
{
_rm.remove_client(pager);
}
destroy(env()->heap(), region);
void fault_handler(Signal_context_capability handler)
{
return _rm.fault_handler(handler);
}
_rm.detach(local_addr);
State state()
{
return _rm.state();
}
}
Dataspace_capability dataspace()
{
return _rm.dataspace();
}
};
Pager_capability add_client(Thread_capability thread)
{
return _rm.add_client(thread);
}
void remove_client(Pager_capability pager)
{
_rm.remove_client(pager);
}
void fault_handler(Signal_context_capability handler)
{
return _rm.fault_handler(handler);
}
State state()
{
return _rm.state();
}
Dataspace_capability dataspace()
{
return _rm.dataspace();
}
};
inline void Rm_session_component::Region::dissolve(Dataspace_info &ds)
{
rm.detach(local_addr);
}
inline void Noux::Rm_session_component::Region::dissolve(Dataspace_info &ds)
{
rm.detach(local_addr);
}
#endif /* _NOUX__RM_SESSION_COMPONENT_H_ */