From a60966150e56a89bfb4da854abe840b27892e7ab Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Sun, 12 Jan 2014 00:29:17 +0100 Subject: [PATCH] Service for reflecting reports as ROM modules Issue #1026 --- doc/components.txt | 4 + os/run/report_rom.run | 70 ++++++++ os/src/server/report_rom/README | 24 +++ os/src/server/report_rom/main.cc | 74 ++++++++ os/src/server/report_rom/report_service.h | 113 +++++++++++++ os/src/server/report_rom/rom_module.h | 197 ++++++++++++++++++++++ os/src/server/report_rom/rom_registry.h | 115 +++++++++++++ os/src/server/report_rom/rom_service.h | 152 +++++++++++++++++ os/src/server/report_rom/target.mk | 4 + os/src/test/report_rom/main.cc | 90 ++++++++++ os/src/test/report_rom/target.mk | 4 + 11 files changed, 847 insertions(+) create mode 100644 os/run/report_rom.run create mode 100644 os/src/server/report_rom/README create mode 100644 os/src/server/report_rom/main.cc create mode 100644 os/src/server/report_rom/report_service.h create mode 100644 os/src/server/report_rom/rom_module.h create mode 100644 os/src/server/report_rom/rom_registry.h create mode 100644 os/src/server/report_rom/rom_service.h create mode 100644 os/src/server/report_rom/target.mk create mode 100644 os/src/test/report_rom/main.cc create mode 100644 os/src/test/report_rom/target.mk diff --git a/doc/components.txt b/doc/components.txt index da228ce1d..39997587e 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 000000000..412beb591 --- /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 000000000..8fc92a1ec --- /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 000000000..89ee8186c --- /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 000000000..063a544a6 --- /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 000000000..ea07f493c --- /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 000000000..b586e317a --- /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 000000000..951acfbb7 --- /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 000000000..463d566d3 --- /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 000000000..d187b63af --- /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 000000000..402d555d6 --- /dev/null +++ b/os/src/test/report_rom/target.mk @@ -0,0 +1,4 @@ +TARGET = test-report_rom +SRC_CC = main.cc +LIBS = base +