remote_rom: use UDP and implement go-back-N ARQ

This commit is contained in:
Johannes Schlatow
2018-11-09 09:48:05 +01:00
committed by Norman Feske
parent c137c595c8
commit 8ed98b8459
16 changed files with 1221 additions and 419 deletions

View File

@@ -25,9 +25,11 @@ namespace Remote_rom {
class Exception : public ::Genode::Exception { };
Backend_server_base &backend_init_server(Genode::Env &env,
Genode::Allocator &alloc);
Genode::Allocator &alloc,
Genode::Xml_node config);
Backend_client_base &backend_init_client(Genode::Env &env,
Genode::Allocator &alloc);
Genode::Allocator &alloc,
Genode::Xml_node config);
};
struct Remote_rom::Backend_server_base : Genode::Interface

View File

@@ -24,10 +24,13 @@ namespace Remote_rom {
struct Remote_rom::Rom_forwarder_base : Genode::Interface
{
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;
virtual void start_transmission() = 0;
virtual void finish_transmission() = 0;
virtual const char *module_name() const = 0;
virtual size_t content_size() const = 0;
virtual unsigned content_hash() const = 0;
virtual size_t transfer_content(char *dst, size_t dst_len,
size_t offset=0) const = 0;
};
#endif

View File

@@ -24,8 +24,10 @@ namespace Remote_rom {
struct Remote_rom::Rom_receiver_base : Genode::Interface
{
virtual const char *module_name() const = 0;
virtual char* start_new_content(size_t len) = 0;
virtual const char *module_name() const = 0;
virtual unsigned content_hash() const = 0;
virtual char* start_new_content(unsigned hash,
size_t len) = 0;
virtual void commit_new_content(bool abort=false) = 0;
};

49
include/remote_rom/util.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* \brief Utility functions used by ROM forwarder and receiver.
* \author Johannes Schlatow
* \date 2018-11-14
*/
/*
* Copyright (C) 2018 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__UTIL_H_
#define __INCLUDE__REMOTE_ROM__UTIL_H_
#include <base/fixed_stdint.h>
namespace Remote_rom {
using Genode::uint32_t;
using Genode::int32_t;
using Genode::uint8_t;
using Genode::size_t;
uint32_t cksum(void const * const buf, size_t size);
}
/**
* Calculating checksum compatible to POSIX cksum
*
* \param buf pointer to buffer containing data
* \param size length of buffer in bytes
*
* \return CRC32 checksum of data
*/
Genode::uint32_t Remote_rom::cksum(void const * const buf, size_t size)
{
uint8_t const *p = static_cast<uint8_t const*>(buf);
uint32_t crc = ~0U;
while (size--) {
crc ^= *p++;
for (uint32_t j = 0; j < 8; j++)
crc = (-int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1);
}
return crc ^ ~0U;
}
#endif

View File

@@ -1,7 +1,7 @@
# \author Johannes Schlatow
# \date 2016-03-19
SRC_CC += backend/nic_ip/client.cc backend/nic_ip/server.cc
SRC_CC += backend/nic_ip/base.cc backend/nic_ip/client.cc backend/nic_ip/server.cc
INC_DIR += $(REP_DIR)/src/lib/remote_rom/backend/nic_ip
LIBS += base net

View File

@@ -1,3 +1,10 @@
proc nic_drv_opt {} {
if {[have_spec linux]} {
return "ld=\"no\""
}
return ""
}
#
# Build
#
@@ -36,6 +43,7 @@ install_config {
<service name="IRQ" />
</parent-provides>
<default-route>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100" />
@@ -64,36 +72,22 @@ install_config {
<start name="remote_rom_server">
<resource name="RAM" quantum="8M"/>
<route>
<service name="ROM" label_suffix="test">
<child name="dynamic_rom"/>
<service name="ROM" label_suffix="remote">
<child name="dynamic_rom" label="test"/>
</service>
<service name="Nic"> <child name="nic_bridge"/> </service>
<any-service> <parent/> </any-service>
<any-service> <parent/> <any-child/> </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>
<remote_rom name="remote"
src="192.168.42.10" dst="192.168.42.11" />
</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>
<remote_rom name="remote" src="192.168.42.11" dst="192.168.42.10" />
</config>
</start>
<start name="nic_bridge">
@@ -105,7 +99,7 @@ install_config {
</route>
<config> <default-policy/> </config>
</start>
<start name="nic_drv">
<start name="nic_drv" } [nic_drv_opt] {>
<binary name="} [nic_drv_binary] {" />
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>

View File

@@ -1,3 +1,10 @@
proc nic_drv_opt {} {
if {[have_spec linux]} {
return "ld=\"no\""
}
return ""
}
#
# Build
#
@@ -58,7 +65,7 @@ install_config {
<remote_rom name="remote" src="192.168.42.11" dst="192.168.42.10" />
</config>
</start>
<start name="nic_drv">
<start name="nic_drv" } [nic_drv_opt] {>
<binary name="} [nic_drv_binary] {" />
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>

View File

@@ -1,3 +1,10 @@
proc nic_drv_opt {} {
if {[have_spec linux]} {
return "ld=\"no\""
}
return ""
}
#
# Build
#
@@ -62,18 +69,18 @@ install_config {
<start name="remote_rom_server">
<resource name="RAM" quantum="8M"/>
<route>
<service name="ROM" label_suffix="test">
<child name="dynamic_rom"/>
<service name="ROM" label_suffix="remote">
<child name="dynamic_rom" label="test"/>
</service>
<service name="Nic"> <child name="nic_drv"/> </service>
<any-service> <parent/> </any-service>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<remote_rom localname="test" name="remote"
<remote_rom name="remote"
src="192.168.42.10" dst="192.168.42.11" />
</config>
</start>
<start name="nic_drv">
<start name="nic_drv" } [nic_drv_opt] {>
<binary name="} [nic_drv_binary] {" />
<resource name="RAM" quantum="4M"/>
<provides><service name="Nic"/></provides>

View File

@@ -0,0 +1,103 @@
/*
* \brief Client implementation
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2018-11-06
*/
#include <base.h>
void Remote_rom::Backend_base::_handle_rx_packet()
{
/* we assume that the SUBMIT and ACK queue are equally dimensioned
* i.e. ready_to_ack should always return true */
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
Packet_descriptor pd = _nic.rx()->get_packet();
if (pd.size())
handle_ethernet(_nic.rx()->packet_content(pd), pd.size());
_nic.rx()->acknowledge_packet(pd);
}
}
void Remote_rom::Backend_base::handle_ethernet(void* src, Genode::size_t size)
{
try {
/* parse ethernet frame header */
Size_guard edguard(size);
Ethernet_frame &eth = Ethernet_frame::cast_from(src, edguard);
switch (eth.type()) {
case Ethernet_frame::Type::ARP:
handle_arp(eth, edguard);
break;
case Ethernet_frame::Type::IPV4:
handle_ip(eth, edguard);
break;
default:
;
}
} catch(Size_guard::Exceeded) {
Genode::warning("Packet size guard exceeded!");
}
}
void Remote_rom::Backend_base::handle_arp(Ethernet_frame &eth,
Size_guard &edguard)
{
/* ignore broken packets */
Arp_packet &arp = eth.data<Arp_packet>(edguard);
if (!arp.ethernet_ipv4())
return;
/* look whether the IP address is our's */
if (arp.dst_ip() == _accept_ip) {
if (arp.opcode() == Arp_packet::REQUEST) {
/*
* The ARP packet gets re-written, we interchange source
* and destination MAC and IP addresses, and set the opcode
* to reply, and then push the packet back to the NIC driver.
*/
Ipv4_address old_src_ip = arp.src_ip();
arp.opcode(Arp_packet::REPLY);
arp.dst_mac(arp.src_mac());
arp.src_mac(_mac_address);
arp.src_ip(arp.dst_ip());
arp.dst_ip(old_src_ip);
eth.dst(arp.dst_mac());
/* set our MAC as sender */
eth.src(_mac_address);
send(&eth, edguard.total_size());
}
}
}
void Remote_rom::Backend_base::handle_ip(Ethernet_frame &eth,
Size_guard &edguard)
{
Ipv4_packet &ip = eth.data<Ipv4_packet>(edguard);
if (_accept_ip == Ipv4_packet::broadcast()
|| _accept_ip == ip.dst()) {
if (ip.protocol() == Ipv4_packet::Protocol::UDP) {
/* remote_rom protocol is wrapped in UDP */
Udp_packet &udp = ip.data<Udp_packet>(edguard);
if (udp.dst_port() == _udp_port)
receive(udp.data<Packet>(edguard), edguard);
}
}
}
void Remote_rom::Backend_base::send(Ethernet_frame *eth, Genode::size_t size)
{
try {
/* copy and submit packet */
Packet_descriptor packet = _nic.tx()->alloc_packet(size);
char *content = _nic.tx()->packet_content(packet);
Genode::memcpy((void*)content, (void*)eth, size);
submit_tx_packet(packet);
} catch(Nic::Session::Tx::Source::Packet_alloc_failed) {
Genode::warning("Packet dropped");
}
}

View File

@@ -1,9 +1,16 @@
/*
* \brief Common base for client and server
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2016-02-18
*/
#include <net/ethernet.h>
#include <net/arp.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/port.h>
#include <base/env.h>
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
@@ -11,8 +18,7 @@
#include <nic/packet_allocator.h>
#include <nic_session/connection.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <timer_session/connection.h>
#include <packet.h>
@@ -27,6 +33,9 @@ namespace Remote_rom {
using Net::Mac_address;
using Net::Ipv4_address;
using Net::Size_guard;
using Net::Arp_packet;
using Net::Udp_packet;
using Net::Port;
class Backend_base;
};
@@ -39,24 +48,11 @@ class Remote_rom::Backend_base : public Genode::Interface
Genode::Signal_handler<Backend_base> _link_state_handler;
Genode::Signal_handler<Backend_base> _rx_packet_handler;
void _handle_rx_packet()
{
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
Packet_descriptor _rx_packet = _nic.rx()->get_packet();
Port _udp_port;
Port const _src_port { 51234 };
char *content = _nic.rx()->packet_content(_rx_packet);
Size_guard edguard(_rx_packet.size());
Ethernet_frame &eth = Ethernet_frame::cast_from(content, edguard);
/* check IP */
Ipv4_packet &ip_packet = eth.data<Ipv4_packet>(edguard);
if (_accept_ip == Ipv4_packet::broadcast()
|| _accept_ip == ip_packet.dst())
receive(ip_packet.data<Packet>(edguard), edguard);
_nic.rx()->acknowledge_packet(_rx_packet);
}
}
/* rx packet handler */
void _handle_rx_packet();
void _handle_link_state()
{
@@ -76,99 +72,122 @@ class Remote_rom::Backend_base : public Genode::Interface
protected:
enum {
PACKET_SIZE = 1024,
PACKET_SIZE = 1600,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE
};
Timer::Connection _timer;
const bool _verbose = false;
Nic::Packet_allocator _tx_block_alloc;
Nic::Connection _nic;
Mac_address _mac_address;
Mac_address _dst_mac;
Ipv4_address _src_ip;
Ipv4_address _accept_ip;
Ipv4_address _dst_ip;
bool _chksum_offload { false };
/**
* Handle accepted network packet from the other side
*/
virtual void receive(Packet &packet, Size_guard &) = 0;
Ipv4_packet &_prepare_upper_layers(void *base, Size_guard &size_guard)
Ethernet_frame &prepare_eth(void *base, Size_guard &size_guard)
{
Ethernet_frame &eth = Ethernet_frame::construct_at(base, size_guard);
eth.src(_mac_address);
eth.dst(Ethernet_frame::broadcast());
eth.dst(_dst_mac);
eth.type(Ethernet_frame::Type::IPV4);
return eth;
}
Ipv4_packet &prepare_ipv4(Ethernet_frame &eth, Size_guard &size_guard)
{
Ipv4_packet &ip = eth.construct_at_data<Ipv4_packet>(size_guard);
ip.version(4);
ip.header_length(5);
ip.header_length(sizeof(Ipv4_packet) / 4);
ip.time_to_live(10);
ip.protocol(Ipv4_packet::Protocol::UDP);
ip.src(_src_ip);
ip.dst(_dst_ip);
return ip;
}
size_t _upper_layer_size(size_t size)
Udp_packet &prepare_udp(Ipv4_packet &ip, Size_guard &size_guard)
{
return sizeof(Ethernet_frame) + sizeof(Ipv4_packet) + size;
}
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
udp.src_port(_src_port);
udp.dst_port(_udp_port);
void _finish_ipv4(Ipv4_packet &ip, size_t payload)
{
ip.total_length(sizeof(ip) + payload);
ip.update_checksum();
return udp;
}
template <typename T>
void _transmit_notification_packet(Packet::Type type, T *frontend)
void transmit_notification(Packet::Type type,
T const &frontend)
{
size_t frame_size = _upper_layer_size(sizeof(Packet));
size_t const frame_size = sizeof(Ethernet_frame)
+ sizeof(Ipv4_packet)
+ sizeof(Udp_packet)
+ sizeof(Packet)
+ sizeof(NotificationPacket);
Nic::Packet_descriptor pd = alloc_tx_packet(frame_size);
Size_guard size_guard(pd.size());
char *content = _nic.tx()->packet_content(pd);
Ipv4_packet &ip = _prepare_upper_layers(content, size_guard);
Packet &pak = ip.construct_at_data<Packet>(size_guard);
Ethernet_frame &eth = prepare_eth(content, size_guard);
size_t const ip_off = size_guard.head_size();
Ipv4_packet &ip = prepare_ipv4(eth, size_guard);
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = prepare_udp(ip, size_guard);
Packet &pak = udp.construct_at_data<Packet>(size_guard);
pak.type(type);
pak.module_name(frontend->module_name());
_finish_ipv4(ip, sizeof(Packet));
pak.module_name(frontend.module_name());
pak.content_hash(frontend.content_hash());
NotificationPacket &npak =
pak.construct_at_data<NotificationPacket>(size_guard);
npak.content_size(frontend.content_size());
/* fill in header values that need the packet to be complete already */
udp.length(size_guard.head_size() - udp_off);
if (!_chksum_offload)
udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum();
submit_tx_packet(pd);
}
explicit Backend_base(Genode::Env &env, Genode::Allocator &alloc)
explicit Backend_base(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node config,
Genode::Xml_node policy)
:
_link_state_handler(env.ep(), *this, &Backend_base::_handle_link_state),
_rx_packet_handler(env.ep(), *this, &Backend_base::_handle_rx_packet),
_udp_port(policy.attribute_value("udp_port", Port(9009))),
_timer(env),
_tx_block_alloc(&alloc),
_nic(env, &_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_mac_address(_nic.mac_address()),
_src_ip(Ipv4_packet::current()),
_accept_ip(Ipv4_packet::broadcast()),
_dst_ip(Ipv4_packet::broadcast())
_dst_mac (policy.attribute_value("dst_mac", Ethernet_frame::broadcast())),
_src_ip (policy.attribute_value("src", Ipv4_packet::current())),
_accept_ip(policy.attribute_value("src", Ipv4_packet::broadcast())),
_dst_ip (policy.attribute_value("dst", Ipv4_packet::broadcast())),
_chksum_offload(config.attribute_value("chksum_offload", _chksum_offload))
{
_nic.link_state_sigh(_link_state_handler);
_nic.rx_channel()->sigh_packet_avail(_rx_packet_handler);
_nic.rx_channel()->sigh_ready_to_ack(_rx_packet_handler);
Genode::Attached_rom_dataspace config = {env, "config"};
try {
char ip_string[15];
Genode::Xml_node remoterom = config.xml().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 (...) {
Genode::warning("No IP configured, falling back to broadcast mode!");
}
}
Nic::Packet_descriptor alloc_tx_packet(Genode::size_t size)
@@ -186,10 +205,50 @@ class Remote_rom::Backend_base : public Genode::Interface
void submit_tx_packet(Nic::Packet_descriptor packet)
{
_nic.tx()->submit_packet(packet);
/* check for acknowledgements */
_tx_ack();
if (!_nic.tx()->ready_to_submit()) {
Genode::error("not ready to submit");
return;
}
_nic.tx()->submit_packet(packet);
}
/**
* Handle an Ethernet packet
*
* \param src Ethernet frame's address
* \param size Ethernet frame's size.
*/
void handle_ethernet(void* src, Genode::size_t size);
/*
* Handle an ARP packet
*
* \param eth Ethernet frame containing the ARP packet.
* \param size size guard
*/
void handle_arp(Ethernet_frame &eth,
Size_guard &edguard);
/*
* Handle an IP packet
*
* \param eth Ethernet frame containing the IP packet.
* \param size size guard
*/
void handle_ip(Ethernet_frame &eth,
Size_guard &edguard);
/**
* Send ethernet frame
*
* \param eth ethernet frame to send.
* \param size ethernet frame's size.
*/
void send(Ethernet_frame *eth, Genode::size_t size);
};
#endif

View File

@@ -1,16 +1,142 @@
/*
* \brief Client implementation
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2018-11-06
*/
#include <backend_base.h>
#include <base.h>
#include <backend_base.h>
namespace Remote_rom {
using Genode::Cstring;
using Genode::Microseconds;
struct Backend_client;
class Content_receiver;
class Backend_client;
};
class Remote_rom::Content_receiver
{
private:
enum {
MAX_PAYLOAD_SIZE = DataPacket::MAX_PAYLOAD_SIZE,
TIMEOUT_DATA_US = 50000 /* 50ms */
};
char *_write_ptr { nullptr };
size_t _buf_size { 0 };
size_t _offset { 0 };
/* window state */
size_t _window_id { 0 };
size_t _window_length { 0 };
size_t _next_packet_id { 0 };
bool _omit_nack { false };
/* timeouts and general object management*/
Timer::One_shot_timeout<Content_receiver> _timeout;
Backend_client &_backend;
Rom_receiver_base *_frontend { nullptr };
size_t _write_offset()
{ return _offset + _next_packet_id * MAX_PAYLOAD_SIZE; }
void timeout_handler(Genode::Duration);
/* Noncopyable */
Content_receiver(Content_receiver const &);
Content_receiver &operator=(Content_receiver const &);
bool _start_window(size_t window_length)
{
/* calculate offset to beginning of new window */
_offset += _window_length * MAX_PAYLOAD_SIZE;
/* zero packet_id marks first window */
if (_next_packet_id > 0)
_window_id++;
_window_length = window_length;
_next_packet_id = 0;
if (_offset >= _buf_size)
return false;
return true;
}
void _write(const void *data, size_t packet_id, size_t size)
{
if (!_write_ptr) return;
if (packet_id != _next_packet_id) return;
size_t const offset = _write_offset();
if (offset >= _buf_size)
return;
size_t const len = Genode::min(size, _buf_size-offset);
Genode::memcpy(_write_ptr+offset, data, len);
}
public:
Content_receiver(Timer::Connection &timer,
Backend_client &backend)
: _timeout(timer, *this, &Content_receiver::timeout_handler),
_backend(backend)
{ }
void register_receiver(Rom_receiver_base *frontend)
{
_frontend = frontend;
}
void start_new_content(unsigned hash,
size_t size)
{
if (!_frontend) return;
_write_ptr = _frontend->start_new_content(hash, size);
_buf_size = _write_ptr ? size : 0;
_offset = 0;
_window_id = 0;
_window_length = 0;
_next_packet_id = 0;
_omit_nack = false;
if (_timeout.scheduled())
_timeout.discard();
}
/**********************
* frontend accessors *
**********************/
unsigned content_hash() const
{ return _frontend ? _frontend->content_hash() : 0; }
char const *module_name() const
{ return _frontend ? _frontend->module_name() : ""; }
size_t content_size() const
{ return _buf_size; }
bool window_complete()
{
/* remark: also returns true if we havent started any window yet */
return _next_packet_id == _window_length;
}
bool complete()
{
return _write_offset() >= _buf_size;
}
bool accept_packet(const DataPacket &p);
size_t window_id() { return _window_id; }
size_t ack_until() { return _next_packet_id; }
};
class Remote_rom::Backend_client :
@@ -18,97 +144,43 @@ class Remote_rom::Backend_client :
public Backend_base
{
private:
friend class Content_receiver;
Rom_receiver_base *_receiver;
char *_write_ptr;
size_t _buf_size;
Content_receiver _content_receiver { _timer, *this };
Backend_client(Backend_client &);
Backend_client &operator= (Backend_client &);
void write(const void *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();
}
void update(const char* module_name)
{
if (!_receiver) return;
/* check module name */
if (Genode::strcmp(module_name, _receiver->module_name()))
if (Genode::strcmp(module_name, _content_receiver.module_name()))
return;
_transmit_notification_packet(Packet::UPDATE, _receiver);
if (_verbose)
Genode::log("sending UPDATE(", _content_receiver.module_name(), ")");
transmit_notification(Packet::UPDATE, _content_receiver);
}
void send_ack(Content_receiver const &recv);
void receive(Packet &packet, Size_guard &size_guard)
{
switch (packet.type())
{
case Packet::SIGNAL:
if (_verbose)
Genode::log("receiving SIGNAL(",
Cstring(packet.module_name()),
") packet");
/* send update request */
update(packet.module_name());
break;
case Packet::DATA:
{
if (_verbose)
Genode::log("receiving DATA(",
Cstring(packet.module_name()),
") packet");
/* write into buffer */
if (!_receiver) return;
/* check module name */
if (Genode::strcmp(packet.module_name(), _receiver->module_name()))
return;
const DataPacket &data = packet.data<DataPacket>(size_guard);
size_guard.consume_head(data.payload_size());
if (!data.offset()) {
_write_ptr = _receiver->start_new_content(data.content_size());
_buf_size = (_write_ptr) ? data.content_size() : 0;
}
write(data.addr(), data.offset(), data.payload_size());
break;
}
default:
break;
}
}
void receive(Packet &packet, Size_guard &size_guard);
public:
Backend_client(Genode::Env &env, Genode::Allocator &alloc) :
Backend_base(env, alloc),
_receiver(nullptr), _write_ptr(nullptr),
_buf_size(0)
Backend_client(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node config,
Genode::Xml_node policy)
: Backend_base(env, alloc, config, policy)
{ }
void register_receiver(Rom_receiver_base *receiver)
{
/* TODO support multiple receivers (ROM names) */
_receiver = receiver;
_content_receiver.register_receiver(receiver);
/*
* FIXME request update on startup
@@ -122,10 +194,195 @@ class Remote_rom::Backend_client :
namespace Remote_rom {
using Genode::Env;
using Genode::Allocator;
using Genode::Xml_node;
Backend_client_base &backend_init_client(Env &env, Allocator &alloc)
Backend_client_base &backend_init_client(Env &env,
Allocator &alloc,
Xml_node config)
{
static Backend_client backend(env, alloc);
static Backend_client backend(env,
alloc,
config,
config.sub_node("remote_rom"));
return backend;
}
};
void Remote_rom::Backend_client::send_ack(Content_receiver const &recv)
{
size_t const frame_size = sizeof(Ethernet_frame)
+ sizeof(Ipv4_packet)
+ sizeof(Udp_packet)
+ sizeof(Packet)
+ sizeof(AckPacket);
Nic::Packet_descriptor pd = alloc_tx_packet(frame_size);
Size_guard size_guard(pd.size());
char *content = _nic.tx()->packet_content(pd);
Ethernet_frame &eth = prepare_eth(content, size_guard);
size_t const ip_off = size_guard.head_size();
Ipv4_packet &ip = prepare_ipv4(eth, size_guard);
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = prepare_udp(ip, size_guard);
Packet &pak = udp.construct_at_data<Packet>(size_guard);
pak.type(Packet::ACK);
pak.module_name(recv.module_name());
pak.content_hash(recv.content_hash());
AckPacket &ack =
pak.construct_at_data<AckPacket>(size_guard);
ack.window_id(_content_receiver.window_id());
ack.ack_until(_content_receiver.ack_until());
/* fill in header values that need the packet to be complete already */
udp.length(size_guard.head_size() - udp_off);
if (!_chksum_offload)
udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum();
submit_tx_packet(pd);
if (_verbose)
Genode::log("Sent ACK for window ", _content_receiver.window_id());
}
void Remote_rom::Backend_client::receive(Packet &packet,
Size_guard &size_guard)
{
switch (packet.type())
{
case Packet::SIGNAL:
{
const NotificationPacket &signal
= packet.data<NotificationPacket>(size_guard);
if (_verbose)
Genode::log("receiving SIGNAL(",
Cstring(packet.module_name()),
") packet, size ",
signal.content_size());
/* start new content with given size and hash */
_content_receiver.start_new_content(
packet.content_hash(),
signal.content_size());
/* send update request */
update(packet.module_name());
break;
}
case Packet::DATA:
{
/* check module name */
if (Genode::strcmp(packet.module_name(), _content_receiver.module_name()))
return;
/* check hash */
if (packet.content_hash() != _content_receiver.content_hash()) {
Genode::warning("ignoring hash mismatch ",
Genode::Hex(packet.content_hash()),
" != ",
Genode::Hex(_content_receiver.content_hash()));
return;
}
const DataPacket &data = packet.data<DataPacket>(size_guard);
size_guard.consume_head(data.payload_size());
_content_receiver.accept_packet(data);
break;
}
case Packet::UPDATE:
/* drop UPDATE packets received from other clients */
if (_verbose)
Genode::log("ignoring UPDATE");
break;
default:
Genode::error("unknown packet type (", Genode::Hex(packet.type()), ").");
break;
}
}
void Remote_rom::Content_receiver::timeout_handler(Genode::Duration)
{
Genode::warning("timeout occurred waiting for packet ", _next_packet_id,
" in window ", _window_id, " of length ", _window_length);
_backend.send_ack(*this);
}
bool Remote_rom::Content_receiver::accept_packet(const DataPacket &p)
{
/**
* TODO replace return value with exceptions
*/
if (complete() || !_frontend) return false;
if (_timeout.scheduled())
_timeout.discard();
if (window_complete()) {
if (!_start_window(p.window_length())) {
Genode::warning("unexpected error starting window of size ",
p.window_length());
return false;
}
}
/* drop packets with wrong window id */
if (p.window_id() != _window_id) {
if (p.window_id() == _window_id-1
&& p.packet_id() == p.window_length()-1) {
/* rollback the window */
_offset -= _window_length * MAX_PAYLOAD_SIZE;
_window_id = p.window_id();
_window_length = p.window_length();
_next_packet_id = _window_length;
/* send acknowledge if we receive the last packet, as
* the sender missed our previous ACK */
Genode::log("re-sending ACK");
_backend.send_ack(*this);
}
return false;
}
/* NACK if we missed a packet */
if (p.packet_id() != _next_packet_id) {
if (!_omit_nack) {
Genode::log("lost packet, sending NACK");
_backend.send_ack(*this);
/* omit any further NACKs until we received a correct
* package or a timeout occurred */
_omit_nack = true;
}
return false;
}
else {
_omit_nack = false;
}
_write(p.addr(), p.packet_id(), p.payload_size());
_next_packet_id++;
if (window_complete())
_backend.send_ack(*this);
if (complete())
_frontend->commit_new_content();
else
_timeout.schedule(Microseconds(TIMEOUT_DATA_US));
return true;
}

View File

@@ -1,6 +1,7 @@
/*
* \brief Packet format we use for inter-system communication
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2018-11-05
*/
@@ -13,13 +14,19 @@
namespace Remote_rom {
using Genode::size_t;
using Genode::uint8_t;
using Genode::uint16_t;
using Genode::uint32_t;
struct Packet;
struct DataPacket;
class Window_state;
class Packet;
class NotificationPacket;
class AckPacket;
class DataPacket;
}
class Remote_rom::Packet
{
public:
@@ -31,17 +38,15 @@ class Remote_rom::Packet
SIGNAL = 1, /* signal that ROM content has changed */
UPDATE = 2, /* request transmission of updated content */
DATA = 3, /* data packet */
ACK = 4, /* acknowledge data packets */
} Type;
private:
char _module_name[MAX_NAME_LEN]; /* the ROM module name */
uint32_t _content_hash; /* serves as version */
Type _type; /* packet type */
/*****************************************************
** 'payload' must be the last member of this class **
*****************************************************/
char payload[0];
char _data[0];
public:
/**
@@ -59,6 +64,9 @@ class Remote_rom::Packet
_type = type;
}
void content_hash(uint32_t hash) { _content_hash = hash; }
uint32_t content_hash() const { return _content_hash; }
void module_name(const char *module)
{
Genode::strncpy(_module_name, module, MAX_NAME_LEN);
@@ -68,30 +76,66 @@ class Remote_rom::Packet
T const &data(Net::Size_guard &size_guard) const
{
size_guard.consume_head(sizeof(T));
return *(T const *)(payload);
return *(T const *)(_data);
}
template <typename T>
T &construct_at_data(Net::Size_guard &size_guard)
{
size_guard.consume_head(sizeof(T));
return *Genode::construct_at<T>(payload);
return *Genode::construct_at<T>(_data);
}
} __attribute__((packed));
class Remote_rom::NotificationPacket
{
private:
uint32_t _content_size; /* ROM content size in bytes */
public:
void content_size(size_t size) { _content_size = size; }
size_t content_size() const { return _content_size; }
} __attribute__((packed));
class Remote_rom::AckPacket
{
private:
uint16_t _window_id; /* refers to this window id */
uint16_t _ack_until; /* acknowledge until this packet id - 1 */
/**
* TODO Implement selective ARQ by using a Bit_array to save
* the ACK state in the window.
* We should, however, not use the Bit_array directly as a
* Packet member.
*/
public:
void window_id(size_t window_id) { _window_id = window_id; }
void ack_until(size_t packet_id) { _ack_until = packet_id; }
size_t window_id() const { return _window_id; }
size_t ack_until() const { return _ack_until; }
} __attribute__((packed));
class Remote_rom::DataPacket
{
public:
static const size_t MAX_PAYLOAD_SIZE = 1024;
static const size_t MAX_PAYLOAD_SIZE = 1350;
private:
uint32_t _content_size; /* ROM content size in bytes */
uint32_t _offset; /* offset in bytes */
uint16_t _payload_size; /* payload size in bytes */
uint16_t _payload_size; /* payload size in bytes */
uint16_t _window_id; /* window id */
uint16_t _packet_id; /* packet number within window */
uint16_t _window_length; /* 0: no ARQ, >0: ARQ window length */
char payload[0];
char _data[0];
public:
/**
@@ -99,18 +143,13 @@ class Remote_rom::DataPacket
*/
size_t size() const { return _payload_size + sizeof(*this); }
/**
* Return content_size of the packet
*/
size_t content_size() const { return _content_size; }
void window_length(size_t len) { _window_length = len; }
void window_id(size_t id) { _window_id = id; }
void packet_id(size_t id) { _packet_id = id; }
/**
* Return offset of the packet
*/
size_t offset() const { return _offset; }
void content_size(size_t size) { _content_size = size; }
void offset(size_t offset) { _offset = offset; }
size_t window_length() const { return _window_length; }
size_t window_id() const { return _window_id; }
size_t packet_id() const { return _packet_id; }
/**
* Set payload size of the packet
@@ -126,17 +165,10 @@ class Remote_rom::DataPacket
size_t payload_size() const { return _payload_size; }
/**
* Return address of the payload
*/
void *addr() { return payload; }
const void *addr() const { return payload; }
/**
* Return packet size for given payload
*/
static size_t packet_size(size_t payload) {
return sizeof(DataPacket) + Genode::min(payload, MAX_PAYLOAD_SIZE);
}
* Return address of the payload
*/
void *addr() { return _data; }
const void *addr() const { return _data; }
} __attribute__((packed));

View File

@@ -1,16 +1,182 @@
/*
* \brief Server implementation
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2018-11-06
*/
#include <backend_base.h>
#include <base.h>
#include <backend_base.h>
namespace Remote_rom {
using Genode::Cstring;
using Genode::Microseconds;
struct Backend_server;
class Content_sender;
class Backend_server;
};
class Remote_rom::Content_sender
{
private:
enum {
MAX_PAYLOAD_SIZE = DataPacket::MAX_PAYLOAD_SIZE,
MAX_WINDOW_SIZE = 900,
TIMEOUT_ACK_US = 1000000 /* 1000ms */
};
/* total data size */
size_t _data_size { 0 };
/* current window length */
size_t _window_length { MAX_WINDOW_SIZE };
/* current window id */
size_t _window_id { 0 };
/* data offset of current window */
size_t _offset { 0 };
/* current packed id */
size_t _packet_id { 0 };
size_t _errors { 0 };
/* timeouts and general object management*/
Timer::One_shot_timeout<Content_sender> _timeout;
Backend_server &_backend;
Rom_forwarder_base *_frontend { nullptr };
void timeout_handler(Genode::Duration)
{
Genode::warning("no ACK received for window ", _window_id);
/* only handle */
if (_errors == 0) {
/* go back to beginning of window */
go_back(0);
transmit(false);
}
else {
reset();
_frontend->finish_transmission();
Genode::warning("transmission cancelled");
}
_errors++;
}
/* Noncopyable */
Content_sender(Content_sender const &);
Content_sender &operator=(Content_sender const &);
static size_t _calculate_window_size(size_t size)
{
size_t const mod = size % MAX_PAYLOAD_SIZE;
size_t const packets = size / MAX_PAYLOAD_SIZE + (mod ? 1 : 0);
return Genode::min((size_t)MAX_WINDOW_SIZE, packets);
}
/**
* Go to next packet. Returns false if window is complete.
*/
inline bool _next_packet()
{ return ++_packet_id < _window_length; }
inline bool _window_complete() const
{ return _packet_id == _window_length; }
inline bool _transmission_complete() const
{ return _offset >= _data_size; }
/**
* Return absolute data offset of current packet.
*/
inline size_t _data_offset() const
{ return _offset + _packet_id * MAX_PAYLOAD_SIZE; }
/**
* Go to next window. Returns false if end of data was reached.
* TODO we may adapt the window size if retransmission occurred
*/
bool _next_window() {
/* advance offset by data transmitted in the last window */
_offset += _window_length * MAX_PAYLOAD_SIZE;
if (_transmission_complete())
return false;
_window_id++;
_window_length = _calculate_window_size(_data_size-_offset);
_packet_id = 0;
return true;
}
public:
Content_sender(Timer::Connection &timer, Backend_server &backend)
: _timeout(timer, *this, &Content_sender::timeout_handler),
_backend(backend)
{ }
void register_forwarder(Rom_forwarder_base *forwarder)
{
_frontend = forwarder;
}
void reset()
{
_offset = 0;
_packet_id = 0;
_window_id = 0;
_data_size = 0;
_window_length = 0;
_errors = 0;
}
bool transmitting() { return _packet_id > 0; }
/**********************
* frontend accessors *
**********************/
unsigned content_hash() const
{ return _frontend ? _frontend->content_hash() : 0; }
size_t content_size() const
{ return _frontend ? _frontend->content_size() : 0; }
char const *module_name() const
{ return _frontend ? _frontend->module_name() : ""; }
size_t transfer_content(char* dst, size_t max_size) const
{
if (!_frontend) return 0;
return _frontend->transfer_content(dst, max_size, _data_offset());
}
/************************
* transmission control *
************************/
bool transmit(bool restart);
void go_back(size_t packet_id) { _packet_id = packet_id; }
/*************************************
* accessors for packet construction *
*************************************/
/**
* Return payload size of current packet.
*/
size_t payload_size() const
{ return Genode::min(_data_size-_data_offset(),
(size_t)MAX_PAYLOAD_SIZE); }
size_t window_id() const { return _window_id; }
size_t window_length() const { return _window_length; }
size_t packet_id() const { return _packet_id; }
};
class Remote_rom::Backend_server :
@@ -19,101 +185,221 @@ class Remote_rom::Backend_server :
{
private:
Rom_forwarder_base *_forwarder;
friend class Content_sender;
Content_sender _content_sender { _timer, *this };
Backend_server(Backend_server &);
Backend_server &operator= (Backend_server &);
void send_data()
{
if (!_forwarder) return;
void send_packet(Content_sender const &sender);
size_t offset = 0;
size_t size = _forwarder->content_size();
while (offset < size)
{
/* create and transmit packet via NIC session */
size_t max_size = _upper_layer_size(sizeof(Packet)
+ DataPacket::packet_size(size));
Nic::Packet_descriptor pd = alloc_tx_packet(max_size);
Size_guard size_guard(pd.size());
char *content = _nic.tx()->packet_content(pd);
Ipv4_packet &ip = _prepare_upper_layers(content, size_guard);
Packet &pak = ip.construct_at_data<Packet>(size_guard);
pak.type(Packet::DATA);
pak.module_name(_forwarder->module_name());
DataPacket &data = pak.construct_at_data<DataPacket>(size_guard);
data.offset(offset);
data.content_size(size);
const size_t max = DataPacket::MAX_PAYLOAD_SIZE;
data.payload_size(_forwarder->transfer_content((char*)data.addr(),
max,
offset));
_finish_ipv4(ip, sizeof(Packet) + data.size());
submit_tx_packet(pd);
offset += data.payload_size();
}
}
void receive(Packet &packet, Size_guard &)
{
switch (packet.type())
{
case Packet::UPDATE:
if (_verbose)
Genode::log("receiving UPDATE (",
Cstring(packet.module_name()),
") packet");
if (!_forwarder)
return;
/* check module name */
if (Genode::strcmp(packet.module_name(), _forwarder->module_name()))
return;
send_data();
break;
default:
break;
}
}
void receive(Packet &packet, Size_guard &);
public:
Backend_server(Genode::Env &env, Genode::Allocator &alloc) :
Backend_base(env, alloc),
_forwarder(nullptr)
{ }
Backend_server(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node config,
Genode::Xml_node policy)
: Backend_base(env, alloc, config, policy)
{ }
void register_forwarder(Rom_forwarder_base *forwarder)
{
_forwarder = forwarder;
}
{ _content_sender.register_forwarder(forwarder); }
void send_update()
{
if (!_forwarder) return;
_transmit_notification_packet(Packet::SIGNAL, _forwarder);
if (!_content_sender.content_size()) return;
if (_verbose)
Genode::log("sending SIGNAL(", _content_sender.module_name(), ")");
/* TODO re-send SIGNAL packet after a timeout */
transmit_notification(Packet::SIGNAL, _content_sender);
}
};
namespace Remote_rom {
using Genode::Env;
using Genode::Allocator;
using Genode::Xml_node;
Backend_server_base &backend_init_server(Env &env, Allocator &alloc)
Backend_server_base &backend_init_server(Env &env,
Allocator &alloc,
Xml_node config)
{
static Backend_server backend(env, alloc);
static Backend_server backend(env,
alloc,
config,
config.sub_node("remote_rom"));
return backend;
}
};
void Remote_rom::Backend_server::send_packet(Content_sender const &sender)
{
/* create and transmit packet via NIC session */
size_t const max_payload = sender.payload_size();
size_t const max_size = sizeof(Ethernet_frame)
+ sizeof(Ipv4_packet)
+ sizeof(Udp_packet)
+ sizeof(Packet)
+ sizeof(DataPacket)
+ max_payload;
Nic::Packet_descriptor pd = alloc_tx_packet(max_size);
Size_guard size_guard(pd.size());
char* const content = _nic.tx()->packet_content(pd);
Ethernet_frame &eth = prepare_eth(content, size_guard);
size_t const ip_off = size_guard.head_size();
Ipv4_packet &ip = prepare_ipv4(eth, size_guard);
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = prepare_udp(ip, size_guard);
Packet &pak = udp.construct_at_data<Packet>(size_guard);
pak.type(Packet::DATA);
pak.module_name(sender.module_name());
pak.content_hash(sender.content_hash());
DataPacket &data = pak.construct_at_data<DataPacket>(size_guard);
data.window_id(sender.window_id());
data.window_length(sender.window_length());
data.packet_id(sender.packet_id());
size_guard.consume_head(max_payload);
data.payload_size(sender.transfer_content((char*)data.addr(),
max_payload));
/* fill in header values that need the packet to be complete already */
udp.length(size_guard.head_size() - udp_off);
if (!_chksum_offload)
udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum();
submit_tx_packet(pd);
}
void Remote_rom::Backend_server::receive(Packet &packet,
Size_guard &size_guard)
{
switch (packet.type())
{
case Packet::UPDATE:
if (_verbose)
Genode::log("receiving UPDATE (",
Cstring(packet.module_name()),
") packet");
/* check module name */
if (Genode::strcmp(packet.module_name(), _content_sender.module_name()))
return;
/* compare content hash */
if (packet.content_hash() != _content_sender.content_hash()) {
if (_verbose)
Genode::log("ignoring UPDATE with invalid hash");
return;
}
if (_verbose) {
Genode::log("Sending data of size ", _content_sender.content_size());
}
_content_sender.transmit(true);
break;
case Packet::SIGNAL:
if (_verbose)
Genode::log("ignoring SIGNAL");
break;
case Packet::DATA:
if (_verbose)
Genode::log("ignoring DATA");
break;
case Packet::ACK:
{
if (!_content_sender.transmitting())
return;
if (Genode::strcmp(packet.module_name(), _content_sender.module_name()))
return;
if (packet.content_hash() != _content_sender.content_hash()) {
if (_verbose)
Genode::warning("ignoring ACK with wrong hash");
return;
}
AckPacket const &ack = packet.data<AckPacket>(size_guard);
if (ack.window_id() != _content_sender.window_id()) {
if (_verbose)
Genode::warning("ignoring ACK with wrong window id");
return;
}
if (ack.ack_until() < _content_sender.packet_id()) {
if (_verbose)
Genode::warning("Go back to packet id ", ack.ack_until());
_content_sender.go_back(ack.ack_until());
}
_content_sender.transmit(false);
break;
}
default:
break;
}
}
bool Remote_rom::Content_sender::transmit(bool restart)
{
if (!_frontend) return false;
if (_timeout.scheduled())
_timeout.discard();
_errors = 0;
if (restart) {
/* do not start if we are still transmitting */
if (!_transmission_complete())
return false;
_frontend->start_transmission();
reset();
_data_size = _frontend->content_size();
_window_length = _calculate_window_size(_data_size);
}
else if (_window_complete()) {
if (!_next_window()) {
_frontend->finish_transmission();
if (transmitting()) {
reset();
return true;
}
else
return false;
}
}
do {
_backend.send_packet(*this);
} while (_next_packet());
/* set ACK timeout */
_timeout.schedule(Microseconds(TIMEOUT_ACK_US));
return false;
}

View File

@@ -18,15 +18,20 @@ policy for this.
:'nic_ip':
This back end uses a Nic_session to transmit network packets with IPv4
headers.
and UDP 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.
config. The _name_ attribute specifies the ROMs module name. The source IP
address is specified by the _src_ attribute and the destination IP address
by the _dst_ attribute. The _dst_mac_ attribute may specify the destination
MAC address (default: broadcast). Attribute _udp_port_ specifies the
destination port (default: 9009).
A boolean _binary_ attribute can be used to switch between transmission of
the entire ROM dataspace (binary="true") or transmission of string content
using strlen.
Example
~~~~~~~

View File

@@ -33,16 +33,18 @@
#include <base/component.h>
#include <backend_base.h>
#include <util.h>
namespace Remote_rom {
using Genode::size_t;
using Genode::Constructible;
using Genode::Attached_ram_dataspace;
using Genode::Rom_dataspace_capability;
using Genode::Signal_context_capability;
class Session_component;
class Root;
struct Main;
struct Read_buffer;
struct Rom_module;
typedef Genode::List_element<Session_component> Session_element;
typedef Genode::List<Session_element> Session_list;
@@ -53,10 +55,68 @@ namespace Remote_rom {
* Interface used by the sessions to obtain the ROM data received from the
* remote server
*/
struct Remote_rom::Read_buffer : Genode::Interface
class Remote_rom::Rom_module
{
virtual size_t content_size() const = 0;
virtual size_t export_content(char *dst, size_t dst_len) const = 0;
private:
Genode::Ram_allocator &_ram;
Attached_ram_dataspace _fg; /* dataspace delivered to clients */
Attached_ram_dataspace _bg; /* dataspace for receiving data */
unsigned _bg_hash { 0 };
size_t _bg_size { 0 };
public:
Rom_module(Genode::Ram_allocator &ram, Genode::Env &env)
: _ram(ram),
_fg(_ram, env.rm(), 0),
_bg(_ram, env.rm(), 4096)
{ }
Rom_dataspace_capability fg_dataspace() const
{
using namespace Genode;
if (!_fg.size())
return Rom_dataspace_capability();
Dataspace_capability ds_cap = _fg.cap();
return static_cap_cast<Rom_dataspace>(ds_cap);
}
/**
* Return pointer to buffer that is ready to be filled with data.
*
* Data is written into the background dataspace.
* Once it is ready, the 'commit_bg()' function is called.
*/
char* base(size_t size)
{
/* let background buffer grow if needed */
if (_bg.size() < size)
_bg.realloc(&_ram, size);
_bg_size = size;
return _bg.local_addr<char>();
}
/**
* Commit data contained in background dataspace
* (swap foreground and background dataspace)
*/
bool commit_bg()
{
if (_bg_hash != cksum(_bg.local_addr<char>(), _bg_size)) {
Genode::error("checksum error");
return false;
}
_fg.swap(_bg);
return true;
}
unsigned hash() const { return _bg_hash; }
void hash(unsigned v) { _bg_hash = v; }
};
class Remote_rom::Session_component :
@@ -67,22 +127,19 @@ class Remote_rom::Session_component :
Signal_context_capability _sigh;
Read_buffer const &_buffer;
Rom_module const &_rom_module;
Session_list &_sessions;
Session_element _element;
Constructible<Genode::Attached_ram_dataspace> _ram_ds;
public:
static int version() { return 1; }
Session_component(Genode::Env &env, Session_list &sessions,
Read_buffer const &buffer)
Rom_module const &rom_module)
:
_env(env), _sigh(), _buffer(buffer), _sessions(sessions), _element(this),
_ram_ds()
_env(env), _sigh(), _rom_module(rom_module), _sessions(sessions), _element(this)
{
_sessions.insert(&_element);
}
@@ -101,27 +158,7 @@ class Remote_rom::Session_component :
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(), _env.rm(), _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);
return _rom_module.fg_dataspace();
}
void sigh(Genode::Signal_context_capability sigh) override
@@ -130,13 +167,12 @@ class Remote_rom::Session_component :
}
};
class Remote_rom::Root : public Genode::Root_component<Session_component>
{
private:
Genode::Env &_env;
Read_buffer &_buffer;
Rom_module &_rom_module;
Session_list _sessions;
protected:
@@ -150,16 +186,16 @@ class Remote_rom::Root : public Genode::Root_component<Session_component>
* */
return new (Root::md_alloc())
Session_component(_env, _sessions, _buffer);
Session_component(_env, _sessions, _rom_module);
}
public:
Root(Genode::Env &env, Genode::Allocator &md_alloc, Read_buffer &buffer)
Root(Genode::Env &env, Genode::Allocator &md_alloc, Rom_module &rom_module)
:
Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env),
_buffer(buffer),
_rom_module(rom_module),
_sessions()
{ }
@@ -170,14 +206,12 @@ class Remote_rom::Root : public Genode::Root_component<Session_component>
}
};
struct Remote_rom::Main : public Read_buffer, public Rom_receiver_base
struct Remote_rom::Main : public Rom_receiver_base
{
Genode::Env &env;
Genode::Heap heap = { &env.ram(), &env.rm() };
Root remote_rom_root = { env, heap, *this };
Genode::Constructible<Genode::Attached_ram_dataspace> _ds;
size_t _ds_content_size;
Genode::Heap heap { &env.ram(), &env.rm() };
Rom_module rom_module { env.ram(), env };
Root remote_rom_root { env, heap, rom_module };
Genode::Attached_rom_dataspace _config = { env, "config" };
@@ -186,8 +220,8 @@ struct Remote_rom::Main : public Read_buffer, public Rom_receiver_base
char remotename[255];
Main(Genode::Env &env) :
env(env), _ds(), _ds_content_size(1024),
_backend(backend_init_client(env, heap))
env(env),
_backend(backend_init_client(env, heap, _config.xml()))
{
try {
Genode::Xml_node remote_rom = _config.xml().sub_node("remote_rom");
@@ -202,71 +236,27 @@ struct Remote_rom::Main : public Read_buffer, public Rom_receiver_base
_backend.register_receiver(this);
}
const char* module_name() const { return remotename; }
const char* module_name() const { return remotename; }
unsigned content_hash() const { return rom_module.hash(); }
char* start_new_content(size_t len)
char* start_new_content(unsigned hash, size_t len)
{
/* Create buffer for new data */
_ds_content_size = len;
/* save expected hash */
/* TODO (optional) skip if we already have the same data */
rom_module.hash(hash);
// TODO (optional) implement double buffering
if (!_ds.is_constructed() || _ds_content_size > _ds->size())
_ds.construct(env.ram(), env.rm(), _ds_content_size);
// TODO set write lock
return _ds->local_addr<char>();
return rom_module.base(len);
}
void commit_new_content(bool abort=false)
{
if (abort)
Genode::error("abort not supported");
return;
// TODO release write lock
remote_rom_root.notify_clients();
if (rom_module.commit_bg())
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 = _config.xml().
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 = _config.xml().
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 {

View File

@@ -21,11 +21,12 @@
#include <base/env.h>
#include <base/heap.h>
#include <backend_base.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <backend_base.h>
#include <util.h>
namespace Remote_rom {
using Genode::size_t;
using Genode::Attached_rom_dataspace;
@@ -34,7 +35,6 @@ namespace Remote_rom {
struct Main;
static char modulename[255];
static char remotename[255];
static bool binary = false;
};
@@ -44,6 +44,10 @@ struct Remote_rom::Rom_forwarder : Rom_forwarder_base
Attached_rom_dataspace &_rom;
Backend_server_base &_backend;
unsigned _current_hash { 0 };
bool _transmitting { false };
bool _update_received { false };
Attached_rom_dataspace &_config;
Rom_forwarder(Attached_rom_dataspace &rom, Backend_server_base &backend,
@@ -56,17 +60,42 @@ struct Remote_rom::Rom_forwarder : Rom_forwarder_base
update();
}
const char *module_name() const { return remotename; }
void start_transmission() { _transmitting = true; }
void finish_transmission()
{
_transmitting = false;
/* if we received and update during transmission,
* we are now able to process it */
if (_update_received)
update();
}
const char *module_name() const { return modulename; }
void update()
{
/* TODO don't update ROM if a transfer is still in progress */
if (_transmitting) {
/* if transmission is ongoing, do not update ROM */
_update_received = true;
return;
}
_update_received = false;
/* refresh dataspace if valid*/
/* refresh dataspace if valid */
_rom.update();
/* trigger backend_server */
_backend.send_update();
if (_rom.is_valid()) {
_current_hash = cksum(_rom.local_addr<char>(), content_size());
/* trigger backend_server */
_backend.send_update();
}
}
unsigned content_hash() const
{
return _current_hash;
}
size_t content_size() const
@@ -75,17 +104,9 @@ struct Remote_rom::Rom_forwarder : Rom_forwarder_base
if (binary)
return _rom.size();
else
return Genode::min(1+Genode::strlen(_rom.local_addr<char>()),
return Genode::min(Genode::strlen(_rom.local_addr<char>()),
_rom.size());
}
else {
try {
Genode::Xml_node default_content = _config.xml().
sub_node("remote_rom").
sub_node("default");
return default_content.content_size();
} catch (...) { }
}
return 0;
}
@@ -94,21 +115,12 @@ struct Remote_rom::Rom_forwarder : Rom_forwarder_base
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;
/* clear remaining buffer to prevent data leakage */
if (dst_len > len) {
Genode::memset(dst + len, 0, dst_len-len);
}
return dst_len;
}
else {
/* transfer default content if set */
try {
Genode::Xml_node default_content = _config.xml().
sub_node("remote_rom").
sub_node("default");
size_t const remainder = default_content.content_size()-offset;
size_t const len = Genode::min(dst_len, remainder);
Genode::memcpy(dst, default_content.content_base() + offset, len);
return len;
} catch (...) { }
}
return 0;
}
};
@@ -128,7 +140,8 @@ struct Remote_rom::Main
Main(Genode::Env &env)
: _env(env),
_rom(env, modulename),
_forwarder(_rom, backend_init_server(env, _heap), _config)
_forwarder(_rom, backend_init_server(env, _heap, _config.xml()),
_config)
{
/* register update dispatcher */
_rom.sigh(_dispatcher);
@@ -142,18 +155,11 @@ namespace Component {
void construct(Genode::Env &env)
{
using Remote_rom::modulename;
using Remote_rom::remotename;
Genode::Attached_rom_dataspace config = { env, "config" };
try {
Genode::Xml_node remote_rom = config.xml().sub_node("remote_rom");
if (remote_rom.has_attribute("localname"))
remote_rom.attribute("localname").value(modulename,
sizeof(modulename));
else
remote_rom.attribute("name").value(modulename, sizeof(modulename));
remote_rom.attribute("name").value(remotename, sizeof(remotename));
remote_rom.attribute("name").value(modulename, sizeof(modulename));
try {
remote_rom.attribute("binary").value(&Remote_rom::binary);
} catch (...) { }