Service for reflecting reports as ROM modules

Issue #1026
This commit is contained in:
Norman Feske 2014-01-12 00:29:17 +01:00 committed by Christian Helmuth
parent 7c23d6cd81
commit a60966150e
11 changed files with 847 additions and 0 deletions

View File

@ -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':

70
os/run/report_rom.run Normal file
View File

@ -0,0 +1,70 @@
build "core init server/report_rom test/report_rom drivers/timer"
create_boot_directory
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="CPU"/>
<service name="RM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="IRQ"/>
<service name="IO_PORT"/>
<service name="IO_MEM"/>
<service name="SIGNAL"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="report_rom">
<resource name="RAM" quantum="2M"/>
<provides> <service name="ROM" />
<service name="Report" /> </provides>
</start>
<start name="test-report_rom">
<resource name="RAM" quantum="2M"/>
<route>
<service name="ROM">
<if-arg key="label" value="test-report_rom/brightness" />
<child name="report_rom" />
</service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</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] -> <brightness brightness="10"/>
[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] -> <brightness brightness="77"/>
[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 ---
}

View File

@ -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:
! <config>
! <rom>
! <policy label="decorator -> pointer" report="nitpicker -> pointer"/>
! <policy ... />
! ...
! </rom>
! </config>
The label of an incoming ROM session is matched against the 'label' attribute
of all '<policy>' 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.

View File

@ -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 <base/heap.h>
#include <base/env.h>
#include <os/server.h>
#include <os/config.h>
/* local includes */
#include <rom_service.h>
#include <report_service.h>
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 <rom> configuration");
return Xml_node("<rom>");
}
}
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);
}
}

View File

@ -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 <util/arg_string.h>
#include <report_session/report_session.h>
#include <root/component.h>
/* local includes */
#include <rom_registry.h>
namespace Report {
struct Session_component;
struct Root;
}
struct Report::Session_component : Genode::Rpc_object<Session>, 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 &registry)
:
_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<char>(),
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<Session_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<Session_component>(&ep.rpc_ep(), &md_alloc),
_rom_registry(rom_registry)
{ }
};
#endif /* _REPORT_SERVICE_H_ */

View File

@ -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 <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 */
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<char>(), 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<char>(), _size);
return _size;
}
size_t size() const { return _size; }
};
#endif /* _ROM_MODULE_ */

View File

@ -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 <rom_module.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;
};
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 *>(&module));
}
template <typename USER>
Module &_lookup(USER const &user, Module::Name const &name)
{
Module &module = _lookup(name);
module._register(user);
return module;
}
template <typename USER>
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_ */

View File

@ -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 <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;
Genode::Signal_context_capability _sigh;
public:
Session_component(Registry_for_reader &registry,
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<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);
}
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 &registry,
Xml_node const &config)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_registry(registry),
_config(config)
{ }
};
#endif /* _ROM_SERVICE_H_ */

View File

@ -0,0 +1,4 @@
TARGET = report_rom
SRC_CC = main.cc
LIBS = base server config
INC_DIR += $(PRG_DIR)

View File

@ -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 <base/printf.h>
#include <os/reporter.h>
#include <os/attached_rom_dataspace.h>
#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<char>());
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<char>());
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;
}

View File

@ -0,0 +1,4 @@
TARGET = test-report_rom
SRC_CC = main.cc
LIBS = base