diff --git a/repos/os/run/rom_filter.run b/repos/os/run/rom_filter.run
new file mode 100644
index 000000000..035cb7486
--- /dev/null
+++ b/repos/os/run/rom_filter.run
@@ -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 {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+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]
+[init -> rom_logger] ROM 'generated':
+[init -> rom_logger]
+[init -> rom_logger] ROM 'generated':
+[init -> rom_logger]
+[init -> rom_logger] ROM 'generated':
+[init -> rom_logger]
+}
+
diff --git a/repos/os/src/server/rom_filter/README b/repos/os/src/server/rom_filter/README
new file mode 100644
index 000000000..32facf0d2
--- /dev/null
+++ b/repos/os/src/server/rom_filter/README
@@ -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 ' ' 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 '' node.
+The type of the top-level XML node must be specified via the 'node' attribute.
+The '' node can contain the following sub nodes:
+
+:'':
+ Contains content to be written to the output.
+
+:'':
+ Produces output depending on a condition (see below). If the condition
+ is satisfied, the '' sub node is evaluated. Otherwise, the ''
+ sub node is evaluated. Each of those sub nodes can contain the same
+ nodes as the '' node.
+
+
+Conditions
+----------
+
+The '' 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.
diff --git a/repos/os/src/server/rom_filter/input_rom_registry.h b/repos/os/src/server/rom_filter/input_rom_registry.h
new file mode 100644
index 000000000..046580efc
--- /dev/null
+++ b/repos/os/src/server/rom_filter/input_rom_registry.h
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+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::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 { " " };
+
+ void _handle_rom_changed(unsigned)
+ {
+ _rom_ds.update();
+
+ try {
+ _top_level = Xml_node(_rom_ds.local_addr());
+ } catch (...) {
+ _top_level = Xml_node(" ");
+ }
+
+ /* trigger re-evaluation of the inputs */
+ _input_rom_changed_fn.input_rom_changed();
+ }
+
+ Genode::Signal_rpc_member _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 _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
+ 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));
+ Genode::destroy(_alloc, const_cast(&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_ */
diff --git a/repos/os/src/server/rom_filter/main.cc b/repos/os/src/server/rom_filter/main.cc
new file mode 100644
index 000000000..c66822152
--- /dev/null
+++ b/repos/os/src/server/rom_filter/main.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+/* 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_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,
+ public Session_list::Element
+{
+ private:
+
+ Signal_context_capability _sigh;
+
+ Output_buffer const &_output_buffer;
+
+ Session_list &_sessions;
+
+ Lazy_volatile_object _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();
+ 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(_ram_ds->cap());
+ return static_cap_cast(ds_cap);
+ }
+
+ void sigh(Genode::Signal_context_capability sigh) override
+ {
+ _sigh = sigh;
+ }
+};
+
+
+class Rom_filter::Root : public Genode::Root_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(&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 _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 _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(), 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 '' 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 '' node");
+ return;
+ }
+
+ Node_type_name const node_type =
+ output.attribute_value("node", Node_type_name(""));
+
+ /* generate output */
+ Xml_generator xml(_xml_ds->local_addr(),
+ _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);
+ }
+}
diff --git a/repos/os/src/server/rom_filter/target.mk b/repos/os/src/server/rom_filter/target.mk
new file mode 100644
index 000000000..04d8c3a15
--- /dev/null
+++ b/repos/os/src/server/rom_filter/target.mk
@@ -0,0 +1,3 @@
+TARGET = rom_filter
+SRC_CC = main.cc
+LIBS = base server config