diff --git a/include/remote_rom/backend_base.h b/include/remote_rom/backend_base.h
index 8d85996..f7084bb 100644
--- a/include/remote_rom/backend_base.h
+++ b/include/remote_rom/backend_base.h
@@ -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
diff --git a/include/remote_rom/rom_forwarder.h b/include/remote_rom/rom_forwarder.h
index 5a32b26..bc1639c 100644
--- a/include/remote_rom/rom_forwarder.h
+++ b/include/remote_rom/rom_forwarder.h
@@ -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
diff --git a/include/remote_rom/rom_receiver.h b/include/remote_rom/rom_receiver.h
index 82fe489..91d6f6e 100644
--- a/include/remote_rom/rom_receiver.h
+++ b/include/remote_rom/rom_receiver.h
@@ -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;
};
diff --git a/include/remote_rom/util.h b/include/remote_rom/util.h
new file mode 100644
index 0000000..92cccb9
--- /dev/null
+++ b/include/remote_rom/util.h
@@ -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
+
+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(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
diff --git a/lib/mk/remote_rom_backend_nic_ip.mk b/lib/mk/remote_rom_backend_nic_ip.mk
index 43c5553..6ea304d 100644
--- a/lib/mk/remote_rom_backend_nic_ip.mk
+++ b/lib/mk/remote_rom_backend_nic_ip.mk
@@ -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
diff --git a/run/remote_rom_backend_nic_ip.run b/run/remote_rom_backend_nic_ip.run
index 1ee37ec..18bbe7d 100644
--- a/run/remote_rom_backend_nic_ip.run
+++ b/run/remote_rom_backend_nic_ip.run
@@ -1,3 +1,10 @@
+proc nic_drv_opt {} {
+ if {[have_spec linux]} {
+ return "ld=\"no\""
+ }
+ return ""
+}
+
#
# Build
#
@@ -36,6 +43,7 @@ install_config {
+
@@ -64,36 +72,22 @@ install_config {
-
-
+
+
-
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
@@ -105,7 +99,7 @@ install_config {
-
+
diff --git a/run/remote_rom_backend_nic_ip_client.run b/run/remote_rom_backend_nic_ip_client.run
index a1cf944..3bbd2f7 100644
--- a/run/remote_rom_backend_nic_ip_client.run
+++ b/run/remote_rom_backend_nic_ip_client.run
@@ -1,3 +1,10 @@
+proc nic_drv_opt {} {
+ if {[have_spec linux]} {
+ return "ld=\"no\""
+ }
+ return ""
+}
+
#
# Build
#
@@ -58,7 +65,7 @@ install_config {
-
+
diff --git a/run/remote_rom_backend_nic_ip_server.run b/run/remote_rom_backend_nic_ip_server.run
index 47b0946..76ba4e8 100644
--- a/run/remote_rom_backend_nic_ip_server.run
+++ b/run/remote_rom_backend_nic_ip_server.run
@@ -1,3 +1,10 @@
+proc nic_drv_opt {} {
+ if {[have_spec linux]} {
+ return "ld=\"no\""
+ }
+ return ""
+}
+
#
# Build
#
@@ -62,18 +69,18 @@ install_config {
-
-
+
+
-
+
-
-
+
diff --git a/src/lib/remote_rom/backend/nic_ip/base.cc b/src/lib/remote_rom/backend/nic_ip/base.cc
new file mode 100644
index 0000000..b3e446b
--- /dev/null
+++ b/src/lib/remote_rom/backend/nic_ip/base.cc
@@ -0,0 +1,103 @@
+/*
+ * \brief Client implementation
+ * \author Johannes Schlatow
+ * \author Edgard Schmidt
+ * \date 2018-11-06
+ */
+
+#include
+
+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(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(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(edguard);
+ if (udp.dst_port() == _udp_port)
+ receive(udp.data(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");
+ }
+}
diff --git a/src/lib/remote_rom/backend/nic_ip/base.h b/src/lib/remote_rom/backend/nic_ip/base.h
index 7d59344..f364ccb 100644
--- a/src/lib/remote_rom/backend/nic_ip/base.h
+++ b/src/lib/remote_rom/backend/nic_ip/base.h
@@ -1,9 +1,16 @@
/*
* \brief Common base for client and server
* \author Johannes Schlatow
+ * \author Edgard Schmidt
* \date 2016-02-18
*/
+#include
+#include
+#include
+#include
+#include
+
#include
#include
#include
@@ -11,8 +18,7 @@
#include
#include
-#include
-#include
+#include
#include
@@ -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 _link_state_handler;
Genode::Signal_handler _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(edguard);
- if (_accept_ip == Ipv4_packet::broadcast()
- || _accept_ip == ip_packet.dst())
- receive(ip_packet.data(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(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(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
- 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(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(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(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
diff --git a/src/lib/remote_rom/backend/nic_ip/client.cc b/src/lib/remote_rom/backend/nic_ip/client.cc
index 3bfa31c..0d2c6da 100644
--- a/src/lib/remote_rom/backend/nic_ip/client.cc
+++ b/src/lib/remote_rom/backend/nic_ip/client.cc
@@ -1,16 +1,142 @@
/*
* \brief Client implementation
* \author Johannes Schlatow
+ * \author Edgard Schmidt
* \date 2018-11-06
*/
-#include
#include
+#include
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 _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(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(size_guard);
+ pak.type(Packet::ACK);
+ pak.module_name(recv.module_name());
+ pak.content_hash(recv.content_hash());
+
+ AckPacket &ack =
+ pak.construct_at_data(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(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(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;
+}
diff --git a/src/lib/remote_rom/backend/nic_ip/packet.h b/src/lib/remote_rom/backend/nic_ip/packet.h
index c94c41e..5f377a6 100644
--- a/src/lib/remote_rom/backend/nic_ip/packet.h
+++ b/src/lib/remote_rom/backend/nic_ip/packet.h
@@ -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
T &construct_at_data(Net::Size_guard &size_guard)
{
size_guard.consume_head(sizeof(T));
- return *Genode::construct_at(payload);
+ return *Genode::construct_at(_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));
diff --git a/src/lib/remote_rom/backend/nic_ip/server.cc b/src/lib/remote_rom/backend/nic_ip/server.cc
index 86c4984..d302d18 100644
--- a/src/lib/remote_rom/backend/nic_ip/server.cc
+++ b/src/lib/remote_rom/backend/nic_ip/server.cc
@@ -1,16 +1,182 @@
/*
* \brief Server implementation
* \author Johannes Schlatow
+ * \author Edgard Schmidt
* \date 2018-11-06
*/
-#include
#include
+#include
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 _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(size_guard);
- pak.type(Packet::DATA);
- pak.module_name(_forwarder->module_name());
-
- DataPacket &data = pak.construct_at_data(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(size_guard);
+ pak.type(Packet::DATA);
+ pak.module_name(sender.module_name());
+ pak.content_hash(sender.content_hash());
+
+ DataPacket &data = pak.construct_at_data(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(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;
+}
diff --git a/src/proxy/remote_rom/README b/src/proxy/remote_rom/README
index 8dc387c..1df6c95 100644
--- a/src/proxy/remote_rom/README
+++ b/src/proxy/remote_rom/README
@@ -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 '' node of their
-config. The _name_ attribute specifies the ROMs module name. The
-'' node may further contain a '' 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
~~~~~~~
diff --git a/src/proxy/remote_rom/client/main.cc b/src/proxy/remote_rom/client/main.cc
index f39ceac..d623887 100644
--- a/src/proxy/remote_rom/client/main.cc
+++ b/src/proxy/remote_rom/client/main.cc
@@ -33,16 +33,18 @@
#include
#include
+#include
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_element;
typedef Genode::List 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(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();
+ }
+
+ /**
+ * Commit data contained in background dataspace
+ * (swap foreground and background dataspace)
+ */
+ bool commit_bg()
+ {
+ if (_bg_hash != cksum(_bg.local_addr(), _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 _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();
- 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(_ram_ds->cap());
- return static_cap_cast(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
{
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
* */
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(&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
}
};
-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 _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();
+ 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(), 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 {
diff --git a/src/proxy/remote_rom/server/main.cc b/src/proxy/remote_rom/server/main.cc
index e8cdd9c..1807563 100644
--- a/src/proxy/remote_rom/server/main.cc
+++ b/src/proxy/remote_rom/server/main.cc
@@ -21,11 +21,12 @@
#include
#include
-#include
-
#include
#include
+#include
+#include
+
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(), 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()),
+ return Genode::min(Genode::strlen(_rom.local_addr()),
_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() + 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 (...) { }