diff --git a/repos/os/recipes/pkg/pc_nic/README b/repos/os/recipes/pkg/pc_nic/README new file mode 100644 index 0000000000..a591b739d2 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/README @@ -0,0 +1,2 @@ + + Runtime for using the PC NIC driver with the NIC Uplink adapter diff --git a/repos/os/recipes/pkg/pc_nic/archives b/repos/os/recipes/pkg/pc_nic/archives new file mode 100755 index 0000000000..973c9f3f39 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/archives @@ -0,0 +1,3 @@ +_/src/nic_uplink +_/src/pc_nic_drv +_/src/init diff --git a/repos/os/recipes/pkg/pc_nic/hash b/repos/os/recipes/pkg/pc_nic/hash new file mode 100644 index 0000000000..a7c00559c1 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/hash @@ -0,0 +1 @@ +2023-07-20 b182af537d83f8d29d58a3dd70e8c49f775c26d0 diff --git a/repos/os/recipes/pkg/pc_nic/runtime b/repos/os/recipes/pkg/pc_nic/runtime new file mode 100755 index 0000000000..33d2326d23 --- /dev/null +++ b/repos/os/recipes/pkg/pc_nic/runtime @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/recipes/src/nic_uplink/content.mk b/repos/os/recipes/src/nic_uplink/content.mk new file mode 100644 index 0000000000..94ea8b1333 --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/content.mk @@ -0,0 +1,17 @@ +MIRROR_FROM_REP_DIR := \ + $(addprefix src/server/nic_router/, \ + communication_buffer.cc \ + communication_buffer.h \ + session_env.h \ + session_creation.h \ + list.h \ + ) + +content: $(MIRROR_FROM_REP_DIR) + +$(MIRROR_FROM_REP_DIR): + mkdir -p $(dir $@) + cp -r $(REP_DIR)/$@ $@ + +SRC_DIR = src/server/nic_uplink +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/os/recipes/src/nic_uplink/hash b/repos/os/recipes/src/nic_uplink/hash new file mode 100644 index 0000000000..55f5f5e165 --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/hash @@ -0,0 +1 @@ +2023-07-20 fefd70d2d32066af73cbdc53ba606eec73b73c7b diff --git a/repos/os/recipes/src/nic_uplink/used_apis b/repos/os/recipes/src/nic_uplink/used_apis new file mode 100644 index 0000000000..73fa31d89a --- /dev/null +++ b/repos/os/recipes/src/nic_uplink/used_apis @@ -0,0 +1,5 @@ +base +os +net +nic_session +uplink_session diff --git a/repos/os/run/nic_uplink.run b/repos/os/run/nic_uplink.run new file mode 100755 index 0000000000..8f23dd2251 --- /dev/null +++ b/repos/os/run/nic_uplink.run @@ -0,0 +1,141 @@ +# +# there are no nic driver packages for rpi3 and imx53 tz +# +if {[have_board rpi3] || [have_board imx53_qsb_tz]} { + puts "Run script is not supported on this platform." + exit 0 +} + +# +# lx & riscv would require extra setup on the test machine which is not desired +# +if {[get_cmd_switch --autopilot] && ([have_spec linux] || + [have_board virt_qemu_riscv])} { + + puts "Autopilot mode is not supported on this platform." + exit 0 +} + +proc test_timeout { } { + if {[have_spec sel4] && [have_board pc]} { return 60 } + return 30 +} + +proc dst_ip { } { + if {[expr ![have_include power_on/qemu]]} { + return "10.0.0.2" + } else { + return "10.0.2.2" + } +} + +build { server/nic_uplink app/ping } + +create_boot_directory + +import_from_depot [depot_user]/src/[base_src] \ + [depot_user]/pkg/[drivers_nic_pkg] \ + [depot_user]/src/init + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +build_boot_image [build_artifacts] + +append qemu_args " -nographic " +append_qemu_nic_args + +run_genode_until "child \"ping_.\" exited with exit value 0.*child \"ping_.\" exited with exit value 0.*\n" [test_timeout] diff --git a/repos/os/src/server/nic_uplink/README b/repos/os/src/server/nic_uplink/README new file mode 100644 index 0000000000..098aa7031b --- /dev/null +++ b/repos/os/src/server/nic_uplink/README @@ -0,0 +1,28 @@ +Component for forwarding network packets between a NIC session and an Uplink +session. + +On startup, the component announces only an Uplink service and waits for an +Uplink session to be created. Once the component hosts its first Uplink +session, it also announces a NIC service. The component takes the MAC address +of the first Uplink session and uses it as MAC address for all NIC sessions to +come throughout the entire component lifetime. Later Uplink session requests +must have the same MAC address as the first one or they will be denied. +This implies that switching between two network devices is not possible without +restarting the component. + +The component supports only one Uplink session but multiple NIC session at a +time. A packet received at the Uplink session is forwarded unmodified to all +NIC sessions that exist at that time. A packet received at a NIC session is +forwarded to the one Uplink session if it exists. The link state of each NIC +session is equal to the existence of an Uplink session. + +This is an exemplary configuration of the component: + +! + +The verbose flag is optional, disabled by default, and toggles whether the +component provides diagnostic output on its LOG session. The diagnostic output +consists of a full packet trace as well as component errors and warnings. + +The component is accompanied by the os/run/nic_uplink.run test that is suitable +for hardware and Qemu. diff --git a/repos/os/src/server/nic_uplink/assertion.h b/repos/os/src/server/nic_uplink/assertion.h new file mode 100644 index 0000000000..4141014656 --- /dev/null +++ b/repos/os/src/server/nic_uplink/assertion.h @@ -0,0 +1,35 @@ +/* + * \brief Assertion macros + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _ASSERTION_H_ +#define _ASSERTION_H_ + +/* base includes */ +#include +#include + +#define ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " assertion \"", #condition, "\" failed "); \ + Genode::sleep_forever(); \ + } \ + } while (false) + +#define ASSERT_NEVER_REACHED \ + do { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " should have never been reached"); \ + Genode::sleep_forever(); \ + } while (false) + +#endif /* _ASSERTION_H_ */ diff --git a/repos/os/src/server/nic_uplink/config.xsd b/repos/os/src/server/nic_uplink/config.xsd new file mode 100644 index 0000000000..aef8844770 --- /dev/null +++ b/repos/os/src/server/nic_uplink/config.xsd @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/repos/os/src/server/nic_uplink/main.cc b/repos/os/src/server/nic_uplink/main.cc new file mode 100755 index 0000000000..eae4380d1d --- /dev/null +++ b/repos/os/src/server/nic_uplink/main.cc @@ -0,0 +1,820 @@ +/* + * \brief Component construct and main component object + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* base includes */ +#include +#include +#include +#include + +/* nic_uplink includes */ +#include +#include + +/* nic_router includes */ +#include +#include +#include + +/* os includes */ +#include +#include +#include +#include + +using namespace Genode; +using namespace Net; + +namespace Nic_uplink { + + class Main; +} + +using namespace Nic_uplink; + +namespace Net { + + enum { PKT_STREAM_QUEUE_SIZE = 1024 }; + + template + void log_if(bool condition, ARGS &&... args) + { + if (condition) + log(args...); + } + + using Packet_descriptor = Genode::Packet_descriptor; + using Packet_stream_policy = Genode::Packet_stream_policy; + using Packet_stream_sink = Genode::Packet_stream_sink; + using Packet_stream_source = Genode::Packet_stream_source; + + class Network_interface; + class Uplink_session_component_base; + class Uplink_session_component; + class Uplink_session_root; + class Nic_session_component_base; + class Nic_session_component; + class Nic_session_root; + using Nic_session_list_item = List_element; + using Nic_session_list = Net::List; +} + + +class Net::Network_interface +{ + public: + + using Label = String<32>; + + private: + + Packet_stream_sink &_sink; + Packet_stream_source &_source; + Label const _label; + bool const _verbose; + + public: + + Network_interface(Packet_stream_sink &sink, + Packet_stream_source &source, + Label const &label, + bool verbose) + : + _sink { sink }, + _source { source }, + _label { label }, + _verbose { verbose } + { } + + virtual ~Network_interface() { } + + template + void send_packet(size_t pkt_size, GENERATE_PKT && generate_pkt) + { + if (!_source.ready_to_submit()) { + log_if(_verbose, "[", _label, "] failed to send packet"); + return; + } + _source.alloc_packet_attempt(pkt_size).with_result( + [&] (Packet_descriptor pkt) + { + void *pkt_base { _source.packet_content(pkt) }; + generate_pkt(Byte_range_ptr { (char *)pkt_base, pkt_size }); + Size_guard size_guard(pkt_size); + log_if(_verbose, "[", _label, "] snd ", Ethernet_frame::cast_from(pkt_base, size_guard)); + _source.try_submit_packet(pkt); + }, + [&] (Packet_stream_source::Alloc_packet_error) + { + log_if(_verbose, "[", _label, "] failed to alloc packet"); + } + ); + } + + void forward_packet(Byte_range_ptr const &src); + + template + void handle_received_packets(HANDLE_PKT && handle_pkt) const + { + while (_source.ack_avail()) { + _source.release_packet(_source.try_get_acked_packet()); + } + while (_sink.packet_avail()) { + Packet_descriptor const pkt { _sink.get_packet() }; + handle_pkt(Byte_range_ptr { _sink.packet_content(pkt), pkt.size() }); + if (!_sink.try_ack_packet(pkt)) + log_if(_verbose, "[", _label, "] failed to ack packet"); + } + } + + void wakeup_source() { _source.wakeup(); }; + + void wakeup_sink() { _sink.wakeup(); } +}; + + +class Net::Uplink_session_component_base +{ + protected: + + Session_env &_session_env; + Heap _alloc; + Nic::Packet_allocator _packet_alloc; + Communication_buffer _tx_buf; + Communication_buffer _rx_buf; + + public: + + Uplink_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size); +}; + + +class Net::Uplink_session_component +: + private Uplink_session_component_base, + public ::Uplink::Session_rpc_object +{ + private: + + Main &_main; + Ram_dataspace_capability const _ram_ds; + Network_interface _net_if; + Signal_handler _pkt_stream_signal_handler; + + void _handle_pkt_stream_signal(); + + public: + + Uplink_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main); + + void forward_packet(Byte_range_ptr const &src) { _net_if.forward_packet(src); } + + void wakeup_source() { _net_if.wakeup_source(); }; + + void wakeup_sink() { _net_if.wakeup_sink(); } + + + /*************** + ** Accessors ** + ***************/ + + Ram_dataspace_capability ram_ds() const { return _ram_ds; }; + Session_env const &session_env() const { return _session_env; }; +}; + + +class Net::Uplink_session_root +: + public Root_component +{ + private: + + Env &_env; + Quota &_shared_quota; + Main &_main; + + + /******************** + ** Root_component ** + ********************/ + + Uplink_session_component *_create_session(char const *args) override; + void _destroy_session(Uplink_session_component *session) override; + + public: + + Uplink_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main); +}; + + +class Net::Nic_session_component_base +{ + protected: + + Session_env &_session_env; + Heap _alloc; + Nic::Packet_allocator _packet_alloc; + Communication_buffer _tx_buf; + Communication_buffer _rx_buf; + + public: + + Nic_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size); +}; + + +class Net::Nic_session_component +: + private Nic_session_component_base, + public ::Nic::Session_rpc_object +{ + private: + + Main &_main; + Ram_dataspace_capability const _ram_ds; + Network_interface _net_if; + Signal_handler _pkt_stream_signal_handler; + Signal_context_capability _link_state_sigh { }; + Nic_session_list_item _list_item { this }; + + void _handle_pkt_stream_signal(); + + public: + + Nic_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main); + + void forward_packet(Byte_range_ptr const &src) { _net_if.forward_packet(src); } + + void submit_link_state_signal(); + + void wakeup_source() { _net_if.wakeup_source(); }; + + void wakeup_sink() { _net_if.wakeup_sink(); } + + + /****************** + ** Nic::Session ** + ******************/ + + Mac_address mac_address() override; + bool link_state() override; + void link_state_sigh(Signal_context_capability sigh) override; + + + /*************** + ** Accessors ** + ***************/ + + Ram_dataspace_capability ram_ds() const { return _ram_ds; }; + Session_env const &session_env() const { return _session_env; }; + + template + void with_list_item(FUNC && func) { func(_list_item); } +}; + + +class Net::Nic_session_root +: + public Root_component +{ + private: + + Env &_env; + Quota &_shared_quota; + Main &_main; + + + /******************** + ** Root_component ** + ********************/ + + Nic_session_component *_create_session(char const *args) override; + void _destroy_session(Nic_session_component *session) override; + + public: + + Nic_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main); +}; + + +class Nic_uplink::Main +{ + private: + + Env &_env; + Quota _shared_quota { }; + Heap _heap { &_env.ram(), &_env.rm() }; + Uplink_session_component *_uplink_session_ptr { nullptr }; + Nic_session_list _nic_session_list { }; + Uplink_session_root _uplink_session_root { _env, _heap, _shared_quota, *this }; + Nic_session_root _nic_session_root { _env, _heap, _shared_quota, *this }; + bool _nic_service_announced { false }; + Mac_address _uplink_mac { }; + bool _uplink_mac_valid { false }; + Attached_rom_dataspace _config_rom { _env, "config" }; + bool const _verbose { _config_rom.xml().attribute_value("verbose", false) }; + + Main(Main const &) = delete; + + Main & operator = (Main const &) = delete; + + public: + + Main(Env &env); + + bool verbose() const { return _verbose; } + + bool ready_to_manage_uplink_session() const { return !_uplink_session_ptr; } + + void manage_uplink_session(Uplink_session_component &session, + Mac_address const &mac); + + template + void with_uplink_session(FUNC && func) + { + if (_uplink_session_ptr) + func(*_uplink_session_ptr, _uplink_mac); + } + + void dissolve_uplink_session(Uplink_session_component &session); + + void manage_nic_session(Nic_session_component &session); + + template + void for_each_nic_session(FUNC && func) + { + _nic_session_list.for_each([&] (Nic_session_list_item &list_item) + { + func(*list_item.object()); + }); + } + + void dissolve_nic_session(Nic_session_component &session); +}; + + +/************************************* + ** Net::Nic_session_component_base ** + *************************************/ + +Nic_session_component_base::Nic_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size) +: + _session_env { session_env }, + _alloc { _session_env, _session_env }, + _packet_alloc { &_alloc }, + _tx_buf { _session_env, tx_buf_size }, + _rx_buf { _session_env, rx_buf_size } +{ } + + +/******************************** + ** Net::Nic_session_component ** + ********************************/ + +Net::Nic_session_component::Nic_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main) +: + Nic_session_component_base { session_env, tx_buf_size,rx_buf_size }, + Session_rpc_object { + _session_env, _tx_buf.ds(), _rx_buf.ds(), &_packet_alloc, + _session_env.ep().rpc_ep() }, + _main { main }, + _ram_ds { ram_ds }, + _net_if { *_tx.sink(), *_rx.source(), "nic", _main.verbose() }, + _pkt_stream_signal_handler { session_env.ep(), *this, &Nic_session_component::_handle_pkt_stream_signal } +{ + /* install packet stream signal handlers */ + _tx.sigh_packet_avail(_pkt_stream_signal_handler); + _rx.sigh_ack_avail(_pkt_stream_signal_handler); + + /* + * We do not install ready_to_submit because submission is only triggered by + * incoming packets (and dropped if the submit queue is full). + * The ack queue should never be full otherwise we'll be leaking packets. + */ +} + + +void Net::Nic_session_component::_handle_pkt_stream_signal() +{ + _net_if.handle_received_packets([&] (Byte_range_ptr const &src) { + + Size_guard size_guard { src.num_bytes }; + Ethernet_frame ð { Ethernet_frame::cast_from(src.start, size_guard) }; + log_if(_main.verbose(), "[nic] rcv ", eth); + + _main.with_uplink_session([&] (Uplink_session_component &uplink_session, + Mac_address const &) + { + uplink_session.forward_packet(src); + }); + }); + _main.with_uplink_session([&] (Uplink_session_component &uplink_session, + Mac_address const &) + { + uplink_session.wakeup_source(); + }); + wakeup_sink(); +} + + +Mac_address Net::Nic_session_component::mac_address() +{ + Mac_address result { }; + _main.with_uplink_session([&] (Uplink_session_component &, + Mac_address const &uplink_mac) + { + result = uplink_mac; + }); + return result; +} + + +bool Net::Nic_session_component::link_state() +{ + bool result { false }; + _main.with_uplink_session([&] (Uplink_session_component &, + Mac_address const &) + { + result = true; + }); + return result; +} + + +void Net::Nic_session_component::link_state_sigh(Signal_context_capability sigh) +{ + _link_state_sigh = sigh; +} + + +void Net::Nic_session_component::submit_link_state_signal() +{ + Signal_transmitter { _link_state_sigh }.submit(); +} + + +/**************************** + ** Net::Network_interface ** + ****************************/ + +void Net::Network_interface::forward_packet(Byte_range_ptr const &src) +{ + send_packet(src.num_bytes, [&] (Byte_range_ptr const &dst) { + memcpy(dst.start, src.start, dst.num_bytes); + }); +} + + +/**************************************** + ** Net::Uplink_session_component_base ** + ****************************************/ + +Net::Uplink_session_component_base:: +Uplink_session_component_base(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size) +: + _session_env { session_env }, + _alloc { _session_env, _session_env }, + _packet_alloc { &_alloc }, + _tx_buf { _session_env, tx_buf_size }, + _rx_buf { _session_env, rx_buf_size } +{ } + + +/*********************************** + ** Net::Uplink_session_component ** + ***********************************/ + +void Net::Uplink_session_component::_handle_pkt_stream_signal() +{ + _net_if.handle_received_packets([&] (Byte_range_ptr const &src) + { + Size_guard size_guard { src.num_bytes }; + Ethernet_frame ð { Ethernet_frame::cast_from(src.start, size_guard) }; + log_if(_main.verbose(), "[uplink] rcv ", eth); + + _main.for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.forward_packet(src); + }); + }); + _main.for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.wakeup_source(); + }); + wakeup_sink(); +} + + +Net::Uplink_session_component::Uplink_session_component(Session_env &session_env, + size_t const tx_buf_size, + size_t const rx_buf_size, + Ram_dataspace_capability const ram_ds, + Main &main) +: + Uplink_session_component_base { session_env, tx_buf_size,rx_buf_size }, + Session_rpc_object { + _session_env, _tx_buf.ds(), _rx_buf.ds(), &_packet_alloc, + _session_env.ep().rpc_ep() }, + _main { main }, + _ram_ds { ram_ds }, + _net_if { *_tx.sink(), *_rx.source(), "uplink", _main.verbose() }, + _pkt_stream_signal_handler { session_env.ep(), *this, &Uplink_session_component::_handle_pkt_stream_signal } +{ + /* install packet stream signal handlers */ + _tx.sigh_packet_avail(_pkt_stream_signal_handler); + _rx.sigh_ack_avail(_pkt_stream_signal_handler); + + /* + * We do not install ready_to_submit because submission is only triggered + * by incoming packets (and dropped if the submit queue is full). + * The ack queue should never be full otherwise we'll be leaking packets. + */ +} + + +/****************************** + ** Net::Uplink_session_root ** + ******************************/ + +Net::Uplink_session_root::Uplink_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main) +: + Root_component { &env.ep().rpc_ep(), &alloc }, + _env { env }, + _shared_quota { shared_quota }, + _main { main } +{ } + + +Uplink_session_component * +Net::Uplink_session_root::_create_session(char const *args) +{ + Session_creation session_creation { }; + if (!_main.ready_to_manage_uplink_session()) { + log_if(_main.verbose(), "[uplink] failed to manage new session"); + throw Service_denied(); + } + try { + return session_creation.execute( + _env, _shared_quota, args, + [&] (Session_env &session_env, void *session_at, Ram_dataspace_capability ram_ds) + { + enum { MAC_STR_LENGTH = 19 }; + char mac_str [MAC_STR_LENGTH]; + Arg mac_arg { Arg_string::find_arg(args, "mac_address") }; + + if (!mac_arg.valid()) { + log_if(_main.verbose(), "[uplink] failed to find 'mac_address' arg"); + throw Service_denied(); + } + mac_arg.string(mac_str, MAC_STR_LENGTH, ""); + Mac_address mac { }; + ascii_to(mac_str, mac); + if (mac == Mac_address { }) { + log_if(_main.verbose(), "[uplink] malformed 'mac_address' arg"); + throw Service_denied(); + } + Uplink_session_component &session { + *construct_at( + session_at, + session_env, + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), + ram_ds, _main) }; + + _main.manage_uplink_session(session, mac); + return &session; + }); + } + catch (Region_map::Invalid_dataspace) { + log_if(_main.verbose(), "[uplink] failed to attach RAM"); + throw Service_denied(); + } + catch (Region_map::Region_conflict) { + log_if(_main.verbose(), "[uplink] failed to attach RAM"); + throw Service_denied(); + } + catch (Out_of_ram) { + log_if(_main.verbose(), "[uplink] insufficient session RAM quota"); + throw Insufficient_ram_quota(); + } + catch (Out_of_caps) { + log_if(_main.verbose(), "[uplink] insufficient session CAP quota"); + throw Insufficient_cap_quota(); + } +} + +void Net::Uplink_session_root::_destroy_session(Uplink_session_component *session_ptr) +{ + _main.dissolve_uplink_session(*session_ptr); + + /* read out initial dataspace and session env and destruct session */ + Ram_dataspace_capability ram_ds { session_ptr->ram_ds() }; + Session_env const &session_env { session_ptr->session_env() }; + session_ptr->~Uplink_session_component(); + + /* copy session env to stack and detach/free all session data */ + Session_env session_env_stack { session_env }; + session_env_stack.detach(session_ptr); + session_env_stack.detach(&session_env); + session_env_stack.free(ram_ds); + + /* check for leaked quota */ + if (session_env_stack.ram_guard().used().value) + log_if(_main.verbose(), "[uplink] session leaks RAM quota of ", + session_env_stack.ram_guard().used().value, " byte(s)"); + if (session_env_stack.cap_guard().used().value) + log_if(_main.verbose(), "[uplink] session leaks CAP quota of ", + session_env_stack.cap_guard().used().value, " cap(s)"); +} + + +/*************************** + ** Net::Nic_session_root ** + ***************************/ + +Net::Nic_session_root::Nic_session_root(Env &env, + Allocator &alloc, + Quota &shared_quota, + Main &main) +: + Root_component { &env.ep().rpc_ep(), &alloc }, + _env { env }, + _shared_quota { shared_quota }, + _main { main } +{ } + + +Nic_session_component *Net::Nic_session_root::_create_session(char const *args) +{ + Session_creation session_creation { }; + try { + return session_creation.execute( + _env, _shared_quota, args, + [&] (Session_env &session_env, void *session_at, Ram_dataspace_capability ram_ds) + { + Nic_session_component &session { + *construct_at( + session_at, session_env, + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), + ram_ds, _main) }; + + _main.manage_nic_session(session); + return &session; + } + ); + } + catch (Region_map::Invalid_dataspace) { + log_if(_main.verbose(), "[nic] failed to attach RAM"); + throw Service_denied(); + } + catch (Region_map::Region_conflict) { + log_if(_main.verbose(), "[nic] failed to attach RAM"); + throw Service_denied(); + } + catch (Out_of_ram) { + log_if(_main.verbose(), "[nic] insufficient session RAM quota"); + throw Insufficient_ram_quota(); + } + catch (Out_of_caps) { + log_if(_main.verbose(), "[nic] insufficient session CAP quota"); + throw Insufficient_cap_quota(); + } +} + +void Net::Nic_session_root::_destroy_session(Nic_session_component *session_ptr) +{ + _main.dissolve_nic_session(*session_ptr); + + /* read out initial dataspace and session env and destruct session */ + Ram_dataspace_capability ram_ds { session_ptr->ram_ds() }; + Session_env const &session_env { session_ptr->session_env() }; + session_ptr->~Nic_session_component(); + + /* copy session env to stack and detach/free all session data */ + Session_env session_env_stack { session_env }; + session_env_stack.detach(session_ptr); + session_env_stack.detach(&session_env); + session_env_stack.free(ram_ds); + + /* check for leaked quota */ + if (session_env_stack.ram_guard().used().value) + log_if(_main.verbose(), "[nic] session leaks RAM quota of ", + session_env_stack.ram_guard().used().value, " byte(s)"); + if (session_env_stack.cap_guard().used().value) + log_if(_main.verbose(), "[nic] session leaks CAP quota of ", + session_env_stack.cap_guard().used().value, " cap(s)"); +} + + +/********************** + ** Nic_uplink::Main ** + **********************/ + +Nic_uplink::Main::Main(Env &env) +: + _env { env } +{ + _env.parent().announce(_env.ep().manage(_uplink_session_root)); +} + + +void Nic_uplink::Main::manage_uplink_session(Uplink_session_component &session, + Mac_address const &mac) +{ + ASSERT(!_uplink_session_ptr); + + if (_uplink_mac_valid) + ASSERT(_uplink_mac == mac); + + _uplink_session_ptr = &session; + _uplink_mac = mac; + + if (!_nic_service_announced) { + _env.parent().announce(_env.ep().manage(_nic_session_root)); + _nic_service_announced = true; + } + for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.submit_link_state_signal(); + }); + log_if(_verbose, "[uplink] session created! mac=", _uplink_mac); +} + + +void Nic_uplink::Main::manage_nic_session(Nic_session_component &session) +{ + session.with_list_item([&] (Nic_session_list_item &item) + { + _nic_session_list.insert(&item); + }); + log_if(_verbose, "[nic] session created!"); +} + + +void Nic_uplink::Main::dissolve_uplink_session(Uplink_session_component &session) +{ + ASSERT(_uplink_session_ptr == &session); + _uplink_session_ptr = nullptr; + for_each_nic_session([&] (Nic_session_component &nic_session) + { + nic_session.submit_link_state_signal(); + }); + log_if(_verbose, "[uplink] session dissolved!"); +} + + +void Nic_uplink::Main::dissolve_nic_session(Nic_session_component &session) +{ + session.with_list_item([&] (Nic_session_list_item &item) + { + _nic_session_list.remove(&item); + }); + log_if(_verbose, "[nic] session dissolved!"); +} + + +/*********************** + ** Genode::Component ** + ***********************/ + +void Component::construct(Env &env) { static Nic_uplink::Main main { env }; } diff --git a/repos/os/src/server/nic_uplink/quota.h b/repos/os/src/server/nic_uplink/quota.h new file mode 100644 index 0000000000..11484bf5f7 --- /dev/null +++ b/repos/os/src/server/nic_uplink/quota.h @@ -0,0 +1,28 @@ +/* + * \brief Session quota + * \author Martin Stein + * \date 2023-07-13 + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _QUOTA_H_ +#define _QUOTA_H_ + +namespace Net { + + class Quota; +} + +struct Net::Quota +{ + Genode::size_t ram { 0 }; + Genode::size_t cap { 0 }; +}; + +#endif /* _QUOTA_H_ */ diff --git a/repos/os/src/server/nic_uplink/target.mk b/repos/os/src/server/nic_uplink/target.mk new file mode 100755 index 0000000000..70d3222d2a --- /dev/null +++ b/repos/os/src/server/nic_uplink/target.mk @@ -0,0 +1,14 @@ +TARGET = nic_uplink + +LIBS += base net + +INC_DIR += $(PRG_DIR) +INC_DIR += $(REP_DIR)/src/server/nic_router + +SRC_CC += main.cc +SRC_CC += communication_buffer.cc + +vpath main.cc $(PRG_DIR) +vpath communication_buffer.cc $(REP_DIR)/src/server/nic_router + +CONFIG_XSD = config.xsd diff --git a/tool/autopilot.list b/tool/autopilot.list index 6f47444752..0d13efe99a 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -54,6 +54,7 @@ nic_router_flood nic_router_ipv4_fragm nic_router_stress nic_router_uplinks +nic_uplink nvme ping ping_nic_router