253 lines
6.2 KiB
C++
253 lines
6.2 KiB
C++
/*
|
|
* \brief RM session implementation used by Noux processes
|
|
* \author Norman Feske
|
|
* \date 2012-02-22
|
|
*
|
|
* The custom RM implementation is used for recording all RM regions attached
|
|
* to the region-manager session. Using the recorded information, the address-
|
|
* space layout can then be replayed onto a new process created via fork.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2012 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.
|
|
*/
|
|
|
|
#ifndef _NOUX__RM_SESSION_COMPONENT_H_
|
|
#define _NOUX__RM_SESSION_COMPONENT_H_
|
|
|
|
/* Genode includes */
|
|
#include <rm_session/connection.h>
|
|
#include <base/rpc_server.h>
|
|
|
|
namespace Noux {
|
|
|
|
using namespace Genode;
|
|
|
|
class Rm_session_component : public Rpc_object<Rm_session>
|
|
{
|
|
private:
|
|
|
|
/**
|
|
* Record of an attached dataspace
|
|
*/
|
|
struct Region : List<Region>::Element
|
|
{
|
|
Dataspace_capability ds;
|
|
size_t size;
|
|
off_t offset;
|
|
addr_t local_addr;
|
|
|
|
Region(Dataspace_capability ds, size_t size,
|
|
off_t offset, addr_t local_addr)
|
|
:
|
|
ds(ds), size(size), offset(offset), local_addr(local_addr)
|
|
{ }
|
|
|
|
/**
|
|
* Return true if region contains specified address
|
|
*/
|
|
bool contains(addr_t addr) const
|
|
{
|
|
return (addr >= local_addr)
|
|
&& (addr < local_addr + size);
|
|
}
|
|
};
|
|
|
|
List<Region> _regions;
|
|
|
|
Region *_lookup_region_by_addr(addr_t local_addr)
|
|
{
|
|
Region *curr = _regions.first();
|
|
for (; curr; curr = curr->next()) {
|
|
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)
|
|
:
|
|
_rm(start, size), _ds_registry(ds_registry)
|
|
{ }
|
|
|
|
~Rm_session_component()
|
|
{
|
|
Region *curr = 0;
|
|
for (; curr; curr = curr->next())
|
|
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
|
|
*/
|
|
void replay(Ram_session_capability dst_ram,
|
|
Rm_session_capability dst_rm,
|
|
Dataspace_registry &ds_registry,
|
|
Rpc_entrypoint &ep)
|
|
{
|
|
for (Region *curr = _regions.first(); curr; curr = curr->next()) {
|
|
|
|
Dataspace_capability ds;
|
|
|
|
Dataspace_info *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 {
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
void poke(addr_t dst_addr, void const *src, size_t len)
|
|
{
|
|
Region *region = _lookup_region_by_addr(dst_addr);
|
|
if (!region) {
|
|
PERR("poke: no region at 0x%lx", dst_addr);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Test if start and end address occupied by the object
|
|
* type refers to the same region.
|
|
*/
|
|
if (region != _lookup_region_by_addr(dst_addr + len - 1)) {
|
|
PERR("attempt to write beyond region boundary");
|
|
return;
|
|
}
|
|
|
|
Dataspace_info *info = _ds_registry.lookup_info(region->ds);
|
|
if (!info) {
|
|
PERR("attempt to write to unknown dataspace type");
|
|
for (;;);
|
|
return;
|
|
}
|
|
|
|
if (region->offset) {
|
|
PERR("poke: writing to region with offset is not supported");
|
|
return;
|
|
}
|
|
|
|
info->poke(dst_addr - region->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)
|
|
{
|
|
if (size == 0)
|
|
size = Dataspace_client(ds).size();
|
|
|
|
/*
|
|
* XXX look if we can identify the specified dataspace.
|
|
* Is it a dataspace allocated via 'Local_ram_session'?
|
|
*/
|
|
|
|
local_addr = _rm.attach(ds, size, offset,
|
|
use_local_addr, local_addr,
|
|
executable);
|
|
|
|
/*
|
|
* Record attachement for later replay (needed during
|
|
* fork)
|
|
*/
|
|
_regions.insert(new (Genode::env()->heap())
|
|
Region(ds, size, offset, local_addr));
|
|
return local_addr;
|
|
}
|
|
|
|
void detach(Local_addr local_addr)
|
|
{
|
|
_rm.detach(local_addr);
|
|
|
|
Region *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);
|
|
destroy(Genode::env()->heap(), region);
|
|
}
|
|
|
|
Pager_capability add_client(Thread_capability thread)
|
|
{
|
|
return _rm.add_client(thread);
|
|
}
|
|
|
|
void fault_handler(Signal_context_capability handler)
|
|
{
|
|
return _rm.fault_handler(handler);
|
|
}
|
|
|
|
State state()
|
|
{
|
|
return _rm.state();
|
|
}
|
|
|
|
Dataspace_capability dataspace()
|
|
{
|
|
return _rm.dataspace();
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif /* _NOUX__RM_SESSION_COMPONENT_H_ */
|