diff --git a/include/remote_rom/backend_base.h b/include/remote_rom/backend_base.h new file mode 100644 index 0000000..2d8b786 --- /dev/null +++ b/include/remote_rom/backend_base.h @@ -0,0 +1,40 @@ +/* + * \brief Backend interface used by the Remote_rom client and server + * \author Johannes Schlatow + * \date 2016-02-17 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __INCLUDE__REMOTE_ROM__BACKEND_BASE_H_ +#define __INCLUDE__REMOTE_ROM__BACKEND_BASE_H_ + +#include +#include + +namespace Remote_rom { + struct Backend_server_base; + struct Backend_client_base; + + class Exception : public ::Genode::Exception { }; + Backend_server_base &backend_init_server(); + Backend_client_base &backend_init_client(); +}; + +struct Remote_rom::Backend_server_base +{ + virtual void send_update() = 0; + virtual void register_forwarder(Rom_forwarder_base *forwarder) = 0; +}; + +struct Remote_rom::Backend_client_base +{ + virtual void register_receiver(Rom_receiver_base *receiver) = 0; +}; + +#endif diff --git a/include/remote_rom/rom_forwarder.h b/include/remote_rom/rom_forwarder.h new file mode 100644 index 0000000..2c8d988 --- /dev/null +++ b/include/remote_rom/rom_forwarder.h @@ -0,0 +1,31 @@ +/* + * \brief Interface used by the backend to transfer ROM content to the remote clients. + * \author Johannes Schlatow + * \date 2016-02-17 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __INCLUDE__REMOTE_ROM__ROM_FORWARDER_H_ +#define __INCLUDE__REMOTE_ROM__ROM_FORWARDER_H_ + +#include + +namespace Remote_rom { + using Genode::size_t; + struct Rom_forwarder_base; +} + +struct Remote_rom::Rom_forwarder_base +{ + virtual const char *module_name() const = 0; + virtual size_t content_size() const = 0; + virtual size_t transfer_content(char *dst, size_t dst_len, size_t offset=0) const = 0; +}; + +#endif diff --git a/include/remote_rom/rom_receiver.h b/include/remote_rom/rom_receiver.h new file mode 100644 index 0000000..735905c --- /dev/null +++ b/include/remote_rom/rom_receiver.h @@ -0,0 +1,31 @@ +/* + * \brief Interface used by the backend to write the ROM data received from the remote server + * \author Johannes Schlatow + * \date 2016-02-18 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef __INCLUDE__REMOTE_ROM__ROM_RECEIVER_H_ +#define __INCLUDE__REMOTE_ROM__ROM_RECEIVER_H_ + +#include + +namespace Remote_rom { + using Genode::size_t; + struct Rom_receiver_base; +} + +struct Remote_rom::Rom_receiver_base +{ + virtual const char *module_name() const = 0; + virtual char* start_new_content(size_t len) = 0; + virtual void commit_new_content(bool abort=false) = 0; +}; + +#endif diff --git a/lib/mk/remote_rom_backend.inc b/lib/mk/remote_rom_backend.inc new file mode 100644 index 0000000..6ff1f1e --- /dev/null +++ b/lib/mk/remote_rom_backend.inc @@ -0,0 +1,3 @@ +INC_DIR += $(REP_DIR)/include/remote_rom + +vpath % $(REP_DIR)/src/lib/remote_rom diff --git a/lib/mk/remote_rom_backend_nic_ip.mk b/lib/mk/remote_rom_backend_nic_ip.mk new file mode 100644 index 0000000..5cbd77b --- /dev/null +++ b/lib/mk/remote_rom_backend_nic_ip.mk @@ -0,0 +1,9 @@ +# \author Johannes Schlatow +# \date 2016-03-19 + +SRC_CC += backend/nic_ip/backend.cc + +LIBS += base config net + +# include less specific configuration +include $(REP_DIR)/lib/mk/remote_rom_backend.inc diff --git a/run/remote_rom_backend_nic_ip.run b/run/remote_rom_backend_nic_ip.run new file mode 100644 index 0000000..9d1ad29 --- /dev/null +++ b/run/remote_rom_backend_nic_ip.run @@ -0,0 +1,147 @@ +# +# Build +# + +build { core init drivers/timer + server/dynamic_rom + proxy/remote_rom/backend/nic_ip + app/rom_logger + drivers/nic + server/nic_bridge +} + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +# +# Boot image +# + +build_boot_image { core init timer + remote_rom_server + remote_rom_client + dynamic_rom + rom_logger + nic_drv + nic_bridge +} + +append qemu_args " -nographic " + +run_genode_until {.*change \(finished\).*} 30 + +grep_output {init -> rom_logger} +compare_output_to { +[init -> rom_logger] ROM 'remote': +[init -> rom_logger] +[init -> rom_logger] +[init -> rom_logger] ROM 'remote': +[init -> rom_logger] +[init -> rom_logger] +[init -> rom_logger] ROM 'remote': +[init -> rom_logger] +[init -> rom_logger] +[init -> rom_logger] ROM 'remote': +[init -> rom_logger] +[init -> rom_logger] +} diff --git a/run/remote_rom_backend_nic_ip_client.run b/run/remote_rom_backend_nic_ip_client.run new file mode 100644 index 0000000..447e8bc --- /dev/null +++ b/run/remote_rom_backend_nic_ip_client.run @@ -0,0 +1,75 @@ +# +# Build +# + +build { core init drivers/timer + app/rom_logger + proxy/remote_rom/backend/nic_ip/client + drivers/nic +} + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +# +# Boot image +# + +build_boot_image { core init timer + remote_rom_client + rom_logger + nic_drv +} + +append qemu_args " -nographic " +append qemu_args " -net tap,ifname=tap2 " + +run_genode_until forever diff --git a/run/remote_rom_backend_nic_ip_server.run b/run/remote_rom_backend_nic_ip_server.run new file mode 100644 index 0000000..c044f15 --- /dev/null +++ b/run/remote_rom_backend_nic_ip_server.run @@ -0,0 +1,89 @@ +# +# Build +# + +build { core init drivers/timer + server/dynamic_rom + proxy/remote_rom/backend/nic_ip/server + drivers/nic +} + +create_boot_directory + +# +# Generate config +# + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +# +# Boot image +# + +build_boot_image { core init timer + remote_rom_server + dynamic_rom + nic_drv +} + +append qemu_args " -nographic " +append qemu_args " -net tap,ifname=tap2 " + +run_genode_until forever diff --git a/src/lib/remote_rom/backend/nic_ip/backend.cc b/src/lib/remote_rom/backend/nic_ip/backend.cc new file mode 100644 index 0000000..f47476a --- /dev/null +++ b/src/lib/remote_rom/backend/nic_ip/backend.cc @@ -0,0 +1,563 @@ +/* + * \brief TODO + * \author Johannes Schlatow + * \date 2016-02-18 + */ + +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace Remote_rom { + bool verbose = false; + using Genode::size_t; + using Genode::uint16_t; + using Genode::uint32_t; + using Genode::Packet_descriptor; + using Genode::env; + using Net::Ethernet_frame; + using Net::Ipv4_packet; + + template + class Backend_base; + class Backend_server; + class Backend_client; + + struct Packet_base; + struct SignalPacket; + struct UpdatePacket; + struct DataPacket; +}; + +/* Packet format we use for inter-system communication */ +class Remote_rom::Packet_base : public Ethernet_frame, public Ipv4_packet +{ + public: + enum { + MAX_NAME_LEN = 64 /* maximum length of the module name */ + }; + + typedef enum { + SIGNAL = 1, /* signal that ROM content has changed */ + UPDATE = 2, /* request transmission of updated content */ + DATA = 3, /* first data packet */ + DATA_CONT = 4, /* following data packets */ + } Type; + + protected: + char _module_name[MAX_NAME_LEN]; /* the ROM module name */ + Type _type; /* packet type */ + uint32_t _content_size; /* ROM content size in bytes */ + uint16_t _offset; /* offset in bytes */ + uint16_t _payload_size; /* payload size in bytes */ + + /***************************************************** + ** 'payload' must be the last member of this class ** + *****************************************************/ + + char payload[0]; + + public: + + Packet_base(size_t size) : Ethernet_frame(sizeof(Packet_base) + size), Ipv4_packet(sizeof(Packet_base) + size - sizeof(Ethernet_frame)), _payload_size(size) { } + + void const * base() const { return &payload; } + + /** + * Return type of the packet + */ + Type type() const { return _type; } + + /** + * Return size of the packet + */ + size_t size() const { return _payload_size + sizeof(Packet_base); } + + /** + * Return content_size of the packet + */ + size_t content_size() const { return _content_size; } + + /** + * Return offset of the packet + */ + size_t offset() const { return _offset; } + + /** + * Return module_name of the packet + */ + char* module_name() { return _module_name; } + + /** + * Set payload size of the packet + */ + void payload_size(Genode::size_t payload_size) + { + _payload_size = payload_size; + Ipv4_packet::total_length(size() - sizeof(Ethernet_frame)); + } + + /** + * Return payload size of the packet + */ + size_t payload_size() const { return _payload_size; } + + /** + * Return address of the payload + */ + void *addr() { return payload; } + + void prepare_ethernet(const Mac_address &src, const Mac_address &dst=Ethernet_frame::BROADCAST) + { + Ethernet_frame::src(src); + Ethernet_frame::dst(dst); + Ethernet_frame::type(IPV4); + } + + void prepare_ipv4(const Ipv4_address &src, const Ipv4_address &dst=Ipv4_packet::BROADCAST) + { + Ipv4_packet::version(4); + Ipv4_packet::header_length(5); + Ipv4_packet::time_to_live(10); + Ipv4_packet::src(src); + Ipv4_packet::dst(dst); + Ipv4_packet::total_length(size() - sizeof(Ethernet_frame)); + } + + void set_checksums() + { + Ipv4_packet::checksum(Ipv4_packet::calculate_checksum(*this)); + } + +} __attribute__((packed)); + +class Remote_rom::SignalPacket : public Packet_base +{ + public: + SignalPacket() : Packet_base(0) + { } + + void prepare(const char *module) + { + Genode::strncpy(_module_name, module, MAX_NAME_LEN); + _type = SIGNAL; + _payload_size = 0; + } +} __attribute__((packed)); + +class Remote_rom::UpdatePacket : public Packet_base +{ + public: + UpdatePacket() : Packet_base(0) + { } + + void prepare(const char *module) + { + Genode::strncpy(_module_name, module, MAX_NAME_LEN); + _type = UPDATE; + _payload_size = 0; + } +} __attribute__((packed)); + +class Remote_rom::DataPacket : public Packet_base +{ + public: + enum { MAX_PAYLOAD_SIZE = 1024 }; + + char payload[MAX_PAYLOAD_SIZE]; + + DataPacket() : Packet_base(MAX_PAYLOAD_SIZE) + { } + + void prepare(const char* module, size_t offset, size_t content_size) + { + Genode::strncpy(_module_name, module, MAX_NAME_LEN); + + _payload_size = MAX_PAYLOAD_SIZE; + _offset = offset; + _content_size = content_size; + + if (offset == 0) + _type = DATA; + else + _type = DATA_CONT; + } + + /** + * Return packet size for given payload + */ + static size_t packet_size(size_t payload) { return sizeof(Packet_base) + Genode::min(payload, MAX_PAYLOAD_SIZE); } + +} __attribute__((packed)); + +template +class Remote_rom::Backend_base +{ + protected: + enum { + PACKET_SIZE = 1024, + BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE + }; + + class Rx_thread : public Genode::Thread + { + protected: + Ipv4_packet::Ipv4_address &_accept_ip; + Nic::Connection &_nic; + HANDLER &_handler; + + Genode::Signal_receiver _sig_rec; + Genode::Signal_dispatcher _link_state_dispatcher; + Genode::Signal_dispatcher _rx_packet_avail_dispatcher; + Genode::Signal_dispatcher _rx_ready_to_ack_dispatcher; + + void _handle_rx_packet_avail(unsigned) + { + while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) { + Packet_descriptor _rx_packet = _nic.rx()->get_packet(); + + char *content = _nic.rx()->packet_content(_rx_packet); + + /* check IP */ + Ipv4_packet &ip_packet = *(Packet_base*)content; + if (_accept_ip == Ipv4_packet::BROADCAST || _accept_ip == ip_packet.dst()) + _handler.receive(*(Packet_base*)content); + + _nic.rx()->acknowledge_packet(_rx_packet); + } + } + + void _handle_rx_ready_to_ack(unsigned) { _handle_rx_packet_avail(0); } + + void _handle_link_state(unsigned) + { + PINF("link state changed"); + } + + public: + Rx_thread(Nic::Connection &nic, HANDLER &handler, Ipv4_packet::Ipv4_address &ip) + : Genode::Thread(Weight::DEFAULT_WEIGHT, "backend_nic_rx", 8192), + _accept_ip(ip), + _nic(nic), _handler(handler), + _link_state_dispatcher(_sig_rec, *this, &Rx_thread::_handle_link_state), + _rx_packet_avail_dispatcher(_sig_rec, *this, &Rx_thread::_handle_rx_packet_avail), + _rx_ready_to_ack_dispatcher(_sig_rec, *this, &Rx_thread::_handle_rx_ready_to_ack) + { + _nic.link_state_sigh(_link_state_dispatcher); + _nic.rx_channel()->sigh_packet_avail(_rx_packet_avail_dispatcher); + _nic.rx_channel()->sigh_ready_to_ack(_rx_ready_to_ack_dispatcher); + } + + void entry() + { + while(true) + { + Genode::Signal sig = _sig_rec.wait_for_signal(); + int num = sig.num(); + + Genode::Signal_dispatcher_base *dispatcher; + dispatcher = dynamic_cast(sig.context()); + dispatcher->dispatch(num); + } + } + }; + + Nic::Packet_allocator _tx_block_alloc; + Nic::Connection _nic; + Rx_thread _rx_thread; + Ethernet_frame::Mac_address _mac_address; + Ipv4_packet::Ipv4_address _src_ip; + Ipv4_packet::Ipv4_address _accept_ip; + Ipv4_packet::Ipv4_address _dst_ip; + + protected: + void _tx_ack(bool block = false) + { + /* check for acknowledgements */ + while (_nic.tx()->ack_avail() || block) { + Nic::Packet_descriptor acked_packet = _nic.tx()->get_acked_packet(); + _nic.tx()->release_packet(acked_packet); + block = false; + } + } + + public: + explicit Backend_base(Genode::Allocator &alloc, HANDLER &handler) : _tx_block_alloc(&alloc), _nic(&_tx_block_alloc, BUF_SIZE, BUF_SIZE), _rx_thread(_nic, handler, _accept_ip) + { + /* start dispatcher thread */ + _rx_thread.start(); + + /* convert and store mac address */ + Nic::Mac_address mac = _nic.mac_address(); + _mac_address.addr[0] = mac.addr[0]; + _mac_address.addr[1] = mac.addr[1]; + _mac_address.addr[2] = mac.addr[2]; + _mac_address.addr[3] = mac.addr[3]; + _mac_address.addr[4] = mac.addr[4]; + _mac_address.addr[5] = mac.addr[5]; + + try { + char ip_string[15]; + Genode::Xml_node remoterom = Genode::config()->xml_node().sub_node("remote_rom"); + remoterom.attribute("src").value(ip_string, sizeof(ip_string)); + _src_ip = Ipv4_packet::ip_from_string(ip_string); + + remoterom.attribute("dst").value(ip_string, sizeof(ip_string)); + _dst_ip = Ipv4_packet::ip_from_string(ip_string); + + _accept_ip = _src_ip; + } catch (...) { + PWRN("No IP configured, falling back to broadcast mode!"); + _src_ip = Ipv4_packet::CURRENT; + _dst_ip = Ipv4_packet::BROADCAST; + _accept_ip = Ipv4_packet::BROADCAST; + } + } + + Nic::Packet_descriptor alloc_tx_packet(Genode::size_t size) + { + while (true) { + try { + Nic::Packet_descriptor packet = _nic.tx()->alloc_packet(size); + return packet; + } catch(Nic::Session::Tx::Source::Packet_alloc_failed) { + /* packet allocator exhausted, wait for acknowledgements */ + _tx_ack(true); + } + } + } + + void submit_tx_packet(Nic::Packet_descriptor packet) + { + _nic.tx()->submit_packet(packet); + /* check for acknowledgements */ + _tx_ack(); + } +}; + +class Remote_rom::Backend_server : public Backend_server_base, public Backend_base +{ + private: + static Backend_server_base* _instance; + Rom_forwarder_base *_forwarder; + + Backend_server(Genode::Allocator &alloc) : Backend_base(alloc, *this), _forwarder(nullptr) + { } + + void send_data() + { + if (!_forwarder) return; + + size_t offset = 0; + size_t size = _forwarder->content_size(); + while (offset < size) + { + /* create and transmit packet via NIC session */ + Nic::Packet_descriptor pd = alloc_tx_packet(DataPacket::packet_size(size)); + DataPacket *packet = (DataPacket*)_nic.tx()->packet_content(pd); + + packet->prepare_ethernet(_mac_address, Ethernet_frame::BROADCAST); + packet->prepare_ipv4(_src_ip, _dst_ip); + packet->prepare(_forwarder->module_name(), offset, size); + + packet->payload_size(_forwarder->transfer_content((char*)packet->addr(), DataPacket::MAX_PAYLOAD_SIZE, offset)); + packet->set_checksums(); + + submit_tx_packet(pd); + + offset += packet->payload_size(); + } + } + + public: + static Backend_server_base &instance() + { + if (!_instance) { + _instance = new (env()->heap()) Backend_server(*env()->heap()); + + if (!_instance) + throw Exception(); + } + + return *_instance; + } + + void register_forwarder(Rom_forwarder_base *forwarder) + { + _forwarder = forwarder; + } + + void send_update() + { + if (!_forwarder) return; + + /* create and transmit packet via NIC session */ + Nic::Packet_descriptor pd = alloc_tx_packet(sizeof(SignalPacket)); + SignalPacket *packet = (SignalPacket*)_nic.tx()->packet_content(pd); + + packet->prepare_ethernet(_mac_address); + packet->prepare_ipv4(_src_ip, _dst_ip); + packet->prepare(_forwarder->module_name()); + packet->set_checksums(); + + submit_tx_packet(pd); + } + + void receive(Packet_base &packet) + { + switch (packet.type()) + { + case Packet_base::UPDATE: + if (verbose) PINF("receiving UPDATE (%s) packet", packet.module_name()); + + if (!_forwarder) + return; + + /* check module name */ + if (Genode::strcmp(packet.module_name(), _forwarder->module_name())) + return; + + /* TODO (optional) dont send data within Rx_Thread's context */ + send_data(); + + break; + default: + break; + } + } +}; + +class Remote_rom::Backend_client : public Backend_client_base, public Backend_base +{ + private: + static Backend_client_base *_instance; + Rom_receiver_base *_receiver; + char *_write_ptr; + size_t _buf_size; + + Backend_client(Genode::Allocator &alloc) : Backend_base(alloc, *this), _receiver(nullptr), _write_ptr(nullptr), _buf_size(0) + { + } + + void write(char *data, size_t offset, size_t size) + { + if (!_write_ptr) return; + + size_t const len = Genode::min(size, _buf_size-offset); + Genode::memcpy(_write_ptr+offset, data, len); + + if (offset + len >= _buf_size) + _receiver->commit_new_content(); + } + + public: + static Backend_client_base &instance() + { + if (!_instance) { + _instance = new (env()->heap()) Backend_client(*env()->heap()); + + if (!_instance) + throw Exception(); + } + + return *_instance; + } + + void register_receiver(Rom_receiver_base *receiver) + { + /* TODO support multiple receivers (ROM names) */ + _receiver = receiver; + + /* FIXME request update on startup (occasionally triggers invalid signal-context capability) */ +// if (_receiver) +// update(_receiver->module_name()); + } + + + void update(const char* module_name) + { + if (!_receiver) return; + + /* check module name */ + if (Genode::strcmp(module_name, _receiver->module_name())) + return; + + /* create and transmit packet via NIC session */ + Nic::Packet_descriptor pd = alloc_tx_packet(sizeof(UpdatePacket)); + UpdatePacket *packet = (UpdatePacket*)_nic.tx()->packet_content(pd); + + packet->prepare_ethernet(_mac_address); + packet->prepare_ipv4(_src_ip, _dst_ip); + packet->prepare(_receiver->module_name()); + packet->set_checksums(); + + submit_tx_packet(pd); + } + + void receive(Packet_base &packet) + { + switch (packet.type()) + { + case Packet_base::SIGNAL: + if (verbose) PINF("receiving SIGNAL(%s) packet", packet.module_name()); + + /* send update request */ + update(packet.module_name()); + + break; + case Packet_base::DATA: + if (verbose) PINF("receiving DATA(%s) packet", packet.module_name()); + + /* write into buffer */ + if (!_receiver) return; + + /* check module name */ + if (Genode::strcmp(packet.module_name(), _receiver->module_name())) + return; + + _write_ptr = _receiver->start_new_content(packet.content_size()); + _buf_size = (_write_ptr) ? packet.content_size() : 0; + + write((char*)packet.addr(), packet.offset(), packet.payload_size()); + + break; + case Packet_base::DATA_CONT: + if (verbose) PINF("receiving DATA_CONT(%s) packet", packet.module_name()); + + if (!_receiver) return; + + /* check module name */ + if (Genode::strcmp(packet.module_name(), _receiver->module_name())) + return; + + /* write into buffer */ + write((char*)packet.addr(), packet.offset(), packet.payload_size()); + + break; + default: + break; + } + } +}; + +Remote_rom::Backend_server_base *Remote_rom::Backend_server::_instance = nullptr; +Remote_rom::Backend_client_base *Remote_rom::Backend_client::_instance = nullptr; + +Remote_rom::Backend_server_base &Remote_rom::backend_init_server() +{ + return Backend_server::instance(); +} + +Remote_rom::Backend_client_base &Remote_rom::backend_init_client() +{ + return Backend_client::instance(); +} diff --git a/src/proxy/remote_rom/README b/src/proxy/remote_rom/README new file mode 100644 index 0000000..07bfa60 --- /dev/null +++ b/src/proxy/remote_rom/README @@ -0,0 +1,32 @@ +The remote ROM allows providing ROM services over a network or similar communication +medium and thus implements publisher/subscriber communication in a distributed system. + +The remote_rom server connects to a local ROM service and provides a remote ROM service +via the specified backend. The remote_rom client connects to a remote ROM service via +the specified backend and provides a ROM service via local RPC. + +Backends +-------- + +The remote_rom can be compiled with one of multiple backends that use different session +interfaces or libraries to forward and receive the packets. By exchanging the backend, +we can also easily change the publication strategy (e.g. notification+polling vs. multicast). +Furthermore, a backend is responsible for access control and optionally allows the +specification of a policy for this. + +:'nic_ip': + This backend uses a Nic_session to transmit network packets with IPv4 headers. + +Configuration +------------- + +Both, the client and the server evaluate the 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. + +Example +~~~~~~~ + +For an example that illustrates the use of these components, please refer to the +_run/test-remote_rom_backend_nic.run_ script. diff --git a/src/proxy/remote_rom/backend/nic_ip/client/target.mk b/src/proxy/remote_rom/backend/nic_ip/client/target.mk new file mode 100644 index 0000000..6712492 --- /dev/null +++ b/src/proxy/remote_rom/backend/nic_ip/client/target.mk @@ -0,0 +1,2 @@ +include $(PRG_DIR)/../target.inc +include $(PRG_DIR)/../../../client/target.inc diff --git a/src/proxy/remote_rom/backend/nic_ip/server/target.mk b/src/proxy/remote_rom/backend/nic_ip/server/target.mk new file mode 100644 index 0000000..ca0fc73 --- /dev/null +++ b/src/proxy/remote_rom/backend/nic_ip/server/target.mk @@ -0,0 +1,2 @@ +include $(PRG_DIR)/../target.inc +include $(PRG_DIR)/../../../server/target.inc diff --git a/src/proxy/remote_rom/backend/nic_ip/target.inc b/src/proxy/remote_rom/backend/nic_ip/target.inc new file mode 100644 index 0000000..6659549 --- /dev/null +++ b/src/proxy/remote_rom/backend/nic_ip/target.inc @@ -0,0 +1 @@ +LIBS += remote_rom_backend_nic_ip diff --git a/src/proxy/remote_rom/client/main.cc b/src/proxy/remote_rom/client/main.cc new file mode 100644 index 0000000..2b4a45c --- /dev/null +++ b/src/proxy/remote_rom/client/main.cc @@ -0,0 +1,259 @@ +/* + * \brief Client-side proxy component translating a rom_session provided over a network into a local rom_session. + * \author Johannes Schlatow + * \date 2016-02-15 + * + * Usage scenario: + * __________ ________________ _________________ __________ + * | server | -> | remote_rom | -> (network) -> | remote_rom | -> | client | + * | | | server | | client | | | + * ---------- ---------------- ----------------- ---------- + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +namespace Remote_rom { + using Genode::size_t; + using Genode::Lazy_volatile_object; + using Genode::Signal_context_capability; + + class Session_component; + class Root; + struct Main; + struct Read_buffer; + + static char remotename[255]; + + typedef Genode::List Session_list; +}; + + +/** + * Interface used by the sessions to obtain the ROM data received from the remote server + */ +struct Remote_rom::Read_buffer +{ + virtual size_t content_size() const = 0; + virtual size_t export_content(char *dst, size_t dst_len) const = 0; +}; + +class Remote_rom::Session_component : public Genode::Rpc_object, + public Session_list::Element +{ + private: + Genode::Env &_env; + + Signal_context_capability _sigh; + + Read_buffer const &_buffer; + + Session_list &_sessions; + + Lazy_volatile_object _ram_ds; + + public: + + static int version() { return 1; } + + Session_component(Genode::Env &env, Session_list &sessions, Read_buffer const &buffer) + : + _env(env), _buffer(buffer), _sessions(sessions) + { + _sessions.insert(this); + } + + ~Session_component() { _sessions.remove(this); } + + void notify_client() + { + if (!_sigh.valid()) + return; + + Genode::Signal_transmitter(_sigh).submit(); + } + + /* ROM session implementation */ + + Genode::Rom_dataspace_capability dataspace() override + { + using namespace Genode; + + /* replace dataspace by new one as needed */ + if (!_ram_ds.is_constructed() + || _buffer.content_size() > _ram_ds->size()) { + + _ram_ds.construct(&_env.ram(), _buffer.content_size()); + } + + char *dst = _ram_ds->local_addr(); + 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); + } + + void sigh(Genode::Signal_context_capability sigh) override + { + _sigh = sigh; + } +}; + + +class Remote_rom::Root : public Genode::Root_component +{ + private: + + Genode::Env &_env; + Read_buffer &_buffer; + Session_list _sessions; + + protected: + + Session_component *_create_session(const char *args) + { + using namespace Genode; + + /* TODO compare requested module name with provided module name (config) */ + + return new (Root::md_alloc()) + Session_component(_env, _sessions, _buffer); + } + + public: + + Root(Genode::Env &env, Genode::Allocator &md_alloc, Read_buffer &buffer) + : Genode::Root_component(&env.ep().rpc_ep(), &md_alloc), _env(env), _buffer(buffer) + { } + + void notify_clients() + { + for (Session_component *s = _sessions.first(); s; s = s->next()) + s->notify_client(); + } +}; + +struct Remote_rom::Main : public Remote_rom::Read_buffer, public Remote_rom::Rom_receiver_base +{ + Genode::Env &env; + Genode::Heap heap = { &env.ram(), &env.rm() }; + Root remote_rom_root = { env, heap, *this }; + + Genode::Lazy_volatile_object _ds; + size_t _ds_content_size; + + Backend_client_base &_backend; + + Main(Genode::Env &env) : env(env), _ds_content_size(1024), _backend(backend_init_client()) + { + env.parent().announce(env.ep().manage(remote_rom_root)); + + /* initialise backend */ + _backend.register_receiver(this); + +// _ds.construct(Genode::env()->ram_session(), _ds_content_size); + } + + const char* module_name() const { return remotename; } + + char* start_new_content(size_t len) + { + /* Create buffer for new data */ + _ds_content_size = len; + + // TODO (optional) implement double buffering + if (!_ds.is_constructed() || _ds_content_size > _ds->size()) + _ds.construct(&env.ram(), _ds_content_size); + + // TODO set write lock + + return _ds->local_addr(); + } + + void commit_new_content(bool abort=false) + { + if (abort) + PERR("abort not supported"); + + // TODO release write lock + + remote_rom_root.notify_clients(); + } + + size_t content_size() const + { + if (_ds.is_constructed()) { + return _ds_content_size; + } + else { + /* transfer default content if set */ + try { + Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default"); + return default_content.content_size(); + } catch (...) { } + } + + return 0; + } + + size_t export_content(char *dst, size_t dst_len) const + { + if (_ds.is_constructed()) { + size_t const len = Genode::min(dst_len, _ds_content_size); + Genode::memcpy(dst, _ds->local_addr(), len); + return len; + } + else { + /* transfer default content if set */ + try { + Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default"); + size_t const len = Genode::min(dst_len, default_content.content_size()); + Genode::memcpy(dst, default_content.content_base(), len); + return len; + } catch (...) { } + } + return 0; + } +}; + +namespace Component { + Genode::size_t stack_size() { return 2*1024*sizeof(long); } + void construct(Genode::Env &env) + { + try { + Genode::Xml_node remote_rom = Genode::config()->xml_node().sub_node("remote_rom"); + remote_rom.attribute("name").value(Remote_rom::remotename, sizeof(Remote_rom::remotename)); + } catch (...) { + PERR("No ROM module configured!"); + } + + static Remote_rom::Main main(env); + } +} diff --git a/src/proxy/remote_rom/client/target.inc b/src/proxy/remote_rom/client/target.inc new file mode 100644 index 0000000..eaf6f3d --- /dev/null +++ b/src/proxy/remote_rom/client/target.inc @@ -0,0 +1,8 @@ +SRC_CC = main.cc +TARGET = remote_rom_client + +LIBS += base config + +INC_DIR += $(REP_DIR)/include/remote_rom + +vpath main.cc $(REP_DIR)/src/proxy/remote_rom/client diff --git a/src/proxy/remote_rom/server/main.cc b/src/proxy/remote_rom/server/main.cc new file mode 100644 index 0000000..1969ede --- /dev/null +++ b/src/proxy/remote_rom/server/main.cc @@ -0,0 +1,141 @@ +/* + * \brief Server-side proxy component providing a rom_session over a network. + * \author Johannes Schlatow + * \date 2016-02-15 + * + * Usage scenario: + * __________ ________________ _________________ __________ + * | server | -> | remote_rom | -> (network) -> | remote_rom | -> | client | + * | | | server | | client | | | + * ---------- ---------------- ----------------- ---------- + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +#include + +#include +#include +#include + +namespace Remote_rom { + using Genode::size_t; + using Genode::Attached_rom_dataspace; + + class Rom_forwarder; + struct Main; + + static char modulename[255]; + static char remotename[255]; + static bool binary = false; + +}; + +struct Remote_rom::Rom_forwarder : Rom_forwarder_base +{ + Attached_rom_dataspace &_rom; + Backend_server_base &_backend; + + Rom_forwarder(Attached_rom_dataspace &rom, Backend_server_base &backend) : _rom(rom), _backend(backend) + { + _backend.register_forwarder(this); + + /* on startup, send an update message to remote client */ + update(); + } + + const char *module_name() const { return remotename; } + + void update() + { + /* TODO don't update ROM if a transfer is still in progress */ + + /* refresh dataspace if valid*/ + _rom.update(); + + /* trigger backend_server */ + _backend.send_update(); + } + + size_t content_size() const + { + if (_rom.is_valid()) { + if (binary) + return _rom.size(); + else + return Genode::min(1+Genode::strlen(_rom.local_addr()), _rom.size()); + } + else { + try { + Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default"); + return default_content.content_size(); + } catch (...) { } + } + return 0; + } + + size_t transfer_content(char *dst, size_t dst_len, size_t offset=0) const + { + if (_rom.is_valid()) { + size_t const len = Genode::min(dst_len, content_size()-offset); + Genode::memcpy(dst, _rom.local_addr() + offset, len); + return len; + } + else { + /* transfer default content if set */ + try { + Genode::Xml_node default_content = Genode::config()->xml_node().sub_node("remote_rom").sub_node("default"); + size_t const len = Genode::min(dst_len, default_content.content_size()-offset); + Genode::memcpy(dst, default_content.content_base() + offset, len); + return len; + } catch (...) { } + } + + return 0; + } +}; + +struct Remote_rom::Main +{ + Genode::Entrypoint &_ep; + Attached_rom_dataspace _rom; + Rom_forwarder _forwarder; + + Genode::Signal_handler _dispatcher = { _ep, _forwarder, &Rom_forwarder::update }; + + Main(Genode::Entrypoint &ep) + : _ep(ep), + _rom(modulename), + _forwarder(_rom, backend_init_server()) + { + /* register update dispatcher */ + _rom.sigh(_dispatcher); + } +}; + +namespace Component { + Genode::size_t stack_size() { return 2*1024*sizeof(long); } + void construct(Genode::Env &env) + { + try { + Genode::Xml_node remote_rom = Genode::config()->xml_node().sub_node("remote_rom"); + remote_rom.attribute("localname").value(Remote_rom::modulename, sizeof(Remote_rom::modulename)); + remote_rom.attribute("name").value(Remote_rom::remotename, sizeof(Remote_rom::remotename)); + try { + remote_rom.attribute("binary").value(&Remote_rom::binary); + } catch (...) { } + } catch (...) { + PERR("No ROM module configured!"); + } + + static Remote_rom::Main main(env.ep()); + } +} diff --git a/src/proxy/remote_rom/server/target.inc b/src/proxy/remote_rom/server/target.inc new file mode 100644 index 0000000..36f30a8 --- /dev/null +++ b/src/proxy/remote_rom/server/target.inc @@ -0,0 +1,8 @@ +SRC_CC = main.cc +TARGET = remote_rom_server + +LIBS += base config + +INC_DIR += $(REP_DIR)/include/remote_rom + +vpath main.cc $(REP_DIR)/src/proxy/remote_rom/server