genode/repos/os/src/server/loader/rom.h

245 lines
4.9 KiB
C++

/*
* \brief ROM service exposed to the loaded subsystem
* \author Norman Feske
* \date 2012-04-17
*/
#ifndef _ROM_H_
#define _ROM_H_
//#include <util/string.h>
#include <rom_session/rom_session.h>
#include <base/rpc_server.h>
#include <os/attached_ram_dataspace.h>
namespace Genode {
class Rom_module : public List<Rom_module>::Element
{
private:
enum { MAX_NAME_LEN = 64 };
char _name[MAX_NAME_LEN];
Ram_session &_ram;
Attached_ram_dataspace _fg;
Attached_ram_dataspace _bg;
bool _bg_has_pending_data;
Signal_context_capability _sigh;
Lock _lock;
public:
Rom_module(char const *name, Ram_session &ram_session)
:
_ram(ram_session),
_fg(&_ram, 0), _bg(&_ram, 0),
_bg_has_pending_data(false),
_lock(Lock::LOCKED)
{
strncpy(_name, name, sizeof(_name));
}
bool has_name(char const *name) const
{
return strcmp(_name, name) == 0;
}
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
/**
* 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 (!_fg.size() && !_bg_has_pending_data) {
PERR("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) { _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:
Lock _lock;
Ram_session &_ram_session;
Allocator &_md_alloc;
List<Rom_module> _list;
public:
/**
* Exception type
*/
class Lookup_failed { };
/**
* Constructor
*
* \param ram_session RAM session used as backing store for
* module data
* \param md_alloc backing store for ROM module meta data
*/
Rom_module_registry(Ram_session &ram_session, Allocator &md_alloc)
:
_ram_session(ram_session), _md_alloc(md_alloc)
{ }
~Rom_module_registry()
{
Lock::Guard guard(_lock);
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(char const *name)
{
Lock::Guard guard(_lock);
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(char 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) {
Lock::Guard guard(_lock);
Rom_module *module = new (&_md_alloc)
Rom_module(name, _ram_session);
Rom_module_lock_guard module_guard(*module);
_list.insert(module);
return module->bg_dataspace(size);
}
}
/**
* \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>,
public List<Rom_session_component>::Element
{
private:
Rom_module &_rom_module;
public:
Rom_session_component(Rom_module &rom_module)
: _rom_module(rom_module) { }
Rom_dataspace_capability dataspace()
{
Rom_module_lock_guard guard(_rom_module);
return _rom_module.fg_dataspace();
}
void sigh(Signal_context_capability sigh)
{
Rom_module_lock_guard guard(_rom_module);
_rom_module.sigh(sigh);
}
};
}
#endif /* _ROM_H_ */