diff --git a/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis b/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis index b343b02413..6c0feab87e 100644 --- a/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis +++ b/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis @@ -1,5 +1,7 @@ base os nic_session +uplink_session +nic_driver platform_session timer_session diff --git a/repos/dde_ipxe/src/drivers/nic/main.cc b/repos/dde_ipxe/src/drivers/nic/main.cc index aa574f1309..be816a42bf 100644 --- a/repos/dde_ipxe/src/drivers/nic/main.cc +++ b/repos/dde_ipxe/src/drivers/nic/main.cc @@ -12,14 +12,21 @@ * version 2. */ -/* Genode */ +/* Genode includes */ #include #include #include #include #include #include +#include +#include +/* NIC driver includes */ +#include +#include + +/* DDE iPXE includes */ #include #include @@ -61,13 +68,13 @@ class Ipxe_session_component : public Nic::Session_component Packet_descriptor packet = _tx.sink()->get_packet(); if (!packet.size()) { - Genode::warning("Invalid tx packet"); + warning("Invalid tx packet"); return true; } if (link_state()) { if (dde_ipxe_nic_tx(1, _tx.sink()->packet_content(packet), packet.size())) - Genode::warning("Sending packet failed!"); + warning("Sending packet failed!"); } _tx.sink()->acknowledge_packet(packet); @@ -83,10 +90,10 @@ class Ipxe_session_component : public Nic::Session_component try { Nic::Packet_descriptor p = _rx.source()->alloc_packet(packet_len); - Genode::memcpy(_rx.source()->packet_content(p), packet, packet_len); + memcpy(_rx.source()->packet_content(p), packet, packet_len); _rx.source()->submit_packet(p); } catch (...) { - Genode::warning(__func__, ": failed to process received packet"); + warning(__func__, ": failed to process received packet"); } } @@ -100,11 +107,11 @@ class Ipxe_session_component : public Nic::Session_component public: - Ipxe_session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator &rx_block_md_alloc, - Genode::Env &env) - : Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, + Ipxe_session_component(size_t const tx_buf_size, + size_t const rx_buf_size, + Allocator &rx_block_md_alloc, + Env &env) + : Session_component(tx_buf_size, rx_buf_size, CACHED, rx_block_md_alloc, env) { instance = this; @@ -113,7 +120,7 @@ class Ipxe_session_component : public Nic::Session_component dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr); - Genode::log("MAC address ", _mac_addr); + log("MAC address ", _mac_addr); } ~Ipxe_session_component() @@ -138,32 +145,126 @@ class Ipxe_session_component : public Nic::Session_component Ipxe_session_component *Ipxe_session_component::instance; +class Uplink_client : public Uplink_client_base +{ + public: + + static Uplink_client *instance; + + private: + + Nic::Mac_address _init_drv_mac_addr() + { + instance = this; + dde_ipxe_nic_register_callbacks( + _drv_rx_callback, _drv_link_callback); + + Nic::Mac_address mac_addr { }; + dde_ipxe_nic_get_mac_addr(1, mac_addr.addr); + return mac_addr; + } + + + /*********************************** + ** Interface towards iPXE driver ** + ***********************************/ + + static void _drv_rx_callback(unsigned interface_idx, + const char *drv_rx_pkt_base, + unsigned drv_rx_pkt_size) + { + instance->_drv_rx_handle_pkt( + drv_rx_pkt_size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy(conn_tx_pkt_base, drv_rx_pkt_base, drv_rx_pkt_size); + return Write_result::WRITE_SUCCEEDED; + }); + } + + static void _drv_link_callback() + { + instance->_drv_handle_link_state(dde_ipxe_nic_link_state(1)); + } + + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + if (dde_ipxe_nic_tx(1, conn_rx_pkt_base, conn_rx_pkt_size) == 0) { + + return Transmit_result::ACCEPTED; + } + return Transmit_result::REJECTED; + } + + public: + + Uplink_client(Env &env, + Allocator &alloc) + : + Uplink_client_base { env, alloc, _init_drv_mac_addr() } + { + _drv_handle_link_state(dde_ipxe_nic_link_state(1)); + } + + ~Uplink_client() + { + dde_ipxe_nic_unregister_callbacks(); + instance = nullptr; + } +}; + + +Uplink_client *Uplink_client::instance; + + struct Main { - Env &_env; - - Heap _heap { _env.ram(), _env.rm() }; - - Nic::Root root {_env, _heap }; + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Attached_rom_dataspace _config_rom { _env, "config" }; Main(Env &env) : _env(env) { - Genode::log("--- iPXE NIC driver started ---"); + log("--- iPXE NIC driver started ---"); - Genode::log("-- init iPXE NIC"); + log("-- init iPXE NIC"); /* pass Env to backend */ dde_support_init(_env, _heap); if (!dde_ipxe_nic_init()) { - Genode::error("could not find usable NIC device"); + error("could not find usable NIC device"); } + Nic_driver_mode const mode { + read_nic_driver_mode(_config_rom.xml()) }; - _env.parent().announce(_env.ep().manage(root)); + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + { + Nic::Root &root { + *new (_heap) + Nic::Root(_env, _heap) }; + + _env.parent().announce(_env.ep().manage(root)); + break; + } + case Nic_driver_mode::UPLINK_CLIENT: + + new (_heap) Uplink_client(_env, _heap); + break; + } } }; -void Component::construct(Genode::Env &env) +void Component::construct(Env &env) { /* XXX execute constructors of global statics */ env.exec_static_constructors(); diff --git a/repos/dde_ipxe/src/drivers/nic/target.mk b/repos/dde_ipxe/src/drivers/nic/target.mk index 95f6bff4d8..facbd98758 100644 --- a/repos/dde_ipxe/src/drivers/nic/target.mk +++ b/repos/dde_ipxe/src/drivers/nic/target.mk @@ -1,6 +1,6 @@ TARGET = ipxe_nic_drv REQUIRES = x86 -LIBS = base dde_ipxe_nic +LIBS = base dde_ipxe_nic nic_driver SRC_CC = main.cc CC_CXX_WARN_STRICT = diff --git a/repos/dde_linux/lib/mk/wifi.inc b/repos/dde_linux/lib/mk/wifi.inc index dddb2a80f8..18922c6dbc 100644 --- a/repos/dde_linux/lib/mk/wifi.inc +++ b/repos/dde_linux/lib/mk/wifi.inc @@ -11,7 +11,7 @@ SHARED_LIB = yes # wifi_include *must* be the first library, otherwise the include # order is wrong # -LIBS += wifi_include lx_kit_setjmp +LIBS += wifi_include lx_kit_setjmp nic_driver LD_OPT += --version-script=$(LIB_DIR)/symbol.map diff --git a/repos/dde_linux/recipes/src/fec_nic_drv/content.mk b/repos/dde_linux/recipes/src/fec_nic_drv/content.mk index 1f17cba1a0..09b87aa2bb 100644 --- a/repos/dde_linux/recipes/src/fec_nic_drv/content.mk +++ b/repos/dde_linux/recipes/src/fec_nic_drv/content.mk @@ -4,6 +4,8 @@ LIB_MK := lib/mk/fec_nic_include.mk \ PORT_DIR := $(call port_dir,$(REP_DIR)/ports/dde_linux) MIRROR_FROM_REP_DIR := $(LIB_MK) \ + src/drivers/nic/linux_network_session_base.cc \ + src/drivers/nic/linux_network_session_base.h \ lib/import/import-fec_nic_include.mk \ src/include src/lx_kit \ $(shell cd $(REP_DIR); find src/drivers/nic/fec -type f) diff --git a/repos/dde_linux/recipes/src/fec_nic_drv/used_apis b/repos/dde_linux/recipes/src/fec_nic_drv/used_apis index a985c7f217..fbea6143a0 100644 --- a/repos/dde_linux/recipes/src/fec_nic_drv/used_apis +++ b/repos/dde_linux/recipes/src/fec_nic_drv/used_apis @@ -3,4 +3,6 @@ os platform_session timer_session nic_session +uplink_session +nic_driver gpio_session diff --git a/repos/dde_linux/recipes/src/usb_drv/used_apis b/repos/dde_linux/recipes/src/usb_drv/used_apis index 3ef1daeb98..797b95b382 100644 --- a/repos/dde_linux/recipes/src/usb_drv/used_apis +++ b/repos/dde_linux/recipes/src/usb_drv/used_apis @@ -1,6 +1,8 @@ base os nic_session +uplink_session +nic_driver usb_session gpio_session event_session diff --git a/repos/dde_linux/recipes/src/wifi_drv/used_apis b/repos/dde_linux/recipes/src/wifi_drv/used_apis index 718ec71d56..fc4a522bed 100644 --- a/repos/dde_linux/recipes/src/wifi_drv/used_apis +++ b/repos/dde_linux/recipes/src/wifi_drv/used_apis @@ -4,6 +4,8 @@ libc libcrypto libssl nic_session +uplink_session +nic_driver platform_session report_session timer_session diff --git a/repos/dde_linux/src/drivers/nic/fec/component.cc b/repos/dde_linux/src/drivers/nic/fec/component.cc index db7a41480d..6f131c5c9a 100644 --- a/repos/dde_linux/src/drivers/nic/fec/component.cc +++ b/repos/dde_linux/src/drivers/nic/fec/component.cc @@ -18,52 +18,6 @@ extern "C" { #include }; -void Session_component::_run_rx_task(void * args) -{ - Rx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - int ret = 0; - struct napi_struct * n = data->napi; - - for (;;) { - - /* This NAPI_STATE_SCHED test is for avoiding a race - * with netpoll's poll_napi(). Only the entity which - * obtains the lock and sees NAPI_STATE_SCHED set will - * actually make the ->poll() call. Therefore we avoid - * accidentally calling ->poll() when NAPI is not scheduled. - */ - if (!test_bit(NAPI_STATE_SCHED, &n->state)) break; - - int weight = n->weight; - int work = n->poll(n, weight); - ret += work; - - if (work < weight) break; - - Genode::warning("Too much incoming traffic, we should schedule RX more intelligent"); - } - } -} - - -void Session_component::_run_tx_task(void * args) -{ - Tx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - net_device * ndev = data->ndev; - struct sk_buff * skb = data->skb; - - ndev->netdev_ops->ndo_start_xmit(skb, ndev); - } -} - bool Session_component::_send() { @@ -87,8 +41,6 @@ bool Session_component::_send() return true; } - enum { HEAD_ROOM = 8 }; - struct sk_buff *skb = lxc_alloc_skb(packet.size() + HEAD_ROOM, HEAD_ROOM); unsigned char *data = lxc_skb_put(skb, packet.size()); @@ -121,13 +73,6 @@ void Session_component::_handle_packet_stream() } -void Session_component::unblock_rx_task(napi_struct * n) -{ - _rx_data.napi = n; - _rx_task.unblock(); -} - - Nic::Mac_address Session_component::mac_address() { return _ndev ? Nic::Mac_address(_ndev->dev_addr) : Nic::Mac_address(); @@ -169,15 +114,14 @@ void Session_component::link_state(bool link) } -Session_component::Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label) -: Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, - rx_block_md_alloc, env), - _ndev(_register_session_component(*this, label)), - _has_link(_ndev ? !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)) : false) -{ - if (!_ndev) throw Genode::Service_denied(); -} +Session_component::Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label) +: + Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env }, + Linux_network_session_base { label }, + _has_link { _read_link_state_from_ndev() } +{ } diff --git a/repos/dde_linux/src/drivers/nic/fec/component.h b/repos/dde_linux/src/drivers/nic/fec/component.h index 09ae289173..55ab7c8139 100644 --- a/repos/dde_linux/src/drivers/nic/fec/component.h +++ b/repos/dde_linux/src/drivers/nic/fec/component.h @@ -18,43 +18,16 @@ #include #include -#include +/* local includes */ +#include -extern "C" { - struct net_device; - struct napi_struct; - struct sk_buff; -} -class Session_component : public Nic::Session_component +class Session_component : public Nic::Session_component, + public Linux_network_session_base { private: - struct Tx_data - { - net_device * ndev; - sk_buff * skb; - }; - - struct Rx_data - { - struct napi_struct * napi; - }; - - net_device * _ndev = nullptr; - bool _has_link = false; - Tx_data _tx_data; - Rx_data _rx_data; - - static void _run_tx_task(void * args); - static void _run_rx_task(void * args); - static net_device * _register_session_component(Session_component &, - Genode::Session_label); - - Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; - Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; + bool _has_link = false; bool _send(); void _handle_rx(); @@ -62,17 +35,27 @@ class Session_component : public Nic::Session_component public: - Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label); + Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label); + + + /**************************** + ** Nic::Session_component ** + ****************************/ Nic::Mac_address mac_address() override; bool link_state() override { return _has_link; } - void link_state(bool link); - void receive(struct sk_buff *skb); - void unblock_rx_task(napi_struct * n); + + + /******************************** + ** Linux_network_session_base ** + ********************************/ + + void link_state(bool link) override; + void receive(struct sk_buff *skb) override; }; @@ -97,15 +80,14 @@ class Root : public Genode::Root_component /* deplete ram quota by the memory needed for the session structure */ size_t session_size = max(4096UL, (unsigned long)sizeof(Session_component)); - if (ram_quota < session_size) - throw Genode::Insufficient_ram_quota(); /* * Check if donated ram quota suffices for both communication * buffers and check for overflow */ - if (tx_buf_size + rx_buf_size < tx_buf_size || - tx_buf_size + rx_buf_size > ram_quota - session_size) { + if ((ram_quota < session_size) || + (tx_buf_size + rx_buf_size < tx_buf_size || + tx_buf_size + rx_buf_size > ram_quota - session_size)) { Genode::error("insufficient 'ram_quota', got ", ram_quota, ", " "need ", tx_buf_size + rx_buf_size + session_size); throw Genode::Insufficient_ram_quota(); diff --git a/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc b/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc index e3e285829a..38ebba643f 100644 --- a/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc +++ b/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc @@ -279,7 +279,7 @@ struct Fec : public Genode::List::Element int tx_queues { 1 }; int rx_queues { 1 }; struct net_device * net_dev { nullptr }; - Session_component * session { nullptr }; + Linux_network_session_base *session { nullptr }; Genode::Attached_dataspace io_ds { Lx_kit::env().env().rm(), device.io_mem_dataspace() }; Genode::Constructible mdio; @@ -319,8 +319,10 @@ static Genode::List & fec_devices() } -net_device * Session_component::_register_session_component(Session_component & s, - Genode::Session_label policy) +net_device * +Linux_network_session_base:: +_register_session(Linux_network_session_base &session, + Genode::Session_label policy) { unsigned number = 0; for (Fec * f = fec_devices().first(); f; f = f->next()) { number++; } @@ -335,7 +337,7 @@ net_device * Session_component::_register_session_component(Session_component & /* Session already in use? */ if (f->session) continue; - f->session = &s; + f->session = &session; return f->net_dev; } return nullptr; diff --git a/repos/dde_linux/src/drivers/nic/fec/main.cc b/repos/dde_linux/src/drivers/nic/fec/main.cc index f6e7b0d3ac..e77eaa347a 100644 --- a/repos/dde_linux/src/drivers/nic/fec/main.cc +++ b/repos/dde_linux/src/drivers/nic/fec/main.cc @@ -11,11 +11,17 @@ * version 2. */ + /* Genode includes */ #include #include #include +/* NIC driver includes */ +#include + +/* local includes */ +#include #include /* Linux emulation environment includes */ @@ -42,10 +48,13 @@ unsigned long jiffies; struct Main { - Genode::Env &env; - Genode::Entrypoint &ep { env.ep() }; - Genode::Heap heap { env.ram(), env.rm() }; - Root root { env, heap }; + Genode::Env &env; + Genode::Entrypoint &ep { env.ep() }; + Genode::Attached_rom_dataspace config_rom { env, "config" }; + Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) }; + Genode::Heap heap { env.ram(), env.rm() }; + Genode::Constructible root { }; + Genode::Constructible uplink_client { }; /* Linux task that handles the initialization */ Genode::Constructible linux; @@ -54,6 +63,11 @@ struct Main { Genode::log("--- freescale ethernet driver ---"); + if (mode == Genode::Nic_driver_mode::NIC_SERVER) { + + root.construct(env, heap); + } + Lx_kit::construct_env(env); LX_MUTEX_INIT(mdio_board_lock); @@ -80,7 +94,23 @@ struct Main Lx::scheduler().schedule(); } - void announce() { env.parent().announce(ep.manage(root)); } + void announce() + { + switch (mode) { + case Genode::Nic_driver_mode::NIC_SERVER: + + env.parent().announce(ep.manage(*root)); + break; + + case Genode::Nic_driver_mode::UPLINK_CLIENT: + + uplink_client.construct( + env, heap, + config_rom.xml().attribute_value( + "uplink_label", Genode::Session_label::String { "" })); + break; + } + } Lx::Task &linux_task() { return *linux; } }; diff --git a/repos/dde_linux/src/drivers/nic/fec/target.inc b/repos/dde_linux/src/drivers/nic/fec/target.inc index fd2328dae2..22bfc7ef3a 100644 --- a/repos/dde_linux/src/drivers/nic/fec/target.inc +++ b/repos/dde_linux/src/drivers/nic/fec/target.inc @@ -1,8 +1,10 @@ TARGET = fec_nic_drv -LIBS = base lx_kit_setjmp fec_nic_include -SRC_CC = main.cc platform.cc lx_emul.cc component.cc +LIBS = base lx_kit_setjmp fec_nic_include nic_driver +SRC_CC += main.cc platform.cc lx_emul.cc component.cc uplink_client.cc +SRC_CC += linux_network_session_base.cc SRC_C += dummy.c lxc.c INC_DIR += $(PRG_DIR)/../.. +INC_DIR += $(REP_DIR)/src/drivers/nic # lx_kit SRC_CC += env.cc irq.cc malloc.cc scheduler.cc timer.cc work.cc printf.cc @@ -36,6 +38,7 @@ CC_OPT_phy = -Wno-unused-function -Wno-unused-but-set-variable CC_OPT_phy_device = -Wno-unused-function CC_OPT_at803x = -Wno-unused-variable +vpath linux_network_session_base.cc $(REP_DIR)/src/drivers/nic vpath %.c $(LX_CONTRIB_DIR)/drivers/net/ethernet/freescale vpath %.c $(LX_CONTRIB_DIR)/drivers/net/phy vpath %.c $(LX_CONTRIB_DIR)/net/core diff --git a/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc b/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc new file mode 100644 index 0000000000..af6d1206de --- /dev/null +++ b/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc @@ -0,0 +1,93 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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. + */ + +/* local include */ +#include + +#include + +extern "C" { +#include +}; + + +Genode::Uplink_client::Transmit_result +Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) +{ + /* + * We must not be called from another task, just from the + * packet stream dispatcher. + */ + if (Lx::scheduler().active()) { + warning("scheduler active"); + return Transmit_result::RETRY; + } + + struct sk_buff *skb { + lxc_alloc_skb(conn_rx_pkt_size + + HEAD_ROOM, HEAD_ROOM) }; + + unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size); + memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size); + + _tx_data.ndev = _ndev; + _tx_data.skb = skb; + + _tx_task.unblock(); + Lx::scheduler().schedule(); + return Transmit_result::ACCEPTED; +} + + +Genode::Uplink_client::Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label) +: + Linux_network_session_base { label }, + Uplink_client_base { env, alloc, + Net::Mac_address(_ndev->dev_addr) } +{ + _drv_handle_link_state(_read_link_state_from_ndev()); +} + + +void Genode::Uplink_client::link_state(bool state) +{ + _drv_handle_link_state(state); +} + + +void Genode::Uplink_client::receive(struct sk_buff *skb) +{ + Skb skb_helpr { skb_helper(skb) }; + _drv_rx_handle_pkt( + skb_helpr.packet_size + skb_helpr.frag_size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy( + conn_tx_pkt_base, + skb_helpr.packet, + skb_helpr.packet_size); + + if (skb_helpr.frag_size) { + + memcpy( + (char *)conn_tx_pkt_base + skb_helpr.packet_size, + skb_helpr.frag, + skb_helpr.frag_size); + } + return Write_result::WRITE_SUCCEEDED; + }); +} diff --git a/repos/dde_linux/src/drivers/nic/fec/uplink_client.h b/repos/dde_linux/src/drivers/nic/fec/uplink_client.h new file mode 100644 index 0000000000..96e44aabe3 --- /dev/null +++ b/repos/dde_linux/src/drivers/nic/fec/uplink_client.h @@ -0,0 +1,58 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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 _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ +#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ + +/* NIC driver includes */ +#include + +/* local include */ +#include + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Linux_network_session_base, + public Uplink_client_base +{ + private: + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override; + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label); + + + /******************************** + ** Linux_network_session_base ** + ********************************/ + + void link_state(bool state) override; + + void receive(struct sk_buff *skb) override; +}; + +#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */ diff --git a/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc b/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc new file mode 100644 index 0000000000..737bf76864 --- /dev/null +++ b/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc @@ -0,0 +1,89 @@ +/* + * \brief Generic base of a network session using a DDE Linux back end + * \author Martin Stein + * \date 2020-12-13 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include +#include + + +void Linux_network_session_base::_run_tx_task(void * args) +{ + Tx_data *data = static_cast(args); + + while (1) { + Lx::scheduler().current()->block_and_schedule(); + + net_device * ndev = data->ndev; + struct sk_buff * skb = data->skb; + + ndev->netdev_ops->ndo_start_xmit(skb, ndev); + } +} + + +void Linux_network_session_base::_run_rx_task(void * args) +{ + Rx_data *data = static_cast(args); + + while (1) { + Lx::scheduler().current()->block_and_schedule(); + + int ret = 0; + struct napi_struct * n = data->napi; + + for (;;) { + + /* This NAPI_STATE_SCHED test is for avoiding a race + * with netpoll's poll_napi(). Only the entity which + * obtains the lock and sees NAPI_STATE_SCHED set will + * actually make the ->poll() call. Therefore we avoid + * accidentally calling ->poll() when NAPI is not scheduled. + */ + if (!test_bit(NAPI_STATE_SCHED, &n->state)) break; + + int weight = n->weight; + int work = n->poll(n, weight); + ret += work; + + if (work < weight) break; + + Genode::warning("Too much incoming traffic, we should " + "schedule RX more intelligent"); + } + } +} + + +Linux_network_session_base:: +Linux_network_session_base(Genode::Session_label const &label) +: + _ndev { _register_session(*this, label) } +{ + if (!_ndev) { + error("failed to register session with label \"", label, "\""); + throw Genode::Service_denied(); + } +} + + +bool Linux_network_session_base::_read_link_state_from_ndev() const +{ + return !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)); +} + + +void Linux_network_session_base::unblock_rx_task(napi_struct * n) +{ + _rx_data.napi = n; + _rx_task.unblock(); +} diff --git a/repos/dde_linux/src/drivers/nic/linux_network_session_base.h b/repos/dde_linux/src/drivers/nic/linux_network_session_base.h new file mode 100644 index 0000000000..dca7faa1a1 --- /dev/null +++ b/repos/dde_linux/src/drivers/nic/linux_network_session_base.h @@ -0,0 +1,78 @@ +/* + * \brief Virtual interface of a network session connected to the driver + * \author Martin Stein + * \date 2020-12-13 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_ +#define _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_ + +#include + +/* forward declarations */ +extern "C" { + struct net_device; + struct napi_struct; + struct sk_buff; +} + + +class Linux_network_session_base +{ + protected: + + enum { HEAD_ROOM = 8 }; + + struct Tx_data + { + net_device * ndev; + sk_buff * skb; + }; + + struct Rx_data + { + struct napi_struct * napi; + }; + + net_device * _ndev = nullptr; + Tx_data _tx_data; + Rx_data _rx_data; + Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task", + Lx::Task::PRIORITY_1, Lx::scheduler() }; + Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task", + Lx::Task::PRIORITY_1, Lx::scheduler() }; + + static void _run_tx_task(void * args); + + static void _run_rx_task(void * args); + + static net_device * + _register_session(Linux_network_session_base &session, + Genode::Session_label label); + + bool _read_link_state_from_ndev() const; + + public: + + Linux_network_session_base(Genode::Session_label const &label); + + void unblock_rx_task(napi_struct * n); + + + /*********************** + ** Virtual interface ** + ***********************/ + + virtual void receive(struct sk_buff *skb) = 0; + + virtual void link_state(bool state) = 0; +}; + +#endif /* _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_ */ diff --git a/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h b/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h index 11ed11ff4a..2bd9426384 100644 --- a/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h +++ b/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h @@ -14,27 +14,54 @@ #ifndef _USB_NIC_COMPONENT_H_ #define _USB_NIC_COMPONENT_H_ +/* Genode includes */ #include #include #include +/* Linux emulation environment includes */ #include #include #include #include #include +/* NIC driver includes */ +#include + namespace Usb_nic { + using namespace Genode; using Genode::size_t; + class Session_component; struct Device; }; +class Usb_network_session +{ + protected: + + Usb_nic::Device &_device; + + public: + + Usb_network_session(Usb_nic::Device &device) + : + _device { device } + { } + + virtual void link_state_changed() = 0; + + virtual void rx(Genode::addr_t virt, + Genode::size_t size) = 0; +}; + + struct Usb_nic::Device { - Session_component *_session; + Usb_network_session *_session; /** * Transmit data to driver @@ -54,7 +81,7 @@ struct Usb_nic::Device /** * Set session belonging to this driver */ - void session(Session_component *s) { _session = s; } + void session(Usb_network_session *s) { _session = s; } /** * Check for session @@ -91,12 +118,9 @@ struct Usb_nic::Device }; -class Usb_nic::Session_component : public Nic::Session_component +class Usb_nic::Session_component : public Usb_network_session, + public Nic::Session_component { - private: - - Device *_device; /* device this session is using */ - protected: void _send_burst() @@ -112,7 +136,7 @@ class Usb_nic::Session_component : public Nic::Session_component { /* alloc skb */ if (!skb) { - if (!(skb = _device->alloc_skb())) + if (!(skb = _device.alloc_skb())) return; ptr = skb->data; @@ -122,9 +146,9 @@ class Usb_nic::Session_component : public Nic::Session_component Packet_descriptor packet = save.size() ? save : _tx.sink()->get_packet(); save = Packet_descriptor(); - if (!_device->skb_fill(&work_skb, ptr, packet.size(), skb->end)) { + if (!_device.skb_fill(&work_skb, ptr, packet.size(), skb->end)) { /* submit batch */ - _device->tx_skb(skb); + _device.tx_skb(skb); skb = nullptr; save = packet; continue; @@ -134,7 +158,7 @@ class Usb_nic::Session_component : public Nic::Session_component try { Genode::memcpy(work_skb.data, _tx.sink()->packet_content(packet), packet.size()); } catch (Genode::Packet_descriptor::Invalid_packet) { } /* call fixup on dummy SKB */ - _device->tx_fixup(&work_skb); + _device.tx_fixup(&work_skb); /* advance to next slot */ ptr = work_skb.end; skb->len += work_skb.truesize; @@ -144,7 +168,7 @@ class Usb_nic::Session_component : public Nic::Session_component /* submit last skb */ if (skb) - _device->tx_skb(skb); + _device.tx_skb(skb); } bool _send() @@ -161,7 +185,7 @@ class Usb_nic::Session_component : public Nic::Session_component return true; } - bool ret = _device->tx((addr_t)_tx.sink()->packet_content(packet), packet.size()); + bool ret = _device.tx((addr_t)_tx.sink()->packet_content(packet), packet.size()); _tx.sink()->acknowledge_packet(packet); return ret; @@ -172,7 +196,7 @@ class Usb_nic::Session_component : public Nic::Session_component while (_rx.source()->ack_avail()) _rx.source()->release_packet(_rx.source()->get_acked_packet()); - if (_device->burst()) + if (_device.burst()) _send_burst(); else while (_send()); @@ -187,21 +211,34 @@ class Usb_nic::Session_component : public Nic::Session_component Genode::size_t const rx_buf_size, Genode::Allocator &rx_block_md_alloc, Genode::Env &env, - Device *device) - : Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, - rx_block_md_alloc, env), - _device(device) - { _device->session(this); } + Device &device) + : + Usb_network_session { device }, + Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env } + { + _device.session(this); + } - Nic::Mac_address mac_address() override { return _device->mac_address(); } - bool link_state() override { return _device->link_state(); } - void link_state_changed() { _link_state_changed(); } + /**************************** + ** Nic::Session_component ** + ****************************/ + + Nic::Mac_address mac_address() override { return _device.mac_address(); } + bool link_state() override { return _device.link_state(); } + + + /************************* + ** Usb_network_session ** + *************************/ + + void link_state_changed() override { _link_state_changed(); } /** * Send packet to client (called from driver) */ - void rx(addr_t virt, size_t size) + void rx(addr_t virt, size_t size) override { _handle_packet_stream(); @@ -231,8 +268,8 @@ class Root : public Root_component { private: - Genode::Env &_env; - Usb_nic::Device *_device; + Genode::Env &_env; + Usb_nic::Device &_device; protected: @@ -262,17 +299,143 @@ class Root : public Root_component } return new (Root::md_alloc()) - Usb_nic::Session_component(tx_buf_size, rx_buf_size, - Lx::Malloc::mem(), _env, _device); + Usb_nic::Session_component(tx_buf_size, rx_buf_size, + Lx::Malloc::mem(), _env, + _device); } public: - Root(Genode::Env &env, Genode::Allocator &md_alloc, - Usb_nic::Device *device) - : Root_component(&env.ep().rpc_ep(), &md_alloc), - _env(env), _device(device) + Root(Genode::Env &env, + Genode::Allocator &md_alloc, + Usb_nic::Device &device) + : + Root_component { &env.ep().rpc_ep(), &md_alloc }, + _env { env }, + _device { device } { } }; + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Usb_network_session, + public Uplink_client_base +{ + private: + + sk_buff _burst_work_skb { }; + sk_buff *_burst_skb { nullptr }; + unsigned char *_burst_ptr { nullptr }; + + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + if (_device.tx((addr_t)conn_rx_pkt_base, conn_rx_pkt_size) == 0) { + return Transmit_result::ACCEPTED; + } + return Transmit_result::REJECTED; + } + + void _drv_transmit_pkt_burst_prepare() override + { + _burst_skb = nullptr; + _burst_ptr = nullptr; + } + + Burst_result + _drv_transmit_pkt_burst_step(Packet_descriptor const &packet, + char const *packet_base, + Packet_descriptor &save) override + { + /* alloc _burst_skb */ + if (!_burst_skb) { + if (!(_burst_skb = _device.alloc_skb())) + return Burst_result::BURST_FAILED; + + _burst_ptr = _burst_skb->data; + _burst_work_skb.data = nullptr; + } + + if (!_device.skb_fill(&_burst_work_skb, _burst_ptr, packet.size(), _burst_skb->end)) { + + /* submit batch */ + _device.tx_skb(_burst_skb); + _burst_skb = nullptr; + save = packet; + return Burst_result::BURST_CONTINUE; + } + + /* copy packet to current data pos */ + Genode::memcpy(_burst_work_skb.data, packet_base, packet.size()); + + /* call fixup on dummy SKB */ + _device.tx_fixup(&_burst_work_skb); + + /* advance to next slot */ + _burst_ptr = _burst_work_skb.end; + _burst_skb->len += _burst_work_skb.truesize; + + return Burst_result::BURST_SUCCEEDED; + } + + void _drv_transmit_pkt_burst_finish() override + { + /* submit last _burst_skb */ + if (_burst_skb) + _device.tx_skb(_burst_skb); + } + + bool _drv_supports_transmit_pkt_burst() override + { + return _device.burst(); + } + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Usb_nic::Device &device) + : + Usb_network_session { device }, + Uplink_client_base { env, alloc, _device.mac_address() } + { + _device.session(this); + _drv_handle_link_state(_device.link_state()); + } + + + /************************* + ** Usb_network_session ** + *************************/ + + void link_state_changed() override + { + _drv_handle_link_state(_device.link_state()); + } + + void rx(Genode::addr_t virt, + Genode::size_t size) override + { + _drv_rx_handle_pkt( + size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy(conn_tx_pkt_base, (void *)virt, size); + return Write_result::WRITE_SUCCEEDED; + }); + } +}; + #endif /* _USB_NIC_COMPONENT_H_ */ diff --git a/repos/dde_linux/src/drivers/usb/nic/nic.cc b/repos/dde_linux/src/drivers/usb/nic/nic.cc index 2068237b4b..8900732dc8 100644 --- a/repos/dde_linux/src/drivers/usb/nic/nic.cc +++ b/repos/dde_linux/src/drivers/usb/nic/nic.cc @@ -11,17 +11,24 @@ * version 2. */ +/* Genode includes */ #include #include #include #include +/* Linux emulation environment includes */ #include #include +/* NIC driver includes */ +#include + +/* local includes */ #include #include "signal.h" + static Signal_helper *_signal = 0; enum { @@ -307,27 +314,54 @@ void Nic::init(Genode::Env &env) { int register_netdev(struct net_device *ndev) { using namespace Genode; - static bool announce = false; + static bool registered = false; int err = -ENODEV; Nic_device *nic = Nic_device::add(ndev); + if (nic == nullptr) { + + class Invalid_nic_device { }; + throw Invalid_nic_device { }; + } /* XXX: move to 'main' */ - if (!announce) { - static ::Root root(_signal->env(), Lx::Malloc::mem(), nic); + if (!registered) { - announce = true; + registered = true; - ndev->state |= 1 << __LINK_STATE_START; + Nic_driver_mode const mode { + read_nic_driver_mode(Lx_kit::env().config_rom().xml()) }; - if ((err = ndev->netdev_ops->ndo_open(ndev))) - return err; + switch (mode) { + case Genode::Nic_driver_mode::NIC_SERVER: - if (ndev->netdev_ops->ndo_set_rx_mode) - ndev->netdev_ops->ndo_set_rx_mode(ndev); + static ::Root root(_signal->env(), Lx::Malloc::mem(), *nic); + ndev->state |= 1 << __LINK_STATE_START; - _nic = nic; - _signal->parent().announce(_signal->ep().rpc_ep().manage(&root)); + if ((err = ndev->netdev_ops->ndo_open(ndev))) + return err; + + if (ndev->netdev_ops->ndo_set_rx_mode) + ndev->netdev_ops->ndo_set_rx_mode(ndev); + + _nic = nic; + _signal->parent().announce(_signal->ep().rpc_ep().manage(&root)); + log("Acting as Nic server"); + break; + + case Genode::Nic_driver_mode::UPLINK_CLIENT: + + ndev->state |= 1 << __LINK_STATE_START; + if ((err = ndev->netdev_ops->ndo_open(ndev))) + return err; + + _nic = nic; + static Uplink_client uplink_client { + _signal->env(), Lx::Malloc::mem(), *nic }; + + log("Acting as Uplink client"); + break; + } } return err; diff --git a/repos/dde_linux/src/drivers/usb/target.inc b/repos/dde_linux/src/drivers/usb/target.inc index f72e792cba..c6b4f5cedf 100644 --- a/repos/dde_linux/src/drivers/usb/target.inc +++ b/repos/dde_linux/src/drivers/usb/target.inc @@ -1,5 +1,5 @@ SRC_CC += main.cc -LIBS = base +LIBS = base nic_driver CC_CXX_WARN_STRICT = diff --git a/repos/dde_linux/src/drivers/usb_modem/component.cc b/repos/dde_linux/src/drivers/usb_modem/component.cc index 16d3162007..9a7d7b20e5 100644 --- a/repos/dde_linux/src/drivers/usb_modem/component.cc +++ b/repos/dde_linux/src/drivers/usb_modem/component.cc @@ -19,52 +19,6 @@ extern "C" { #include }; -void Session_component::_run_rx_task(void * args) -{ - Rx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - int ret = 0; - struct napi_struct * n = data->napi; - - for (;;) { - - /* This NAPI_STATE_SCHED test is for avoiding a race - * with netpoll's poll_napi(). Only the entity which - * obtains the lock and sees NAPI_STATE_SCHED set will - * actually make the ->poll() call. Therefore we avoid - * accidentally calling ->poll() when NAPI is not scheduled. - */ - if (!test_bit(NAPI_STATE_SCHED, &n->state)) break; - - int weight = n->weight; - int work = n->poll(n, weight); - ret += work; - - if (work < weight) break; - - Genode::warning("Too much incoming traffic, we should schedule RX more intelligent"); - } - } -} - - -void Session_component::_run_tx_task(void * args) -{ - Tx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - net_device * ndev = data->ndev; - struct sk_buff * skb = data->skb; - - ndev->netdev_ops->ndo_start_xmit(skb, ndev); - } -} - bool Session_component::_send() { @@ -88,8 +42,6 @@ bool Session_component::_send() return true; } - enum { HEAD_ROOM = 8 }; - struct sk_buff *skb = lxc_alloc_skb(packet.size() + HEAD_ROOM, HEAD_ROOM); unsigned char *data = lxc_skb_put(skb, packet.size()); @@ -122,13 +74,6 @@ void Session_component::_handle_packet_stream() } -void Session_component::unblock_rx_task(napi_struct * n) -{ - _rx_data.napi = n; - _rx_task.unblock(); -} - - Nic::Mac_address Session_component::mac_address() { return _ndev ? Nic::Mac_address(_ndev->dev_addr) : Nic::Mac_address(); @@ -170,14 +115,14 @@ void Session_component::link_state(bool link) } -Session_component::Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label) -: Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env), - _ndev(_register_session_component(*this, label)), - _has_link(_ndev ? !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)) : false) -{ - if (!_ndev) throw Genode::Service_denied(); -} +Session_component::Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label) +: + Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env }, + Fec_nic { label }, + _has_link { _read_link_state_from_ndev() } +{ } diff --git a/repos/dde_linux/src/drivers/usb_modem/component.h b/repos/dde_linux/src/drivers/usb_modem/component.h index 5b5224a8fa..7494243049 100644 --- a/repos/dde_linux/src/drivers/usb_modem/component.h +++ b/repos/dde_linux/src/drivers/usb_modem/component.h @@ -19,43 +19,16 @@ #include #include -#include +/* local includes */ +#include -extern "C" { - struct net_device; - struct napi_struct; - struct sk_buff; -} -class Session_component : public Nic::Session_component +class Session_component : public Nic::Session_component, + public Fec_nic { private: - struct Tx_data - { - net_device * ndev; - sk_buff * skb; - }; - - struct Rx_data - { - struct napi_struct * napi; - }; - - net_device * _ndev = nullptr; - bool _has_link = false; - Tx_data _tx_data; - Rx_data _rx_data; - - static void _run_tx_task(void * args); - static void _run_rx_task(void * args); - static net_device * _register_session_component(Session_component &, - Genode::Session_label); - - Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; - Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; + bool _has_link = false; bool _send(); void _handle_rx(); @@ -63,17 +36,27 @@ class Session_component : public Nic::Session_component public: - Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label); + Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label); + + + /**************************** + ** Nic::Session_component ** + ****************************/ Nic::Mac_address mac_address() override; bool link_state() override { return _has_link; } - void link_state(bool link); - void receive(struct sk_buff *skb); - void unblock_rx_task(napi_struct * n); + + + /************* + ** Fec_nic ** + *************/ + + void link_state(bool link) override; + void receive(struct sk_buff *skb) override; }; diff --git a/repos/dde_linux/src/drivers/usb_modem/driver.h b/repos/dde_linux/src/drivers/usb_modem/driver.h index 4eae5d16aa..d1415bf519 100644 --- a/repos/dde_linux/src/drivers/usb_modem/driver.h +++ b/repos/dde_linux/src/drivers/usb_modem/driver.h @@ -15,20 +15,28 @@ #ifndef _SRC__DRIVERS__USB_MODEM__DRIVER_H_ #define _SRC__DRIVERS__USB_MODEM__DRIVER_H_ +/* Genode includes */ #include #include #include #include -#include +#include +/* local includes */ +#include #include #include +/* Linux emulation environment includes */ +#include #include #include #include #include +/* NIC driver includes */ +#include + struct usb_device_id; struct usb_interface; struct usb_device; @@ -144,19 +152,39 @@ struct Driver } }; - - Devices devices; - Genode::Env &env; - Genode::Entrypoint &ep { env.ep() }; - Genode::Heap heap { env.ram(), env.rm() }; - Genode::Allocator_avl alloc { &heap }; - Root root { env, heap }; - Terminal::Root terminal_root { env, heap }; - Genode::Constructible main_task; - Genode::Constructible report_rom; + Devices devices; + Genode::Env &env; + Genode::Entrypoint &ep { env.ep() }; + Genode::Attached_rom_dataspace config_rom { env, "config" }; + Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) }; + Genode::Heap heap { env.ram(), env.rm() }; + Genode::Allocator_avl alloc { &heap }; + Genode::Constructible root { }; + Genode::Constructible uplink_client { }; + Terminal::Root terminal_root { env, heap }; + Genode::Constructible main_task; + Genode::Constructible report_rom; Driver(Genode::Env &env); + void activate_network_session() + { + switch (mode) { + case Genode::Nic_driver_mode::NIC_SERVER: + + env.parent().announce(ep.manage(*root)); + break; + + case Genode::Nic_driver_mode::UPLINK_CLIENT: + + uplink_client.construct( + env, heap, + config_rom.xml().attribute_value( + "uplink_label", Genode::Session_label::String { "" })); + break; + } + } + static void main_task_entry(void *); }; diff --git a/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc b/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc new file mode 100644 index 0000000000..06b1bdb201 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc @@ -0,0 +1,89 @@ +/* + * \brief Virtual interface of a network session connected to the driver + * \author Martin Stein + * \date 2020-12-13 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include +#include + + +void Fec_nic::_run_tx_task(void * args) +{ + Tx_data *data = static_cast(args); + + while (1) { + Lx::scheduler().current()->block_and_schedule(); + + net_device * ndev = data->ndev; + struct sk_buff * skb = data->skb; + + ndev->netdev_ops->ndo_start_xmit(skb, ndev); + } +} + + +void Fec_nic::_run_rx_task(void * args) +{ + Rx_data *data = static_cast(args); + + while (1) { + Lx::scheduler().current()->block_and_schedule(); + + int ret = 0; + struct napi_struct * n = data->napi; + + for (;;) { + + /* This NAPI_STATE_SCHED test is for avoiding a race + * with netpoll's poll_napi(). Only the entity which + * obtains the lock and sees NAPI_STATE_SCHED set will + * actually make the ->poll() call. Therefore we avoid + * accidentally calling ->poll() when NAPI is not scheduled. + */ + if (!test_bit(NAPI_STATE_SCHED, &n->state)) break; + + int weight = n->weight; + int work = n->poll(n, weight); + ret += work; + + if (work < weight) break; + + Genode::warning("Too much incoming traffic, we should " + "schedule RX more intelligent"); + } + } +} + + +Fec_nic::Fec_nic(Genode::Session_label const &label) +: + _ndev { _register_fec_nic(*this, label) } +{ + + if (!_ndev) { + + throw Genode::Service_denied(); + } +} + + +bool Fec_nic::_read_link_state_from_ndev() const +{ + return !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)); +} + + +void Fec_nic::unblock_rx_task(napi_struct * n) +{ + _rx_data.napi = n; + _rx_task.unblock(); +} diff --git a/repos/dde_linux/src/drivers/usb_modem/fec_nic.h b/repos/dde_linux/src/drivers/usb_modem/fec_nic.h new file mode 100644 index 0000000000..4829e9e448 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_modem/fec_nic.h @@ -0,0 +1,77 @@ +/* + * \brief Virtual interface of a network session connected to the driver + * \author Martin Stein + * \date 2020-12-13 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_ +#define _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_ + +#include + +/* forward declarations */ +extern "C" { + struct net_device; + struct napi_struct; + struct sk_buff; +} + + +class Fec_nic +{ + protected: + + enum { HEAD_ROOM = 8 }; + + struct Tx_data + { + net_device * ndev; + sk_buff * skb; + }; + + struct Rx_data + { + struct napi_struct * napi; + }; + + net_device * _ndev = nullptr; + Tx_data _tx_data; + Rx_data _rx_data; + Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task", + Lx::Task::PRIORITY_1, Lx::scheduler() }; + Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task", + Lx::Task::PRIORITY_1, Lx::scheduler() }; + + static void _run_tx_task(void * args); + + static void _run_rx_task(void * args); + + static net_device *_register_fec_nic(Fec_nic &fec_nic, + Genode::Session_label label); + + bool _read_link_state_from_ndev() const; + + public: + + Fec_nic(Genode::Session_label const &label); + + void unblock_rx_task(napi_struct * n); + + + /*********************** + ** Virtual interface ** + ***********************/ + + virtual void receive(struct sk_buff *skb) = 0; + + virtual void link_state(bool state) = 0; +}; + +#endif /* _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_ */ diff --git a/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc index b9aeba5438..3988dedd23 100644 --- a/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc +++ b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc @@ -438,10 +438,10 @@ int register_netdev(struct net_device *dev) }; -net_device * Session_component::_register_session_component(Session_component & s, - Genode::Session_label policy) +net_device * Fec_nic::_register_fec_nic(Fec_nic &fec_nic, + Genode::Session_label policy) { - if (single_net_device) single_net_device->session_component = (void*) &s; + if (single_net_device) single_net_device->session_component = &fec_nic; return single_net_device; } @@ -494,7 +494,7 @@ void netif_carrier_off(struct net_device *dev) { dev->state |= 1 << __LINK_STATE_NOCARRIER; if (dev->session_component) - reinterpret_cast(dev->session_component)->link_state(false); + reinterpret_cast(dev->session_component)->link_state(false); } @@ -521,14 +521,14 @@ void netif_carrier_on(struct net_device *dev) { dev->state &= ~(1 << __LINK_STATE_NOCARRIER); if (dev->session_component) - reinterpret_cast(dev->session_component)->link_state(true); + reinterpret_cast(dev->session_component)->link_state(true); } int netif_rx(struct sk_buff * skb) { if (skb->dev->session_component) - reinterpret_cast(skb->dev->session_component)->receive(skb); + reinterpret_cast(skb->dev->session_component)->receive(skb); dev_kfree_skb(skb); return NET_RX_SUCCESS; diff --git a/repos/dde_linux/src/drivers/usb_modem/main.cc b/repos/dde_linux/src/drivers/usb_modem/main.cc index cf202014ab..3e87c95158 100644 --- a/repos/dde_linux/src/drivers/usb_modem/main.cc +++ b/repos/dde_linux/src/drivers/usb_modem/main.cc @@ -131,7 +131,7 @@ void Driver::Device::register_device() probe_interface(udev->config->interface[i], &id); } - driver.env.parent().announce(driver.ep.manage(driver.root)); + driver.activate_network_session(); driver.env.parent().announce(driver.ep.manage(driver.terminal_root)); } @@ -226,6 +226,9 @@ Driver::Driver(Genode::Env &env) : env(env) { Genode::log("--- USB net driver ---"); + if (mode == Genode::Nic_driver_mode::NIC_SERVER) { + root.construct(env, heap); + } Lx_kit::construct_env(env); Lx::scheduler(&env); Lx::malloc_init(env, heap); diff --git a/repos/dde_linux/src/drivers/usb_modem/target.mk b/repos/dde_linux/src/drivers/usb_modem/target.mk index d02c441314..5f7fb1562e 100644 --- a/repos/dde_linux/src/drivers/usb_modem/target.mk +++ b/repos/dde_linux/src/drivers/usb_modem/target.mk @@ -1,9 +1,10 @@ TARGET := usb_modem_drv SRC_C := dummies.c lxc.c -SRC_CC := main.cc lx_emul.cc component.cc terminal.cc +SRC_CC := main.cc lx_emul.cc component.cc terminal.cc fec_nic.cc SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc +SRC_CC += uplink_client.cc -LIBS := base usb_modem_include lx_kit_setjmp +LIBS := base usb_modem_include lx_kit_setjmp nic_driver USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_modem diff --git a/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc b/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc new file mode 100644 index 0000000000..84998c0ba8 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc @@ -0,0 +1,93 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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. + */ + +/* local include */ +#include + +#include + +extern "C" { +#include +}; + + +Genode::Uplink_client::Transmit_result +Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) +{ + /* + * We must not be called from another task, just from the + * packet stream dispatcher. + */ + if (Lx::scheduler().active()) { + warning("scheduler active"); + return Transmit_result::RETRY; + } + + struct sk_buff *skb { + lxc_alloc_skb(conn_rx_pkt_size + + HEAD_ROOM, HEAD_ROOM) }; + + unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size); + memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size); + + _tx_data.ndev = _ndev; + _tx_data.skb = skb; + + _tx_task.unblock(); + Lx::scheduler().schedule(); + return Transmit_result::ACCEPTED; +} + + +Genode::Uplink_client::Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label) +: + Fec_nic { label }, + Uplink_client_base { env, alloc, + Net::Mac_address(_ndev->dev_addr) } +{ + _drv_handle_link_state(_read_link_state_from_ndev()); +} + + +void Genode::Uplink_client::link_state(bool state) +{ + _drv_handle_link_state(state); +} + + +void Genode::Uplink_client::receive(struct sk_buff *skb) +{ + Skb skb_helpr { skb_helper(skb) }; + _drv_rx_handle_pkt( + skb_helpr.packet_size + skb_helpr.frag_size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy( + conn_tx_pkt_base, + skb_helpr.packet, + skb_helpr.packet_size); + + if (skb_helpr.frag_size) { + + memcpy( + (char *)conn_tx_pkt_base + skb_helpr.packet_size, + skb_helpr.frag, + skb_helpr.frag_size); + } + return Write_result::WRITE_SUCCEEDED; + }); +} diff --git a/repos/dde_linux/src/drivers/usb_modem/uplink_client.h b/repos/dde_linux/src/drivers/usb_modem/uplink_client.h new file mode 100644 index 0000000000..e75c926d6e --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_modem/uplink_client.h @@ -0,0 +1,58 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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 _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ +#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ + +/* NIC driver includes */ +#include + +/* local include */ +#include + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Fec_nic, + public Uplink_client_base +{ + private: + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override; + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label); + + + /************* + ** Fec_nic ** + *************/ + + void link_state(bool state) override; + + void receive(struct sk_buff *skb) override; +}; + +#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */ diff --git a/repos/dde_linux/src/drivers/usb_net/component.cc b/repos/dde_linux/src/drivers/usb_net/component.cc index 6bb4ae1d5c..6f131c5c9a 100644 --- a/repos/dde_linux/src/drivers/usb_net/component.cc +++ b/repos/dde_linux/src/drivers/usb_net/component.cc @@ -18,52 +18,6 @@ extern "C" { #include }; -void Session_component::_run_rx_task(void * args) -{ - Rx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - int ret = 0; - struct napi_struct * n = data->napi; - - for (;;) { - - /* This NAPI_STATE_SCHED test is for avoiding a race - * with netpoll's poll_napi(). Only the entity which - * obtains the lock and sees NAPI_STATE_SCHED set will - * actually make the ->poll() call. Therefore we avoid - * accidentally calling ->poll() when NAPI is not scheduled. - */ - if (!test_bit(NAPI_STATE_SCHED, &n->state)) break; - - int weight = n->weight; - int work = n->poll(n, weight); - ret += work; - - if (work < weight) break; - - Genode::warning("Too much incoming traffic, we should schedule RX more intelligent"); - } - } -} - - -void Session_component::_run_tx_task(void * args) -{ - Tx_data *data = static_cast(args); - - while (1) { - Lx::scheduler().current()->block_and_schedule(); - - net_device * ndev = data->ndev; - struct sk_buff * skb = data->skb; - - ndev->netdev_ops->ndo_start_xmit(skb, ndev); - } -} - bool Session_component::_send() { @@ -87,8 +41,6 @@ bool Session_component::_send() return true; } - enum { HEAD_ROOM = 8 }; - struct sk_buff *skb = lxc_alloc_skb(packet.size() + HEAD_ROOM, HEAD_ROOM); unsigned char *data = lxc_skb_put(skb, packet.size()); @@ -121,13 +73,6 @@ void Session_component::_handle_packet_stream() } -void Session_component::unblock_rx_task(napi_struct * n) -{ - _rx_data.napi = n; - _rx_task.unblock(); -} - - Nic::Mac_address Session_component::mac_address() { return _ndev ? Nic::Mac_address(_ndev->dev_addr) : Nic::Mac_address(); @@ -169,14 +114,14 @@ void Session_component::link_state(bool link) } -Session_component::Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label) -: Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env), - _ndev(_register_session_component(*this, label)), - _has_link(_ndev ? !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)) : false) -{ - if (!_ndev) throw Genode::Service_denied(); -} +Session_component::Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label) +: + Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env }, + Linux_network_session_base { label }, + _has_link { _read_link_state_from_ndev() } +{ } diff --git a/repos/dde_linux/src/drivers/usb_net/component.h b/repos/dde_linux/src/drivers/usb_net/component.h index c9867692c0..55ab7c8139 100644 --- a/repos/dde_linux/src/drivers/usb_net/component.h +++ b/repos/dde_linux/src/drivers/usb_net/component.h @@ -18,43 +18,16 @@ #include #include -#include +/* local includes */ +#include -extern "C" { - struct net_device; - struct napi_struct; - struct sk_buff; -} -class Session_component : public Nic::Session_component +class Session_component : public Nic::Session_component, + public Linux_network_session_base { private: - struct Tx_data - { - net_device * ndev; - sk_buff * skb; - }; - - struct Rx_data - { - struct napi_struct * napi; - }; - - net_device * _ndev = nullptr; - bool _has_link = false; - Tx_data _tx_data; - Rx_data _rx_data; - - static void _run_tx_task(void * args); - static void _run_rx_task(void * args); - static net_device * _register_session_component(Session_component &, - Genode::Session_label); - - Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; - Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task", - Lx::Task::PRIORITY_1, Lx::scheduler() }; + bool _has_link = false; bool _send(); void _handle_rx(); @@ -62,17 +35,27 @@ class Session_component : public Nic::Session_component public: - Session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env, - Genode::Session_label label); + Session_component(Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator &rx_block_md_alloc, + Genode::Env &env, + Genode::Session_label const &label); + + + /**************************** + ** Nic::Session_component ** + ****************************/ Nic::Mac_address mac_address() override; bool link_state() override { return _has_link; } - void link_state(bool link); - void receive(struct sk_buff *skb); - void unblock_rx_task(napi_struct * n); + + + /******************************** + ** Linux_network_session_base ** + ********************************/ + + void link_state(bool link) override; + void receive(struct sk_buff *skb) override; }; diff --git a/repos/dde_linux/src/drivers/usb_net/driver.h b/repos/dde_linux/src/drivers/usb_net/driver.h index d0af2b0ad8..4ce0dcaf9c 100644 --- a/repos/dde_linux/src/drivers/usb_net/driver.h +++ b/repos/dde_linux/src/drivers/usb_net/driver.h @@ -14,13 +14,22 @@ #ifndef _SRC__DRIVERS__USB_NET__DRIVER_H_ #define _SRC__DRIVERS__USB_NET__DRIVER_H_ +/* Genode includes */ #include #include #include #include +#include + +/* local includes */ +#include +#include + +/* Linux emulation environment includes */ #include -#include +/* NIC driver includes */ +#include struct usb_device_id; struct usb_interface; @@ -88,17 +97,38 @@ struct Driver } }; - Devices devices; - Genode::Env &env; - Genode::Entrypoint &ep { env.ep() }; - Genode::Heap heap { env.ram(), env.rm() }; - Genode::Allocator_avl alloc { &heap }; - Root root { env, heap }; - Genode::Constructible main_task; - Genode::Constructible report_rom; + Devices devices; + Genode::Env &env; + Genode::Entrypoint &ep { env.ep() }; + Genode::Attached_rom_dataspace config_rom { env, "config" }; + Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) }; + Genode::Heap heap { env.ram(), env.rm() }; + Genode::Allocator_avl alloc { &heap }; + Genode::Constructible root { }; + Genode::Constructible uplink_client { }; + Genode::Constructible main_task; + Genode::Constructible report_rom; Driver(Genode::Env &env); + void activate_network_session() + { + switch (mode) { + case Genode::Nic_driver_mode::NIC_SERVER: + + env.parent().announce(ep.manage(*root)); + break; + + case Genode::Nic_driver_mode::UPLINK_CLIENT: + + uplink_client.construct( + env, heap, + config_rom.xml().attribute_value( + "uplink_label", Genode::Session_label::String { "" })); + break; + } + } + static void main_task_entry(void *); }; diff --git a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc index 47545fc969..fe4513d113 100644 --- a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc +++ b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc @@ -380,10 +380,12 @@ int register_netdev(struct net_device *dev) }; -net_device * Session_component::_register_session_component(Session_component & s, - Genode::Session_label policy) +net_device * +Linux_network_session_base:: +_register_session(Linux_network_session_base &session, + Genode::Session_label policy) { - if (single_net_device) single_net_device->session_component = (void*) &s; + if (single_net_device) single_net_device->session_component = &session; return single_net_device; } @@ -430,7 +432,8 @@ void netif_carrier_off(struct net_device *dev) { dev->state |= 1 << __LINK_STATE_NOCARRIER; if (dev->session_component) - reinterpret_cast(dev->session_component)->link_state(false); + reinterpret_cast(dev->session_component)-> + link_state(false); } @@ -457,14 +460,16 @@ void netif_carrier_on(struct net_device *dev) { dev->state &= ~(1 << __LINK_STATE_NOCARRIER); if (dev->session_component) - reinterpret_cast(dev->session_component)->link_state(true); + reinterpret_cast(dev->session_component)-> + link_state(true); } int netif_rx(struct sk_buff * skb) { if (skb->dev->session_component) - reinterpret_cast(skb->dev->session_component)->receive(skb); + reinterpret_cast(skb->dev->session_component)-> + receive(skb); dev_kfree_skb(skb); return NET_RX_SUCCESS; diff --git a/repos/dde_linux/src/drivers/usb_net/main.cc b/repos/dde_linux/src/drivers/usb_net/main.cc index 5e4d28808f..421b88c27d 100644 --- a/repos/dde_linux/src/drivers/usb_net/main.cc +++ b/repos/dde_linux/src/drivers/usb_net/main.cc @@ -74,7 +74,7 @@ void Driver::Device::scan_interfaces(unsigned iface_idx) probe_interface(iface, &id); udev->config->interface[iface_idx] = iface; - driver.env.parent().announce(driver.ep.manage(driver.root)); + driver.activate_network_session(); }; @@ -195,6 +195,9 @@ Driver::Driver(Genode::Env &env) : env(env) { Genode::log("--- USB net driver ---"); + if (mode == Genode::Nic_driver_mode::NIC_SERVER) { + root.construct(env, heap); + } Lx_kit::construct_env(env); Lx::scheduler(&env); Lx::malloc_init(env, heap); diff --git a/repos/dde_linux/src/drivers/usb_net/target.mk b/repos/dde_linux/src/drivers/usb_net/target.mk index bbe981aae7..a2a50c4f51 100644 --- a/repos/dde_linux/src/drivers/usb_net/target.mk +++ b/repos/dde_linux/src/drivers/usb_net/target.mk @@ -1,14 +1,16 @@ TARGET := usb_net_drv SRC_C := dummies.c lxc.c -SRC_CC := main.cc lx_emul.cc component.cc +SRC_CC := main.cc lx_emul.cc component.cc linux_network_session_base.cc SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc +SRC_CC += uplink_client.cc -LIBS := base usb_net_include lx_kit_setjmp +LIBS := base usb_net_include lx_kit_setjmp nic_driver USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_net INC_DIR += $(PRG_DIR) INC_DIR += $(REP_DIR)/src/include +INC_DIR += $(REP_DIR)/src/drivers/nic SRC_C += drivers/net/usb/asix_common.c SRC_C += drivers/net/usb/asix_devices.c @@ -28,5 +30,6 @@ CC_C_OPT += -Wno-comment -Wno-int-conversion -Wno-incompatible-pointer-types \ CC_CXX_WARN_STRICT = +vpath linux_network_session_base.cc $(REP_DIR)/src/drivers/nic vpath %.c $(USB_CONTRIB_DIR) vpath %.cc $(REP_DIR)/src/lx_kit diff --git a/repos/dde_linux/src/drivers/usb_net/uplink_client.cc b/repos/dde_linux/src/drivers/usb_net/uplink_client.cc new file mode 100644 index 0000000000..af6d1206de --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_net/uplink_client.cc @@ -0,0 +1,93 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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. + */ + +/* local include */ +#include + +#include + +extern "C" { +#include +}; + + +Genode::Uplink_client::Transmit_result +Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) +{ + /* + * We must not be called from another task, just from the + * packet stream dispatcher. + */ + if (Lx::scheduler().active()) { + warning("scheduler active"); + return Transmit_result::RETRY; + } + + struct sk_buff *skb { + lxc_alloc_skb(conn_rx_pkt_size + + HEAD_ROOM, HEAD_ROOM) }; + + unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size); + memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size); + + _tx_data.ndev = _ndev; + _tx_data.skb = skb; + + _tx_task.unblock(); + Lx::scheduler().schedule(); + return Transmit_result::ACCEPTED; +} + + +Genode::Uplink_client::Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label) +: + Linux_network_session_base { label }, + Uplink_client_base { env, alloc, + Net::Mac_address(_ndev->dev_addr) } +{ + _drv_handle_link_state(_read_link_state_from_ndev()); +} + + +void Genode::Uplink_client::link_state(bool state) +{ + _drv_handle_link_state(state); +} + + +void Genode::Uplink_client::receive(struct sk_buff *skb) +{ + Skb skb_helpr { skb_helper(skb) }; + _drv_rx_handle_pkt( + skb_helpr.packet_size + skb_helpr.frag_size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy( + conn_tx_pkt_base, + skb_helpr.packet, + skb_helpr.packet_size); + + if (skb_helpr.frag_size) { + + memcpy( + (char *)conn_tx_pkt_base + skb_helpr.packet_size, + skb_helpr.frag, + skb_helpr.frag_size); + } + return Write_result::WRITE_SUCCEEDED; + }); +} diff --git a/repos/dde_linux/src/drivers/usb_net/uplink_client.h b/repos/dde_linux/src/drivers/usb_net/uplink_client.h new file mode 100644 index 0000000000..96e44aabe3 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_net/uplink_client.h @@ -0,0 +1,58 @@ +/* + * \brief Uplink session client role of the driver + * \author Martin Stein + * \date 2020-12-10 + */ + +/* + * Copyright (C) 2020 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 _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ +#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ + +/* NIC driver includes */ +#include + +/* local include */ +#include + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Linux_network_session_base, + public Uplink_client_base +{ + private: + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override; + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Session_label const &label); + + + /******************************** + ** Linux_network_session_base ** + ********************************/ + + void link_state(bool state) override; + + void receive(struct sk_buff *skb) override; +}; + +#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */ diff --git a/repos/dde_linux/src/drivers/wifi/frontend.h b/repos/dde_linux/src/drivers/wifi/frontend.h index b238ffd19d..8804ec8835 100644 --- a/repos/dde_linux/src/drivers/wifi/frontend.h +++ b/repos/dde_linux/src/drivers/wifi/frontend.h @@ -53,6 +53,9 @@ #include #include +/* NIC driver includes */ +#include + /* rep includes */ #include #include @@ -361,6 +364,8 @@ struct Wifi::Frontend Genode::Attached_rom_dataspace _config_rom; Genode::Signal_handler _config_sigh; + Genode::Nic_driver_mode const _mode; + bool _verbose { false }; bool _verbose_state { false }; bool _use_11n { true }; @@ -1560,6 +1565,9 @@ struct Wifi::Frontend _rfkill_handler(env.ep(), *this, &Wifi::Frontend::_handle_rfkill), _config_rom(env, "wifi_config"), _config_sigh(env.ep(), *this, &Wifi::Frontend::_handle_config_update), + _mode( + read_nic_driver_mode( + Genode::Attached_rom_dataspace(env, "config").xml())), _scan_timer(env), _scan_timer_sigh(env.ep(), *this, &Wifi::Frontend::_handle_scan_timer), _events_handler(env.ep(), *this, &Wifi::Frontend::_handle_events), @@ -1651,6 +1659,8 @@ struct Wifi::Frontend * Used for communication between front end and wpa_supplicant. */ Msg_buffer &msg_buffer() { return _msg; } + + Genode::Nic_driver_mode mode() const { return _mode; } }; #endif /* _WIFI_FRONTEND_H_ */ diff --git a/repos/dde_linux/src/drivers/wifi/main.cc b/repos/dde_linux/src/drivers/wifi/main.cc index 9182d6f259..93336a16f7 100644 --- a/repos/dde_linux/src/drivers/wifi/main.cc +++ b/repos/dde_linux/src/drivers/wifi/main.cc @@ -93,8 +93,11 @@ void *wifi_get_buffer(void) /* exported by wifi.lib.so */ -extern void wifi_init(Genode::Env&, Genode::Blockade&, bool, - Genode::Signal_context_capability); +extern void wifi_init(Genode::Env&, + Genode::Blockade&, + bool, + Genode::Signal_context_capability, + Genode::Nic_driver_mode); struct Main @@ -119,7 +122,7 @@ struct Main */ bool const disable_11n = !_frontend->use_11n(); wifi_init(env, _wpa_startup_blockade, disable_11n, - _frontend->rfkill_sigh()); + _frontend->rfkill_sigh(), _frontend->mode()); } }; diff --git a/repos/dde_linux/src/drivers/wifi/target.mk b/repos/dde_linux/src/drivers/wifi/target.mk index 631330579c..dd8be379e4 100644 --- a/repos/dde_linux/src/drivers/wifi/target.mk +++ b/repos/dde_linux/src/drivers/wifi/target.mk @@ -3,7 +3,7 @@ REQUIRES = x86 TARGET = wifi_drv SRC_CC = main.cc wpa.cc LIBS = base wifi iwl_firmware -LIBS += wpa_supplicant libc +LIBS += wpa_supplicant libc nic_driver # needed for firmware.h INC_DIR += $(REP_DIR)/src/lib/wifi/include diff --git a/repos/dde_linux/src/lib/wifi/init.cc b/repos/dde_linux/src/lib/wifi/init.cc index 8113e4b8f0..442f384341 100644 --- a/repos/dde_linux/src/lib/wifi/init.cc +++ b/repos/dde_linux/src/lib/wifi/init.cc @@ -180,8 +180,11 @@ static void run_linux(void *args) unsigned long jiffies; -void wifi_init(Genode::Env &env, Genode::Blockade &blockade, bool disable_11n, - Genode::Signal_context_capability rfkill) +void wifi_init(Genode::Env &env, + Genode::Blockade &blockade, + bool disable_11n, + Genode::Signal_context_capability rfkill, + Genode::Nic_driver_mode mode) { Lx_kit::construct_env(env); @@ -208,7 +211,7 @@ void wifi_init(Genode::Env &env, Genode::Blockade &blockade, bool disable_11n, Lx::Work::work_queue(&Lx_kit::env().heap()); Lx::socket_init(env.ep(), Lx_kit::env().heap()); - Lx::nic_init(env, Lx_kit::env().heap()); + Lx::nic_init(env, Lx_kit::env().heap(), mode); Lx::pci_init(env, env.ram(), Lx_kit::env().heap()); Lx::malloc_init(env, Lx_kit::env().heap()); diff --git a/repos/dde_linux/src/lib/wifi/lx.h b/repos/dde_linux/src/lib/wifi/lx.h index 82e25a6fd9..7430558a70 100644 --- a/repos/dde_linux/src/lib/wifi/lx.h +++ b/repos/dde_linux/src/lib/wifi/lx.h @@ -17,6 +17,9 @@ /* Genode includes */ #include +/* NIC driver includes */ +#include + /* local includes */ #include @@ -33,7 +36,9 @@ namespace Lx void socket_init(Genode::Entrypoint&, Genode::Allocator&); void socket_kick(); - void nic_init(Genode::Env&, Genode::Allocator&); + void nic_init(Genode::Env&, + Genode::Allocator&, + Genode::Nic_driver_mode); Genode::Ram_dataspace_capability backend_alloc(Genode::addr_t, Genode::Cache_attribute); void backend_free(Genode::Ram_dataspace_capability); diff --git a/repos/dde_linux/src/lib/wifi/nic.cc b/repos/dde_linux/src/lib/wifi/nic.cc index d653a1d16e..6a32da392b 100644 --- a/repos/dde_linux/src/lib/wifi/nic.cc +++ b/repos/dde_linux/src/lib/wifi/nic.cc @@ -20,6 +20,10 @@ #include #include #include +#include + +/* NIC driver includes */ +#include /* local includes */ #include @@ -99,6 +103,62 @@ bool tx_task_send(struct sk_buff *skb) } +class Wifi_nic +{ + private: + + net_device *_device { nullptr }; + + static Wifi_nic *_instance; + + public: + + void device(net_device &device) + { + _device = &device; + } + + bool device_set() const + { + return _device != nullptr; + } + + net_device &device() + { + if (_device == nullptr) { + + class Invalid { }; + throw Invalid { }; + } + return *_device; + } + + static void instance(Wifi_nic &instance) + { + _instance = &instance; + } + + static Wifi_nic &instance() + { + if (!_instance) { + + class Invalid { }; + throw Invalid { }; + } + return *_instance; + } + + virtual void activate() = 0; + + virtual void handle_driver_rx_packet(struct sk_buff *skb) = 0; + + virtual void handle_driver_link_state(bool state) = 0; +}; + + +Wifi_nic *Wifi_nic::_instance { nullptr }; + + /** * Nic::Session implementation */ @@ -249,7 +309,8 @@ class Wifi_session_component : public Nic::Session_component * NIC root implementation */ class Root : public Genode::Root_component + Genode::Single_client>, + public Wifi_nic { private: @@ -284,20 +345,19 @@ class Root : public Genode::Root_componentsession = nullptr; + session = nullptr; Genode::Root_component::_destroy_session(session); } public: - net_device *device = nullptr; Wifi_session_component *session = nullptr; static Root *instance; @@ -306,23 +366,194 @@ class Root : public Genode::Root_componentreceive(skb); + } + } + + void handle_driver_link_state(bool state) override + { + if (session) { + session->link_state(state); + } + } }; -Root *Root::instance; +namespace Genode { class Wifi_uplink; } -void Lx::nic_init(Genode::Env &env, Genode::Allocator &alloc) +class Genode::Wifi_uplink : public Wifi_nic { - static Root root(env, alloc); - Root::instance = &root; + private: + + class Uplink_client : public Uplink_client_base + { + private: + + net_device &_ndev; + + Nic::Mac_address _init_drv_mac_addr(net_device &ndev) + { + Nic::Mac_address mac_addr { }; + memcpy(&mac_addr, ndev.perm_addr, ETH_ALEN); + return mac_addr; + } + + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + /* + * We must not be called from another task, just from the + * packet stream dispatcher. + */ + if (Lx::scheduler().active()) { + warning("scheduler active"); + return Transmit_result::RETRY; + } + + struct sk_buff *skb { + lxc_alloc_skb(conn_rx_pkt_size + + HEAD_ROOM, HEAD_ROOM) }; + + skb->dev = &_ndev; + + unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size); + memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size); + + _tx_data.ndev = &_ndev; + _tx_data.skb = skb; + + _tx_task->unblock(); + Lx::scheduler().schedule(); + return Transmit_result::ACCEPTED; + } + + public: + + Uplink_client(Env &env, + Allocator &alloc, + net_device &ndev) + : + Uplink_client_base { env, alloc, + _init_drv_mac_addr(ndev) }, + _ndev { ndev } + { + _drv_handle_link_state( + !(_ndev.state & 1UL << __LINK_STATE_NOCARRIER)); + } + + void handle_driver_link_state(bool state) + { + _drv_handle_link_state(state); + } + + void handle_driver_rx_packet(struct sk_buff *skb) + { + Skb skbh { skb_helper(skb) }; + _drv_rx_handle_pkt( + skbh.packet_size + skbh.frag_size, + [&] (void *conn_tx_pkt_base, + size_t &) + { + memcpy( + conn_tx_pkt_base, + skbh.packet, + skbh.packet_size); + + if (skbh.frag_size) { + + memcpy( + (char *)conn_tx_pkt_base + skbh.packet_size, + skbh.frag, + skbh.frag_size); + } + return Write_result::WRITE_SUCCEEDED; + }); + } + }; + + Env &_env; + Allocator &_alloc; + Constructible _client { }; + + public: + + Wifi_uplink(Env &env, + Allocator &alloc) + : + _env { env }, + _alloc { alloc } + { } + + + /************** + ** Wifi_nic ** + **************/ + + void activate() override + { + _client.construct(_env, _alloc, device()); + } + + void handle_driver_rx_packet(struct sk_buff *skb) override + { + if (_client.constructed()) { + _client->handle_driver_rx_packet(skb); + } + } + + void handle_driver_link_state(bool state) override + { + if (_client.constructed()) { + _client->handle_driver_link_state(state); + } + } +}; + + +void Lx::nic_init(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Nic_driver_mode mode) +{ + switch (mode) { + case Genode::Nic_driver_mode::NIC_SERVER: + + Genode::log("Acting as NIC server"); + static Root root(env, alloc); + Wifi_nic::instance(root); + break; + + case Genode::Nic_driver_mode::UPLINK_CLIENT: + + Genode::log("Acting as Uplink client"); + Wifi_nic::instance(*new (alloc) Genode::Wifi_uplink(env, alloc)); + break; + } } void Lx::get_mac_address(unsigned char *addr) { - memcpy(addr, Root::instance->device->perm_addr, ETH_ALEN); + memcpy(addr, Wifi_nic::instance().device().perm_addr, ETH_ALEN); } @@ -535,12 +766,12 @@ extern "C" void __dev_remove_pack(struct packet_type *pt) extern "C" struct net_device *__dev_get_by_index(struct net *net, int ifindex) { - if (!Root::instance->device) { + if (!Wifi_nic::instance().device_set()) { Genode::error("no net device registered!"); return 0; } - return Root::instance->device; + return &Wifi_nic::instance().device(); } @@ -580,8 +811,7 @@ extern "C" int dev_parse_header(const struct sk_buff *skb, unsigned char *haddr) extern "C" int dev_queue_xmit(struct sk_buff *skb) { - struct net_device *dev = skb->dev; - struct net_device_ops const *ops = dev->netdev_ops; + struct net_device *dev = skb->dev; if (skb->next) { Genode::warning("more skb's queued"); @@ -619,12 +849,12 @@ extern "C" void dev_close(struct net_device *ndev) bool Lx::open_device() { - if (!Root::instance->device) { + if (!Wifi_nic::instance().device_set()) { Genode::error("no net_device available"); return false; } - struct net_device * const ndev = Root::instance->device; + struct net_device * const ndev = &Wifi_nic::instance().device(); int err = ndev->netdev_ops->ndo_open(ndev); if (err) { @@ -660,7 +890,11 @@ extern "C" int register_netdevice(struct net_device *ndev) already_registered = true; - Root::instance->device = ndev; + if (ndev == nullptr) { + class Invalid_net_device { }; + throw Invalid_net_device { }; + } + Wifi_nic::instance().device(*ndev); ndev->state |= 1UL << __LINK_STATE_START; netif_carrier_off(ndev); @@ -698,7 +932,7 @@ extern "C" int register_netdevice(struct net_device *ndev) if (ndev->netdev_ops->ndo_set_rx_mode) ndev->netdev_ops->ndo_set_rx_mode(ndev); - Root::instance->announce(); + Wifi_nic::instance().activate(); list_add_tail_rcu(&ndev->dev_list, &init_net.dev_base_head); @@ -724,22 +958,14 @@ extern "C" int netif_carrier_ok(const struct net_device *dev) extern "C" void netif_carrier_on(struct net_device *dev) { dev->state &= ~(1UL << __LINK_STATE_NOCARRIER); - - Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device; - - if (session) - session->link_state(true); + Wifi_nic::instance().handle_driver_link_state(true); } extern "C" void netif_carrier_off(struct net_device *dev) { dev->state |= 1UL << __LINK_STATE_NOCARRIER; - - Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device; - - if (session) - session->link_state(false); + Wifi_nic::instance().handle_driver_link_state(false); } @@ -754,14 +980,12 @@ extern "C" int netif_receive_skb(struct sk_buff *skb) if (is_eapol(skb)) { /* XXX call only AF_PACKET hook */ for (Proto_hook* ph = proto_hook_list().first(); ph; ph = ph->next()) { - ph->pt.func(skb, Root::instance->device, &ph->pt, Root::instance->device); + ph->pt.func(skb, &Wifi_nic::instance().device(), &ph->pt, &Wifi_nic::instance().device()); } return NET_RX_SUCCESS; } - if (Root::instance->session) - Root::instance->session->receive(skb); - + Wifi_nic::instance().handle_driver_rx_packet(skb); dev_kfree_skb(skb); return NET_RX_SUCCESS; } diff --git a/repos/os/lib/import/import-nic_driver.mk b/repos/os/lib/import/import-nic_driver.mk new file mode 100644 index 0000000000..e9d49b5c35 --- /dev/null +++ b/repos/os/lib/import/import-nic_driver.mk @@ -0,0 +1 @@ +REP_INC_DIR += src/drivers/nic/include diff --git a/repos/os/lib/mk/nic_driver.mk b/repos/os/lib/mk/nic_driver.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/repos/os/recipes/api/nic_driver/content.mk b/repos/os/recipes/api/nic_driver/content.mk new file mode 100644 index 0000000000..6b395e271a --- /dev/null +++ b/repos/os/recipes/api/nic_driver/content.mk @@ -0,0 +1,12 @@ +MIRROR_FROM_REP_DIR := \ + src/drivers/nic/include \ + lib/mk/nic_driver.mk \ + lib/import/import-nic_driver.mk + +content: $(MIRROR_FROM_REP_DIR) LICENSE + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ diff --git a/repos/os/recipes/api/nic_driver/hash b/repos/os/recipes/api/nic_driver/hash new file mode 100644 index 0000000000..5367907f68 --- /dev/null +++ b/repos/os/recipes/api/nic_driver/hash @@ -0,0 +1 @@ +2020-12-09-b 594a8cbf9fc6c84f60947a4b940540b5aa6e1486 diff --git a/repos/os/recipes/src/lan9118_nic_drv/used_apis b/repos/os/recipes/src/lan9118_nic_drv/used_apis index f135ea45d1..add8b7b85c 100644 --- a/repos/os/recipes/src/lan9118_nic_drv/used_apis +++ b/repos/os/recipes/src/lan9118_nic_drv/used_apis @@ -2,3 +2,5 @@ base os platform_session nic_session +uplink_session +nic_driver diff --git a/repos/os/recipes/src/linux_nic_drv/used_apis b/repos/os/recipes/src/linux_nic_drv/used_apis index f8896ba8d7..37fabb6132 100644 --- a/repos/os/recipes/src/linux_nic_drv/used_apis +++ b/repos/os/recipes/src/linux_nic_drv/used_apis @@ -2,3 +2,5 @@ base base-linux os nic_session +uplink_session +nic_driver diff --git a/repos/os/recipes/src/virtio_nic_drv/used_apis b/repos/os/recipes/src/virtio_nic_drv/used_apis index 626bf71970..3714501d9c 100644 --- a/repos/os/recipes/src/virtio_nic_drv/used_apis +++ b/repos/os/recipes/src/virtio_nic_drv/used_apis @@ -2,4 +2,6 @@ base os virtio nic_session +uplink_session +nic_driver platform_session diff --git a/repos/os/recipes/src/zynq_nic_drv/used_apis b/repos/os/recipes/src/zynq_nic_drv/used_apis index 51485221e5..61ee78a6b5 100644 --- a/repos/os/recipes/src/zynq_nic_drv/used_apis +++ b/repos/os/recipes/src/zynq_nic_drv/used_apis @@ -1,3 +1,5 @@ base os nic_session +uplink_session +nic_driver diff --git a/repos/os/src/drivers/nic/include/drivers/nic/mode.h b/repos/os/src/drivers/nic/include/drivers/nic/mode.h new file mode 100644 index 0000000000..85e7336c90 --- /dev/null +++ b/repos/os/src/drivers/nic/include/drivers/nic/mode.h @@ -0,0 +1,41 @@ +/* + * \brief NIC driver mode regarding the session used for packet transmission + * \author Martin Stein + * \date 2020-12-07 + */ + +/* + * Copyright (C) 2020 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 _DRIVERS__NIC__MODE_H_ +#define _DRIVERS__NIC__MODE_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + enum class Nic_driver_mode { NIC_SERVER, UPLINK_CLIENT }; + + inline Nic_driver_mode read_nic_driver_mode(Xml_node const &driver_cfg) + { + String<16> const mode_str { + driver_cfg.attribute_value("mode", String<16>("")) }; + + if (mode_str == "nic_server") { + + return Nic_driver_mode::NIC_SERVER; + } + if (mode_str == "uplink_client") { + + return Nic_driver_mode::UPLINK_CLIENT; + } + return Nic_driver_mode::NIC_SERVER; + } +} + +#endif /* _DRIVERS__NIC__MODE_H_ */ diff --git a/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h b/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h new file mode 100644 index 0000000000..a3b31fcda3 --- /dev/null +++ b/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h @@ -0,0 +1,341 @@ +/* + * \brief Generic base class for the Uplink client role of NIC drivers + * \author Martin Stein + * \date 2020-12-07 + */ + +/* + * Copyright (C) 2020 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 _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_ +#define _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode { + + class Uplink_client_base; +} + +class Genode::Uplink_client_base : Noncopyable +{ + protected: + + enum class Transmit_result { ACCEPTED, REJECTED, RETRY }; + + enum class Write_result { WRITE_SUCCEEDED, WRITE_FAILED }; + + enum class Burst_result + { + BURST_SUCCEEDED, + BURST_FAILED, + BURST_CONTINUE + }; + + enum { PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE }; + enum { BUF_SIZE = Uplink::Session::QUEUE_SIZE * PKT_SIZE }; + + Env &_env; + Allocator &_alloc; + Net::Mac_address _drv_mac_addr; + bool _drv_mac_addr_used { false }; + bool _drv_link_state { false }; + Constructible _conn { }; + Nic::Packet_allocator _conn_pkt_alloc { &_alloc }; + Signal_handler _conn_rx_ready_to_ack_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_ready_to_ack }; + Signal_handler _conn_rx_packet_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_packet_avail }; + Signal_handler _conn_tx_ack_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ack_avail }; + Signal_handler _conn_tx_ready_to_submit_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ready_to_submit }; + Packet_descriptor _save { }; + + + /***************************************** + ** Interface towards Uplink connection ** + *****************************************/ + + void _conn_tx_handle_ready_to_submit() { } + + void _conn_rx_handle_ready_to_ack() { } + + void _conn_tx_handle_ack_avail() + { + while (_conn->tx()->ack_avail()) { + + _conn->tx()->release_packet(_conn->tx()->get_acked_packet()); + } + } + + void _conn_rx_handle_packet_avail() + { + if (_custom_conn_rx_packet_avail_handler()) { + + _custom_conn_rx_handle_packet_avail(); + + } else if (_drv_supports_transmit_pkt_burst()) { + + _drv_transmit_pkt_burst_prepare(); + + /* submit received packets to lower layer */ + while (((_conn->rx()->packet_avail() || _save.size()) && _conn->rx()->ready_to_ack())) { + + Packet_descriptor packet = _save.size() ? _save : _conn->rx()->get_packet(); + _save = Packet_descriptor(); + + if (!packet.size()) { + + class Invalid_packet { }; + throw Invalid_packet { }; + } + + char const *packet_base { + _conn->rx()->packet_content(packet) }; + + Burst_result const result { + _drv_transmit_pkt_burst_step( + packet, packet_base, _save) }; + + switch (result) { + case Burst_result::BURST_FAILED: + return; + case Burst_result::BURST_CONTINUE: + continue; + case Burst_result::BURST_SUCCEEDED: + break; + } + + /* acknowledge to client */ + _conn->rx()->acknowledge_packet(packet); + } + _drv_transmit_pkt_burst_finish(); + + } else { + + bool drv_ready_to_transmit_pkt { _drv_link_state }; + bool pkts_transmitted { false }; + + while (drv_ready_to_transmit_pkt && + _conn->rx()->packet_avail() && + _conn->rx()->ready_to_ack()) { + + Packet_descriptor const conn_rx_pkt { + _conn->rx()->get_packet() }; + + if (conn_rx_pkt.size() > 0 && + _conn->rx()->packet_valid(conn_rx_pkt)) { + + const char *const conn_rx_pkt_base { + _conn->rx()->packet_content(conn_rx_pkt) }; + + switch (_drv_transmit_pkt(conn_rx_pkt_base, + conn_rx_pkt.size())) { + + case Transmit_result::ACCEPTED: + + pkts_transmitted = true; + _conn->rx()->acknowledge_packet(conn_rx_pkt); + break; + + case Transmit_result::REJECTED: + + warning("failed to forward packet from " + "Uplink-connection RX to driver"); + + _conn->rx()->acknowledge_packet(conn_rx_pkt); + break; + + case Transmit_result::RETRY: + + drv_ready_to_transmit_pkt = false; + break; + } + + } else { + + warning("ignoring invalid packet from Uplink-" + "connection RX"); + } + } + + if (pkts_transmitted) { + + _drv_finish_transmitted_pkts(); + } + } + } + + + /*************************************************** + ** Generic back end for interface towards driver ** + ***************************************************/ + + template + void _drv_rx_handle_pkt(size_t conn_tx_pkt_size, + FUNC && write_to_conn_tx_pkt) + { + if (!_conn.constructed()) { + return; + } + _conn_tx_handle_ack_avail(); + + if (!_conn->tx()->ready_to_submit()) { + return; + } + try { + Packet_descriptor conn_tx_pkt { + _conn->tx()->alloc_packet(conn_tx_pkt_size) }; + + void *conn_tx_pkt_base { + _conn->tx()->packet_content(conn_tx_pkt) }; + + size_t adjusted_conn_tx_pkt_size { + conn_tx_pkt_size }; + + Write_result write_result { + write_to_conn_tx_pkt( + conn_tx_pkt_base, + adjusted_conn_tx_pkt_size) }; + + switch (write_result) { + case Write_result::WRITE_SUCCEEDED: + + if (adjusted_conn_tx_pkt_size == conn_tx_pkt_size) { + + _conn->tx()->submit_packet(conn_tx_pkt); + + } else if (adjusted_conn_tx_pkt_size < conn_tx_pkt_size) { + + Packet_descriptor adjusted_conn_tx_pkt { + conn_tx_pkt.offset(), adjusted_conn_tx_pkt_size }; + + _conn->tx()->submit_packet(adjusted_conn_tx_pkt); + + } else { + + class Bad_size { }; + throw Bad_size { }; + } + break; + + case Write_result::WRITE_FAILED: + + _conn->tx()->release_packet(conn_tx_pkt); + } + + } catch (...) { + + warning("exception while trying to forward packet from driver" + "to Uplink connection TX"); + + return; + } + } + + void _drv_handle_link_state(bool drv_link_state) + { + if (_drv_link_state == drv_link_state) { + return; + } + _drv_link_state = drv_link_state; + if (drv_link_state) { + + /* create connection */ + _drv_mac_addr_used = true; + _conn.construct( + _env, &_conn_pkt_alloc, BUF_SIZE, BUF_SIZE, + _drv_mac_addr); + + /* install signal handlers at connection */ + _conn->rx_channel()->sigh_ready_to_ack( + _conn_rx_ready_to_ack_handler); + + _conn->rx_channel()->sigh_packet_avail( + _conn_rx_packet_avail_handler); + + _conn->tx_channel()->sigh_ack_avail( + _conn_tx_ack_avail_handler); + + _conn->tx_channel()->sigh_ready_to_submit( + _conn_tx_ready_to_submit_handler); + } else { + + _conn.destruct(); + } + } + + virtual void _drv_finish_transmitted_pkts() { } + + virtual bool _drv_supports_transmit_pkt_burst() + { + return false; + } + + virtual void _drv_transmit_pkt_burst_finish() + { + class Unexpected_call { }; + throw Unexpected_call { }; + } + + virtual void _drv_transmit_pkt_burst_prepare() + { + class Unexpected_call { }; + throw Unexpected_call { }; + } + + virtual Burst_result + _drv_transmit_pkt_burst_step(Packet_descriptor const &packet, + char const *packet_base, + Packet_descriptor &save) + { + (void)packet; + (void)packet_base; + (void)save; + class Unexpected_call { }; + throw Unexpected_call { }; + } + + virtual Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) = 0; + + virtual void _custom_conn_rx_handle_packet_avail() + { + class Unexpected_call { }; + throw Unexpected_call { }; + } + + virtual bool _custom_conn_rx_packet_avail_handler() { return false; } + + public: + + Uplink_client_base(Env &env, + Allocator &alloc, + Net::Mac_address const &drv_mac_addr) + : + _env { env }, + _alloc { alloc }, + _drv_mac_addr { drv_mac_addr } + { + log("MAC address ", _drv_mac_addr); + } + + void mac_address(Net::Mac_address const &mac_address) + { + if (_drv_mac_addr_used) { + + class Already_in_use { }; + throw Already_in_use { }; + } + _drv_mac_addr = mac_address; + } + + virtual ~Uplink_client_base() { } +}; + +#endif /* _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_ */ diff --git a/repos/os/src/drivers/nic/lan9118/lan9118.h b/repos/os/src/drivers/nic/lan9118/lan9118.h index 636db377e9..ebcd81e98f 100644 --- a/repos/os/src/drivers/nic/lan9118/lan9118.h +++ b/repos/os/src/drivers/nic/lan9118/lan9118.h @@ -15,6 +15,7 @@ #ifndef _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_ #define _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_ +/* Genode includes */ #include #include #include @@ -22,15 +23,20 @@ #include #include -class Lan9118 : public Nic::Session_component +/* NIC driver includes */ +#include + +class Lan9118_base { private: /* * Noncopyable */ - Lan9118(Lan9118 const &); - Lan9118 &operator = (Lan9118 const &); + Lan9118_base(Lan9118_base const &); + Lan9118_base &operator = (Lan9118_base const &); + + protected: /** * MMIO register offsets @@ -74,13 +80,6 @@ class Lan9118 : public Nic::Session_component MAC_CSR_CMD_WRITE = (0 << 30), }; - Genode::Attached_dataspace _mmio; - volatile Genode::uint32_t *_reg_base; - Timer::Connection _timer; - Nic::Mac_address _mac_addr { }; - Genode::Irq_session_client _irq; - Genode::Signal_handler _irq_handler; - /** * Information about a received packet, used internally */ @@ -93,6 +92,12 @@ class Lan9118 : public Nic::Session_component { } }; + Genode::Attached_dataspace _mmio; + volatile Genode::uint32_t *_reg_base; + Timer::Connection _timer; + Nic::Mac_address _mac_addr { }; + Genode::Irq_session_client _irq; + /** * Read 32-bit wide MMIO register */ @@ -193,46 +198,38 @@ class Lan9118 : public Nic::Session_component return _reg_read(RX_FIFO_INF) & 0xffff; } - protected: - - bool _send() + void _drv_tx_transmit_pkt(Genode::size_t packet_size, + Genode::uint32_t const *src, + bool &continue_sending, + bool &ack_packet) { - if (!_tx.sink()->ready_to_ack()) - return false; - - if (!_tx.sink()->packet_avail()) - return false; - - Genode::Packet_descriptor packet = _tx.sink()->get_packet(); - if (!packet.size() || !_tx.sink()->packet_valid(packet)) { - Genode::warning("Invalid tx packet"); - return true; - } - /* limit size to 11 bits, the maximum supported by lan9118 */ enum { MAX_PACKET_SIZE_LOG2 = 11 }; Genode::size_t const max_size = (1 << MAX_PACKET_SIZE_LOG2) - 1; - if (packet.size() > max_size) { - Genode::error("packet size ", packet.size(), " too large, " + if (packet_size > max_size) { + Genode::error("packet size ", packet_size, " too large, " "limit is ", max_size); - return true; + + continue_sending = true; + ack_packet = false; + return; } enum { FIRST_SEG = (1 << 13), LAST_SEG = (1 << 12) }; - Genode::uint32_t const cmd_a = packet.size() | FIRST_SEG | LAST_SEG, - cmd_b = packet.size(); + Genode::uint32_t const cmd_a = packet_size | FIRST_SEG | LAST_SEG, + cmd_b = packet_size; - unsigned count = Genode::align_addr(packet.size(), 2) >> 2; - Genode::uint32_t *src = (Genode::uint32_t *)_tx.sink()->packet_content(packet); + unsigned count = Genode::align_addr(packet_size, 2) >> 2; /* check space left in tx data fifo */ Genode::size_t const fifo_avail = _reg_read(TX_FIFO_INF) & 0xffff; if (fifo_avail < count*4 + sizeof(cmd_a) + sizeof(cmd_b)) { Genode::error("tx fifo overrun, ignore packet"); - _tx.sink()->acknowledge_packet(packet); - return false; + continue_sending = false; + ack_packet = true; + return; } _reg_write(TX_DATA_FIFO, cmd_a); @@ -242,56 +239,39 @@ class Lan9118 : public Nic::Session_component for (; count--; src++) _reg_write(TX_DATA_FIFO, *src); - _tx.sink()->acknowledge_packet(packet); - return true; + continue_sending = true; + ack_packet = true; + return; } - void _handle_packet_stream() override + void _finish_handle_irq() { - while (_rx.source()->ack_avail()) - _rx.source()->release_packet(_rx.source()->get_acked_packet()); - - while (_send()) ; - } - - void _handle_irq() - { - using namespace Genode; - - _handle_packet_stream(); - - while (_rx_packet_avail() && _rx.source()->ready_to_submit()) { - - /* read packet from NIC, copy to client buffer */ - Rx_packet_info packet = _rx_packet_info(); - - /* align size to 32-bit boundary */ - size_t const size = align_addr(packet.size, 2); - - /* allocate rx packet buffer */ - Nic::Packet_descriptor p; - try { - p = _rx.source()->alloc_packet(size); - } catch (Session::Rx::Source::Packet_alloc_failed) { return; } - - uint32_t *dst = (uint32_t *)_rx.source()->packet_content(p); - - /* calculate number of words to be read from rx fifo */ - size_t count = min(size, _rx_data_pending()) >> 2; - - /* copy payload from rx fifo to client buffer */ - for (; count--; ) - *dst++ = _reg_read(RX_DATA_FIFO); - - _rx.source()->submit_packet(p); - } - /* acknowledge all pending irqs */ _reg_write(INT_STS, ~0); _irq.ack_irq(); } + void _drv_rx_copy_pkt(Genode::size_t size, + Genode::uint32_t *dst) + { + /* calculate number of words to be read from rx fifo */ + Genode::size_t count = Genode::min(size, _rx_data_pending()) >> 2; + + /* copy payload from rx fifo to client buffer */ + for (; count--; ) + *dst++ = _reg_read(RX_DATA_FIFO); + } + + Genode::size_t _drv_rx_pkt_size() + { + /* read packet from NIC, copy to client buffer */ + Rx_packet_info packet = _rx_packet_info(); + + /* align size to 32-bit boundary */ + return Genode::align_addr(packet.size, 2); + } + public: /** @@ -299,26 +279,17 @@ class Lan9118 : public Nic::Session_component */ class Device_not_supported { }; - /** - * Constructor - * - * \throw Device_not_supported - */ - Lan9118(Genode::Io_mem_dataspace_capability ds_cap, - Genode::Irq_session_capability irq_cap, - Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator & rx_block_md_alloc, - Genode::Env & env) - : Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, - rx_block_md_alloc, env), - _mmio(env.rm(), ds_cap), - _reg_base(_mmio.local_addr()), - _timer(env), - _irq(irq_cap), - _irq_handler(env.ep(), *this, &Lan9118::_handle_irq) + Lan9118_base(Genode::Io_mem_dataspace_capability ds_cap, + Genode::Irq_session_capability irq_cap, + Genode::Signal_context_capability irq_handler, + Genode::Env & env) + : + _mmio { env.rm(), ds_cap }, + _reg_base { _mmio.local_addr() }, + _timer { env }, + _irq { irq_cap } { - _irq.sigh(_irq_handler); + _irq.sigh(irq_handler); _irq.ack_irq(); unsigned long const id_rev = _reg_read(ID_REV), @@ -387,10 +358,7 @@ class Lan9118 : public Nic::Session_component _reg_write(IRQ_CFG, IRQ_CFG_EN | IRQ_CFG_POL | IRQ_CFG_TYPE); } - /** - * Destructor - */ - ~Lan9118() + virtual ~Lan9118_base() { Genode::log("disable NIC"); @@ -400,6 +368,93 @@ class Lan9118 : public Nic::Session_component /* disable rx and tx at MAX */ _mac_csr_write(MAC_CR, 0); } +}; + + +class Lan9118 : public Nic::Session_component, + public Genode::Signal_handler, + public Lan9118_base +{ + protected: + + bool _send() + { + if (!_tx.sink()->ready_to_ack()) + return false; + + if (!_tx.sink()->packet_avail()) + return false; + + Genode::Packet_descriptor packet = _tx.sink()->get_packet(); + if (!packet.size() || !_tx.sink()->packet_valid(packet)) { + Genode::warning("Invalid tx packet"); + return true; + } + + bool continue_sending { false }; + bool ack_packet { false }; + _drv_tx_transmit_pkt( + packet.size(), + (Genode::uint32_t *)_tx.sink()->packet_content(packet), + continue_sending, + ack_packet); + + if (ack_packet) { + _tx.sink()->acknowledge_packet(packet); + } + return continue_sending; + } + + void _handle_packet_stream() override + { + while (_rx.source()->ack_avail()) + _rx.source()->release_packet(_rx.source()->get_acked_packet()); + + while (_send()) ; + } + + void _handle_irq() + { + using namespace Genode; + + _handle_packet_stream(); + + while (_rx_packet_avail() && _rx.source()->ready_to_submit()) { + + /* allocate rx packet buffer */ + size_t const size { _drv_rx_pkt_size() }; + Nic::Packet_descriptor p; + try { + p = _rx.source()->alloc_packet(size); + } catch (Session::Rx::Source::Packet_alloc_failed) { return; } + + uint32_t *dst = (uint32_t *)_rx.source()->packet_content(p); + _drv_rx_copy_pkt(size, dst); + + _rx.source()->submit_packet(p); + } + _finish_handle_irq(); + } + + public: + + /** + * Constructor + * + * \throw Device_not_supported + */ + Lan9118(Genode::Io_mem_dataspace_capability ds_cap, + Genode::Irq_session_capability irq_cap, + Genode::size_t const tx_buf_size, + Genode::size_t const rx_buf_size, + Genode::Allocator & rx_block_md_alloc, + Genode::Env & env) + : + Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env }, + Genode::Signal_handler { env.ep(), *this, &Lan9118::_handle_irq }, + Lan9118_base { ds_cap, irq_cap, *this, env } + { } /************************************** ** Nic::Session_component interface ** @@ -417,4 +472,98 @@ class Lan9118 : public Nic::Session_component } }; + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Signal_handler, + public Lan9118_base, + public Uplink_client_base +{ + private: + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + bool continue_sending { false }; + bool ack_packet { false }; + _drv_tx_transmit_pkt( + conn_rx_pkt_size, + (Genode::uint32_t const*)conn_rx_pkt_base, + continue_sending, + ack_packet); + + /* + * FIXME This seems to be a bug in the original (NIC client) + * driver. When having packets that don't fit the maximum + * transmittable size (limited by hardware) the driver + * didn't acknowledge the packet but continued forwarding + * packets from the session to the driver. Normally, we + * would expect the driver to acknowledge the packet without + * transmitting it (i.e., reject it). + */ + if (!ack_packet && continue_sending) { + return Transmit_result::REJECTED; + } + /* + * FIXME This seems to be a bug in the original (NIC client) + * driver. When the hardware TX FIFO doesn't have enough + * space left to transmit the packet, the driver acknowledge + * the packet but didn't continue forwarding packets from + * the session to the hardware. Normally, we would expect + * the driver to not acknowledge the packet nor continue + * forwarding other packets (i.e., retry later). + */ + if (ack_packet && !continue_sending) { + return Transmit_result::RETRY; + } + if (ack_packet && continue_sending) { + return Transmit_result::ACCEPTED; + } + class Unexpected_transmit_result { }; + throw Unexpected_transmit_result { }; + } + + void _handle_irq() + { + while (_rx_packet_avail()) { + + _drv_rx_handle_pkt( + _drv_rx_pkt_size(), + [&] (void *conn_tx_pkt_base, + size_t &conn_tx_pkt_size) + { + _drv_rx_copy_pkt( + conn_tx_pkt_size, + (uint32_t *)conn_tx_pkt_base); + + return Write_result::WRITE_SUCCEEDED; + }); + } + _finish_handle_irq(); + } + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Io_mem_dataspace_capability ds_cap, + Irq_session_capability irq_cap) + : + Signal_handler { env.ep(), *this, &Uplink_client::_handle_irq }, + Lan9118_base { ds_cap, irq_cap, *this, env }, + Uplink_client_base { env, alloc, _mac_addr } + { + _drv_handle_link_state(true); + } +}; + #endif /* _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_ */ diff --git a/repos/os/src/drivers/nic/lan9118/main.cc b/repos/os/src/drivers/nic/lan9118/main.cc index 231c88254c..bc9657bbd5 100644 --- a/repos/os/src/drivers/nic/lan9118/main.cc +++ b/repos/os/src/drivers/nic/lan9118/main.cc @@ -24,23 +24,28 @@ #include #include +/* NIC driver includes */ +#include + /* driver code */ #include -class Root : public Genode::Root_component +using namespace Genode; + +class Nic_root : public Root_component { private: - Genode::Env & _env; - Platform::Connection _platform { _env }; - Platform::Device_client _device { _platform.device_by_index(0) }; + Env &_env; + Platform::Device_client &_device; - protected: + + /******************** + ** Root_component ** + ********************/ Lan9118 *_create_session(const char *args) override { - using namespace Genode; - size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0); @@ -53,27 +58,68 @@ class Root : public Genode::Root_component tx_buf_size + rx_buf_size > ram_quota) { error("insufficient 'ram_quota', got ", ram_quota, ", " "need ", tx_buf_size + rx_buf_size); - throw Genode::Insufficient_ram_quota(); + throw Insufficient_ram_quota(); } - return new (Root::md_alloc()) + return new (md_alloc()) Lan9118(_device.io_mem_dataspace(), _device.irq(), tx_buf_size, rx_buf_size, *md_alloc(), _env); } public: - Root(Genode::Env &env, Genode::Allocator &md_alloc) - : Genode::Root_component(env.ep(), md_alloc), - _env(env) { } + Nic_root(Env &env, + Allocator &md_alloc, + Platform::Device_client &device) + : + Root_component { env.ep(), md_alloc }, + _env { env }, + _device { device } + { } +}; + +class Main +{ + private: + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Platform::Connection _platform { _env }; + Platform::Device_client _device { _platform.device_by_index(0) }; + Constructible _nic_root { }; + Constructible _uplink_client { }; + + public: + + Main (Env &env) + : + _env { env } + { + log("--- LAN9118 NIC driver started ---"); + + Attached_rom_dataspace config_rom { _env, "config" }; + Nic_driver_mode const mode { + read_nic_driver_mode(config_rom.xml()) }; + + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + + _nic_root.construct(_env, _heap, _device); + _env.parent().announce(_env.ep().manage(*_nic_root)); + break; + + case Nic_driver_mode::UPLINK_CLIENT: + + _uplink_client.construct( + _env, _heap, _device.io_mem_dataspace(), _device.irq()); + + break; + } + } }; -void Component::construct(Genode::Env &env) +void Component::construct(Env &env) { - static Genode::Heap heap(env.ram(), env.rm()); - static Root nic_root(env, heap); - Genode::log("--- LAN9118 NIC driver started ---"); - env.parent().announce(env.ep().manage(nic_root)); + static Main main { env }; } diff --git a/repos/os/src/drivers/nic/lan9118/target.mk b/repos/os/src/drivers/nic/lan9118/target.mk index 25fcba4e77..9ecfe7f85d 100644 --- a/repos/os/src/drivers/nic/lan9118/target.mk +++ b/repos/os/src/drivers/nic/lan9118/target.mk @@ -1,5 +1,5 @@ REQUIRES = arm_v7 TARGET = lan9118_nic_drv SRC_CC = main.cc -LIBS = base +LIBS = base nic_driver INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/drivers/nic/spec/linux/main.cc b/repos/os/src/drivers/nic/spec/linux/main.cc index 841f725e02..f309065cf4 100644 --- a/repos/os/src/drivers/nic/spec/linux/main.cc +++ b/repos/os/src/drivers/nic/spec/linux/main.cc @@ -32,6 +32,10 @@ #include #include +/* NIC driver includes */ +#include +#include + /* Linux */ #include #include @@ -47,6 +51,20 @@ namespace Server { } +static Net::Mac_address default_mac_address() +{ + /* fall back to fake MAC address (unicast, locally managed) */ + Nic::Mac_address mac_addr { }; + mac_addr.addr[0] = 0x02; + mac_addr.addr[1] = 0x00; + mac_addr.addr[2] = 0x00; + mac_addr.addr[3] = 0x00; + mac_addr.addr[4] = 0x00; + mac_addr.addr[5] = 0x01; + return mac_addr; +} + + class Linux_session_component : public Nic::Session_component { private: @@ -231,15 +249,10 @@ class Linux_session_component : public Nic::Session_component : Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env), _config_rom(env, "config"), - _tap_fd(_setup_tap_fd()), _rx_thread(env, _tap_fd, _packet_stream_dispatcher) + _tap_fd(_setup_tap_fd()), + _rx_thread(env, _tap_fd, _packet_stream_dispatcher) { - /* fall back to fake MAC address (unicast, locally managed) */ - _mac_addr.addr[0] = 0x02; - _mac_addr.addr[1] = 0x00; - _mac_addr.addr[2] = 0x00; - _mac_addr.addr[3] = 0x00; - _mac_addr.addr[4] = 0x00; - _mac_addr.addr[5] = 0x01; + _mac_addr = default_mac_address(); /* try using configured MAC address */ try { @@ -256,16 +269,198 @@ class Linux_session_component : public Nic::Session_component }; +class Uplink_client : public Genode::Uplink_client_base +{ + private: + + struct Rx_signal_thread : Genode::Thread + { + int fd; + Genode::Signal_context_capability sigh; + Genode::Blockade blockade { }; + + Rx_signal_thread(Genode::Env &env, int fd, Genode::Signal_context_capability sigh) + : Genode::Thread(env, "rx_signal", 0x1000), fd(fd), sigh(sigh) { } + + void entry() override + { + while (true) { + /* wait for packet arrival on fd */ + int ret; + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + do { ret = select(fd + 1, &rfds, 0, 0, 0); } while (ret < 0); + + /* signal incoming packet */ + Genode::Signal_transmitter(sigh).submit(); + + blockade.block(); + } + } + }; + + int _tap_fd; + Genode::Signal_handler _rx_handler { _env.ep(), *this, &Uplink_client::_handle_rx }; + Rx_signal_thread _rx_thread { _env, _tap_fd, _rx_handler }; + + static int _init_tap_fd(Genode::Xml_node const &config) + { + /* open TAP device */ + int ret; + struct ifreq ifr; + + int fd = open("/dev/net/tun", O_RDWR); + if (fd < 0) { + Genode::error("could not open /dev/net/tun: no virtual network emulation"); + throw Genode::Exception(); + } + + /* set fd to non-blocking */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + Genode::error("could not set /dev/net/tun to non-blocking"); + throw Genode::Exception(); + } + + Genode::memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + /* get tap device from config */ + try { + Genode::Xml_node nic_node = config.sub_node("nic"); + nic_node.attribute("tap").with_raw_value([&] (char const *ptr, size_t len) { + len = Genode::min(len, sizeof(ifr.ifr_name) - 1); + Genode::memcpy(ifr.ifr_name, ptr, len); + ifr.ifr_name[len] = 0; + }); + Genode::log("using tap device \"", Genode::Cstring(ifr.ifr_name), "\""); + } catch (...) { + /* use tap0 if no config has been provided */ + Genode::copy_cstring(ifr.ifr_name, "tap0", sizeof(ifr.ifr_name)); + Genode::log("no config provided, using tap0"); + } + + ret = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (ret != 0) { + Genode::error("could not configure /dev/net/tun: no virtual network emulation"); + close(fd); + /* this error is fatal */ + throw Genode::Exception(); + } + + return fd; + } + + static Net::Mac_address + _init_mac_address(Genode::Xml_node const &config) + { + try { + return + config.sub_node("nic"). + attribute_value("mac", default_mac_address()); + + } catch (...) { + + return default_mac_address(); + } + } + + void _handle_rx() + { + bool progress { true }; + while (progress) { + + progress = false; + size_t const max_pkt_size { + Nic::Packet_allocator::DEFAULT_PACKET_SIZE }; + + _drv_rx_handle_pkt( + max_pkt_size, + [&] (void *conn_tx_pkt_base, + size_t &adjusted_conn_tx_pkt_size) + { + long int const read_result { + read(_tap_fd, conn_tx_pkt_base, max_pkt_size) }; + + if (read_result <= 0) { + + _rx_thread.blockade.wakeup(); + return Write_result::WRITE_FAILED; + } + adjusted_conn_tx_pkt_size = read_result; + progress = true; + return Write_result::WRITE_SUCCEEDED; + }); + } + } + + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + int ret; + + /* non-blocking-write packet to TAP */ + do { + ret = write(_tap_fd, conn_rx_pkt_base, conn_rx_pkt_size); + /* drop packet if write would block */ + if (ret < 0 && errno == EAGAIN) + continue; + + if (ret < 0) Genode::error("write: errno=", errno); + } while (ret < 0); + + return Transmit_result::ACCEPTED; + } + + public: + + Uplink_client(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Xml_node const &config) + : + Uplink_client_base { + env, alloc, _init_mac_address(config) }, + + _tap_fd { _init_tap_fd(config) } + { + _drv_handle_link_state(true); + _rx_thread.start(); + } +}; + + struct Server::Main { - Env &_env; - Heap _heap { _env.ram(), _env.rm() }; - - Nic::Root nic_root { _env, _heap }; + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Genode::Attached_rom_dataspace _config_rom { _env, "config" }; Main(Env &env) : _env(env) { - _env.parent().announce(_env.ep().manage(nic_root)); + Nic_driver_mode const mode { + read_nic_driver_mode(_config_rom.xml()) }; + + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + { + Nic::Root &nic_root { + *new (_heap) Nic::Root(_env, _heap) }; + + _env.parent().announce(_env.ep().manage(nic_root)); + break; + } + case Nic_driver_mode::UPLINK_CLIENT: + + new (_heap) Uplink_client(_env, _heap, _config_rom.xml()); + break; + } } }; diff --git a/repos/os/src/drivers/nic/spec/linux/target.mk b/repos/os/src/drivers/nic/spec/linux/target.mk index 6d3ce2b422..b95e73002f 100644 --- a/repos/os/src/drivers/nic/spec/linux/target.mk +++ b/repos/os/src/drivers/nic/spec/linux/target.mk @@ -1,4 +1,4 @@ TARGET = linux_nic_drv REQUIRES = linux -LIBS = lx_hybrid +LIBS = lx_hybrid nic_driver SRC_CC = main.cc diff --git a/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h b/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h index bff42e275c..525df294fb 100644 --- a/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h +++ b/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h @@ -22,6 +22,9 @@ #include #include +/* NIC driver includes */ +#include + /* local includes */ #include "system_control.h" #include "tx_buffer_descriptor.h" @@ -30,16 +33,13 @@ namespace Genode { - /** - * Base driver Xilinx EMAC PS module - */ - class Cadence_gem + class Cadence_gem_base : - private Genode::Attached_mmio, - public Nic::Session_component, + protected Genode::Attached_mmio, public Phyio { private: + /** * Control register */ @@ -96,8 +96,6 @@ namespace Genode struct Phy_mgmt_idle : Bitfield<2, 1> {}; }; - - /** * DMA Config register */ @@ -349,119 +347,13 @@ namespace Genode class Phy_timeout_for_idle : public Genode::Exception {}; class Unkown_ethernet_speed : public Genode::Exception {}; - - Timer::Connection _timer; - System_control _sys_ctrl; - Tx_buffer_descriptor _tx_buffer; - Rx_buffer_descriptor _rx_buffer; - Genode::Irq_connection _irq; - Genode::Signal_handler _irq_handler; - Marvel_phy _phy; - - addr_t _rx_buf_region; - addr_t _tx_buf_region; - size_t _rx_buf_size; - size_t _tx_buf_size; - - void _init() - { - /* see 16.3.2 Configure the Controller */ - - /* 1. Program the Network Configuration register (gem.net_cfg) */ - write( - Config::Gige_en::bits(1) | - Config::Speed_100::bits(1) | - Config::Pause_en::bits(1) | - Config::Full_duplex::bits(1) | - Config::Multi_hash_en::bits(1) | - Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) | - Config::Dis_cp_pause::bits(1) | - Config::Rx_chksum_en::bits(1) | - Config::Fcs_remove::bits(1) - ); - - - write( _rx_buffer.phys_addr() ); - write( _tx_buffer.phys_addr() ); - - - /* 3. Program the DMA Configuration register (gem.dma_cfg) */ - write( Dma_config::init() ); - - - /* - * 4. Program the Network Control Register (gem.net_ctrl) - * Enable MDIO, transmitter and receiver - */ - write(Control::init()); - - - - _phy.init(); - - /* change emac clocks depending on phy autonegation result */ - uint32_t rclk = 0; - uint32_t clk = 0; - switch (_phy.eth_speed()) { - case SPEED_1000: - write(1); - rclk = (0 << 4) | (1 << 0); - clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0); - log("Autonegotiation result: 1Gbit/s"); - break; - case SPEED_100: - write(0); - write(1); - rclk = 1 << 0; - clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); - log("Autonegotiation result: 100Mbit/s"); - break; - case SPEED_10: - write(0); - write(0); - rclk = 1 << 0; - /* FIXME untested */ - clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); - log("Autonegotiation result: 10Mbit/s"); - break; - default: - throw Unkown_ethernet_speed(); - } - _sys_ctrl.set_clk(clk, rclk); - - - /* 16.3.6 Configure Interrupts */ - write(Interrupt_enable::Rx_complete::bits(1) | - Interrupt_enable::Rx_overrun::bits(1) | - Interrupt_enable::Pause_received::bits(1) | - Interrupt_enable::Pause_zero::bits(1) | - Interrupt_enable::Rx_used_read::bits(1)); - } - - void _deinit() - { - /* 16.3.1 Initialize the Controller */ - - /* Disable all interrupts */ - write(0x7FFFEFF); - - /* Disable the receiver & transmitter */ - write(0); - write(Control::Clear_statistics::bits(1)); - - write(0xFF); - write(0x0F); - write(0); - - write(0); - write(0); - - /* Clear the Hash registers for the mac address - * pointed by AddressPtr - */ - write(0); - } - + Timer::Connection _timer; + System_control _sys_ctrl; + Genode::Irq_connection _irq; + Marvel_phy _phy; + Tx_buffer_sink &_tx_buffer_sink; + Tx_buffer_descriptor _tx_buffer; + Rx_buffer_descriptor _rx_buffer; void _mdio_wait() { @@ -479,7 +371,6 @@ namespace Genode } } - void _phy_setup_op(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data, const Phy_maintenance::Operation::Type op) { _mdio_wait(); @@ -496,15 +387,69 @@ namespace Genode _mdio_wait(); } - inline void _handle_acks() + + /*********** + ** Phyio ** + ***********/ + + void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data) override { - while (_rx.source()->ack_avail()) { - Nic::Packet_descriptor p = _rx.source()->get_acked_packet(); - _rx_buffer.reset_descriptor(p); - } + _phy_setup_op(phyaddr, regnum, data, Phy_maintenance::Operation::WRITE); } - virtual void _handle_irq() + void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t& data) override + { + _phy_setup_op(phyaddr, regnum, 0, Phy_maintenance::Operation::READ); + + data = read(); + } + + public: + + /** + * Constructor + * + * \param base MMIO base address + */ + Cadence_gem_base(Genode::Env &env, + addr_t const base, + size_t const size, + int const irq, + Tx_buffer_sink &tx_buffer_sink, + Rx_buffer_source &rx_buffer_source) + : + Genode::Attached_mmio(env, base, size), + _timer(env), + _sys_ctrl(env, _timer), + _irq(env, irq), + _phy(*this, _timer), + _tx_buffer_sink(tx_buffer_sink), + _tx_buffer(env, tx_buffer_sink, _timer), + _rx_buffer(env, rx_buffer_source) + { } + + void transmit_packet(Packet_descriptor packet) + { + _tx_buffer.add_to_queue(packet); + write(Control::start_tx()); + } + + Nic::Mac_address read_mac_address() + { + Nic::Mac_address mac; + uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]); + uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]); + + *low_addr_pointer = read(); + *high_addr_pointer = read(); + + return mac; + } + + template + void handle_irq(RECEIVE_PKT && receive_pkt, + HANDLE_ACKS && handle_acks) { /* 16.3.9 Receiving Frames */ /* read interrupt status, to detect the interrupt reason */ @@ -513,16 +458,11 @@ namespace Genode const Tx_status::access_t txStatus = read(); if ( Interrupt_status::Rx_complete::get(status) ) { + while (_rx_buffer.next_packet()) { - _handle_acks(); - - Nic::Packet_descriptor p = _rx_buffer.get_packet_descriptor(); - if (_rx.source()->packet_valid(p)) - _rx.source()->submit_packet(p); - else - Genode::error("invalid packet descriptor ", Genode::Hex(p.offset()), - " size ", Genode::Hex(p.size())); + handle_acks(); + receive_pkt(_rx_buffer.get_packet_descriptor()); } /* reset receive complete interrupt */ @@ -530,7 +470,7 @@ namespace Genode write(Interrupt_status::Rx_complete::bits(1)); } else { - _handle_acks(); + handle_acks(); } /* handle Rx/Tx errors */ @@ -540,7 +480,7 @@ namespace Genode write(0); write(0); - _tx_buffer.reset(*_tx.sink()); + _tx_buffer.reset(_tx_buffer_sink); _rx_buffer.reset(); write(1); @@ -556,7 +496,7 @@ namespace Genode || Tx_status::Tx_err_bufexh::get(txStatus)) { write(0); - _tx_buffer.reset(*_tx.sink()); + _tx_buffer.reset(_tx_buffer_sink); write(1); Genode::error("Tx error: resetting transceiver"); @@ -632,63 +572,109 @@ namespace Genode _irq.ack_irq(); } - public: - /** - * Constructor - * - * \param base MMIO base address - */ - Cadence_gem(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator &rx_block_md_alloc, - Genode::Env &env, - addr_t const base, size_t const size, const int irq) - : - Genode::Attached_mmio(env, base, size), - Session_component(tx_buf_size, rx_buf_size, Genode::UNCACHED, - rx_block_md_alloc, env), - _timer(env), - _sys_ctrl(env, _timer), - _tx_buffer(env, *_tx.sink(), _timer), - _rx_buffer(env, *_rx.source()), - _irq(env, irq), - _irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq), - _phy(*this, _timer), - _rx_buf_region((addr_t)_tx_ds.local_addr()), - _tx_buf_region((addr_t)_rx_ds.local_addr()), - _rx_buf_size(_tx_ds.size()), - _tx_buf_size(_rx_ds.size()) + void init(Signal_context_capability irq_handler) { - _irq.sigh(_irq_handler); + _irq.sigh(irq_handler); _irq.ack_irq(); - _deinit(); - _init(); + + /* see 16.3.2 Configure the Controller */ + + /* 1. Program the Network Configuration register (gem.net_cfg) */ + write( + Config::Gige_en::bits(1) | + Config::Speed_100::bits(1) | + Config::Pause_en::bits(1) | + Config::Full_duplex::bits(1) | + Config::Multi_hash_en::bits(1) | + Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) | + Config::Dis_cp_pause::bits(1) | + Config::Rx_chksum_en::bits(1) | + Config::Fcs_remove::bits(1) + ); + + + write(_rx_buffer.phys_addr()); + write(_tx_buffer.phys_addr()); + + + /* 3. Program the DMA Configuration register (gem.dma_cfg) */ + write( Dma_config::init() ); + + + /* + * 4. Program the Network Control Register (gem.net_ctrl) + * Enable MDIO, transmitter and receiver + */ + write(Control::init()); + + + + _phy.init(); + + /* change emac clocks depending on phy autonegation result */ + uint32_t rclk = 0; + uint32_t clk = 0; + switch (_phy.eth_speed()) { + case SPEED_1000: + write(1); + rclk = (0 << 4) | (1 << 0); + clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0); + log("Autonegotiation result: 1Gbit/s"); + break; + case SPEED_100: + write(0); + write(1); + rclk = 1 << 0; + clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); + log("Autonegotiation result: 100Mbit/s"); + break; + case SPEED_10: + write(0); + write(0); + rclk = 1 << 0; + /* FIXME untested */ + clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); + log("Autonegotiation result: 10Mbit/s"); + break; + default: + throw Unkown_ethernet_speed(); + } + _sys_ctrl.set_clk(clk, rclk); + + + /* 16.3.6 Configure Interrupts */ + write(Interrupt_enable::Rx_complete::bits(1) | + Interrupt_enable::Rx_overrun::bits(1) | + Interrupt_enable::Pause_received::bits(1) | + Interrupt_enable::Pause_zero::bits(1) | + Interrupt_enable::Rx_used_read::bits(1)); } - ~Cadence_gem() + void deinit() { - // TODO dsiable tx and rx and clean up irq registration - _deinit(); + /* 16.3.1 Initialize the Controller */ + + /* Disable all interrupts */ + write(0x7FFFEFF); + + /* Disable the receiver & transmitter */ + write(0); + write(Control::Clear_statistics::bits(1)); + + write(0xFF); + write(0x0F); + write(0); + + write(0); + write(0); + + /* Clear the Hash registers for the mac address + * pointed by AddressPtr + */ + write(0); } - using Nic::Session_component::cap; - - - void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data) override - { - _phy_setup_op(phyaddr, regnum, data, Phy_maintenance::Operation::WRITE); - } - - - void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t& data) override - { - _phy_setup_op(phyaddr, regnum, 0, Phy_maintenance::Operation::READ); - - data = read(); - } - - - void mac_address(const Nic::Mac_address &mac) + void write_mac_address(const Nic::Mac_address &mac) { const uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]); const uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]); @@ -697,12 +683,158 @@ namespace Genode write(*high_addr_pointer); } + void rx_buffer_reset_pkt(Nic::Packet_descriptor pkt) + { + _rx_buffer.reset_descriptor(pkt); + } + + void tx_buffer_submit_acks() + { + _tx_buffer.submit_acks(_tx_buffer_sink); + } + }; + + class Nic_rx_buffer_source : public Rx_buffer_source + { + private: + + Nic::Session::Tx::Source &_source; + + public: + + Nic_rx_buffer_source(Nic::Session::Tx::Source &source) + : + _source { source } + { } + + + /********************** + ** Rx_buffer_source ** + **********************/ + + Dataspace_capability dataspace() override + { + return _source.dataspace(); + }; + + Packet_descriptor alloc_packet(size_t size) override + { + return _source.alloc_packet(size); + } + }; + + class Nic_tx_buffer_sink : public Tx_buffer_sink + { + private: + + Nic::Session::Rx::Sink &_sink; + + public: + + Nic_tx_buffer_sink(Nic::Session::Rx::Sink &sink) + : + _sink { sink } + { } + + + /******************** + ** Tx_buffer_sink ** + ********************/ + + Dataspace_capability dataspace() override + { + return _sink.dataspace(); + }; + + void acknowledge_packet(Packet_descriptor packet) override + { + _sink.acknowledge_packet(packet); + } + + bool packet_valid(Packet_descriptor packet) override + { + return _sink.packet_valid(packet); + } + }; + + /** + * Base driver Xilinx EMAC PS module + */ + class Cadence_gem : public Nic::Session_component + { + private: + + Nic_rx_buffer_source _rx_buffer_source; + Nic_tx_buffer_sink _tx_buffer_sink; + Cadence_gem_base _cadence_gem; + Signal_handler _irq_handler; + + void _handle_acks() + { + while (_rx.source()->ack_avail()) { + _cadence_gem.rx_buffer_reset_pkt( + _rx.source()->get_acked_packet()); + } + } + + void _handle_irq() + { + _cadence_gem.handle_irq( + [&] (Nic::Packet_descriptor pkt) + { + if (_rx.source()->packet_valid(pkt)) + _rx.source()->submit_packet(pkt); + else + error( + "invalid packet descriptor ", Hex(pkt.offset()), + " size ", Hex(pkt.size())); + }, + [&] () + { + _handle_acks(); + }); + } + + public: + + /** + * Constructor + * + * \param base MMIO base address + */ + Cadence_gem(size_t const tx_buf_size, + size_t const rx_buf_size, + Allocator &rx_block_md_alloc, + Env &env, + addr_t const base, + size_t const size, + int const irq) + : + Session_component(tx_buf_size, rx_buf_size, Genode::UNCACHED, + rx_block_md_alloc, env), + _rx_buffer_source(*_rx.source()), + _tx_buffer_sink(*_tx.sink()), + _cadence_gem(env, base, size, irq, _tx_buffer_sink, _rx_buffer_source), + _irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq) + { + _cadence_gem.deinit(); + _cadence_gem.init(_irq_handler); + } + + ~Cadence_gem() + { + // TODO disable tx and rx and clean up irq registration + _cadence_gem.deinit(); + } + + using Nic::Session_component::cap; + bool _send() { /* first, see whether we can acknowledge any * previously sent packet */ - _tx_buffer.submit_acks(*_tx.sink()); + _cadence_gem.tx_buffer_submit_acks(); if (!_tx.sink()->ready_to_ack()) return false; @@ -710,15 +842,14 @@ namespace Genode if (!_tx.sink()->packet_avail()) return false; - Genode::Packet_descriptor packet = _tx.sink()->get_packet(); + Packet_descriptor packet = _tx.sink()->get_packet(); if (!packet.size()) { Genode::warning("Invalid tx packet"); return true; } try { - _tx_buffer.add_to_queue(packet); - write(Control::start_tx()); + _cadence_gem.transmit_packet(packet); } catch (Tx_buffer_descriptor::Package_send_timeout) { Genode::warning("Package Tx timeout"); return false; @@ -734,14 +865,7 @@ namespace Genode virtual Nic::Mac_address mac_address() override { - Nic::Mac_address mac; - uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]); - uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]); - - *low_addr_pointer = read(); - *high_addr_pointer = read(); - - return mac; + return _cadence_gem.read_mac_address(); } virtual bool link_state() override @@ -756,8 +880,203 @@ namespace Genode while (_send()); } + + void mac_address(const Nic::Mac_address &mac) + { + _cadence_gem.write_mac_address(mac); + } }; } + +namespace Genode { + + class Uplink_client; + + class Uplink_rx_buffer_source : public Rx_buffer_source + { + private: + + Uplink::Session::Tx::Source &_source; + + public: + + Uplink_rx_buffer_source(Uplink::Session::Tx::Source &source) + : + _source { source } + { } + + + /********************** + ** Rx_buffer_source ** + **********************/ + + Dataspace_capability dataspace() override + { + return _source.dataspace(); + }; + + Packet_descriptor alloc_packet(size_t size) override + { + return _source.alloc_packet(size); + } + }; + + class Uplink_tx_buffer_sink : public Tx_buffer_sink + { + private: + + Uplink::Session::Rx::Sink &_sink; + + public: + + Uplink_tx_buffer_sink(Uplink::Session::Rx::Sink &sink) + : + _sink { sink } + { } + + + /******************** + ** Tx_buffer_sink ** + ********************/ + + Dataspace_capability dataspace() override + { + return _sink.dataspace(); + }; + + void acknowledge_packet(Packet_descriptor packet) override + { + _sink.acknowledge_packet(packet); + } + + bool packet_valid(Packet_descriptor packet) override + { + return _sink.packet_valid(packet); + } + }; +} + + +class Genode::Uplink_client : public Uplink_client_base +{ + private: + + Signal_handler _irq_handler; + Constructible _rx_buffer_source { }; + Constructible _tx_buffer_sink { }; + Constructible _cadence_gem { }; + + bool _send() + { + /* first, see whether we can acknowledge any + * previously sent packet */ + _cadence_gem->tx_buffer_submit_acks(); + + if (!_conn->rx()->ready_to_ack()) + return false; + + if (!_conn->rx()->packet_avail()) + return false; + + Packet_descriptor packet = _conn->rx()->get_packet(); + if (!packet.size()) { + Genode::warning("Invalid tx packet"); + return true; + } + + try { + _cadence_gem->transmit_packet(packet); + } catch (Tx_buffer_descriptor::Package_send_timeout) { + Genode::warning("Package Tx timeout"); + return false; + } + + return true; + } + + void _handle_acks() + { + while (_conn->tx()->ack_avail()) { + + _cadence_gem->rx_buffer_reset_pkt( + _conn->tx()->get_acked_packet()); + } + } + + void _handle_irq() + { + if (!_conn.constructed()) { + + class No_connection { }; + throw No_connection { }; + } + _cadence_gem->handle_irq( + [&] (Nic::Packet_descriptor pkt) + { + if (_conn->tx()->packet_valid(pkt)) + _conn->tx()->submit_packet(pkt); + else + error( + "invalid packet descriptor ", Hex(pkt.offset()), + " size ", Hex(pkt.size())); + }, + [&] () + { + _handle_acks(); + }); + } + + + /************************ + ** Uplink_client_base ** + ************************/ + + void _custom_conn_rx_handle_packet_avail() override + { + _handle_acks(); + + while (_send()); + } + + bool _custom_conn_rx_packet_avail_handler() override + { + return true; + } + + Transmit_result + _drv_transmit_pkt(const char *, + size_t ) override + { + class Unexpected_call { }; + throw Unexpected_call { }; + } + + public: + + Uplink_client(Env &env, + Allocator &alloc, + addr_t const base, + size_t const size, + int const irq, + Net::Mac_address const mac_addr) + : + Uplink_client_base { env, alloc, mac_addr }, + _irq_handler { env.ep(), *this, &Uplink_client::_handle_irq } + { + _drv_handle_link_state(true); + _rx_buffer_source.construct(*_conn->tx()); + _tx_buffer_sink.construct(*_conn->rx()), + _cadence_gem.construct( + env, base, size, irq, *_tx_buffer_sink, *_rx_buffer_source); + + _cadence_gem->deinit(); + _cadence_gem->init(_irq_handler); + + /* set mac address */ + _cadence_gem->write_mac_address(mac_addr); + } +}; + #endif /* _INCLUDE__DRIVERS__NIC__XILINX_EMACPS_BASE_H_ */ diff --git a/repos/os/src/drivers/nic/spec/zynq/main.cc b/repos/os/src/drivers/nic/spec/zynq/main.cc index 04d2b35279..4dafe9abd7 100644 --- a/repos/os/src/drivers/nic/spec/zynq/main.cc +++ b/repos/os/src/drivers/nic/spec/zynq/main.cc @@ -18,6 +18,10 @@ #include #include +/* NIC driver includes */ +#include + +/* local includes */ #include "cadence_gem.h" namespace Server { @@ -27,6 +31,31 @@ namespace Server { struct Main; } + +Nic::Mac_address +read_mac_addr_from_config(Genode::Attached_rom_dataspace &config_rom) +{ + Nic::Mac_address mac_addr; + + /* fall back to fake MAC address (unicast, locally managed) */ + mac_addr.addr[0] = 0x02; + mac_addr.addr[1] = 0x00; + mac_addr.addr[2] = 0x00; + mac_addr.addr[3] = 0x00; + mac_addr.addr[4] = 0x00; + mac_addr.addr[5] = 0x01; + + /* try using configured MAC address */ + try { + Genode::Xml_node nic_config = config_rom.xml().sub_node("nic"); + mac_addr = nic_config.attribute_value("mac", mac_addr); + Genode::log("Using configured MAC address ", mac_addr); + } catch (...) { } + + return mac_addr; +} + + class Server::Gem_session_component : public Cadence_gem { private: @@ -46,39 +75,39 @@ class Server::Gem_session_component : public Cadence_gem Zynq::EMAC_0_IRQ), _config_rom(env, "config") { - Nic::Mac_address mac_addr; - - /* fall back to fake MAC address (unicast, locally managed) */ - mac_addr.addr[0] = 0x02; - mac_addr.addr[1] = 0x00; - mac_addr.addr[2] = 0x00; - mac_addr.addr[3] = 0x00; - mac_addr.addr[4] = 0x00; - mac_addr.addr[5] = 0x01; - - /* try using configured MAC address */ - try { - Genode::Xml_node nic_config = _config_rom.xml().sub_node("nic"); - mac_addr = nic_config.attribute_value("mac", mac_addr); - Genode::log("Using configured MAC address ", mac_addr); - } catch (...) { } - - /* set mac address */ - mac_address(mac_addr); + mac_address(read_mac_addr_from_config(_config_rom)); } }; struct Server::Main { - Env &_env; - Heap _heap { _env.ram(), _env.rm() }; - - Nic::Root nic_root{ _env, _heap }; + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Constructible > _nic_root { }; + Constructible _uplink_client { }; Main(Env &env) : _env(env) { - _env.parent().announce(_env.ep().manage(nic_root)); + Attached_rom_dataspace config_rom { _env, "config" }; + Nic_driver_mode const mode { + read_nic_driver_mode(config_rom.xml()) }; + + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + + _nic_root.construct(_env, _heap ); + _env.parent().announce(_env.ep().manage(*_nic_root)); + break; + + case Nic_driver_mode::UPLINK_CLIENT: + + _uplink_client.construct( + _env, _heap, Zynq::EMAC_0_MMIO_BASE, Zynq::EMAC_0_MMIO_SIZE, + Zynq::EMAC_0_IRQ, read_mac_addr_from_config(config_rom)); + + break; + } } }; diff --git a/repos/os/src/drivers/nic/spec/zynq/rx_buffer_descriptor.h b/repos/os/src/drivers/nic/spec/zynq/rx_buffer_descriptor.h index f2f6cf26db..a0e74bcac5 100644 --- a/repos/os/src/drivers/nic/spec/zynq/rx_buffer_descriptor.h +++ b/repos/os/src/drivers/nic/spec/zynq/rx_buffer_descriptor.h @@ -20,6 +20,16 @@ using namespace Genode; +struct Rx_buffer_source +{ + virtual ~Rx_buffer_source() { } + + virtual Dataspace_capability dataspace() = 0; + + virtual Packet_descriptor alloc_packet(size_t size) = 0; +}; + + class Rx_buffer_descriptor : public Buffer_descriptor { private: @@ -61,8 +71,8 @@ class Rx_buffer_descriptor : public Buffer_descriptor } public: - Rx_buffer_descriptor(Genode::Env &env, - Nic::Session::Tx::Source &source) + Rx_buffer_descriptor(Genode::Env &env, + Rx_buffer_source &source) : Buffer_descriptor(env, MAX_BUFFER_COUNT), _phys_base(Dataspace_client(source.dataspace()).phys_addr()) { diff --git a/repos/os/src/drivers/nic/spec/zynq/target.mk b/repos/os/src/drivers/nic/spec/zynq/target.mk index ebdd6b89a6..5e06cdf984 100644 --- a/repos/os/src/drivers/nic/spec/zynq/target.mk +++ b/repos/os/src/drivers/nic/spec/zynq/target.mk @@ -1,5 +1,5 @@ REQUIRES = arm_v7 TARGET = zynq_nic_drv SRC_CC = main.cc -LIBS = base +LIBS = base nic_driver INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/drivers/nic/spec/zynq/tx_buffer_descriptor.h b/repos/os/src/drivers/nic/spec/zynq/tx_buffer_descriptor.h index 788ca4fdc9..407f365e59 100644 --- a/repos/os/src/drivers/nic/spec/zynq/tx_buffer_descriptor.h +++ b/repos/os/src/drivers/nic/spec/zynq/tx_buffer_descriptor.h @@ -23,6 +23,18 @@ using namespace Genode; +struct Tx_buffer_sink +{ + virtual ~Tx_buffer_sink() { } + + virtual Dataspace_capability dataspace() = 0; + + virtual void acknowledge_packet(Packet_descriptor packet) = 0; + + virtual bool packet_valid(Packet_descriptor packet) = 0; +}; + + class Tx_buffer_descriptor : public Buffer_descriptor { private: @@ -66,7 +78,7 @@ class Tx_buffer_descriptor : public Buffer_descriptor class Package_send_timeout : public Genode::Exception {}; Tx_buffer_descriptor(Genode::Env &env, - Nic::Session::Rx::Sink &sink, + Tx_buffer_sink &sink, Timer::Connection &timer) : Buffer_descriptor(env, BUFFER_COUNT), _timer(timer), _phys_base(Dataspace_client(sink.dataspace()).phys_addr()) @@ -78,7 +90,7 @@ class Tx_buffer_descriptor : public Buffer_descriptor } } - void reset(Nic::Session::Rx::Sink &sink) + void reset(Tx_buffer_sink &sink) { /* ack all packets that are still queued */ submit_acks(sink, true); @@ -88,7 +100,7 @@ class Tx_buffer_descriptor : public Buffer_descriptor _reset_tail(); } - void submit_acks(Nic::Session::Rx::Sink &sink, bool force=false) + void submit_acks(Tx_buffer_sink &sink, bool force=false) { /* the tail marks the descriptor for which we wait to * be handed over to software */ diff --git a/repos/os/src/drivers/nic/virtio/component.h b/repos/os/src/drivers/nic/virtio/component.h index 1f1b53e55f..cffa11cee2 100644 --- a/repos/os/src/drivers/nic/virtio/component.h +++ b/repos/os/src/drivers/nic/virtio/component.h @@ -11,7 +11,7 @@ * under the terms of the GNU Affero General Public License version 3. */ -/* Need to come before attached_rom_dataspace.h */ +/* Genode includes */ #include #include #include @@ -23,35 +23,23 @@ #include #include #include +#include + +/* NIC driver includes */ +#include namespace Virtio_nic { using namespace Genode; struct Main; class Root; class Session_component; + class Device; } -class Virtio_nic::Session_component : public Nic::Session_component +class Virtio_nic::Device : Noncopyable { - private: - - /* - * Noncopyable - */ - Session_component(Session_component const &); - Session_component &operator = (Session_component const &); - - struct Unsupported_version : Genode::Exception { }; - struct Device_init_failed : Genode::Exception { }; - struct Features_init_failed : Genode::Exception { }; - struct Queue_init_failed : Genode::Exception { }; - - struct Hardware_features - { - Nic::Mac_address mac = { }; - bool link_status_available = false; - }; + public: /** * See section 5.1.6 of VirtIO 1.0 specification. @@ -81,6 +69,19 @@ class Virtio_nic::Session_component : public Nic::Session_component uint16_t num_buffers = 0; }; + private: + + struct Unsupported_version : Genode::Exception { }; + struct Device_init_failed : Genode::Exception { }; + struct Features_init_failed : Genode::Exception { }; + struct Queue_init_failed : Genode::Exception { }; + + struct Hardware_features + { + Nic::Mac_address mac = { }; + bool link_status_available = false; + }; + /** * VirtIO feature bits relevant to this VirtIO net driver implementation. */ @@ -147,8 +148,6 @@ class Virtio_nic::Session_component : public Nic::Session_component typedef Virtio::Queue Rx_queue_type; typedef Virtio::Queue Tx_queue_type; - typedef Genode::Signal_handler Signal_handler; - bool const _verbose; Virtio::Device &_device; @@ -156,9 +155,6 @@ class Virtio_nic::Session_component : public Nic::Session_component Rx_queue_type _rx_vq; Tx_queue_type _tx_vq; Irq_session_client _irq; - Signal_handler _irq_handler; - bool _link_up = false; - void _init_virtio_device() { @@ -294,7 +290,46 @@ class Virtio_nic::Session_component : public Nic::Session_component } } - void _handle_irq() + public: + + Device(Genode::Env &env, + Virtio::Device &device, + Irq_session_capability irq_cap, + Genode::Xml_node const &xml) + try : + _verbose { xml.attribute_value("verbose", false) }, + _device { device }, + _hw_features { _init_hw_features(xml) }, + _rx_vq { env.ram(), env.rm(), + _vq_size(RX_VQ, xml, "rx_queue_size"), + _buf_size(RX_VQ, xml, "rx_buffer_size") }, + _tx_vq { env.ram(), env.rm(), + _vq_size(TX_VQ, xml, "tx_queue_size"), + _buf_size(TX_VQ, xml, "tx_buffer_size") }, + _irq { irq_cap } + { } + catch (Tx_queue_type::Invalid_buffer_size) + { + error("Invalid TX VirtIO queue buffer size specified!"); + throw; + } + catch (Rx_queue_type::Invalid_buffer_size) + { + error("Invalid RX VirtIO queue buffer size specified!"); + throw; + } + + virtual ~Device() + { + _device.set_status(Virtio::Device::Status::RESET); + } + + bool verbose() { return _verbose; } + + template + void drv_handle_irq(HANDLE_RX && handle_rx, + HANDLE_LINK_STATE && handle_link_state) { const uint32_t reasons = _device.read_isr(); @@ -304,72 +339,27 @@ class Virtio_nic::Session_component : public Nic::Session_component _tx_vq.ack_all_transfers(); if (reasons & IRQ_USED_RING_UPDATE) { - _receive(); + handle_rx(); } - if ((reasons & IRQ_CONFIG_CHANGE) && _hw_features.link_status_available && - (link_state() != _link_up)) { - _link_up = !_link_up; - if (_verbose) - log("Link status changed: ", (_link_up ? "on-line" : "off-line")); - _link_state_changed(); + if ((reasons & IRQ_CONFIG_CHANGE) && _hw_features.link_status_available) { + handle_link_state(); } - _irq.ack_irq(); } - bool _send() + bool tx_vq_write_pkt(char const *pkt_base, + Genode::size_t pkt_size) { - if (!_tx.sink()->ready_to_ack()) - return false; - - if (!_tx.sink()->packet_avail()) - return false; - - auto packet = _tx.sink()->get_packet(); - if (!packet.size() || !_tx.sink()->packet_valid(packet)) { - warning("Invalid tx packet"); - return true; - } - - if (link_state()) { - Virtio_net_header hdr; - auto const *data = _tx.sink()->packet_content(packet); - if (!_tx_vq.write_data(hdr, data, packet.size(), false)) { - warning("Failed to push packet into tx VirtIO queue!"); - return false; - } - } - - _tx.sink()->acknowledge_packet(packet); - return true; + Virtio_net_header hdr; + return _tx_vq.write_data(hdr, pkt_base, pkt_size, false); } - void _receive() + template + void rx_vq_read_pkt(RECEIVE_PKT && rcv_pkt) { - auto rcv_func = [&] (Virtio_net_header const &, - char const *data, - size_t size) { - if (!_rx.source()->ready_to_submit()) { - Genode::warning("Not ready to submit!"); - return false; - } - - try { - auto p = _rx.source()->alloc_packet(size); - char *dst = _rx.source()->packet_content(p); - Genode::memcpy(dst, data, size); - _rx.source()->submit_packet(p); - } catch (Session::Rx::Source::Packet_alloc_failed) { - Genode::warning("Packet alloc failed!"); - return false; - } - - return true; - }; - while (_rx_vq.has_used_buffers()) - _rx_vq.read_data(rcv_func); + _rx_vq.read_data(rcv_pkt); /** * Inform the device the buffers we've just consumed are ready @@ -378,27 +368,19 @@ class Virtio_nic::Session_component : public Nic::Session_component _device.notify_buffers_available(RX_VQ); } - void _handle_packet_stream() override + void _finish_sent_packets() { - while (_rx.source()->ack_avail()) - _rx.source()->release_packet(_rx.source()->get_acked_packet()); + _device.notify_buffers_available(TX_VQ); + } + void rx_vq_ack_pkts() + { /* Reclaim all buffers processed by the device. */ if (_tx_vq.has_used_buffers()) _tx_vq.ack_all_transfers(); - - bool sent_packets = false; - while (_send()) - sent_packets = true; - - if (sent_packets) { - _device.notify_buffers_available(TX_VQ); - } } - public: - - bool link_state() override + bool read_link_state() { /** * According to docs when STATUS feature is not available or has not @@ -419,7 +401,129 @@ class Virtio_nic::Session_component : public Nic::Session_component return status & STATUS_LINK_UP; } - Nic::Mac_address mac_address() override { return _hw_features.mac; } + Nic::Mac_address const &read_mac_address() const + { + return _hw_features.mac; + } + + void init(Genode::Signal_context_capability irq_handler) + { + _setup_virtio_queues(); + _irq.sigh(irq_handler); + _irq.ack_irq(); + } +}; + + +class Virtio_nic::Session_component : public Nic::Session_component, + public Device +{ + private: + + typedef Genode::Signal_handler Signal_handler; + + Signal_handler _irq_handler; + bool _link_up = false; + + void _handle_irq() + { + drv_handle_irq([&] () { + + _receive(); + + }, [&] () { + + if (link_state() == _link_up) { + return; + } + _link_up = !_link_up; + if (verbose()) + log("Link status changed: ", + (_link_up ? "on-line" : "off-line")); + + _link_state_changed(); + }); + } + + bool _send() + { + if (!_tx.sink()->ready_to_ack()) + return false; + + if (!_tx.sink()->packet_avail()) + return false; + + auto packet = _tx.sink()->get_packet(); + if (!packet.size() || !_tx.sink()->packet_valid(packet)) { + warning("Invalid tx packet"); + return true; + } + + if (link_state()) { + char const *data = _tx.sink()->packet_content(packet); + if (!tx_vq_write_pkt(data, packet.size())) { + warning("Failed to push packet into tx VirtIO queue!"); + return false; + } + } + + _tx.sink()->acknowledge_packet(packet); + return true; + } + + void _receive() + { + rx_vq_read_pkt( + [&] (Virtio_net_header const &, + char const *data, + size_t size) + { + if (!_rx.source()->ready_to_submit()) { + Genode::warning("Not ready to submit!"); + return false; + } + + try { + auto p = _rx.source()->alloc_packet(size); + char *dst = _rx.source()->packet_content(p); + Genode::memcpy(dst, data, size); + _rx.source()->submit_packet(p); + } catch (Session::Rx::Source::Packet_alloc_failed) { + Genode::warning("Packet alloc failed!"); + return false; + } + + return true; + }); + } + + void _handle_packet_stream() override + { + while (_rx.source()->ack_avail()) + _rx.source()->release_packet(_rx.source()->get_acked_packet()); + + rx_vq_ack_pkts(); + + bool sent_packets = false; + while (_send()) + sent_packets = true; + + if (sent_packets) { + _finish_sent_packets(); + } + } + + public: + + bool link_state() override + { + return read_link_state(); + } + + Nic::Mac_address mac_address() override + { + return read_mac_address(); + } Session_component(Genode::Env &env, Genode::Allocator &rx_block_md_alloc, @@ -428,44 +532,18 @@ class Virtio_nic::Session_component : public Nic::Session_component Genode::Xml_node const &xml, Genode::size_t const tx_buf_size, Genode::size_t const rx_buf_size) - try : Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, - rx_block_md_alloc, env), - _verbose(xml.attribute_value("verbose", false)), - _device(device), - _hw_features(_init_hw_features(xml)), - _rx_vq(env.ram(), env.rm(), - _vq_size(RX_VQ, xml, "rx_queue_size"), - _buf_size(RX_VQ, xml, "rx_buffer_size")), - _tx_vq(env.ram(), env.rm(), - _vq_size(TX_VQ, xml, "tx_queue_size"), - _buf_size(TX_VQ, xml, "tx_buffer_size")), - _irq(irq_cap), - _irq_handler(env.ep(), *this, &Session_component::_handle_irq), - _link_up(link_state()) + : + Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED, + rx_block_md_alloc, env }, + Device { env, device, irq_cap, xml }, + _irq_handler { env.ep(), *this, &Session_component::_handle_irq }, + _link_up { link_state() } { - _setup_virtio_queues(); - _irq.sigh(_irq_handler); - _irq.ack_irq(); - + Virtio_nic::Device::init(_irq_handler); _link_state_changed(); - - if (_verbose) + if (verbose()) Genode::log("Mac address: ", mac_address()); } - catch (Tx_queue_type::Invalid_buffer_size) - { - error("Invalid TX VirtIO queue buffer size specified!"); - throw; - } - catch (Rx_queue_type::Invalid_buffer_size) - { - error("Invalid RX VirtIO queue buffer size specified!"); - throw; - } - - ~Session_component() { - _device.set_status(Virtio::Device::Status::RESET); - } }; @@ -483,6 +561,7 @@ class Virtio_nic::Root : public Genode::Root_component(env.ep(), md_alloc), - _env(env), _device(device), _irq_cap(irq_cap) + Irq_session_capability irq_cap, + Attached_rom_dataspace &config_rom) + : + Root_component { env.ep(), md_alloc }, + _env { env }, + _device { device }, + _config_rom { config_rom }, + _irq_cap { irq_cap } { } }; + + +namespace Genode { + + class Uplink_client; +} + + +class Genode::Uplink_client : public Virtio_nic::Device, + public Uplink_client_base +{ + private: + + Signal_handler _irq_handler; + + void _receive() + { + rx_vq_read_pkt( + [&] (Virtio_net_header const &, + char const *data, + size_t size) + { + _drv_rx_handle_pkt( + size, + [&] (void *conn_tx_pkt_base, + size_t &conn_tx_pkt_size) + { + memcpy(conn_tx_pkt_base, data, conn_tx_pkt_size); + return Write_result::WRITE_SUCCEEDED; + }); + return true; + }); + } + + void _handle_irq() + { + drv_handle_irq([&] () { + + _receive(); + + }, [&] () { + + _drv_handle_link_state(read_link_state()); + }); + } + + + /************************ + ** Uplink_client_base ** + ************************/ + + Transmit_result + _drv_transmit_pkt(const char *conn_rx_pkt_base, + size_t conn_rx_pkt_size) override + { + rx_vq_ack_pkts(); + if (tx_vq_write_pkt(conn_rx_pkt_base, conn_rx_pkt_size)) { + + return Transmit_result::ACCEPTED; + + } else { + + warning("Failed to push packet into tx VirtIO queue!"); + return Transmit_result::RETRY; + } + } + + void _drv_finish_transmitted_pkts() override + { + _finish_sent_packets(); + } + + public: + + Uplink_client(Env &env, + Allocator &alloc, + Virtio::Device &device, + Irq_session_capability irq_cap, + Genode::Xml_node const &xml) + : + Device { env, device, irq_cap, xml }, + Uplink_client_base { env, alloc, read_mac_address() }, + _irq_handler { env.ep(), *this, &Uplink_client::_handle_irq } + { + Virtio_nic::Device::init(_irq_handler); + _drv_handle_link_state(read_link_state()); + } +}; diff --git a/repos/os/src/drivers/nic/virtio/config.xsd b/repos/os/src/drivers/nic/virtio/config.xsd index 9deae92320..acbf80129c 100644 --- a/repos/os/src/drivers/nic/virtio/config.xsd +++ b/repos/os/src/drivers/nic/virtio/config.xsd @@ -4,6 +4,7 @@ + @@ -19,6 +20,7 @@ + diff --git a/repos/os/src/drivers/nic/virtio/mmio_device.cc b/repos/os/src/drivers/nic/virtio/mmio_device.cc index f8f27b3db0..50df70bf24 100644 --- a/repos/os/src/drivers/nic/virtio/mmio_device.cc +++ b/repos/os/src/drivers/nic/virtio/mmio_device.cc @@ -11,11 +11,16 @@ * under the terms of the GNU Affero General Public License version 3. */ +/* Genode includes */ #include #include #include #include +/* NIC driver includes */ +#include + +/* local includes */ #include "component.h" namespace Virtio_mmio_nic { @@ -61,20 +66,43 @@ struct Virtio_mmio_nic::Main } }; - Genode::Env &env; - Genode::Heap heap { env.ram(), env.rm() }; - Platform::Connection platform { env }; - Device_info info { platform }; - Platform::Device_client platform_device { platform.acquire_device(info.name.string()) }; - Virtio::Device virtio_device { env, platform_device.io_mem_dataspace(), - info.io_mem_offset }; - Virtio_nic::Root root { env, heap, virtio_device, platform_device.irq() }; + Genode::Env &env; + Genode::Heap heap { env.ram(), env.rm() }; + Platform::Connection platform { env }; + Device_info info { platform }; + Platform::Device_client platform_device { platform.acquire_device(info.name.string()) }; + Virtio::Device virtio_device { env, platform_device.io_mem_dataspace(), + info.io_mem_offset }; + Attached_rom_dataspace config_rom { env, "config" }; + Genode::Constructible root { }; + Genode::Constructible uplink_client { }; Main(Env &env) try : env(env) { log("--- VirtIO MMIO NIC driver started ---"); - env.parent().announce(env.ep().manage(root)); + + Nic_driver_mode const mode { + read_nic_driver_mode(config_rom.xml()) }; + + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + + root.construct( + env, heap, virtio_device, platform_device.irq(0), + config_rom); + + env.parent().announce(env.ep().manage(*root)); + break; + + case Nic_driver_mode::UPLINK_CLIENT: + + uplink_client.construct( + env, heap, virtio_device, platform_device.irq(0), + config_rom.xml()); + + break; + } } catch (...) { env.parent().exit(-1); } }; diff --git a/repos/os/src/drivers/nic/virtio/pci_device.cc b/repos/os/src/drivers/nic/virtio/pci_device.cc index abef1ca5ea..a940a3c2d3 100644 --- a/repos/os/src/drivers/nic/virtio/pci_device.cc +++ b/repos/os/src/drivers/nic/virtio/pci_device.cc @@ -11,11 +11,16 @@ * under the terms of the GNU Affero General Public License version 3. */ +/* Genode includes */ #include #include #include #include +/* NIC driver includes */ +#include + +/* local includes */ #include "component.h" namespace Virtio_pci_nic { @@ -27,12 +32,14 @@ struct Virtio_pci_nic::Main { struct Device_not_found : Genode::Exception { }; - Genode::Env &env; - Genode::Heap heap{ env.ram(), env.rm() }; - Platform::Connection pci { env }; - Platform::Device_client platform_device; - Virtio::Device virtio_device { env, platform_device }; - Virtio_nic::Root root { env, heap, virtio_device, platform_device.irq(0) }; + Genode::Env &env; + Genode::Heap heap { env.ram(), env.rm() }; + Platform::Connection pci { env }; + Platform::Device_client platform_device; + Virtio::Device virtio_device { env, platform_device }; + Attached_rom_dataspace config_rom { env, "config" }; + Genode::Constructible root { }; + Genode::Constructible uplink_client { }; Platform::Device_capability find_platform_device() { @@ -48,7 +55,28 @@ struct Virtio_pci_nic::Main try : env(env), platform_device(find_platform_device()) { log("--- VirtIO PCI driver started ---"); - env.parent().announce(env.ep().manage(root)); + + Nic_driver_mode const mode { + read_nic_driver_mode(config_rom.xml()) }; + + switch (mode) { + case Nic_driver_mode::NIC_SERVER: + + root.construct( + env, heap, virtio_device, platform_device.irq(0), + config_rom); + + env.parent().announce(env.ep().manage(*root)); + break; + + case Nic_driver_mode::UPLINK_CLIENT: + + uplink_client.construct( + env, heap, virtio_device, platform_device.irq(0), + config_rom.xml()); + + break; + } } catch (...) { env.parent().exit(-1); } }; diff --git a/repos/os/src/drivers/nic/virtio/spec/x86/target.mk b/repos/os/src/drivers/nic/virtio/spec/x86/target.mk index e8445fab6e..da2d995713 100644 --- a/repos/os/src/drivers/nic/virtio/spec/x86/target.mk +++ b/repos/os/src/drivers/nic/virtio/spec/x86/target.mk @@ -1,7 +1,7 @@ TARGET = virtio_pci_nic REQUIRES = x86 SRC_CC = pci_device.cc -LIBS = base +LIBS = base nic_driver INC_DIR = $(REP_DIR)/src/drivers/nic/virtio CONFIG_XSD = ../../config.xsd diff --git a/repos/os/src/drivers/nic/virtio/target_mmio.inc b/repos/os/src/drivers/nic/virtio/target_mmio.inc index 0723a56c05..5731bfc7b4 100644 --- a/repos/os/src/drivers/nic/virtio/target_mmio.inc +++ b/repos/os/src/drivers/nic/virtio/target_mmio.inc @@ -1,6 +1,6 @@ TARGET = virtio_mmio_nic SRC_CC = mmio_device.cc -LIBS = base +LIBS = base nic_driver INC_DIR = $(REP_DIR)/src/drivers/nic/virtio CONFIG_XSD = ../../config.xsd diff --git a/repos/os/xsd/nic_driver_types.xsd b/repos/os/xsd/nic_driver_types.xsd new file mode 100644 index 0000000000..24ab807827 --- /dev/null +++ b/repos/os/xsd/nic_driver_types.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + +