302 lines
6.5 KiB
C++
302 lines
6.5 KiB
C++
/*
|
|
* \brief ROM service exposed to the loaded subsystem
|
|
* \author Norman Feske
|
|
* \date 2012-04-17
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2012-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 _ROM_H_
|
|
#define _ROM_H_
|
|
|
|
#include <rom_session/rom_session.h>
|
|
#include <base/rpc_server.h>
|
|
#include <base/attached_ram_dataspace.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
|
|
namespace Genode {
|
|
|
|
class Rom_module : public List<Rom_module>::Element
|
|
{
|
|
public:
|
|
|
|
typedef String<128> Name;
|
|
|
|
private:
|
|
|
|
Name const _name;
|
|
|
|
Ram_allocator &_ram;
|
|
Attached_ram_dataspace _fg;
|
|
Attached_ram_dataspace _bg;
|
|
|
|
Constructible<Attached_rom_dataspace> _parent_rom { };
|
|
|
|
bool _bg_has_pending_data = false;
|
|
|
|
Signal_context_capability _sigh { };
|
|
|
|
Blockade _blockade { };
|
|
|
|
public:
|
|
|
|
enum Origin { PARENT_PROVIDED, SESSION_LOCAL };
|
|
|
|
Rom_module(Env &env, Xml_node /* config */, Name const &name,
|
|
Ram_allocator &ram_allocator, Origin origin)
|
|
:
|
|
_name(name), _ram(ram_allocator),
|
|
_fg(_ram, env.rm(), 0), _bg(_ram, env.rm(), 0)
|
|
{
|
|
if (origin == SESSION_LOCAL)
|
|
return;
|
|
|
|
try {
|
|
_parent_rom.construct(env, name.string()); }
|
|
catch (...) {
|
|
warning("ROM ", name, " unavailable from parent, "
|
|
"try to use session-local ROM");
|
|
}
|
|
}
|
|
|
|
bool has_name(Name const &name) const { return _name == name; }
|
|
|
|
void lock() { _blockade.block(); }
|
|
void unlock() { _blockade.wakeup(); }
|
|
|
|
/**
|
|
* Return dataspace as handed out to loader session
|
|
*
|
|
* The loader session submits the data into the ROM module into
|
|
* the background dataspace. Once it is ready, the 'commit_bg()'
|
|
* function is called.
|
|
*/
|
|
Dataspace_capability bg_dataspace(size_t size)
|
|
{
|
|
/* let background buffer grow if needed */
|
|
if (_bg.size() < size)
|
|
_bg.realloc(&_ram, size);
|
|
|
|
return _bg.cap();
|
|
}
|
|
|
|
/**
|
|
* Return dataspace as handed out to ROM session client
|
|
*/
|
|
Rom_dataspace_capability fg_dataspace()
|
|
{
|
|
if (_parent_rom.constructed())
|
|
return static_cap_cast<Rom_dataspace>(_parent_rom->cap());
|
|
|
|
if (!_fg.size() && !_bg_has_pending_data) {
|
|
Genode::error("no data loaded");
|
|
return Rom_dataspace_capability();
|
|
}
|
|
|
|
/*
|
|
* Keep foreground if no background exists. Otherwise, use
|
|
* old background as new foreground.
|
|
*/
|
|
if (_bg_has_pending_data) {
|
|
_fg.swap(_bg);
|
|
_bg_has_pending_data = false;
|
|
}
|
|
|
|
Dataspace_capability ds_cap = _fg.cap();
|
|
return static_cap_cast<Rom_dataspace>(ds_cap);
|
|
}
|
|
|
|
/**
|
|
* Set signal handler to inform about ROM module updates
|
|
*
|
|
* This function is indirectly called by the ROM session client.
|
|
*/
|
|
void sigh(Signal_context_capability sigh)
|
|
{
|
|
if (_parent_rom.constructed())
|
|
_parent_rom->sigh(sigh);
|
|
|
|
_sigh = sigh;
|
|
}
|
|
|
|
/**
|
|
* Commit data contained in background dataspace
|
|
*
|
|
* This function is indirectly called by the loader session.
|
|
*/
|
|
void commit_bg()
|
|
{
|
|
_bg_has_pending_data = true;
|
|
|
|
if (_sigh.valid())
|
|
Signal_transmitter(_sigh).submit();
|
|
}
|
|
};
|
|
|
|
|
|
struct Rom_module_lock_guard
|
|
{
|
|
Rom_module &rom;
|
|
|
|
Rom_module_lock_guard(Rom_module &rom) : rom(rom) { }
|
|
|
|
~Rom_module_lock_guard() { rom.unlock(); }
|
|
};
|
|
|
|
|
|
class Rom_module_registry
|
|
{
|
|
private:
|
|
|
|
Env &_env;
|
|
Xml_node const _config;
|
|
Mutex _mutex { };
|
|
Ram_allocator &_ram_allocator;
|
|
Allocator &_md_alloc;
|
|
List<Rom_module> _list { };
|
|
|
|
public:
|
|
|
|
/**
|
|
* Exception type
|
|
*/
|
|
class Lookup_failed { };
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param ram_allocator RAM allocator used as backing store for
|
|
* module data
|
|
* \param md_alloc backing store for ROM module meta data
|
|
*/
|
|
Rom_module_registry(Env &env, Xml_node config,
|
|
Ram_allocator &ram_allocator,
|
|
Allocator &md_alloc)
|
|
:
|
|
_env(env), _config(config), _ram_allocator(ram_allocator),
|
|
_md_alloc(md_alloc)
|
|
{ }
|
|
|
|
~Rom_module_registry()
|
|
{
|
|
Mutex::Guard guard(_mutex);
|
|
|
|
while (_list.first()) {
|
|
Rom_module *rom = _list.first();
|
|
rom->lock();
|
|
_list.remove(rom);
|
|
destroy(&_md_alloc, rom);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lookup ROM module by name
|
|
*
|
|
* \throw Lookup_failed
|
|
*/
|
|
Rom_module &lookup_and_lock(Rom_module::Name const &name)
|
|
{
|
|
Mutex::Guard guard(_mutex);
|
|
|
|
Rom_module *curr = _list.first();
|
|
for (; curr; curr = curr->next()) {
|
|
if (curr->has_name(name)) {
|
|
curr->lock();
|
|
return *curr;
|
|
}
|
|
}
|
|
throw Lookup_failed();
|
|
}
|
|
|
|
Dataspace_capability alloc_rom_module(Rom_module::Name const &name, size_t size)
|
|
{
|
|
try {
|
|
Rom_module &module = lookup_and_lock(name);
|
|
Rom_module_lock_guard guard(module);
|
|
return module.bg_dataspace(size);
|
|
}
|
|
catch (Lookup_failed) {
|
|
|
|
Mutex::Guard guard(_mutex);
|
|
|
|
Rom_module *module = new (&_md_alloc)
|
|
Rom_module(_env, _config, name, _ram_allocator,
|
|
Rom_module::SESSION_LOCAL);
|
|
|
|
Rom_module_lock_guard module_guard(*module);
|
|
|
|
_list.insert(module);
|
|
|
|
return module->bg_dataspace(size);
|
|
}
|
|
}
|
|
|
|
void fetch_parent_rom_module(Rom_module::Name const &name)
|
|
{
|
|
try {
|
|
lookup_and_lock(name);
|
|
}
|
|
catch (Lookup_failed) {
|
|
|
|
Mutex::Guard guard(_mutex);
|
|
|
|
Rom_module *module = new (&_md_alloc)
|
|
Rom_module(_env, _config, name, _ram_allocator,
|
|
Rom_module::PARENT_PROVIDED);
|
|
|
|
Rom_module_lock_guard module_guard(*module);
|
|
|
|
_list.insert(module);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \throw Lookup_failed
|
|
*/
|
|
void commit_rom_module(char const *name)
|
|
{
|
|
Rom_module &rom_module = lookup_and_lock(name);
|
|
rom_module.commit_bg();
|
|
rom_module.unlock();
|
|
}
|
|
};
|
|
|
|
|
|
class Rom_session_component : public Rpc_object<Rom_session>,
|
|
private List<Rom_session_component>::Element
|
|
{
|
|
private:
|
|
|
|
friend class List<Rom_session_component>;
|
|
|
|
Entrypoint &_ep;
|
|
Rom_module &_rom_module;
|
|
|
|
public:
|
|
|
|
Rom_session_component(Entrypoint &ep, Rom_module &rom_module)
|
|
: _ep(ep), _rom_module(rom_module) { _ep.manage(*this); }
|
|
|
|
~Rom_session_component() { _ep.dissolve(*this); }
|
|
|
|
Rom_dataspace_capability dataspace() override
|
|
{
|
|
Rom_module_lock_guard guard(_rom_module);
|
|
return _rom_module.fg_dataspace();
|
|
}
|
|
|
|
void sigh(Signal_context_capability sigh) override
|
|
{
|
|
Rom_module_lock_guard guard(_rom_module);
|
|
_rom_module.sigh(sigh);
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif /* _ROM_H_ */
|