/* * \brief ROM module written by report service, read by ROM service * \author Norman Feske * \date 2014-01-11 */ /* * Copyright (C) 2014-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #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::Constructible; using Genode::Attached_ram_dataspace; using Genode::Interface; 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 : private Writer_list::Element, Interface { friend class Genode::List; using Writer_list::Element::next; virtual Genode::Session_label label() const = 0; }; struct Rom::Reader : private Reader_list::Element, Interface { friend class Genode::List; using Reader_list::Element::next; virtual void mark_as_outdated() = 0; virtual void mark_as_invalidated() = 0; virtual void notify_client() = 0; }; struct Rom::Readable_module : Interface { /** * 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 : private Module_list::Element, Readable_module { private: friend class Genode::List; /* * Noncopyable */ Module(Module const &); Module &operator = (Module const &); public: using Module_list::Element::next; typedef Genode::String<200> Name; struct Read_policy : Interface { /** * 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 : Interface { /** * 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; Genode::Ram_allocator &_ram; Genode::Region_map &_rm; 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. */ Constructible _ds { }; /** * Content size, which may less than the capacilty of '_ds'. */ size_t _size = 0; /******************************** ** Interface used by registry ** ********************************/ friend class Registry; /** * Constructor * * \param ram allocator for the module's backing store * \param rm region map of the local address space, needed * to access the allocated backing store * \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(Genode::Ram_allocator &ram, Genode::Region_map &rm, Name const &name, Read_policy const &read_policy, Write_policy const &write_policy) : _name(name), _ram(ram), _rm(rm), _read_policy(read_policy), _write_policy(write_policy) { } /************************************************* ** Interface to be used by the 'Registry' only ** *************************************************/ bool _reader_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 _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; } void _notify_permitted_readers() { if (!_last_writer) return; for (Reader *r = _readers.first(); r; r = r->next()) { if (_read_policy.read_permitted(*this, *_last_writer, *r)) r->notify_client(); } } 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.constructed() || _ds->size() < (src_len + 1)) _ds.construct(_ram, _rm, (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->mark_as_outdated(); else r->mark_as_invalidated(); r->notify_client(); } } /** * Readable_module interface */ size_t read_content(Reader const &reader, char *dst, size_t dst_len) const override { if (!_ds.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_ */