genode/repos/os/src/server/dynamic_rom/main.cc

269 lines
6.5 KiB
C++

/*
* \brief ROM server that changes ROM content driven by time
* \author Norman Feske
* \date 2014-01-10
*/
/*
* Copyright (C) 2014-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.
*/
/* Genode includes */
#include <util/reconstructible.h>
#include <util/arg_string.h>
#include <base/heap.h>
#include <base/component.h>
#include <base/session_label.h>
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <rom_session/rom_session.h>
#include <timer_session/connection.h>
#include <root/component.h>
namespace Dynamic_rom {
using Genode::Entrypoint;
using Genode::Rpc_object;
using Genode::Sliced_heap;
using Genode::Env;
using Genode::Constructible;
using Genode::Signal_context_capability;
using Genode::Signal_handler;
using Genode::Xml_node;
using Genode::Arg_string;
class Session_component;
class Root;
struct Main;
}
class Dynamic_rom::Session_component : public Rpc_object<Genode::Rom_session>
{
private:
Env &_env;
bool &_verbose;
Xml_node _rom_node;
Timer::Connection _timer;
unsigned _curr_idx = 0;
bool _has_content = false;
unsigned _last_content_idx = ~0;
Signal_context_capability _sigh { };
Constructible<Genode::Attached_ram_dataspace> _ram_ds { };
void _notify_client()
{
if (!_sigh.valid())
return;
Genode::Signal_transmitter(_sigh).submit();
}
/**
* Print message prefixed with ROM name
*/
template <typename... ARGS>
void _log(ARGS&&... args)
{
if (!_verbose)
return;
typedef Genode::String<160> Name;
Genode::log(_rom_node.attribute_value("name", Name()), ": ", args...);
}
enum Execution_state { EXEC_CONTINUE, EXEC_BLOCK };
Execution_state _execute_step(Xml_node curr_step)
{
/*
* Replace content of ROM module by new one. Note that the content
* of the currently handed out dataspace remains untouched until
* the ROM client requests the new version by calling 'dataspace'
* the next time.
*/
if (curr_step.has_type("inline")) {
_last_content_idx = _curr_idx;
_has_content = true;
_notify_client();
if (curr_step.has_attribute("description")) {
typedef Genode::String<200> Desc;
Desc desc = curr_step.attribute_value("description", Desc());
_log("change (", desc.string(), ")");
} else {
_log("change");
}
}
/*
* Remove ROM module
*/
if (curr_step.has_type("empty")) {
_last_content_idx = ~0;;
_has_content = false;
_notify_client();
_log("remove");
}
/*
* Sleep some time
*
* The timer will trigger the execution of the next step.
*/
if (curr_step.has_type("sleep")
&& curr_step.has_attribute("milliseconds")) {
Genode::uint64_t const milliseconds =
curr_step.attribute_value("milliseconds", (Genode::uint64_t)0);
_timer.trigger_once(milliseconds*1000);
_log("sleep ", milliseconds, " milliseconds");
return EXEC_BLOCK;
}
return EXEC_CONTINUE;
}
void _execute_steps_until_sleep()
{
Execution_state exec_state = EXEC_CONTINUE;
while (exec_state == EXEC_CONTINUE) {
exec_state = _execute_step(_rom_node.sub_node(_curr_idx));
/* advance step index, wrap at the end */
_curr_idx = (_curr_idx + 1) % _rom_node.num_sub_nodes();
}
}
void _handle_timer() { _execute_steps_until_sleep(); }
Entrypoint &_ep;
typedef Session_component This;
Signal_handler<This> _timer_handler = { _ep, *this, &This::_handle_timer };
public:
Session_component(Env &env, Xml_node rom_node, bool &verbose)
:
_env(env), _verbose(verbose), _rom_node(rom_node), _timer(env), _ep(env.ep())
{
/* init timer signal handler */
_timer.sigh(_timer_handler);
/* execute first step immediately at session-creation time */
_execute_steps_until_sleep();
}
Genode::Rom_dataspace_capability dataspace() override
{
using namespace Genode;
if (!_has_content)
return Rom_dataspace_capability();
/* replace dataspace by new one */
_ram_ds.construct(_env.ram(), _env.rm(), _rom_node.size());
/* fill with content of current step */
Xml_node step_node = _rom_node.sub_node(_last_content_idx);
step_node.with_raw_content([&] (char const *start, size_t size) {
memcpy(_ram_ds->local_addr<char>(), start, size); });
/* cast RAM into ROM dataspace capability */
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ram_ds->cap());
return static_cap_cast<Rom_dataspace>(ds_cap);
}
void sigh(Genode::Signal_context_capability sigh) override
{
_sigh = sigh;
}
};
class Dynamic_rom::Root : public Genode::Root_component<Session_component>
{
private:
Env &_env;
Xml_node _config_node;
bool &_verbose;
class Nonexistent_rom_module { };
Xml_node _lookup_rom_node_in_config(Genode::Session_label const &name)
{
/* lookup ROM module in config */
for (unsigned i = 0; i < _config_node.num_sub_nodes(); i++) {
Xml_node node = _config_node.sub_node(i);
if (node.has_attribute("name")
&& node.attribute("name").has_value(name.string()))
return node;
}
throw Nonexistent_rom_module();
}
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
/* read name of ROM module from args */
Session_label const label = label_from_args(args);
Session_label const module_name = label.last_element();
try {
return new (md_alloc())
Session_component(_env,
_lookup_rom_node_in_config(module_name),
_verbose);
}
catch (Nonexistent_rom_module) {
error("ROM module lookup of '", label.string(), "' failed");
throw Service_denied();
}
}
public:
Root(Env &env, Genode::Allocator &md_alloc,
Xml_node config_node, bool &verbose)
:
Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _config_node(config_node), _verbose(verbose)
{ }
};
struct Dynamic_rom::Main
{
Env &env;
Genode::Attached_rom_dataspace config { env, "config" };
bool verbose = config.xml().attribute_value("verbose", false);
Sliced_heap sliced_heap { env.ram(), env.rm() };
Root root { env, sliced_heap, config.xml(), verbose };
Main(Env &env) : env(env)
{
env.parent().announce(env.ep().manage(root));
}
};
void Component::construct(Genode::Env &env) { static Dynamic_rom::Main main(env); }