mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 19:34:56 +00:00
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.
This commit is contained in:
parent
702646a4a3
commit
02d07655ce
@ -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 <util/arg_string.h>
|
||||
#include <report_session/report_session.h>
|
||||
#include <root/component.h>
|
||||
#include <os/print_lines.h>
|
||||
|
||||
/* local includes */
|
||||
#include <rom_registry.h>
|
||||
#include <util/print_lines.h>
|
||||
#include <report_rom/rom_registry.h>
|
||||
|
||||
|
||||
namespace Report {
|
||||
@ -36,6 +34,8 @@ struct Report::Session_component : Genode::Rpc_object<Session>, 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<Session>, 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<Session>, Rom::Writer
|
||||
_log_lines(_ds.local_addr<char>(), length);
|
||||
}
|
||||
|
||||
_module.write_content(_ds.local_addr<char>(), length);
|
||||
_module.write_content(*this, _ds.local_addr<char>(), length);
|
||||
}
|
||||
|
||||
void response_sigh(Genode::Signal_context_capability) override { }
|
||||
@ -112,16 +111,12 @@ struct Report::Root : Genode::Root_component<Session_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<Session_component>
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _REPORT_SERVICE_H_ */
|
||||
#endif /* _INCLUDE__REPORT_ROM__REPORT_SERVICE_H_ */
|
285
repos/os/include/report_rom/rom_module.h
Normal file
285
repos/os/include/report_rom/rom_module.h
Normal file
@ -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 <util/volatile_object.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
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> Module_list;
|
||||
typedef Genode::List<Reader> Reader_list;
|
||||
typedef Genode::List<Writer> 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<Attached_ram_dataspace> _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<char>(), 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<char>(), src, _size);
|
||||
|
||||
/* append zero termination */
|
||||
_ds->local_addr<char>()[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<char>(), _size);
|
||||
return _size;
|
||||
}
|
||||
|
||||
virtual size_t size() const override { return _size; }
|
||||
|
||||
Name name() const { return _name; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__REPORT_ROM__ROM_MODULE_H_ */
|
51
repos/os/include/report_rom/rom_registry.h
Normal file
51
repos/os/include/report_rom/rom_registry.h
Normal file
@ -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 <report_rom/rom_module.h>
|
||||
|
||||
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_ */
|
180
repos/os/include/report_rom/rom_service.h
Normal file
180
repos/os/include/report_rom/rom_service.h
Normal file
@ -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 <util/arg_string.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <rom_session/rom_session.h>
|
||||
#include <root/component.h>
|
||||
#include <report_rom/rom_registry.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
namespace Rom {
|
||||
class Session_component;
|
||||
class Module_name_fn;
|
||||
class Root;
|
||||
|
||||
using Genode::Xml_node;
|
||||
}
|
||||
|
||||
|
||||
class Rom::Session_component : public Genode::Rpc_object<Genode::Rom_session>,
|
||||
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<Genode::Attached_ram_dataspace> _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<char>(), _ds->size());
|
||||
|
||||
_valid = _content_size > 0;
|
||||
|
||||
/* cast RAM into ROM dataspace capability */
|
||||
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ds->cap());
|
||||
return static_cap_cast<Rom_dataspace>(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<char>(), _ds->size());
|
||||
|
||||
/* clear difference between old and new content */
|
||||
if (new_content_size < _content_size)
|
||||
Genode::memset(_ds->local_addr<char>() + 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<Session_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<Session_component>(&ep.rpc_ep(), &md_alloc),
|
||||
_registry(registry)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__REPORT_ROM__ROM_SERVICE_H_ */
|
@ -69,7 +69,6 @@ compare_output_to {
|
||||
[init -> test-report_rom] -> <brightness brightness="77"/>
|
||||
[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
|
||||
|
@ -16,10 +16,11 @@
|
||||
#include <base/env.h>
|
||||
#include <os/server.h>
|
||||
#include <os/config.h>
|
||||
#include <report_rom/rom_service.h>
|
||||
#include <report_rom/report_service.h>
|
||||
|
||||
/* local includes */
|
||||
#include <rom_service.h>
|
||||
#include <report_service.h>
|
||||
#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)
|
||||
{
|
||||
|
@ -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 <util/volatile_object.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
|
||||
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> Module_list;
|
||||
typedef Genode::List<Reader> 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<Attached_ram_dataspace> _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<char>(), src, _size);
|
||||
|
||||
/* append zero termination */
|
||||
_ds->local_addr<char>()[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<char>(), _size);
|
||||
return _size;
|
||||
}
|
||||
|
||||
size_t size() const { return _size; }
|
||||
|
||||
Name name() const { return _name; }
|
||||
};
|
||||
|
||||
#endif /* _ROM_MODULE_ */
|
@ -14,29 +14,10 @@
|
||||
#ifndef _ROM_REGISTRY_H_
|
||||
#define _ROM_REGISTRY_H_
|
||||
|
||||
/* local includes */
|
||||
#include <rom_module.h>
|
||||
/* Genode includes */
|
||||
#include <report_rom/rom_registry.h>
|
||||
|
||||
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 <typename USER>
|
||||
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 <typename USER>
|
||||
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 &>(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 &>(module));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 <util/arg_string.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <rom_session/rom_session.h>
|
||||
#include <root/component.h>
|
||||
|
||||
/* local includes */
|
||||
#include <rom_registry.h>
|
||||
|
||||
namespace Rom {
|
||||
class Session_component;
|
||||
class Root;
|
||||
|
||||
using Genode::Xml_node;
|
||||
}
|
||||
|
||||
|
||||
class Rom::Session_component : public Genode::Rpc_object<Genode::Rom_session>,
|
||||
public Reader
|
||||
{
|
||||
private:
|
||||
|
||||
Registry_for_reader &_registry;
|
||||
Module const &_module;
|
||||
|
||||
Lazy_volatile_object<Genode::Attached_ram_dataspace> _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<char>(), _ds->size());
|
||||
|
||||
/* cast RAM into ROM dataspace capability */
|
||||
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ds->cap());
|
||||
|
||||
return static_cap_cast<Rom_dataspace>(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<char>(), _ds->size());
|
||||
|
||||
/* clear difference between old and new content */
|
||||
if (new_content_size < _content_size)
|
||||
Genode::memset(_ds->local_addr<char>() + 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<Session_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<Session_component>(&ep.rpc_ep(), &md_alloc),
|
||||
_registry(registry),
|
||||
_config(config)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _ROM_SERVICE_H_ */
|
@ -14,6 +14,7 @@
|
||||
#include <base/printf.h>
|
||||
#include <os/reporter.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
|
||||
#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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user