Add remote_rom proxy component

Fixes #20
This commit is contained in:
Johannes Schlatow
2016-07-05 11:51:45 +02:00
committed by Norman Feske
parent 04c42f34bc
commit 2687de2376
17 changed files with 1441 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
/*
* \brief Backend interface used by the Remote_rom client and server
* \author Johannes Schlatow
* \date 2016-02-17
*/
/*
* Copyright (C) 2016 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__REMOTE_ROM__BACKEND_BASE_H_
#define __INCLUDE__REMOTE_ROM__BACKEND_BASE_H_
#include <rom_forwarder.h>
#include <rom_receiver.h>
namespace Remote_rom {
struct Backend_server_base;
struct Backend_client_base;
class Exception : public ::Genode::Exception { };
Backend_server_base &backend_init_server();
Backend_client_base &backend_init_client();
};
struct Remote_rom::Backend_server_base
{
virtual void send_update() = 0;
virtual void register_forwarder(Rom_forwarder_base *forwarder) = 0;
};
struct Remote_rom::Backend_client_base
{
virtual void register_receiver(Rom_receiver_base *receiver) = 0;
};
#endif

View File

@@ -0,0 +1,31 @@
/*
* \brief Interface used by the backend to transfer ROM content to the remote clients.
* \author Johannes Schlatow
* \date 2016-02-17
*/
/*
* Copyright (C) 2016 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__REMOTE_ROM__ROM_FORWARDER_H_
#define __INCLUDE__REMOTE_ROM__ROM_FORWARDER_H_
#include <base/stdint.h>
namespace Remote_rom {
using Genode::size_t;
struct Rom_forwarder_base;
}
struct Remote_rom::Rom_forwarder_base
{
virtual const char *module_name() const = 0;
virtual size_t content_size() const = 0;
virtual size_t transfer_content(char *dst, size_t dst_len, size_t offset=0) const = 0;
};
#endif

View File

@@ -0,0 +1,31 @@
/*
* \brief Interface used by the backend to write the ROM data received from the remote server
* \author Johannes Schlatow
* \date 2016-02-18
*/
/*
* Copyright (C) 2016 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__REMOTE_ROM__ROM_RECEIVER_H_
#define __INCLUDE__REMOTE_ROM__ROM_RECEIVER_H_
#include <base/stdint.h>
namespace Remote_rom {
using Genode::size_t;
struct Rom_receiver_base;
}
struct Remote_rom::Rom_receiver_base
{
virtual const char *module_name() const = 0;
virtual char* start_new_content(size_t len) = 0;
virtual void commit_new_content(bool abort=false) = 0;
};
#endif

View File

@@ -0,0 +1,3 @@
INC_DIR += $(REP_DIR)/include/remote_rom
vpath % $(REP_DIR)/src/lib/remote_rom

View File

@@ -0,0 +1,9 @@
# \author Johannes Schlatow
# \date 2016-03-19
SRC_CC += backend/nic_ip/backend.cc
LIBS += base config net
# include less specific configuration
include $(REP_DIR)/lib/mk/remote_rom_backend.inc

View File

@@ -0,0 +1,147 @@
#
# Build
#
build { core init drivers/timer
server/dynamic_rom
proxy/remote_rom/backend/nic_ip
app/rom_logger
drivers/nic
server/nic_bridge
}
create_boot_directory
#
# Generate config
#
install_config {
<config>
<parent-provides>
<service name="CAP"/>
<service name="LOG"/>
<service name="RM"/>
<service name="SIGNAL"/>
<service name="ROM" />
<service name="RAM" />
<service name="CPU" />
<service name="PD" />
<service name="IO_MEM" />
<service name="IRQ" />
</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="dynamic_rom">
<resource name="RAM" quantum="4M"/>
<provides><service name="ROM"/></provides>
<config verbose="yes">
<rom name="test">
<sleep milliseconds="1000" />
<inline description="disable">
<test enabled="no"/>
</inline>
<sleep milliseconds="5000" />
<inline description="enable">
<test enabled="yes"/>
</inline>
<sleep milliseconds="10000" />
<inline description="finished"/>
</rom>
</config>
</start>
<start name="remote_rom_server">
<resource name="RAM" quantum="8M"/>
<route>
<service name="ROM"> <child name="dynamic_rom"/> </service>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> </any-service>
</route>
<config>
<remote_rom localname="test" name="remote" src="192.168.42.10" dst="192.168.42.11">
<default>
<default />
</default>
</remote_rom>
</config>
</start>
<start name="remote_rom_client">
<resource name="RAM" quantum="8M"/>
<route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> </any-service>
</route>
<provides><service name="ROM"/></provides>
<config>
<remote_rom name="remote" src="192.168.42.11" dst="192.168.42.10">
<default>
<default />
</default>
</remote_rom>
</config>
</start>
<start name="nic_bridge">
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>
<route>
<service name="Nic"> <child name="nic_drv"/> </service>
<any-service> <parent/> </any-service>
</route>
<config>
<nic tap="tap1"/>
</config>
</start>
<start name="nic_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>
<config>
<nic tap="tap1"/>
</config>
</start>
<start name="rom_logger">
<resource name="RAM" quantum="4M"/>
<config rom="remote" />
<route>
<service name="ROM"> <child name="remote_rom_client"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
</config>}
#
# Boot image
#
build_boot_image { core init timer
remote_rom_server
remote_rom_client
dynamic_rom
rom_logger
nic_drv
nic_bridge
}
append qemu_args " -nographic "
run_genode_until {.*change \(finished\).*} 30
grep_output {init -> rom_logger}
compare_output_to {
[init -> rom_logger] ROM 'remote':
[init -> rom_logger] <default />
[init -> rom_logger]
[init -> rom_logger] ROM 'remote':
[init -> rom_logger] <default />
[init -> rom_logger]
[init -> rom_logger] ROM 'remote':
[init -> rom_logger] <test enabled="no"/>
[init -> rom_logger]
[init -> rom_logger] ROM 'remote':
[init -> rom_logger] <test enabled="yes"/>
[init -> rom_logger]
}

View File

@@ -0,0 +1,75 @@
#
# Build
#
build { core init drivers/timer
app/rom_logger
proxy/remote_rom/backend/nic_ip/client
drivers/nic
}
create_boot_directory
#
# Generate config
#
install_config {
<config>
<parent-provides>
<service name="CAP"/>
<service name="LOG"/>
<service name="RM"/>
<service name="SIGNAL"/>
<service name="ROM" />
<service name="RAM" />
<service name="CPU" />
<service name="PD" />
<service name="IO_MEM" />
<service name="IRQ" />
</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="rom_logger">
<resource name="RAM" quantum="4M"/>
<config rom="remote" />
<route>
<service name="ROM"> <child name="remote_rom_client"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
<start name="remote_rom_client">
<resource name="RAM" quantum="8M"/>
<provides><service name="ROM"/></provides>
<config>
<remote_rom name="remote" src="192.168.42.11" dst="192.168.42.10" />
</config>
</start>
<start name="nic_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>
<config>
<nic tap="tap1"/>
</config>
</start>
</config>}
#
# Boot image
#
build_boot_image { core init timer
remote_rom_client
rom_logger
nic_drv
}
append qemu_args " -nographic "
append qemu_args " -net tap,ifname=tap2 "
run_genode_until forever

View File

@@ -0,0 +1,89 @@
#
# Build
#
build { core init drivers/timer
server/dynamic_rom
proxy/remote_rom/backend/nic_ip/server
drivers/nic
}
create_boot_directory
#
# Generate config
#
install_config {
<config>
<parent-provides>
<service name="CAP"/>
<service name="LOG"/>
<service name="RM"/>
<service name="SIGNAL"/>
<service name="ROM" />
<service name="RAM" />
<service name="CPU" />
<service name="PD" />
<service name="IO_MEM" />
<service name="IRQ" />
</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="dynamic_rom">
<resource name="RAM" quantum="4M"/>
<provides><service name="ROM"/></provides>
<config verbose="yes">
<rom name="test">
<sleep milliseconds="1000" />
<inline description="disable">
<test enabled="no"/>
</inline>
<sleep milliseconds="5000" />
<inline description="enable">
<test enabled="yes"/>
</inline>
<sleep milliseconds="10000" />
<inline description="finished"/>
</rom>
</config>
</start>
<start name="remote_rom_server">
<resource name="RAM" quantum="8M"/>
<route>
<service name="ROM"> <child name="dynamic_rom"/> </service>
<service name="Nic"> <child name="nic_drv"/> </service>
<any-service> <parent/> </any-service>
</route>
<config>
<remote_rom localname="test" name="remote" src="192.168.42.10" dst="192.168.42.11" />
</config>
</start>
<start name="nic_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>
<config>
<nic tap="tap1"/>
</config>
</start>
</config>}
#
# Boot image
#
build_boot_image { core init timer
remote_rom_server
dynamic_rom
nic_drv
}
append qemu_args " -nographic "
append qemu_args " -net tap,ifname=tap2 "
run_genode_until forever

View File

@@ -0,0 +1,563 @@
/*
* \brief TODO
* \author Johannes Schlatow
* \date 2016-02-18
*/
#include <base/env.h>
#include <base/exception.h>
#include <backend_base.h>
#include <nic/packet_allocator.h>
#include <nic_session/connection.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <os/config.h>
namespace Remote_rom {
bool verbose = false;
using Genode::size_t;
using Genode::uint16_t;
using Genode::uint32_t;
using Genode::Packet_descriptor;
using Genode::env;
using Net::Ethernet_frame;
using Net::Ipv4_packet;
template <class>
class Backend_base;
class Backend_server;
class Backend_client;
struct Packet_base;
struct SignalPacket;
struct UpdatePacket;
struct DataPacket;
};
/* Packet format we use for inter-system communication */
class Remote_rom::Packet_base : public Ethernet_frame, public Ipv4_packet
{
public:
enum {
MAX_NAME_LEN = 64 /* maximum length of the module name */
};
typedef enum {
SIGNAL = 1, /* signal that ROM content has changed */
UPDATE = 2, /* request transmission of updated content */
DATA = 3, /* first data packet */
DATA_CONT = 4, /* following data packets */
} Type;
protected:
char _module_name[MAX_NAME_LEN]; /* the ROM module name */
Type _type; /* packet type */
uint32_t _content_size; /* ROM content size in bytes */
uint16_t _offset; /* offset in bytes */
uint16_t _payload_size; /* payload size in bytes */
/*****************************************************
** 'payload' must be the last member of this class **
*****************************************************/
char payload[0];
public:
Packet_base(size_t size) : Ethernet_frame(sizeof(Packet_base) + size), Ipv4_packet(sizeof(Packet_base) + size - sizeof(Ethernet_frame)), _payload_size(size) { }
void const * base() const { return &payload; }
/**
* Return type of the packet
*/
Type type() const { return _type; }
/**
* Return size of the packet
*/
size_t size() const { return _payload_size + sizeof(Packet_base); }
/**
* Return content_size of the packet
*/
size_t content_size() const { return _content_size; }
/**
* Return offset of the packet
*/
size_t offset() const { return _offset; }
/**
* Return module_name of the packet
*/
char* module_name() { return _module_name; }
/**
* Set payload size of the packet
*/
void payload_size(Genode::size_t payload_size)
{
_payload_size = payload_size;
Ipv4_packet::total_length(size() - sizeof(Ethernet_frame));
}
/**
* Return payload size of the packet
*/
size_t payload_size() const { return _payload_size; }
/**
* Return address of the payload
*/
void *addr() { return payload; }
void prepare_ethernet(const Mac_address &src, const Mac_address &dst=Ethernet_frame::BROADCAST)
{
Ethernet_frame::src(src);
Ethernet_frame::dst(dst);
Ethernet_frame::type(IPV4);
}
void prepare_ipv4(const Ipv4_address &src, const Ipv4_address &dst=Ipv4_packet::BROADCAST)
{
Ipv4_packet::version(4);
Ipv4_packet::header_length(5);
Ipv4_packet::time_to_live(10);
Ipv4_packet::src(src);
Ipv4_packet::dst(dst);
Ipv4_packet::total_length(size() - sizeof(Ethernet_frame));
}
void set_checksums()
{
Ipv4_packet::checksum(Ipv4_packet::calculate_checksum(*this));
}
} __attribute__((packed));
class Remote_rom::SignalPacket : public Packet_base
{
public:
SignalPacket() : Packet_base(0)
{ }
void prepare(const char *module)
{
Genode::strncpy(_module_name, module, MAX_NAME_LEN);
_type = SIGNAL;
_payload_size = 0;
}
} __attribute__((packed));
class Remote_rom::UpdatePacket : public Packet_base
{
public:
UpdatePacket() : Packet_base(0)
{ }
void prepare(const char *module)
{
Genode::strncpy(_module_name, module, MAX_NAME_LEN);
_type = UPDATE;
_payload_size = 0;
}
} __attribute__((packed));
class Remote_rom::DataPacket : public Packet_base
{
public:
enum { MAX_PAYLOAD_SIZE = 1024 };
char payload[MAX_PAYLOAD_SIZE];
DataPacket() : Packet_base(MAX_PAYLOAD_SIZE)
{ }
void prepare(const char* module, size_t offset, size_t content_size)
{
Genode::strncpy(_module_name, module, MAX_NAME_LEN);
_payload_size = MAX_PAYLOAD_SIZE;
_offset = offset;
_content_size = content_size;
if (offset == 0)
_type = DATA;
else
_type = DATA_CONT;
}
/**
* Return packet size for given payload
*/
static size_t packet_size(size_t payload) { return sizeof(Packet_base) + Genode::min(payload, MAX_PAYLOAD_SIZE); }
} __attribute__((packed));
template <class HANDLER>
class Remote_rom::Backend_base
{
protected:
enum {
PACKET_SIZE = 1024,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE
};
class Rx_thread : public Genode::Thread
{
protected:
Ipv4_packet::Ipv4_address &_accept_ip;
Nic::Connection &_nic;
HANDLER &_handler;
Genode::Signal_receiver _sig_rec;
Genode::Signal_dispatcher<Rx_thread> _link_state_dispatcher;
Genode::Signal_dispatcher<Rx_thread> _rx_packet_avail_dispatcher;
Genode::Signal_dispatcher<Rx_thread> _rx_ready_to_ack_dispatcher;
void _handle_rx_packet_avail(unsigned)
{
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
Packet_descriptor _rx_packet = _nic.rx()->get_packet();
char *content = _nic.rx()->packet_content(_rx_packet);
/* check IP */
Ipv4_packet &ip_packet = *(Packet_base*)content;
if (_accept_ip == Ipv4_packet::BROADCAST || _accept_ip == ip_packet.dst())
_handler.receive(*(Packet_base*)content);
_nic.rx()->acknowledge_packet(_rx_packet);
}
}
void _handle_rx_ready_to_ack(unsigned) { _handle_rx_packet_avail(0); }
void _handle_link_state(unsigned)
{
PINF("link state changed");
}
public:
Rx_thread(Nic::Connection &nic, HANDLER &handler, Ipv4_packet::Ipv4_address &ip)
: Genode::Thread(Weight::DEFAULT_WEIGHT, "backend_nic_rx", 8192),
_accept_ip(ip),
_nic(nic), _handler(handler),
_link_state_dispatcher(_sig_rec, *this, &Rx_thread::_handle_link_state),
_rx_packet_avail_dispatcher(_sig_rec, *this, &Rx_thread::_handle_rx_packet_avail),
_rx_ready_to_ack_dispatcher(_sig_rec, *this, &Rx_thread::_handle_rx_ready_to_ack)
{
_nic.link_state_sigh(_link_state_dispatcher);
_nic.rx_channel()->sigh_packet_avail(_rx_packet_avail_dispatcher);
_nic.rx_channel()->sigh_ready_to_ack(_rx_ready_to_ack_dispatcher);
}
void entry()
{
while(true)
{
Genode::Signal sig = _sig_rec.wait_for_signal();
int num = sig.num();
Genode::Signal_dispatcher_base *dispatcher;
dispatcher = dynamic_cast<Genode::Signal_dispatcher_base *>(sig.context());
dispatcher->dispatch(num);
}
}
};
Nic::Packet_allocator _tx_block_alloc;
Nic::Connection _nic;
Rx_thread _rx_thread;
Ethernet_frame::Mac_address _mac_address;
Ipv4_packet::Ipv4_address _src_ip;
Ipv4_packet::Ipv4_address _accept_ip;
Ipv4_packet::Ipv4_address _dst_ip;
protected:
void _tx_ack(bool block = false)
{
/* check for acknowledgements */
while (_nic.tx()->ack_avail() || block) {
Nic::Packet_descriptor acked_packet = _nic.tx()->get_acked_packet();
_nic.tx()->release_packet(acked_packet);
block = false;
}
}
public:
explicit Backend_base(Genode::Allocator &alloc, HANDLER &handler) : _tx_block_alloc(&alloc), _nic(&_tx_block_alloc, BUF_SIZE, BUF_SIZE), _rx_thread(_nic, handler, _accept_ip)
{
/* start dispatcher thread */
_rx_thread.start();
/* convert and store mac address */
Nic::Mac_address mac = _nic.mac_address();
_mac_address.addr[0] = mac.addr[0];
_mac_address.addr[1] = mac.addr[1];
_mac_address.addr[2] = mac.addr[2];
_mac_address.addr[3] = mac.addr[3];
_mac_address.addr[4] = mac.addr[4];
_mac_address.addr[5] = mac.addr[5];
try {
char ip_string[15];
Genode::Xml_node remoterom = Genode::config()->xml_node().sub_node("remote_rom");
remoterom.attribute("src").value(ip_string, sizeof(ip_string));
_src_ip = Ipv4_packet::ip_from_string(ip_string);
remoterom.attribute("dst").value(ip_string, sizeof(ip_string));
_dst_ip = Ipv4_packet::ip_from_string(ip_string);
_accept_ip = _src_ip;
} catch (...) {
PWRN("No IP configured, falling back to broadcast mode!");
_src_ip = Ipv4_packet::CURRENT;
_dst_ip = Ipv4_packet::BROADCAST;
_accept_ip = Ipv4_packet::BROADCAST;
}
}
Nic::Packet_descriptor alloc_tx_packet(Genode::size_t size)
{
while (true) {
try {
Nic::Packet_descriptor packet = _nic.tx()->alloc_packet(size);
return packet;
} catch(Nic::Session::Tx::Source::Packet_alloc_failed) {
/* packet allocator exhausted, wait for acknowledgements */
_tx_ack(true);
}
}
}
void submit_tx_packet(Nic::Packet_descriptor packet)
{
_nic.tx()->submit_packet(packet);
/* check for acknowledgements */
_tx_ack();
}
};
class Remote_rom::Backend_server : public Backend_server_base, public Backend_base<Backend_server>
{
private:
static Backend_server_base* _instance;
Rom_forwarder_base *_forwarder;
Backend_server(Genode::Allocator &alloc) : Backend_base(alloc, *this), _forwarder(nullptr)
{ }
void send_data()
{
if (!_forwarder) return;
size_t offset = 0;
size_t size = _forwarder->content_size();
while (offset < size)
{
/* create and transmit packet via NIC session */
Nic::Packet_descriptor pd = alloc_tx_packet(DataPacket::packet_size(size));
DataPacket *packet = (DataPacket*)_nic.tx()->packet_content(pd);
packet->prepare_ethernet(_mac_address, Ethernet_frame::BROADCAST);
packet->prepare_ipv4(_src_ip, _dst_ip);
packet->prepare(_forwarder->module_name(), offset, size);
packet->payload_size(_forwarder->transfer_content((char*)packet->addr(), DataPacket::MAX_PAYLOAD_SIZE, offset));
packet->set_checksums();
submit_tx_packet(pd);
offset += packet->payload_size();
}
}
public:
static Backend_server_base &instance()
{
if (!_instance) {
_instance = new (env()->heap()) Backend_server(*env()->heap());
if (!_instance)
throw Exception();
}
return *_instance;
}
void register_forwarder(Rom_forwarder_base *forwarder)
{
_forwarder = forwarder;
}
void send_update()
{
if (!_forwarder) return;
/* create and transmit packet via NIC session */
Nic::Packet_descriptor pd = alloc_tx_packet(sizeof(SignalPacket));
SignalPacket *packet = (SignalPacket*)_nic.tx()->packet_content(pd);
packet->prepare_ethernet(_mac_address);
packet->prepare_ipv4(_src_ip, _dst_ip);
packet->prepare(_forwarder->module_name());
packet->set_checksums();
submit_tx_packet(pd);
}
void receive(Packet_base &packet)
{
switch (packet.type())
{
case Packet_base::UPDATE:
if (verbose) PINF("receiving UPDATE (%s) packet", packet.module_name());
if (!_forwarder)
return;
/* check module name */
if (Genode::strcmp(packet.module_name(), _forwarder->module_name()))
return;
/* TODO (optional) dont send data within Rx_Thread's context */
send_data();
break;
default:
break;
}
}
};
class Remote_rom::Backend_client : public Backend_client_base, public Backend_base<Backend_client>
{
private:
static Backend_client_base *_instance;
Rom_receiver_base *_receiver;
char *_write_ptr;
size_t _buf_size;
Backend_client(Genode::Allocator &alloc) : Backend_base(alloc, *this), _receiver(nullptr), _write_ptr(nullptr), _buf_size(0)
{
}
void write(char *data, size_t offset, size_t size)
{
if (!_write_ptr) return;
size_t const len = Genode::min(size, _buf_size-offset);
Genode::memcpy(_write_ptr+offset, data, len);
if (offset + len >= _buf_size)
_receiver->commit_new_content();
}
public:
static Backend_client_base &instance()
{
if (!_instance) {
_instance = new (env()->heap()) Backend_client(*env()->heap());
if (!_instance)
throw Exception();
}
return *_instance;
}
void register_receiver(Rom_receiver_base *receiver)
{
/* TODO support multiple receivers (ROM names) */
_receiver = receiver;
/* FIXME request update on startup (occasionally triggers invalid signal-context capability) */
// if (_receiver)
// update(_receiver->module_name());
}
void update(const char* module_name)
{
if (!_receiver) return;
/* check module name */
if (Genode::strcmp(module_name, _receiver->module_name()))
return;
/* create and transmit packet via NIC session */
Nic::Packet_descriptor pd = alloc_tx_packet(sizeof(UpdatePacket));
UpdatePacket *packet = (UpdatePacket*)_nic.tx()->packet_content(pd);
packet->prepare_ethernet(_mac_address);
packet->prepare_ipv4(_src_ip, _dst_ip);
packet->prepare(_receiver->module_name());
packet->set_checksums();
submit_tx_packet(pd);
}
void receive(Packet_base &packet)
{
switch (packet.type())
{
case Packet_base::SIGNAL:
if (verbose) PINF("receiving SIGNAL(%s) packet", packet.module_name());
/* send update request */
update(packet.module_name());
break;
case Packet_base::DATA:
if (verbose) PINF("receiving DATA(%s) packet", packet.module_name());
/* write into buffer */
if (!_receiver) return;
/* check module name */
if (Genode::strcmp(packet.module_name(), _receiver->module_name()))
return;
_write_ptr = _receiver->start_new_content(packet.content_size());
_buf_size = (_write_ptr) ? packet.content_size() : 0;
write((char*)packet.addr(), packet.offset(), packet.payload_size());
break;
case Packet_base::DATA_CONT:
if (verbose) PINF("receiving DATA_CONT(%s) packet", packet.module_name());
if (!_receiver) return;
/* check module name */
if (Genode::strcmp(packet.module_name(), _receiver->module_name()))
return;
/* write into buffer */
write((char*)packet.addr(), packet.offset(), packet.payload_size());
break;
default:
break;
}
}
};
Remote_rom::Backend_server_base *Remote_rom::Backend_server::_instance = nullptr;
Remote_rom::Backend_client_base *Remote_rom::Backend_client::_instance = nullptr;
Remote_rom::Backend_server_base &Remote_rom::backend_init_server()
{
return Backend_server::instance();
}
Remote_rom::Backend_client_base &Remote_rom::backend_init_client()
{
return Backend_client::instance();
}

View File

@@ -0,0 +1,32 @@
The remote ROM allows providing ROM services over a network or similar communication
medium and thus implements publisher/subscriber communication in a distributed system.
The remote_rom server connects to a local ROM service and provides a remote ROM service
via the specified backend. The remote_rom client connects to a remote ROM service via
the specified backend and provides a ROM service via local RPC.
Backends
--------
The remote_rom can be compiled with one of multiple backends that use different session
interfaces or libraries to forward and receive the packets. By exchanging the backend,
we can also easily change the publication strategy (e.g. notification+polling vs. multicast).
Furthermore, a backend is responsible for access control and optionally allows the
specification of a policy for this.
:'nic_ip':
This backend uses a Nic_session to transmit network packets with IPv4 headers.
Configuration
-------------
Both, the client and the server evaluate the <remote_rom> node of their config.
The _name_ attribute specifies the ROMs module name. The <remote_rom> node may
further contain a <default> node that can be used to populate the ROM with a default
content.
Example
~~~~~~~
For an example that illustrates the use of these components, please refer to the
_run/test-remote_rom_backend_nic.run_ script.

View File

@@ -0,0 +1,2 @@
include $(PRG_DIR)/../target.inc
include $(PRG_DIR)/../../../client/target.inc

View File

@@ -0,0 +1,2 @@
include $(PRG_DIR)/../target.inc
include $(PRG_DIR)/../../../server/target.inc

View File

@@ -0,0 +1 @@
LIBS += remote_rom_backend_nic_ip

View File

@@ -0,0 +1,259 @@
/*
* \brief Client-side proxy component translating a rom_session provided over a network into a local rom_session.
* \author Johannes Schlatow
* \date 2016-02-15
*
* Usage scenario:
* __________ ________________ _________________ __________
* | server | -> | remote_rom | -> (network) -> | remote_rom | -> | client |
* | | | server | | client | | |
* ---------- ---------------- ----------------- ----------
*/
/*
* Copyright (C) 2016 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 <base/env.h>
#include <util/volatile_object.h>
#include <util/list.h>
#include <base/rpc_server.h>
#include <root/component.h>
#include <os/attached_ram_dataspace.h>
#include <rom_session/rom_session.h>
#include <base/component.h>
#include <os/config.h>
#include <backend_base.h>
namespace Remote_rom {
using Genode::size_t;
using Genode::Lazy_volatile_object;
using Genode::Signal_context_capability;
class Session_component;
class Root;
struct Main;
struct Read_buffer;
static char remotename[255];
typedef Genode::List<Session_component> Session_list;
};
/**
* Interface used by the sessions to obtain the ROM data received from the remote server
*/
struct Remote_rom::Read_buffer
{
virtual size_t content_size() const = 0;
virtual size_t export_content(char *dst, size_t dst_len) const = 0;
};
class Remote_rom::Session_component : public Genode::Rpc_object<Genode::Rom_session, Remote_rom::Session_component>,
public Session_list::Element
{
private:
Genode::Env &_env;
Signal_context_capability _sigh;
Read_buffer const &_buffer;
Session_list &_sessions;
Lazy_volatile_object<Genode::Attached_ram_dataspace> _ram_ds;
public:
static int version() { return 1; }
Session_component(Genode::Env &env, Session_list &sessions, Read_buffer const &buffer)
:
_env(env), _buffer(buffer), _sessions(sessions)
{
_sessions.insert(this);
}
~Session_component() { _sessions.remove(this); }
void notify_client()
{
if (!_sigh.valid())
return;
Genode::Signal_transmitter(_sigh).submit();
}
/* ROM session implementation */
Genode::Rom_dataspace_capability dataspace() override
{
using namespace Genode;
/* replace dataspace by new one as needed */
if (!_ram_ds.is_constructed()
|| _buffer.content_size() > _ram_ds->size()) {
_ram_ds.construct(&_env.ram(), _buffer.content_size());
}
char *dst = _ram_ds->local_addr<char>();
size_t const dst_size = _ram_ds->size();
/* fill with content of current evaluation result */
size_t const copied_len = _buffer.export_content(dst, dst_size);
/* clear remainder of dataspace */
Genode::memset(dst + copied_len, 0, dst_size - copied_len);
/* cast RAM into ROM dataspace capability */
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ram_ds->cap());
return static_cap_cast<Rom_dataspace>(ds_cap);
}
void sigh(Genode::Signal_context_capability sigh) override
{
_sigh = sigh;
}
};
class Remote_rom::Root : public Genode::Root_component<Session_component>
{
private:
Genode::Env &_env;
Read_buffer &_buffer;
Session_list _sessions;
protected:
Session_component *_create_session(const char *args)
{
using namespace Genode;
/* TODO compare requested module name with provided module name (config) */
return new (Root::md_alloc())
Session_component(_env, _sessions, _buffer);
}
public:
Root(Genode::Env &env, Genode::Allocator &md_alloc, Read_buffer &buffer)
: Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc), _env(env), _buffer(buffer)
{ }
void notify_clients()
{
for (Session_component *s = _sessions.first(); s; s = s->next())
s->notify_client();
}
};
struct Remote_rom::Main : public Remote_rom::Read_buffer, public Remote_rom::Rom_receiver_base
{
Genode::Env &env;
Genode::Heap heap = { &env.ram(), &env.rm() };
Root remote_rom_root = { env, heap, *this };
Genode::Lazy_volatile_object<Genode::Attached_ram_dataspace> _ds;
size_t _ds_content_size;
Backend_client_base &_backend;
Main(Genode::Env &env) : env(env), _ds_content_size(1024), _backend(backend_init_client())
{
env.parent().announce(env.ep().manage(remote_rom_root));
/* initialise backend */
_backend.register_receiver(this);
// _ds.construct(Genode::env()->ram_session(), _ds_content_size);
}
const char* module_name() const { return remotename; }
char* start_new_content(size_t len)
{
/* Create buffer for new data */
_ds_content_size = len;
// TODO (optional) implement double buffering
if (!_ds.is_constructed() || _ds_content_size > _ds->size())
_ds.construct(&env.ram(), _ds_content_size);
// TODO set write lock
return _ds->local_addr<char>();
}
void commit_new_content(bool abort=false)
{
if (abort)
PERR("abort not supported");
// TODO release write lock
remote_rom_root.notify_clients();
}
size_t content_size() const
{
if (_ds.is_constructed()) {
return _ds_content_size;
}
else {
/* transfer default content if set */
try {
Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default");
return default_content.content_size();
} catch (...) { }
}
return 0;
}
size_t export_content(char *dst, size_t dst_len) const
{
if (_ds.is_constructed()) {
size_t const len = Genode::min(dst_len, _ds_content_size);
Genode::memcpy(dst, _ds->local_addr<char>(), len);
return len;
}
else {
/* transfer default content if set */
try {
Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default");
size_t const len = Genode::min(dst_len, default_content.content_size());
Genode::memcpy(dst, default_content.content_base(), len);
return len;
} catch (...) { }
}
return 0;
}
};
namespace Component {
Genode::size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Genode::Env &env)
{
try {
Genode::Xml_node remote_rom = Genode::config()->xml_node().sub_node("remote_rom");
remote_rom.attribute("name").value(Remote_rom::remotename, sizeof(Remote_rom::remotename));
} catch (...) {
PERR("No ROM module configured!");
}
static Remote_rom::Main main(env);
}
}

View File

@@ -0,0 +1,8 @@
SRC_CC = main.cc
TARGET = remote_rom_client
LIBS += base config
INC_DIR += $(REP_DIR)/include/remote_rom
vpath main.cc $(REP_DIR)/src/proxy/remote_rom/client

View File

@@ -0,0 +1,141 @@
/*
* \brief Server-side proxy component providing a rom_session over a network.
* \author Johannes Schlatow
* \date 2016-02-15
*
* Usage scenario:
* __________ ________________ _________________ __________
* | server | -> | remote_rom | -> (network) -> | remote_rom | -> | client |
* | | | server | | client | | |
* ---------- ---------------- ----------------- ----------
*/
/*
* Copyright (C) 2016 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 <base/env.h>
#include <backend_base.h>
#include <base/component.h>
#include <os/config.h>
#include <os/attached_rom_dataspace.h>
namespace Remote_rom {
using Genode::size_t;
using Genode::Attached_rom_dataspace;
class Rom_forwarder;
struct Main;
static char modulename[255];
static char remotename[255];
static bool binary = false;
};
struct Remote_rom::Rom_forwarder : Rom_forwarder_base
{
Attached_rom_dataspace &_rom;
Backend_server_base &_backend;
Rom_forwarder(Attached_rom_dataspace &rom, Backend_server_base &backend) : _rom(rom), _backend(backend)
{
_backend.register_forwarder(this);
/* on startup, send an update message to remote client */
update();
}
const char *module_name() const { return remotename; }
void update()
{
/* TODO don't update ROM if a transfer is still in progress */
/* refresh dataspace if valid*/
_rom.update();
/* trigger backend_server */
_backend.send_update();
}
size_t content_size() const
{
if (_rom.is_valid()) {
if (binary)
return _rom.size();
else
return Genode::min(1+Genode::strlen(_rom.local_addr<char>()), _rom.size());
}
else {
try {
Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default");
return default_content.content_size();
} catch (...) { }
}
return 0;
}
size_t transfer_content(char *dst, size_t dst_len, size_t offset=0) const
{
if (_rom.is_valid()) {
size_t const len = Genode::min(dst_len, content_size()-offset);
Genode::memcpy(dst, _rom.local_addr<char>() + offset, len);
return len;
}
else {
/* transfer default content if set */
try {
Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default");
size_t const len = Genode::min(dst_len, default_content.content_size()-offset);
Genode::memcpy(dst, default_content.content_base() + offset, len);
return len;
} catch (...) { }
}
return 0;
}
};
struct Remote_rom::Main
{
Genode::Entrypoint &_ep;
Attached_rom_dataspace _rom;
Rom_forwarder _forwarder;
Genode::Signal_handler<Rom_forwarder> _dispatcher = { _ep, _forwarder, &Rom_forwarder::update };
Main(Genode::Entrypoint &ep)
: _ep(ep),
_rom(modulename),
_forwarder(_rom, backend_init_server())
{
/* register update dispatcher */
_rom.sigh(_dispatcher);
}
};
namespace Component {
Genode::size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Genode::Env &env)
{
try {
Genode::Xml_node remote_rom = Genode::config()->xml_node().sub_node("remote_rom");
remote_rom.attribute("localname").value(Remote_rom::modulename, sizeof(Remote_rom::modulename));
remote_rom.attribute("name").value(Remote_rom::remotename, sizeof(Remote_rom::remotename));
try {
remote_rom.attribute("binary").value(&Remote_rom::binary);
} catch (...) { }
} catch (...) {
PERR("No ROM module configured!");
}
static Remote_rom::Main main(env.ep());
}
}

View File

@@ -0,0 +1,8 @@
SRC_CC = main.cc
TARGET = remote_rom_server
LIBS += base config
INC_DIR += $(REP_DIR)/include/remote_rom
vpath main.cc $(REP_DIR)/src/proxy/remote_rom/server