From 02d07655cec4e133ddabfc8fbbf9eac99bee5aca Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 23 Sep 2015 11:06:25 +0200 Subject: [PATCH] os: make internal report_rom classes reusable This patch moves the formerly internal classes of the report-ROM service to the public location os/include/report_rom/ so that they can be reused by other components such as the upcoming clipboard. --- .../report_rom/report_service.h | 39 ++- repos/os/include/report_rom/rom_module.h | 285 ++++++++++++++++++ repos/os/include/report_rom/rom_registry.h | 51 ++++ repos/os/include/report_rom/rom_service.h | 180 +++++++++++ repos/os/run/report_rom.run | 1 - repos/os/src/server/report_rom/main.cc | 13 +- repos/os/src/server/report_rom/rom_module.h | 208 ------------- repos/os/src/server/report_rom/rom_registry.h | 131 +++++--- repos/os/src/server/report_rom/rom_service.h | 172 ----------- repos/os/src/test/report_rom/main.cc | 6 +- 10 files changed, 639 insertions(+), 447 deletions(-) rename repos/os/{src/server => include}/report_rom/report_service.h (79%) create mode 100644 repos/os/include/report_rom/rom_module.h create mode 100644 repos/os/include/report_rom/rom_registry.h create mode 100644 repos/os/include/report_rom/rom_service.h delete mode 100644 repos/os/src/server/report_rom/rom_module.h delete mode 100644 repos/os/src/server/report_rom/rom_service.h diff --git a/repos/os/src/server/report_rom/report_service.h b/repos/os/include/report_rom/report_service.h similarity index 79% rename from repos/os/src/server/report_rom/report_service.h rename to repos/os/include/report_rom/report_service.h index 0c28230c7..e8e26dda9 100644 --- a/repos/os/src/server/report_rom/report_service.h +++ b/repos/os/include/report_rom/report_service.h @@ -11,17 +11,15 @@ * under the terms of the GNU General Public License version 2. */ -#ifndef _REPORT_SERVICE_H_ -#define _REPORT_SERVICE_H_ +#ifndef _INCLUDE__REPORT_ROM__REPORT_SERVICE_H_ +#define _INCLUDE__REPORT_ROM__REPORT_SERVICE_H_ /* Genode includes */ #include #include #include -#include - -/* local includes */ -#include +#include +#include namespace Report { @@ -36,6 +34,8 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer Rom::Registry_for_writer &_registry; + Genode::Session_label const _label; + Genode::Attached_ram_dataspace _ds; Rom::Module &_module; @@ -59,26 +59,25 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer public: - Session_component(Rom::Module::Name const &name, size_t buffer_size, + Session_component(Genode::Session_label const &label, size_t buffer_size, Rom::Registry_for_writer ®istry, bool &verbose) : - _registry(registry), + _registry(registry), _label(label), _ds(Genode::env()->ram_session(), buffer_size), - _module(_create_module(name)), + _module(_create_module(label.string())), _verbose(verbose) { } - /** - * Destructor - * - * Clear report when the report session gets closes. - */ ~Session_component() { - _module.write_content(0, 0); _registry.release(*this, _module); } + /** + * Rom::Writer interface + */ + Genode::Session_label label() const override { return _label; } + Dataspace_capability dataspace() override { return _ds.cap(); } void submit(size_t length) override @@ -90,7 +89,7 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer _log_lines(_ds.local_addr(), length); } - _module.write_content(_ds.local_addr(), length); + _module.write_content(*this, _ds.local_addr(), length); } void response_sigh(Genode::Signal_context_capability) override { } @@ -112,16 +111,12 @@ struct Report::Root : Genode::Root_component { 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").ulong_value(0); return new (md_alloc()) - Session_component(Rom::Module::Name(label), buffer_size, + Session_component(Genode::Session_label(args), buffer_size, _rom_registry, _verbose); } @@ -137,4 +132,4 @@ struct Report::Root : Genode::Root_component { } }; -#endif /* _REPORT_SERVICE_H_ */ +#endif /* _INCLUDE__REPORT_ROM__REPORT_SERVICE_H_ */ diff --git a/repos/os/include/report_rom/rom_module.h b/repos/os/include/report_rom/rom_module.h new file mode 100644 index 000000000..7db2256be --- /dev/null +++ b/repos/os/include/report_rom/rom_module.h @@ -0,0 +1,285 @@ +/* + * \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 _INCLUDE__REPORT_ROM__ROM_MODULE_H_ +#define _INCLUDE__REPORT_ROM__ROM_MODULE_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Rom { + using Genode::size_t; + using Genode::Lazy_volatile_object; + using Genode::Attached_ram_dataspace; + + class Module; + class Readable_module; + class Registry; + class Writer; + class Reader; + class Buffer; + + typedef Genode::List Module_list; + typedef Genode::List Reader_list; + typedef Genode::List Writer_list; +} + + +struct Rom::Writer : Writer_list::Element +{ + virtual Genode::Session_label label() const = 0; +}; + + +struct Rom::Reader : Reader_list::Element +{ + virtual void notify_module_changed() = 0; + virtual void notify_module_invalidated() = 0; +}; + + +struct Rom::Readable_module +{ + /** + * Exception type + */ + class Buffer_too_small { }; + + /** + * Read content of ROM module + * + * Called by ROM service when a dataspace is obtained by the client. + * + * \throw Buffer_too_small + */ + virtual size_t read_content(Reader const &reader, char *dst, + size_t dst_len) const = 0; + + virtual size_t size() 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 paid + * 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 when no client refers to it anymore. + */ +struct Rom::Module : Module_list::Element, Readable_module +{ + public: + + typedef Genode::String<200> Name; + + struct Read_policy + { + /** + * Return true if the reader is allowed to read the module content + */ + virtual bool read_permitted(Module const &, + Writer const &, Reader const &) const = 0; + }; + + struct Write_policy + { + /** + * Return true of the writer is permitted to write content + * + * This policy hook can be used to implement dynamic policies + * as employed by the clipboard, which blocks reports from + * unfocused clients. + */ + virtual bool write_permitted(Module const &, Writer const &) const = 0; + }; + + private: + + Name _name; + + Read_policy const &_read_policy; + Write_policy const &_write_policy; + + Reader_list mutable _readers; + Writer_list mutable _writers; + + /** + * Origin of the content currently stored in the module + */ + Writer const *_last_writer = nullptr; + + /** + * 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; + + + /******************************** + ** Interface used by registry ** + ********************************/ + + friend class Registry; + + /** + * Constructor + * + * \param name module name + * \param read_policy policy hook function that is evaluated each + * time when the module content is obtained + * \param write_policy policy hook function that is evaluated each + * time when the module content is changed + */ + Module(Name const &name, + Read_policy const &read_policy, + Write_policy const &write_policy) + : + _name(name), _read_policy(read_policy), _write_policy(write_policy) + { } + + + /************************************************* + ** Interface to be used by the 'Registry' only ** + *************************************************/ + + 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 &reader) { _readers.insert(&reader); } + + void _unregister(Reader &reader) { _readers.remove(&reader); } + + void _register(Writer &writer) + { + _writers.insert(&writer); + } + + void _unregister(Writer const &writer) + { + _writers.remove(&writer); + + /* clear content if its origin disappears */ + if (_last_writer == &writer) { + Genode::memset(_ds->local_addr(), 0, _size); + _size = 0; + _last_writer = nullptr; + } + } + + bool _has_name(Name const &name) const { return name == _name; } + + bool _is_in_use() const + { + return _readers.first() || _writers.first(); + } + + unsigned _num_writers() const + { + unsigned cnt = 0; + for (Writer const *w = _writers.first(); w; w = w->next()) + cnt++; + + return cnt; + } + + public: + + /** + * Assign new content to the ROM module + * + * Called by report service when a new report comes in. + */ + void write_content(Writer const &writer, char const * const src, size_t const src_len) + { + if (!_write_policy.write_permitted(*this, writer)) + return; + + _size = 0; + + _last_writer = &writer; + + /* + * Realloc backing store if needed + * + * Take a terminating zero into account, which we append to each + * report. This way, we do not need to trust report clients to + * append a zero termination to textual reports. + */ + if (!_ds.is_constructed() || _ds->size() < (src_len + 1)) + _ds.construct(Genode::env()->ram_session(), (src_len + 1)); + + /* copy content into backing store */ + _size = src_len; + Genode::memcpy(_ds->local_addr(), src, _size); + + /* append zero termination */ + _ds->local_addr()[src_len] = 0; + + /* notify ROM clients that access the module */ + for (Reader *r = _readers.first(); r; r = r->next()) { + + if (_read_policy.read_permitted(*this, *_last_writer, *r)) + r->notify_module_changed(); + else + r->notify_module_invalidated(); + } + } + + /** + * Readable_module interface + */ + size_t read_content(Reader const &reader, char *dst, size_t dst_len) const override + { + if (!_ds.is_constructed() || !_last_writer) + return 0; + + if (!_read_policy.read_permitted(*this, *_last_writer, reader)) + return 0; + + if (dst_len < _size) + throw Buffer_too_small(); + + Genode::memcpy(dst, _ds->local_addr(), _size); + return _size; + } + + virtual size_t size() const override { return _size; } + + Name name() const { return _name; } +}; + +#endif /* _INCLUDE__REPORT_ROM__ROM_MODULE_H_ */ diff --git a/repos/os/include/report_rom/rom_registry.h b/repos/os/include/report_rom/rom_registry.h new file mode 100644 index 000000000..9dfe6a8f3 --- /dev/null +++ b/repos/os/include/report_rom/rom_registry.h @@ -0,0 +1,51 @@ +/* + * \brief Interfaces for the 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 _INCLUDE__REPORT_ROM__ROM_REGISTRY_H_ +#define _INCLUDE__REPORT_ROM__ROM_REGISTRY_H_ + +#include + +namespace Rom { + struct Registry_for_reader; + struct Registry_for_writer; +} + + +struct Rom::Registry_for_reader +{ + /** + * Exception type + */ + class Lookup_failed { }; + + /** + * Lookup ROM module for given ROM session label + * + * \throw Lookup_failed + */ + virtual Readable_module &lookup(Reader &reader, + Module::Name const &rom_label) = 0; + + virtual void release(Reader &reader, Readable_module &module) = 0; +}; + + +struct Rom::Registry_for_writer +{ + virtual Module &lookup(Writer &writer, Module::Name const &name) = 0; + + virtual void release(Writer &writer, Module &module) = 0; +}; + +#endif /* _INCLUDE__REPORT_ROM__ROM_REGISTRY_H_ */ diff --git a/repos/os/include/report_rom/rom_service.h b/repos/os/include/report_rom/rom_service.h new file mode 100644 index 000000000..2a52c3fb8 --- /dev/null +++ b/repos/os/include/report_rom/rom_service.h @@ -0,0 +1,180 @@ +/* + * \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 _INCLUDE__REPORT_ROM__ROM_SERVICE_H_ +#define _INCLUDE__REPORT_ROM__ROM_SERVICE_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +namespace Rom { + class Session_component; + class Module_name_fn; + class Root; + + using Genode::Xml_node; +} + + +class Rom::Session_component : public Genode::Rpc_object, + public Reader +{ + private: + + Registry_for_reader &_registry; + + Genode::Session_label const _label; + + Readable_module &_module; + + Readable_module &_init_module(Genode::Session_label const &label) + { + try { + return _registry.lookup(*this, label.string()); } + catch (Registry_for_reader::Lookup_failed) { + throw Genode::Root::Invalid_args(); } + } + + Lazy_volatile_object _ds; + + size_t _content_size = 0; + + /** + * Keep state of valid content to notify the client only once when + * the ROM module becomes invalid. + */ + bool _valid = false; + + Genode::Signal_context_capability _sigh; + + void _notify_client() + { + if (_sigh.valid()) + Genode::Signal_transmitter(_sigh).submit(); + } + + public: + + Session_component(Registry_for_reader ®istry, + Genode::Session_label const &label) + : + _registry(registry), _label(label), _module(_init_module(label)) + { } + + ~Session_component() + { + _registry.release(*this, _module); + } + + Genode::Session_label label() const { return _label; } + + 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 */ + _content_size = + _module.read_content(*this, _ds->local_addr(), _ds->size()); + + _valid = _content_size > 0; + + /* cast RAM into ROM dataspace capability */ + Dataspace_capability ds_cap = static_cap_cast(_ds->cap()); + return static_cap_cast(ds_cap); + } + + bool update() override + { + if (!_ds.is_constructed() || _module.size() > _ds->size()) + return false; + + size_t const new_content_size = + _module.read_content(*this, _ds->local_addr(), _ds->size()); + + /* clear difference between old and new content */ + if (new_content_size < _content_size) + Genode::memset(_ds->local_addr() + new_content_size, 0, + _content_size - new_content_size); + + _content_size = new_content_size; + + _valid = _content_size > 0; + + return true; + } + + void sigh(Genode::Signal_context_capability sigh) override + { + _sigh = sigh; + } + + /** + * Reader interface + */ + void notify_module_changed() override + { + _notify_client(); + } + + /** + * Reader interface + */ + void notify_module_invalidated() override + { + /* deliver a signal for an invalidated module only once */ + if (!_valid) + return; + + _valid = false; + _notify_client(); + } +}; + + +class Rom::Root : public Genode::Root_component +{ + private: + + Registry_for_reader &_registry; + + protected: + + Session_component *_create_session(const char *args) override + { + using namespace Genode; + + return new (md_alloc()) + Session_component(_registry, Session_label(args)); + } + + public: + + Root(Server::Entrypoint &ep, + Genode::Allocator &md_alloc, + Registry_for_reader ®istry) + : + Genode::Root_component(&ep.rpc_ep(), &md_alloc), + _registry(registry) + { } +}; + +#endif /* _INCLUDE__REPORT_ROM__ROM_SERVICE_H_ */ diff --git a/repos/os/run/report_rom.run b/repos/os/run/report_rom.run index c54a640d5..a30c4300c 100644 --- a/repos/os/run/report_rom.run +++ b/repos/os/run/report_rom.run @@ -69,7 +69,6 @@ compare_output_to { [init -> test-report_rom] -> [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: ROM is available despite report was closed - OK [init -> test-report_rom] Reporter: start reporting (while the ROM client still listens) [init -> test-report_rom] ROM client: wait for update notification diff --git a/repos/os/src/server/report_rom/main.cc b/repos/os/src/server/report_rom/main.cc index acbbdc43a..da4eefaf5 100644 --- a/repos/os/src/server/report_rom/main.cc +++ b/repos/os/src/server/report_rom/main.cc @@ -16,10 +16,11 @@ #include #include #include +#include +#include /* local includes */ -#include -#include +#include "rom_registry.h" namespace Server { @@ -36,9 +37,7 @@ struct Server::Main Genode::Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() }; - Rom::Registry rom_registry = { sliced_heap }; - - Xml_node _rom_config_node() + Xml_node _rom_config_node() const { try { return Genode::config()->xml_node().sub_node("rom"); } @@ -48,7 +47,7 @@ struct Server::Main } } - Xml_node rom_config = _rom_config_node(); + Rom::Registry rom_registry = { sliced_heap, _rom_config_node() }; bool _verbose_config() { @@ -60,7 +59,7 @@ struct Server::Main bool verbose = _verbose_config(); Report::Root report_root = { ep, sliced_heap, rom_registry, verbose }; - Rom ::Root rom_root = { ep, sliced_heap, rom_registry, rom_config}; + Rom ::Root rom_root = { ep, sliced_heap, rom_registry }; Main(Entrypoint &ep) : ep(ep) { diff --git a/repos/os/src/server/report_rom/rom_module.h b/repos/os/src/server/report_rom/rom_module.h deleted file mode 100644 index d7d15884c..000000000 --- a/repos/os/src/server/report_rom/rom_module.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * \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 - * - * Take a terminating zero into account, which we append to each - * report. This way, we do not need to trust report clients to - * append a zero termination to textual reports. - */ - if (!_ds.is_constructed() || _ds->size() < (src_len + 1)) - _ds.construct(Genode::env()->ram_session(), (src_len + 1)); - - /* copy content into backing store */ - _size = src_len; - Genode::memcpy(_ds->local_addr(), src, _size); - - /* append zero termination */ - _ds->local_addr()[src_len] = 0; - - /* 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; } - - Name name() const { return _name; } -}; - -#endif /* _ROM_MODULE_ */ diff --git a/repos/os/src/server/report_rom/rom_registry.h b/repos/os/src/server/report_rom/rom_registry.h index b586e317a..cbfd2bc54 100644 --- a/repos/os/src/server/report_rom/rom_registry.h +++ b/repos/os/src/server/report_rom/rom_registry.h @@ -14,29 +14,10 @@ #ifndef _ROM_REGISTRY_H_ #define _ROM_REGISTRY_H_ -/* local includes */ -#include +/* Genode 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; -}; +namespace Rom { struct Registry; } struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopyable @@ -45,8 +26,38 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy Genode::Allocator &_md_alloc; + Xml_node _config; + Module_list _modules; + struct Read_write_policy : Module::Read_policy, Module::Write_policy + { + bool read_permitted(Module const &, + Writer const &, + Reader const &) const override + { + /* + * The access-control policy is applied at the ROM-session + * construction time by applying the '_report_name' method + * on the session label. Once connected to a ROM module, + * the ROM client is always allowed to read the ROM content. + */ + return true; + } + + bool write_permitted(Module const &, Writer const &) const override + { + /* + * Because the report-session label is used as the module name + * for the writer, each report session refers to a distinct + * module. Report client can write to their respective modules + * at any time. + */ + return true; + } + + } _read_write_policy; + Module &_lookup(Module::Name const name) { for (Module *m = _modules.first(); m; m = m->next()) @@ -57,10 +68,12 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy /* 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); + + Module * const module = new (&_md_alloc) + Module(name, _read_write_policy, _read_write_policy); + _modules.insert(module); return *module; - } void _try_to_destroy(Module const &module) @@ -73,7 +86,7 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy } template - Module &_lookup(USER const &user, Module::Name const &name) + Module &_lookup(USER &user, Module::Name const &name) { Module &module = _lookup(name); module._register(user); @@ -81,34 +94,82 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy } template - void _release(USER const &user, Module const &module) + void _release(USER &user, Module const &module) { - module._unregister(user); + /* + * The '_release' function is called by both the report service + * and the ROM service. The latter has merely a 'const' version + * of the module because it is not supposed to modify it. However, + * when closing a ROM session, we have to disassociate the ROM + * session from the module. To do that, we need a non-const + * reference to the module. + */ + const_cast(module)._unregister(user); _try_to_destroy(module); } + /** + * Return report name that corresponds to the given ROM session label + * + * \throw Registry_for_reader::Lookup_failed + */ + Module::Name _report_name(Module::Name const &rom_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(rom_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\"", rom_label.string()); + throw Root::Invalid_args(); + } + public: - Registry(Genode::Allocator &md_alloc) : _md_alloc(md_alloc) { } + Registry(Genode::Allocator &md_alloc, Xml_node config) + : + _md_alloc(md_alloc), _config(config) + { } - Module &lookup(Writer const &writer, Module::Name const &name) override + Module &lookup(Writer &writer, Module::Name const &name) override { - return _lookup(writer, name); + Module &module = _lookup(writer, name); + + /* + * Enforce invariant that each module can have only one writer at a + * time. + */ + if (module._num_writers() > 1) { + release(writer, module); + throw Root::Invalid_args(); + } + + return module; } - void release(Writer const &writer, Module const &module) override + void release(Writer &writer, Module &module) override { return _release(writer, module); } - Module const &lookup(Reader const &reader, Module::Name const &name) override + Readable_module &lookup(Reader &reader, Module::Name const &rom_label) override { - return _lookup(reader, name); + return _lookup(reader, _report_name(rom_label)); } - void release(Reader const &reader, Module const &module) override + void release(Reader &reader, Readable_module &module) override { - return _release(reader, module); + return _release(reader, static_cast(module)); } }; diff --git a/repos/os/src/server/report_rom/rom_service.h b/repos/os/src/server/report_rom/rom_service.h deleted file mode 100644 index a73bdc990..000000000 --- a/repos/os/src/server/report_rom/rom_service.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * \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; - - size_t _content_size = 0; - - 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 */ - _content_size = _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); - } - - bool update() override - { - if (!_ds.is_constructed() || _module.size() > _ds->size()) - return false; - - size_t const new_content_size = - _module.read_content(_ds->local_addr(), _ds->size()); - - /* clear difference between old and new content */ - if (new_content_size < _content_size) - Genode::memset(_ds->local_addr() + new_content_size, 0, - _content_size - new_content_size); - - _content_size = new_content_size; - - return true; - } - - 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/repos/os/src/test/report_rom/main.cc b/repos/os/src/test/report_rom/main.cc index 1bd900979..e978cac71 100644 --- a/repos/os/src/test/report_rom/main.cc +++ b/repos/os/src/test/report_rom/main.cc @@ -14,6 +14,7 @@ #include #include #include +#include #define ASSERT(cond) \ @@ -68,8 +69,9 @@ int main(int argc, char **argv) printf("Reporter: close report session\n"); brightness_reporter.enabled(false); - printf("ROM client: wait for update notification\n"); - sig_rec.wait_for_signal(); + /* give report_rom some time to close the report session */ + static Timer::Connection timer; + timer.msleep(250); brightness_rom.update(); ASSERT(brightness_rom.is_valid());