remote_rom: use UDP and implement go-back-N ARQ
This commit is contained in:
committed by
Norman Feske
parent
c137c595c8
commit
8ed98b8459
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
49
include/remote_rom/util.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
103
src/lib/remote_rom/backend/nic_ip/base.cc
Normal file
103
src/lib/remote_rom/backend/nic_ip/base.cc
Normal 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 ð = 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 ð,
|
||||
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(ð, edguard.total_size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Remote_rom::Backend_base::handle_ip(Ethernet_frame ð,
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -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 ð = 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 ð = 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 ð, 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 ð = 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 ð,
|
||||
Size_guard &edguard);
|
||||
|
||||
/*
|
||||
* Handle an IP packet
|
||||
*
|
||||
* \param eth Ethernet frame containing the IP packet.
|
||||
* \param size size guard
|
||||
*/
|
||||
void handle_ip(Ethernet_frame ð,
|
||||
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
|
||||
|
||||
@@ -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 ð = 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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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 ð = 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
~~~~~~~
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 (...) { }
|
||||
|
||||
Reference in New Issue
Block a user