diff --git a/doc/components.txt b/doc/components.txt
index da228ce1de..39997587ec 100644
--- a/doc/components.txt
+++ b/doc/components.txt
@@ -384,6 +384,10 @@ Separate components:
A simple ROM service that provides ROM modules that change in time according
to a configured timeline.
+:'os/src/server/report_rom':
+ A service that implements both the report session interface and the ROM
+ session interface. It reflects incoming reports as ROM modules.
+
Libraries:
:'libports/lib/mk/libc':
diff --git a/os/run/report_rom.run b/os/run/report_rom.run
new file mode 100644
index 0000000000..412beb5919
--- /dev/null
+++ b/os/run/report_rom.run
@@ -0,0 +1,70 @@
+build "core init server/report_rom test/report_rom drivers/timer"
+
+create_boot_directory
+
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+build_boot_image "core init timer report_rom test-report_rom"
+
+append qemu_args "-nographic -m 128"
+
+run_genode_until {child exited with exit value 0.*\n} 30
+
+grep_output {^\[init -> test-report_rom}
+
+compare_output_to {
+ [init -> test-report_rom] --- test-report_rom started ---
+ [init -> test-report_rom] Reporter: open session
+ [init -> test-report_rom] Reporter: brightness 10
+ [init -> test-report_rom] ROM client: request brightness report
+ [init -> test-report_rom] ->
+ [init -> test-report_rom] Reporter: updated brightness to 77
+ [init -> test-report_rom] ROM client: wait for update notification
+ [init -> test-report_rom] ROM client: got signal
+ [init -> test-report_rom] ROM client: request updated brightness report
+ [init -> test-report_rom] ->
+ [init -> test-report_rom] Reporter: close report session
+ [init -> test-report_rom] ROM client: wait for update notification
+ [init -> test-report_rom] ROM client: detected vanished report
+ [init -> test-report_rom] Reporter: start reporting (while the ROM client still listens)
+ [init -> test-report_rom] ROM client: wait for update notification
+ [init -> test-report_rom] --- test-report_rom finished ---
+}
diff --git a/os/src/server/report_rom/README b/os/src/server/report_rom/README
new file mode 100644
index 0000000000..8fc92a1ec5
--- /dev/null
+++ b/os/src/server/report_rom/README
@@ -0,0 +1,24 @@
+The "report_rom" component is both a report service and a ROM service. It makes
+incoming reports available as ROM modules. The ROM modules are named after the
+label of the corresponding report session.
+
+Configuration
+-------------
+
+The report-ROM server hands out ROM modules only if explicitly permitted by a
+configured policy. For example:
+
+!
+!
+!
+!
+! ...
+!
+!
+
+The label of an incoming ROM session is matched against the 'label' attribute
+of all '' nodes. If the session label matches a policy label, the
+client obtains the data from the report client with the label specified in the
+'report' attribute. In the example above, the nitpicker GUI server sends
+reports about the pointer position to the report-ROM service. Those reports
+are handed out to a window decorator (labeled "decorator") as ROM module.
diff --git a/os/src/server/report_rom/main.cc b/os/src/server/report_rom/main.cc
new file mode 100644
index 0000000000..89ee8186c9
--- /dev/null
+++ b/os/src/server/report_rom/main.cc
@@ -0,0 +1,74 @@
+/*
+ * \brief Server that aggregates reports and exposes them as ROM modules
+ * \author Norman Feske
+ * \date 2014-01-11
+ */
+
+/*
+ * Copyright (C) 2014 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
+
+/* local includes */
+#include
+#include
+
+
+namespace Server {
+ using Genode::env;
+ using Genode::Xml_node;
+ struct Main;
+}
+
+
+struct Server::Main
+{
+ Entrypoint &ep;
+
+ Genode::Sliced_heap sliced_heap = { env()->ram_session(),
+ env()->rm_session() };
+
+ Rom::Registry rom_registry = { sliced_heap };
+
+ Xml_node _rom_config_node()
+ {
+ try {
+ return Genode::config()->xml_node().sub_node("rom"); }
+ catch (Xml_node::Nonexistent_sub_node) {
+ PWRN("missing configuration");
+ return Xml_node("");
+ }
+ }
+
+ Xml_node rom_config = _rom_config_node();
+
+ Report::Root report_root = { ep, sliced_heap, rom_registry };
+ Rom ::Root rom_root = { ep, sliced_heap, rom_registry, rom_config};
+
+ Main(Entrypoint &ep) : ep(ep)
+ {
+ env()->parent()->announce(ep.manage(report_root));
+ env()->parent()->announce(ep.manage(rom_root));
+ }
+};
+
+
+namespace Server {
+
+ char const *name() { return "report_rom_ep"; }
+
+ size_t stack_size() { return 4*1024*sizeof(long); }
+
+ void construct(Entrypoint &ep)
+ {
+ static Main main(ep);
+ }
+}
diff --git a/os/src/server/report_rom/report_service.h b/os/src/server/report_rom/report_service.h
new file mode 100644
index 0000000000..063a544a6a
--- /dev/null
+++ b/os/src/server/report_rom/report_service.h
@@ -0,0 +1,113 @@
+/*
+ * \brief Server that aggregates reports and exposes them as ROM modules
+ * \author Norman Feske
+ * \date 2014-01-11
+ */
+
+/*
+ * Copyright (C) 2014 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 _REPORT_SERVICE_H_
+#define _REPORT_SERVICE_H_
+
+/* Genode includes */
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Report {
+ struct Session_component;
+ struct Root;
+}
+
+
+struct Report::Session_component : Genode::Rpc_object, Rom::Writer
+{
+ private:
+
+ Rom::Registry_for_writer &_registry;
+
+ Genode::Attached_ram_dataspace _ds;
+
+ Rom::Module &_module;
+
+ public:
+
+ Session_component(Rom::Module::Name const &name, size_t buffer_size,
+ Rom::Registry_for_writer ®istry)
+ :
+ _registry(registry),
+ _ds(Genode::env()->ram_session(), buffer_size),
+ _module(_registry.lookup(*this, name))
+ { }
+
+ /**
+ * Destructor
+ *
+ * Clear report when the report session gets closes.
+ */
+ ~Session_component()
+ {
+ _module.write_content(0, 0);
+ _registry.release(*this, _module);
+ }
+
+ Dataspace_capability dataspace() override { return _ds.cap(); }
+
+ void submit(size_t const length) override
+ {
+ _module.write_content(_ds.local_addr(),
+ Genode::min(length, _ds.size()));
+ }
+
+ void response_sigh(Genode::Signal_context_capability) override { }
+
+ size_t obtain_response() override { return 0; }
+};
+
+
+struct Report::Root : Genode::Root_component
+{
+ private:
+
+ Rom::Registry_for_writer &_rom_registry;
+
+ protected:
+
+ Session_component *_create_session(const char *args) override
+ {
+ using namespace Genode;
+
+ /* read label from session arguments */
+ char label[200];
+ Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
+
+ /* read report buffer size from session arguments */
+ size_t const buffer_size =
+ Arg_string::find_arg(args, "buffer_size").long_value(0);
+
+ return new (md_alloc())
+ Session_component(Rom::Module::Name(label), buffer_size,
+ _rom_registry);
+ }
+
+ public:
+
+ Root(Server::Entrypoint &ep,
+ Genode::Allocator &md_alloc,
+ Rom::Registry_for_writer &rom_registry)
+ :
+ Genode::Root_component(&ep.rpc_ep(), &md_alloc),
+ _rom_registry(rom_registry)
+ { }
+};
+
+#endif /* _REPORT_SERVICE_H_ */
diff --git a/os/src/server/report_rom/rom_module.h b/os/src/server/report_rom/rom_module.h
new file mode 100644
index 0000000000..ea07f493cc
--- /dev/null
+++ b/os/src/server/report_rom/rom_module.h
@@ -0,0 +1,197 @@
+/*
+ * \brief ROM module written by report service, read by ROM service
+ * \author Norman Feske
+ * \date 2014-01-11
+ */
+
+/*
+ * Copyright (C) 2014 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 _ROM_MODULE_
+#define _ROM_MODULE_
+
+/* Genode includes */
+#include
+#include
+
+namespace Rom {
+ using Genode::size_t;
+ using Genode::Lazy_volatile_object;
+ using Genode::Attached_ram_dataspace;
+
+ class Module;
+ class Registry;
+ class Writer;
+ class Reader;
+ class Buffer;
+
+ typedef Genode::List Module_list;
+ typedef Genode::List Reader_list;
+}
+
+
+struct Rom::Writer { };
+
+
+struct Rom::Reader : Reader_list::Element
+{
+ virtual void notify_module_changed() const = 0;
+};
+
+
+/**
+ * A Rom::Module gets created as soon as either a ROM client or a Report client
+ * refers to it.
+ *
+ * XXX We never know which of both types of client is actually connected. How
+ * should pay for it? There are two choices: The backing store could be payed
+ * by the server, thereby exposing the server to possibe resource exhaustion
+ * triggered by a malicious client. Alternatively, we could make all clients of
+ * either kind of service pay that refer to the Rom::Module. In the worst case,
+ * however, if there are many client for a single report, the paid-for RAM
+ * quota will never be used. For now, we simply allocate the backing store from
+ * the server's quota.
+ *
+ * The Rom::Module gets destroyed no client refers to it anymore.
+ */
+struct Rom::Module : Module_list::Element
+{
+ public:
+
+ typedef Genode::String<200> Name;
+
+ private:
+
+ Name _name;
+
+ /**
+ * The ROM module may be read by any number of ROM clients
+ */
+ Reader_list mutable _readers;
+
+ /**
+ * There must be only one or no writer
+ */
+ Writer const mutable * _writer = 0;
+
+ /**
+ * Dataspace used as backing store
+ *
+ * The buffer for the content is not allocated from the heap to
+ * allow for the immediate release of the underlying backing store when
+ * the module gets destructed.
+ */
+ Lazy_volatile_object _ds;
+
+ /**
+ * Content size, which may less than the capacilty of '_ds'.
+ */
+ size_t _size = 0;
+
+ void _notify_readers() const
+ {
+ for (Reader const *r = _readers.first(); r; r = r->next())
+ r->notify_module_changed();
+ }
+
+
+ /********************************
+ ** Interface used by registry **
+ ********************************/
+
+ friend class Registry;
+
+ /**
+ * Constructor
+ */
+ Module(Name const &name) : _name(name) { }
+
+ bool _reader_is_registered(Reader const &reader) const
+ {
+ for (Reader const *r = _readers.first(); r; r = r->next())
+ if (r == &reader)
+ return true;
+
+ return false;
+ }
+
+ void _register(Reader const &reader) const { _readers.insert(&reader); }
+
+ void _unregister(Reader const &reader) const { _readers.remove(&reader); }
+
+ void _register(Writer const &writer) const
+ {
+ class Unexpected_multiple_writers { };
+ if (_writer)
+ throw Unexpected_multiple_writers();
+
+ _writer = &writer;
+ }
+
+ void _unregister(Writer const &writer) const
+ {
+ class Unexpected_unknown_writer { };
+ if (_writer != &writer)
+ throw Unexpected_unknown_writer();
+
+ _writer = 0;
+ }
+
+ bool _has_name(Name const &name) const { return name == _name; }
+
+ bool _is_in_use() const { return _readers.first() || _writer; }
+
+ public:
+
+ /**
+ * Assign new content to the ROM module
+ *
+ * Called by report service when a new report comes in.
+ */
+ void write_content(char const * const src, size_t const src_len)
+ {
+ _size = 0;
+
+ /* realloc backing store if needed */
+ if (!_ds.is_constructed() || _ds->size() < src_len)
+ _ds.construct(Genode::env()->ram_session(), src_len);
+
+ /* copy content into backing store */
+ _size = src_len;
+ Genode::memcpy(_ds->local_addr(), src, _size);
+
+ /* notify ROM clients that access the module */
+ for (Reader const *r = _readers.first(); r; r = r->next())
+ r->notify_module_changed();
+ }
+
+ /**
+ * Exception type
+ */
+ class Buffer_too_small { };
+
+ /**
+ * Read content of ROM module
+ *
+ * Called by ROM service when a dataspace is obtained by the client.
+ */
+ size_t read_content(char *dst, size_t dst_len) const
+ {
+ if (!_ds.is_constructed())
+ return 0;
+
+ if (dst_len < _size)
+ throw Buffer_too_small();
+
+ Genode::memcpy(dst, _ds->local_addr(), _size);
+ return _size;
+ }
+
+ size_t size() const { return _size; }
+};
+
+#endif /* _ROM_MODULE_ */
diff --git a/os/src/server/report_rom/rom_registry.h b/os/src/server/report_rom/rom_registry.h
new file mode 100644
index 0000000000..b586e317a5
--- /dev/null
+++ b/os/src/server/report_rom/rom_registry.h
@@ -0,0 +1,115 @@
+/*
+ * \brief Registry of ROM modules
+ * \author Norman Feske
+ * \date 2014-01-11
+ */
+
+/*
+ * Copyright (C) 2014 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 _ROM_REGISTRY_H_
+#define _ROM_REGISTRY_H_
+
+/* local includes */
+#include
+
+namespace Rom {
+ struct Registry_for_reader;
+ struct Registry_for_writer;
+}
+
+
+struct Rom::Registry_for_reader
+{
+ virtual Module const &lookup(Reader const &reader, Module::Name const &name) = 0;
+
+ virtual void release(Reader const &reader, Module const &module) = 0;
+};
+
+
+struct Rom::Registry_for_writer
+{
+ virtual Module &lookup(Writer const &writer, Module::Name const &name) = 0;
+
+ virtual void release(Writer const &writer, Module const &module) = 0;
+};
+
+
+struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopyable
+{
+ private:
+
+ Genode::Allocator &_md_alloc;
+
+ Module_list _modules;
+
+ Module &_lookup(Module::Name const name)
+ {
+ for (Module *m = _modules.first(); m; m = m->next())
+ if (m->_has_name(name))
+ return *m;
+
+ /* module does not exist yet, create one */
+
+ /* XXX proper accounting for the used memory is missing */
+ /* XXX if we run out of memory, the server will abort */
+ Module * const module = new (&_md_alloc) Module(name);
+ _modules.insert(module);
+ return *module;
+
+ }
+
+ void _try_to_destroy(Module const &module)
+ {
+ if (module._is_in_use())
+ return;
+
+ _modules.remove(&module);
+ Genode::destroy(&_md_alloc, const_cast(&module));
+ }
+
+ template
+ Module &_lookup(USER const &user, Module::Name const &name)
+ {
+ Module &module = _lookup(name);
+ module._register(user);
+ return module;
+ }
+
+ template
+ void _release(USER const &user, Module const &module)
+ {
+ module._unregister(user);
+ _try_to_destroy(module);
+ }
+
+ public:
+
+ Registry(Genode::Allocator &md_alloc) : _md_alloc(md_alloc) { }
+
+ Module &lookup(Writer const &writer, Module::Name const &name) override
+ {
+ return _lookup(writer, name);
+ }
+
+ void release(Writer const &writer, Module const &module) override
+ {
+ return _release(writer, module);
+ }
+
+ Module const &lookup(Reader const &reader, Module::Name const &name) override
+ {
+ return _lookup(reader, name);
+ }
+
+ void release(Reader const &reader, Module const &module) override
+ {
+ return _release(reader, module);
+ }
+};
+
+#endif /* _ROM_REGISTRY_H_ */
diff --git a/os/src/server/report_rom/rom_service.h b/os/src/server/report_rom/rom_service.h
new file mode 100644
index 0000000000..951acfbb7d
--- /dev/null
+++ b/os/src/server/report_rom/rom_service.h
@@ -0,0 +1,152 @@
+/*
+ * \brief ROM service
+ * \author Norman Feske
+ * \date 2014-01-11
+ */
+
+/*
+ * Copyright (C) 2014 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 _ROM_SERVICE_H_
+#define _ROM_SERVICE_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+namespace Rom {
+ class Session_component;
+ class Root;
+
+ using Genode::Xml_node;
+}
+
+
+class Rom::Session_component : public Genode::Rpc_object,
+ public Reader
+{
+ private:
+
+ Registry_for_reader &_registry;
+ Module const &_module;
+
+ Lazy_volatile_object _ds;
+
+ Genode::Signal_context_capability _sigh;
+
+ public:
+
+ Session_component(Registry_for_reader ®istry,
+ Rom::Module::Name const &name)
+ :
+ _registry(registry), _module(_registry.lookup(*this, name))
+ { }
+
+ ~Session_component()
+ {
+ _registry.release(*this, _module);
+ }
+
+ Genode::Rom_dataspace_capability dataspace() override
+ {
+ using namespace Genode;
+
+ /* replace dataspace by new one */
+ /* XXX we could keep the old dataspace if the size fits */
+ _ds.construct(env()->ram_session(), _module.size());
+
+ /* fill dataspace content with report contained in module */
+ _module.read_content(_ds->local_addr(), _ds->size());
+
+ /* cast RAM into ROM dataspace capability */
+ Dataspace_capability ds_cap = static_cap_cast(_ds->cap());
+
+ return static_cap_cast(ds_cap);
+ }
+
+ void sigh(Genode::Signal_context_capability sigh) override
+ {
+ _sigh = sigh;
+ }
+
+ /**
+ * Implementation of 'Reader' interface
+ */
+ void notify_module_changed() const override
+ {
+ if (_sigh.valid())
+ Genode::Signal_transmitter(_sigh).submit();
+ }
+};
+
+
+class Rom::Root : public Genode::Root_component
+{
+ private:
+
+ Registry_for_reader &_registry;
+ Xml_node const &_config;
+
+ typedef Rom::Module::Name Label;
+
+ /**
+ * Determine module name for label according to the configured policy
+ */
+ Rom::Module::Name _module_name(Label const &label) const
+ {
+ try {
+ for (Xml_node node = _config.sub_node("policy");
+ true; node = node.next("policy")) {
+
+ if (!node.has_attribute("label")
+ || !node.has_attribute("report")
+ || !node.attribute("label").has_value(label.string()))
+ continue;
+
+ char report[Rom::Module::Name::capacity()];
+ node.attribute("report").value(report, sizeof(report));
+ return Rom::Module::Name(report);
+ }
+ } catch (Xml_node::Nonexistent_sub_node) { }
+
+ PWRN("no valid policy for label \"%s\"", label.string());
+ throw Root::Invalid_args();
+ }
+
+ protected:
+
+ Session_component *_create_session(const char *args) override
+ {
+ using namespace Genode;
+
+ /* read label of ROM module from args */
+ char label[Label::capacity()];
+ Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
+
+ return new (md_alloc())
+ Session_component(_registry, _module_name(Label(label)));
+ }
+
+ public:
+
+ Root(Server::Entrypoint &ep,
+ Genode::Allocator &md_alloc,
+ Registry_for_reader ®istry,
+ Xml_node const &config)
+ :
+ Genode::Root_component(&ep.rpc_ep(), &md_alloc),
+ _registry(registry),
+ _config(config)
+ { }
+};
+
+#endif /* _ROM_SERVICE_H_ */
diff --git a/os/src/server/report_rom/target.mk b/os/src/server/report_rom/target.mk
new file mode 100644
index 0000000000..463d566d37
--- /dev/null
+++ b/os/src/server/report_rom/target.mk
@@ -0,0 +1,4 @@
+TARGET = report_rom
+SRC_CC = main.cc
+LIBS = base server config
+INC_DIR += $(PRG_DIR)
diff --git a/os/src/test/report_rom/main.cc b/os/src/test/report_rom/main.cc
new file mode 100644
index 0000000000..d187b63aff
--- /dev/null
+++ b/os/src/test/report_rom/main.cc
@@ -0,0 +1,90 @@
+/*
+ * \brief Test for report-ROM service
+ * \author Norman Feske
+ * \date 2013-01-10
+ */
+
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include
+#include
+#include
+
+
+#define ASSERT(cond) \
+ if (!(cond)) { \
+ PERR("assertion %s failed", #cond); \
+ return -2; }
+
+
+static void report_brightness(Genode::Reporter &reporter, int value)
+{
+ Genode::Reporter::Xml_generator xml(reporter, [&] () {
+ xml.attribute("brightness", value); });
+}
+
+
+int main(int argc, char **argv)
+{
+ using namespace Genode;
+
+ Signal_receiver sig_rec;
+ Signal_context sig_ctx;
+ Signal_context_capability sig_cap = sig_rec.manage(&sig_ctx);
+
+ printf("--- test-report_rom started ---\n");
+
+ printf("Reporter: open session\n");
+ Reporter brightness_reporter("brightness");
+ brightness_reporter.enabled(true);
+
+ printf("Reporter: brightness 10\n");
+ report_brightness(brightness_reporter, 10);
+
+ printf("ROM client: request brightness report\n");
+ Attached_rom_dataspace brightness_rom("test-report_rom/brightness");
+
+ ASSERT(brightness_rom.is_valid());
+
+ brightness_rom.sigh(sig_cap);
+ printf(" -> %s\n", brightness_rom.local_addr());
+
+ printf("Reporter: updated brightness to 77\n");
+ report_brightness(brightness_reporter, 77);
+
+ printf("ROM client: wait for update notification\n");
+ sig_rec.wait_for_signal();
+ printf("ROM client: got signal\n");
+
+ printf("ROM client: request updated brightness report\n");
+ brightness_rom.update();
+ printf(" -> %s\n", brightness_rom.local_addr());
+
+ printf("Reporter: close report session\n");
+ brightness_reporter.enabled(false);
+
+ printf("ROM client: wait for update notification\n");
+ sig_rec.wait_for_signal();
+
+ brightness_rom.update();
+ ASSERT(!brightness_rom.is_valid());
+ printf("ROM client: detected vanished report\n");
+
+ printf("Reporter: start reporting (while the ROM client still listens)\n");
+ brightness_reporter.enabled(true);
+ report_brightness(brightness_reporter, 99);
+
+ printf("ROM client: wait for update notification\n");
+ sig_rec.wait_for_signal();
+
+ printf("--- test-report_rom finished ---\n");
+
+ sig_rec.dissolve(&sig_ctx);
+
+ return 0;
+}
diff --git a/os/src/test/report_rom/target.mk b/os/src/test/report_rom/target.mk
new file mode 100644
index 0000000000..402d555d6d
--- /dev/null
+++ b/os/src/test/report_rom/target.mk
@@ -0,0 +1,4 @@
+TARGET = test-report_rom
+SRC_CC = main.cc
+LIBS = base
+