parent
8763b6925a
commit
702646a4a3
|
@ -0,0 +1,137 @@
|
||||||
|
#
|
||||||
|
# Build
|
||||||
|
#
|
||||||
|
|
||||||
|
set build_components {
|
||||||
|
core init drivers/timer
|
||||||
|
server/dynamic_rom server/rom_filter app/rom_logger
|
||||||
|
}
|
||||||
|
|
||||||
|
build $build_components
|
||||||
|
|
||||||
|
create_boot_directory
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate config
|
||||||
|
#
|
||||||
|
|
||||||
|
append config {
|
||||||
|
<config>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="RAM"/>
|
||||||
|
<service name="RM"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="IRQ"/>
|
||||||
|
<service name="IO_MEM"/>
|
||||||
|
<service name="IO_PORT"/>
|
||||||
|
<service name="CAP"/>
|
||||||
|
<service name="SIGNAL"/>
|
||||||
|
</parent-provides>
|
||||||
|
|
||||||
|
<default-route>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</default-route>
|
||||||
|
|
||||||
|
<start name="timer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Timer"/></provides>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="dynamic_rom">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<provides><service name="ROM"/></provides>
|
||||||
|
<config verbose="yes">
|
||||||
|
<rom name="xray">
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="disable X-ray mode">
|
||||||
|
<xray enabled="no"/>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="enable X-ray mode">
|
||||||
|
<xray enabled="yes"/>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="leave X-ray mode undefined">
|
||||||
|
<xray/> <!-- undefined -->
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="finished"/>
|
||||||
|
</rom>
|
||||||
|
</config>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="rom_filter">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<provides><service name="ROM"/></provides>
|
||||||
|
<config verbose="no">
|
||||||
|
|
||||||
|
<input name="xray_enabled" rom="xray" node="xray">
|
||||||
|
<attribute name="enabled" />
|
||||||
|
</input>
|
||||||
|
|
||||||
|
<output node="config">
|
||||||
|
<if>
|
||||||
|
<has_value input="xray_enabled" value="no" />
|
||||||
|
<then>
|
||||||
|
<inline><!-- .. flat window decorations ... --></inline>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<if>
|
||||||
|
<has_value input="xray_enabled" value="yes" />
|
||||||
|
<then>
|
||||||
|
<inline><!-- ... colored window decorations ... --></inline>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<inline><!-- ... fallback ... --></inline>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
</output>
|
||||||
|
|
||||||
|
</config>
|
||||||
|
<route>
|
||||||
|
<service name="ROM"> <child name="dynamic_rom"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="rom_logger">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<config rom="generated" />
|
||||||
|
<route>
|
||||||
|
<service name="ROM"> <child name="rom_filter"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</config>}
|
||||||
|
|
||||||
|
install_config $config
|
||||||
|
|
||||||
|
#
|
||||||
|
# Boot modules
|
||||||
|
#
|
||||||
|
|
||||||
|
set boot_modules { core init timer dynamic_rom rom_filter rom_logger }
|
||||||
|
|
||||||
|
build_boot_image $boot_modules
|
||||||
|
|
||||||
|
append qemu_args " -nographic "
|
||||||
|
|
||||||
|
run_genode_until {.*finished.*\n} 20
|
||||||
|
|
||||||
|
# pay only attention to the output of the rom_logger
|
||||||
|
grep_output {^\[init -> rom_logger}
|
||||||
|
|
||||||
|
compare_output_to {
|
||||||
|
[init -> rom_logger] ROM 'generated':
|
||||||
|
[init -> rom_logger] <config><!-- ... fallback ... --></config>
|
||||||
|
[init -> rom_logger] ROM 'generated':
|
||||||
|
[init -> rom_logger] <config><!-- .. flat window decorations ... --></config>
|
||||||
|
[init -> rom_logger] ROM 'generated':
|
||||||
|
[init -> rom_logger] <config><!-- ... colored window decorations ... --></config>
|
||||||
|
[init -> rom_logger] ROM 'generated':
|
||||||
|
[init -> rom_logger] <config><!-- ... fallback ... --></config>
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
The ROM filter provides a ROM module that depends on the content
|
||||||
|
of other ROM modules. Its designated use is the dynamic switching between
|
||||||
|
configuration variants dependent on the state of the system. For example,
|
||||||
|
the configuration of the window decorator may be toggled depending on whether
|
||||||
|
nitpicker's X-ray mode is active or not.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The configuration consists of two parts. The first part is the declaration of
|
||||||
|
input values that are taken into the account. The input values are obtained
|
||||||
|
from ROM modules that contain XML-formatted data. Each input value is
|
||||||
|
represented by an '<input>' node with a unique 'name' attribute. The 'rom'
|
||||||
|
attribute specifies the ROM module to take the input from. If not specified,
|
||||||
|
the 'name' is used as the ROM name. The type of the top-level XML node can be
|
||||||
|
specified via the 'node' attribute. If not present, the top-level XML node is
|
||||||
|
expected to correspond to the 'name' attribute.
|
||||||
|
|
||||||
|
The second part of the configuration defines the output via an '<output>' node.
|
||||||
|
The type of the top-level XML node must be specified via the 'node' attribute.
|
||||||
|
The '<output>' node can contain the following sub nodes:
|
||||||
|
|
||||||
|
:'<inline>':
|
||||||
|
Contains content to be written to the output.
|
||||||
|
|
||||||
|
:'<if>':
|
||||||
|
Produces output depending on a condition (see below). If the condition
|
||||||
|
is satisfied, the '<then>' sub node is evaluated. Otherwise, the '<else>'
|
||||||
|
sub node is evaluated. Each of those sub nodes can contain the same
|
||||||
|
nodes as the '<output>' node.
|
||||||
|
|
||||||
|
|
||||||
|
Conditions
|
||||||
|
----------
|
||||||
|
|
||||||
|
The '<has_value>' condition compares an input value (specified as 'input'
|
||||||
|
attribute) with a predefined value (specified as 'value' attribute). The
|
||||||
|
condition is satisfied if both values are equal.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
For an example that illustrates the use of the component, please refer to the
|
||||||
|
_os/run/conditional_rom.run_ script.
|
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
* \brief Registry of ROM modules used as input for the condition
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2015-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 _INPUT_ROM_REGISTRY_H_
|
||||||
|
#define _INPUT_ROM_REGISTRY_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
#include <os/attached_rom_dataspace.h>
|
||||||
|
#include <os/config.h>
|
||||||
|
#include <os/attached_ram_dataspace.h>
|
||||||
|
#include <os/server.h>
|
||||||
|
#include <base/allocator.h>
|
||||||
|
|
||||||
|
namespace Rom_filter {
|
||||||
|
|
||||||
|
class Input_rom_registry;
|
||||||
|
|
||||||
|
typedef Genode::String<100> Input_rom_name;
|
||||||
|
typedef Genode::String<100> Input_name;
|
||||||
|
typedef Genode::String<100> Input_value;
|
||||||
|
|
||||||
|
typedef Genode::String<80> Node_type_name;
|
||||||
|
typedef Genode::String<80> Attribute_name;
|
||||||
|
|
||||||
|
|
||||||
|
using Genode::env;
|
||||||
|
using Genode::Signal_context_capability;
|
||||||
|
using Genode::Signal_rpc_member;
|
||||||
|
using Genode::Xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Rom_filter::Input_rom_registry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback type
|
||||||
|
*/
|
||||||
|
struct Input_rom_changed_fn
|
||||||
|
{
|
||||||
|
virtual void input_rom_changed() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception type
|
||||||
|
*/
|
||||||
|
class Nonexistent_input_value { };
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
class Entry : public Genode::List<Entry>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Server::Entrypoint &_ep;
|
||||||
|
|
||||||
|
Input_rom_name _name;
|
||||||
|
|
||||||
|
Input_rom_changed_fn &_input_rom_changed_fn;
|
||||||
|
|
||||||
|
Genode::Attached_rom_dataspace _rom_ds { _name.string() };
|
||||||
|
|
||||||
|
Xml_node _top_level { "<empty/>" };
|
||||||
|
|
||||||
|
void _handle_rom_changed(unsigned)
|
||||||
|
{
|
||||||
|
_rom_ds.update();
|
||||||
|
|
||||||
|
try {
|
||||||
|
_top_level = Xml_node(_rom_ds.local_addr<char>());
|
||||||
|
} catch (...) {
|
||||||
|
_top_level = Xml_node("<empty/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trigger re-evaluation of the inputs */
|
||||||
|
_input_rom_changed_fn.input_rom_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Signal_rpc_member<Entry> _rom_changed_dispatcher =
|
||||||
|
{ _ep, *this, &Entry::_handle_rom_changed };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query value from XML-structured ROM content
|
||||||
|
*
|
||||||
|
* \param path XML node that defines the path to the value
|
||||||
|
* \param content XML-structured content, to which the path
|
||||||
|
* is applied
|
||||||
|
*/
|
||||||
|
Input_value _query_value(Xml_node path, Xml_node content) const
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take value of an attribute
|
||||||
|
*/
|
||||||
|
if (path.has_type("attribute")) {
|
||||||
|
|
||||||
|
Attribute_name const attr_name =
|
||||||
|
path.attribute_value("name", Attribute_name(""));
|
||||||
|
|
||||||
|
if (!content.has_attribute(attr_name.string()))
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
|
||||||
|
return content.attribute_value(attr_name.string(),
|
||||||
|
Input_value(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Follow path node
|
||||||
|
*/
|
||||||
|
if (path.has_type("node")) {
|
||||||
|
|
||||||
|
Node_type_name const sub_node_type =
|
||||||
|
path.attribute_value("type", Node_type_name(""));
|
||||||
|
|
||||||
|
content = content.sub_node(sub_node_type.string());
|
||||||
|
path = path.sub_node();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the expected top-level XML node type of a given input
|
||||||
|
*/
|
||||||
|
static Node_type_name _top_level_node_type(Xml_node input_node)
|
||||||
|
{
|
||||||
|
Node_type_name const undefined("");
|
||||||
|
|
||||||
|
if (input_node.has_attribute("node"))
|
||||||
|
return input_node.attribute_value("node", undefined);
|
||||||
|
|
||||||
|
return input_node.attribute_value("name", undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Entry(Input_rom_name const &name, Server::Entrypoint &ep,
|
||||||
|
Input_rom_changed_fn &input_rom_changed_fn)
|
||||||
|
:
|
||||||
|
_ep(ep), _name(name),
|
||||||
|
_input_rom_changed_fn(input_rom_changed_fn)
|
||||||
|
{
|
||||||
|
_rom_ds.sigh(_rom_changed_dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
Input_rom_name name() const { return _name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query input value from ROM modules
|
||||||
|
*
|
||||||
|
* \param input_node XML that describes the path to the
|
||||||
|
* input value
|
||||||
|
*
|
||||||
|
* \throw Nonexistent_input_value
|
||||||
|
*/
|
||||||
|
Input_value query_value(Xml_node input_node) const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* The creation of the XML node may fail with an
|
||||||
|
* exception if the ROM module contains non-XML data.
|
||||||
|
*/
|
||||||
|
Xml_node content_node(_top_level);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check type of top-level node, query value of the
|
||||||
|
* type name matches.
|
||||||
|
*/
|
||||||
|
Node_type_name expected = _top_level_node_type(input_node);
|
||||||
|
if (content_node.has_type(expected.string()))
|
||||||
|
return _query_value(input_node.sub_node(), content_node);
|
||||||
|
else
|
||||||
|
PWRN("top-level node <%s> missing in input ROM %s",
|
||||||
|
expected.string(), name().string());
|
||||||
|
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Genode::Allocator &_alloc;
|
||||||
|
|
||||||
|
Server::Entrypoint &_ep;
|
||||||
|
|
||||||
|
Genode::List<Entry> _input_roms;
|
||||||
|
|
||||||
|
Input_rom_changed_fn &_input_rom_changed_fn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply functor for each input ROM
|
||||||
|
*
|
||||||
|
* The functor is called with 'Input &' as argument.
|
||||||
|
*/
|
||||||
|
template <typename FUNC>
|
||||||
|
void _for_each_input_rom(FUNC const &func) const
|
||||||
|
{
|
||||||
|
Entry const *ir = _input_roms.first();
|
||||||
|
Entry const *next = nullptr;
|
||||||
|
for (; ir; ir = next) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain next element prior calling the functor because
|
||||||
|
* the functor may remove the current element from the list.
|
||||||
|
*/
|
||||||
|
next = ir->next();
|
||||||
|
|
||||||
|
func(*ir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return ROM name of specified XML node
|
||||||
|
*/
|
||||||
|
static inline Input_rom_name _input_rom_name(Xml_node input)
|
||||||
|
{
|
||||||
|
if (input.has_attribute("rom"))
|
||||||
|
return input.attribute_value("rom", Input_rom_name(""));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no 'rom' attribute was specified, we fall back to use the
|
||||||
|
* name of the input as ROM name.
|
||||||
|
*/
|
||||||
|
return input.attribute_value("name", Input_rom_name(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if ROM with specified name is known
|
||||||
|
*/
|
||||||
|
bool _input_rom_exists(Input_rom_name const &name) const
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
_for_each_input_rom([&] (Entry const &input_rom) {
|
||||||
|
|
||||||
|
if (input_rom.name() == name)
|
||||||
|
result = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _config_uses_input_rom(Xml_node config,
|
||||||
|
Input_rom_name const &name)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
config.for_each_sub_node("input", [&] (Xml_node input) {
|
||||||
|
|
||||||
|
if (_input_rom_name(input) == name)
|
||||||
|
result = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry const *_lookup_entry_by_name(Input_rom_name const &name) const
|
||||||
|
{
|
||||||
|
Entry const *entry = nullptr;
|
||||||
|
|
||||||
|
_for_each_input_rom([&] (Entry const &input_rom) {
|
||||||
|
if (input_rom.name() == name)
|
||||||
|
entry = &input_rom; });
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \throw Nonexistent_input_value
|
||||||
|
*/
|
||||||
|
Input_value _query_value_in_roms(Xml_node input_node)
|
||||||
|
{
|
||||||
|
Entry const *entry =
|
||||||
|
_lookup_entry_by_name(_input_rom_name(input_node));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (entry)
|
||||||
|
return entry->query_value(input_node);
|
||||||
|
} catch (...) { }
|
||||||
|
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param sigh signal context capability to install in ROM sessions
|
||||||
|
* for the inputs
|
||||||
|
*/
|
||||||
|
Input_rom_registry(Genode::Allocator &alloc, Server::Entrypoint &ep,
|
||||||
|
Input_rom_changed_fn &input_rom_changed_fn)
|
||||||
|
:
|
||||||
|
_alloc(alloc), _ep(ep), _input_rom_changed_fn(input_rom_changed_fn)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void update_config(Xml_node config)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Remove ROMs that are no longer present in the configuration.
|
||||||
|
*/
|
||||||
|
auto remove_stale_entry = [&] (Entry const &entry) {
|
||||||
|
|
||||||
|
if (_config_uses_input_rom(config, entry.name()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_input_roms.remove(const_cast<Entry *>(&entry));
|
||||||
|
Genode::destroy(_alloc, const_cast<Entry *>(&entry));
|
||||||
|
};
|
||||||
|
_for_each_input_rom(remove_stale_entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add new appearing ROMs.
|
||||||
|
*/
|
||||||
|
auto add_new_entry = [&] (Xml_node input) {
|
||||||
|
|
||||||
|
Input_rom_name name = _input_rom_name(input);
|
||||||
|
|
||||||
|
if (_input_rom_exists(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Entry *entry =
|
||||||
|
new (_alloc) Entry(name, _ep, _input_rom_changed_fn);
|
||||||
|
|
||||||
|
_input_roms.insert(entry);
|
||||||
|
};
|
||||||
|
config.for_each_sub_node("input", add_new_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup value of input with specified name
|
||||||
|
*
|
||||||
|
* \throw Nonexistent_input_value
|
||||||
|
*/
|
||||||
|
Input_value query_value(Xml_node config, Input_name const &input_name) const
|
||||||
|
{
|
||||||
|
Input_value input_value;
|
||||||
|
bool input_value_defined = false;
|
||||||
|
|
||||||
|
auto handle_input_node = [&] (Xml_node input_node) {
|
||||||
|
|
||||||
|
if (input_node.attribute_value("name", Input_name("")) != input_name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_value = _query_value_in_roms(input_node);
|
||||||
|
input_value_defined = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.for_each_sub_node("input", handle_input_node);
|
||||||
|
} catch (...) {
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input_value_defined)
|
||||||
|
throw Nonexistent_input_value();
|
||||||
|
|
||||||
|
return input_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INPUT_ROM_REGISTRY_H_ */
|
|
@ -0,0 +1,344 @@
|
||||||
|
/*
|
||||||
|
* \brief ROM server that generates a ROM depending on other ROMs
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2015-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/volatile_object.h>
|
||||||
|
#include <util/arg_string.h>
|
||||||
|
#include <util/xml_generator.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
#include <base/env.h>
|
||||||
|
#include <root/component.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include "input_rom_registry.h"
|
||||||
|
|
||||||
|
namespace Rom_filter {
|
||||||
|
using Server::Entrypoint;
|
||||||
|
using Genode::Rpc_object;
|
||||||
|
using Genode::Sliced_heap;
|
||||||
|
using Genode::env;
|
||||||
|
using Genode::Lazy_volatile_object;
|
||||||
|
using Genode::Xml_generator;
|
||||||
|
using Genode::size_t;
|
||||||
|
|
||||||
|
class Output_buffer;
|
||||||
|
class Session_component;
|
||||||
|
class Root;
|
||||||
|
struct Main;
|
||||||
|
|
||||||
|
typedef Genode::List<Session_component> Session_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used by the sessions to obtain the XML output data
|
||||||
|
*/
|
||||||
|
struct Rom_filter::Output_buffer
|
||||||
|
{
|
||||||
|
virtual size_t content_size() const = 0;
|
||||||
|
virtual size_t export_content(char *dst, size_t dst_len) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Rom_filter::Session_component : public Rpc_object<Genode::Rom_session>,
|
||||||
|
public Session_list::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_context_capability _sigh;
|
||||||
|
|
||||||
|
Output_buffer const &_output_buffer;
|
||||||
|
|
||||||
|
Session_list &_sessions;
|
||||||
|
|
||||||
|
Lazy_volatile_object<Genode::Attached_ram_dataspace> _ram_ds;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Session_component(Session_list &sessions, Output_buffer const &output_buffer)
|
||||||
|
:
|
||||||
|
_output_buffer(output_buffer), _sessions(sessions)
|
||||||
|
{
|
||||||
|
_sessions.insert(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Session_component() { _sessions.remove(this); }
|
||||||
|
|
||||||
|
void notify_client()
|
||||||
|
{
|
||||||
|
if (!_sigh.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Genode::Signal_transmitter(_sigh).submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Rom_dataspace_capability dataspace() override
|
||||||
|
{
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
/* replace dataspace by new one as needed */
|
||||||
|
if (!_ram_ds.is_constructed()
|
||||||
|
|| _output_buffer.content_size() > _ram_ds->size()) {
|
||||||
|
|
||||||
|
_ram_ds.construct(env()->ram_session(), _output_buffer.content_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dst = _ram_ds->local_addr<char>();
|
||||||
|
size_t const dst_size = _ram_ds->size();
|
||||||
|
|
||||||
|
/* fill with content of current evaluation result */
|
||||||
|
size_t const copied_len = _output_buffer.export_content(dst, dst_size);
|
||||||
|
|
||||||
|
/* clear remainder of dataspace */
|
||||||
|
Genode::memset(dst + copied_len, 0, dst_size - copied_len);
|
||||||
|
|
||||||
|
/* 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 Rom_filter::Root : public Genode::Root_component<Session_component>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Output_buffer &_output_buffer;
|
||||||
|
Session_list _sessions;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Session_component *_create_session(const char *args)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We ignore the name of the ROM module requested
|
||||||
|
*/
|
||||||
|
return new (md_alloc()) Session_component(_sessions, _output_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Root(Entrypoint &ep, Output_buffer &output_buffer,
|
||||||
|
Genode::Allocator &md_alloc)
|
||||||
|
:
|
||||||
|
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
|
||||||
|
_output_buffer(output_buffer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void notify_clients()
|
||||||
|
{
|
||||||
|
for (Session_component *s = _sessions.first(); s; s = s->next())
|
||||||
|
s->notify_client();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Rom_filter::Main : Input_rom_registry::Input_rom_changed_fn,
|
||||||
|
Output_buffer
|
||||||
|
{
|
||||||
|
Entrypoint &_ep;
|
||||||
|
|
||||||
|
Sliced_heap _sliced_heap = { env()->ram_session(), env()->rm_session() };
|
||||||
|
|
||||||
|
Input_rom_registry _input_rom_registry { *env()->heap(), _ep, *this };
|
||||||
|
|
||||||
|
Genode::Lazy_volatile_object<Genode::Attached_ram_dataspace> _xml_ds;
|
||||||
|
|
||||||
|
size_t _xml_output_len = 0;
|
||||||
|
|
||||||
|
void _evaluate_node(Xml_node node, Xml_generator &xml);
|
||||||
|
void _evaluate();
|
||||||
|
|
||||||
|
Root _root = { _ep, *this, _sliced_heap };
|
||||||
|
|
||||||
|
Genode::Signal_rpc_member<Main> _config_dispatcher =
|
||||||
|
{ _ep, *this, &Main::_handle_config };
|
||||||
|
|
||||||
|
void _handle_config(unsigned)
|
||||||
|
{
|
||||||
|
Genode::config()->reload();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create buffer for generated XML data
|
||||||
|
*/
|
||||||
|
Genode::Number_of_bytes xml_ds_size = 4096;
|
||||||
|
|
||||||
|
xml_ds_size = Genode::config()->xml_node().attribute_value("buffer", xml_ds_size);
|
||||||
|
|
||||||
|
if (!_xml_ds.is_constructed() || xml_ds_size != _xml_ds->size())
|
||||||
|
_xml_ds.construct(env()->ram_session(), xml_ds_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain inputs
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
_input_rom_registry.update_config(Genode::config()->xml_node());
|
||||||
|
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate output
|
||||||
|
*/
|
||||||
|
_evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input_rom_registry::Input_rom_changed_fn interface
|
||||||
|
*
|
||||||
|
* Called each time one of the input ROM modules changes.
|
||||||
|
*/
|
||||||
|
void input_rom_changed() override
|
||||||
|
{
|
||||||
|
_evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output_buffer interface
|
||||||
|
*/
|
||||||
|
size_t content_size() const override
|
||||||
|
{
|
||||||
|
return _xml_output_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output_buffer interface
|
||||||
|
*/
|
||||||
|
size_t export_content(char *dst, size_t dst_len) const
|
||||||
|
{
|
||||||
|
size_t const len = Genode::min(dst_len, _xml_output_len);
|
||||||
|
Genode::memcpy(dst, _xml_ds->local_addr<char>(), len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Main(Entrypoint &ep) : _ep(ep)
|
||||||
|
{
|
||||||
|
env()->parent()->announce(_ep.manage(_root));
|
||||||
|
|
||||||
|
_handle_config(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Rom_filter::Main::_evaluate_node(Xml_node node, Xml_generator &xml)
|
||||||
|
{
|
||||||
|
auto process_output_sub_node = [&] (Xml_node node) {
|
||||||
|
|
||||||
|
if (node.has_type("if")) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check condition
|
||||||
|
*/
|
||||||
|
bool condition_satisfied = false;
|
||||||
|
|
||||||
|
if (node.has_sub_node("has_value")) {
|
||||||
|
|
||||||
|
Xml_node const has_value_node = node.sub_node("has_value");
|
||||||
|
|
||||||
|
Input_name const input_name =
|
||||||
|
has_value_node.attribute_value("input", Input_name());
|
||||||
|
|
||||||
|
Input_value const expected_input_value =
|
||||||
|
has_value_node.attribute_value("value", Input_value());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Xml_node config = Genode::config()->xml_node();
|
||||||
|
|
||||||
|
Input_value const input_value =
|
||||||
|
_input_rom_registry.query_value(config, input_name);
|
||||||
|
|
||||||
|
if (input_value == expected_input_value)
|
||||||
|
condition_satisfied = true;
|
||||||
|
}
|
||||||
|
catch (Input_rom_registry::Nonexistent_input_value) {
|
||||||
|
PWRN("could not obtain input value for input %s", input_name.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition_satisfied) {
|
||||||
|
if (node.has_sub_node("then"))
|
||||||
|
_evaluate_node(node.sub_node("then"), xml);
|
||||||
|
} else {
|
||||||
|
if (node.has_sub_node("else"))
|
||||||
|
_evaluate_node(node.sub_node("else"), xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has_type("inline")) {
|
||||||
|
char const *src = node.content_base();
|
||||||
|
size_t src_len = node.content_size();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The 'Xml_generator::append' method puts the content at a fresh
|
||||||
|
* line, and also adds a newline before the closing tag. We strip
|
||||||
|
* eventual newlines from the '<inline>' node content to avoid
|
||||||
|
* double newlines in the output.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* remove leading newline */
|
||||||
|
if (src_len > 0 && src[0] == '\n') {
|
||||||
|
src++;
|
||||||
|
src_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove trailing whilespace including newlines */
|
||||||
|
for (; src_len > 0 && Genode::is_whitespace(src[src_len - 1]); src_len--);
|
||||||
|
|
||||||
|
xml.append(src, src_len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
node.for_each_sub_node(process_output_sub_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Rom_filter::Main::_evaluate()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Xml_node output = Genode::config()->xml_node().sub_node("output");
|
||||||
|
|
||||||
|
if (!output.has_attribute("node")) {
|
||||||
|
PERR("missing 'node' attribute in '<output>' node");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node_type_name const node_type =
|
||||||
|
output.attribute_value("node", Node_type_name(""));
|
||||||
|
|
||||||
|
/* generate output */
|
||||||
|
Xml_generator xml(_xml_ds->local_addr<char>(),
|
||||||
|
_xml_ds->size(), node_type.string(),
|
||||||
|
[&] () { _evaluate_node(output, xml); });
|
||||||
|
|
||||||
|
_xml_output_len = xml.used();
|
||||||
|
|
||||||
|
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||||
|
|
||||||
|
_root.notify_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace Server {
|
||||||
|
|
||||||
|
char const *name() { return "conditional_rom_ep"; }
|
||||||
|
|
||||||
|
size_t stack_size() { return 4*1024*sizeof(long); }
|
||||||
|
|
||||||
|
void construct(Entrypoint &ep)
|
||||||
|
{
|
||||||
|
static Rom_filter::Main main(ep);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
TARGET = rom_filter
|
||||||
|
SRC_CC = main.cc
|
||||||
|
LIBS = base server config
|
Loading…
Reference in New Issue