From f6d195a9de34a7ed41d2e33e6035f7fc615d30a4 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Wed, 6 Jan 2021 16:31:10 +0100 Subject: [PATCH] nic drivers: provide optional Uplink-client mode In order to perform a smooth transition from NIC drivers that act only as NIC session clients to NIC drivers that act only as Uplink session clients, this commit introduces an intermediate state in which all NIC drivers support both modes. That said, a NIC drivers mode is now statically determined through a new optional 'mode' attribute in the drivers tag that can be set to either 'nic_server' (default value) or 'uplink_client'. Reconfiguring this attribute at a driver doesn't have any effects. Whithout this attribute being set, all NIC drivers will behave the same as they did before the commit. When set to 'uplink_client', however, instead of providing a Nic service, they request an Uplink session whenever their network interface becomes "UP" and close the session whenever their network interface becomes "DOWN". Ref #3961 --- .../recipes/src/ipxe_nic_drv/used_apis | 2 + repos/dde_ipxe/src/drivers/nic/main.cc | 143 +++- repos/dde_ipxe/src/drivers/nic/target.mk | 2 +- repos/dde_linux/lib/mk/wifi.inc | 2 +- .../recipes/src/fec_nic_drv/content.mk | 2 + .../recipes/src/fec_nic_drv/used_apis | 2 + repos/dde_linux/recipes/src/usb_drv/used_apis | 2 + .../dde_linux/recipes/src/wifi_drv/used_apis | 2 + .../src/drivers/nic/fec/component.cc | 78 +- .../dde_linux/src/drivers/nic/fec/component.h | 70 +- .../dde_linux/src/drivers/nic/fec/lx_emul.cc | 10 +- repos/dde_linux/src/drivers/nic/fec/main.cc | 40 +- .../dde_linux/src/drivers/nic/fec/target.inc | 7 +- .../src/drivers/nic/fec/uplink_client.cc | 93 +++ .../src/drivers/nic/fec/uplink_client.h | 58 ++ .../drivers/nic/linux_network_session_base.cc | 89 +++ .../drivers/nic/linux_network_session_base.h | 78 ++ .../drivers/usb/include/usb_nic_component.h | 225 +++++- repos/dde_linux/src/drivers/usb/nic/nic.cc | 56 +- repos/dde_linux/src/drivers/usb/target.inc | 2 +- .../src/drivers/usb_modem/component.cc | 77 +- .../src/drivers/usb_modem/component.h | 63 +- .../dde_linux/src/drivers/usb_modem/driver.h | 50 +- .../src/drivers/usb_modem/fec_nic.cc | 89 +++ .../dde_linux/src/drivers/usb_modem/fec_nic.h | 77 ++ .../src/drivers/usb_modem/lx_emul.cc | 12 +- repos/dde_linux/src/drivers/usb_modem/main.cc | 5 +- .../dde_linux/src/drivers/usb_modem/target.mk | 5 +- .../src/drivers/usb_modem/uplink_client.cc | 93 +++ .../src/drivers/usb_modem/uplink_client.h | 58 ++ .../src/drivers/usb_net/component.cc | 77 +- .../dde_linux/src/drivers/usb_net/component.h | 63 +- repos/dde_linux/src/drivers/usb_net/driver.h | 48 +- .../dde_linux/src/drivers/usb_net/lx_emul.cc | 17 +- repos/dde_linux/src/drivers/usb_net/main.cc | 5 +- repos/dde_linux/src/drivers/usb_net/target.mk | 7 +- .../src/drivers/usb_net/uplink_client.cc | 93 +++ .../src/drivers/usb_net/uplink_client.h | 58 ++ repos/dde_linux/src/drivers/wifi/frontend.h | 10 + repos/dde_linux/src/drivers/wifi/main.cc | 9 +- repos/dde_linux/src/drivers/wifi/target.mk | 2 +- repos/dde_linux/src/lib/wifi/init.cc | 9 +- repos/dde_linux/src/lib/wifi/lx.h | 7 +- repos/dde_linux/src/lib/wifi/nic.cc | 288 ++++++- repos/os/lib/import/import-nic_driver.mk | 1 + repos/os/lib/mk/nic_driver.mk | 0 repos/os/recipes/api/nic_driver/content.mk | 12 + repos/os/recipes/api/nic_driver/hash | 1 + .../os/recipes/src/lan9118_nic_drv/used_apis | 2 + repos/os/recipes/src/linux_nic_drv/used_apis | 2 + repos/os/recipes/src/virtio_nic_drv/used_apis | 2 + repos/os/recipes/src/zynq_nic_drv/used_apis | 2 + .../drivers/nic/include/drivers/nic/mode.h | 41 + .../include/drivers/nic/uplink_client_base.h | 341 +++++++++ repos/os/src/drivers/nic/lan9118/lan9118.h | 345 ++++++--- repos/os/src/drivers/nic/lan9118/main.cc | 82 +- repos/os/src/drivers/nic/lan9118/target.mk | 2 +- repos/os/src/drivers/nic/spec/linux/main.cc | 221 +++++- repos/os/src/drivers/nic/spec/linux/target.mk | 2 +- .../src/drivers/nic/spec/zynq/cadence_gem.h | 721 +++++++++++++----- repos/os/src/drivers/nic/spec/zynq/main.cc | 77 +- .../nic/spec/zynq/rx_buffer_descriptor.h | 14 +- repos/os/src/drivers/nic/spec/zynq/target.mk | 2 +- .../nic/spec/zynq/tx_buffer_descriptor.h | 18 +- repos/os/src/drivers/nic/virtio/component.h | 443 +++++++---- repos/os/src/drivers/nic/virtio/config.xsd | 2 + .../os/src/drivers/nic/virtio/mmio_device.cc | 46 +- repos/os/src/drivers/nic/virtio/pci_device.cc | 42 +- .../src/drivers/nic/virtio/spec/x86/target.mk | 2 +- .../os/src/drivers/nic/virtio/target_mmio.inc | 2 +- repos/os/xsd/nic_driver_types.xsd | 11 + 71 files changed, 3627 insertions(+), 994 deletions(-) create mode 100644 repos/dde_linux/src/drivers/nic/fec/uplink_client.cc create mode 100644 repos/dde_linux/src/drivers/nic/fec/uplink_client.h create mode 100644 repos/dde_linux/src/drivers/nic/linux_network_session_base.cc create mode 100644 repos/dde_linux/src/drivers/nic/linux_network_session_base.h create mode 100644 repos/dde_linux/src/drivers/usb_modem/fec_nic.cc create mode 100644 repos/dde_linux/src/drivers/usb_modem/fec_nic.h create mode 100644 repos/dde_linux/src/drivers/usb_modem/uplink_client.cc create mode 100644 repos/dde_linux/src/drivers/usb_modem/uplink_client.h create mode 100644 repos/dde_linux/src/drivers/usb_net/uplink_client.cc create mode 100644 repos/dde_linux/src/drivers/usb_net/uplink_client.h create mode 100644 repos/os/lib/import/import-nic_driver.mk create mode 100644 repos/os/lib/mk/nic_driver.mk create mode 100644 repos/os/recipes/api/nic_driver/content.mk create mode 100644 repos/os/recipes/api/nic_driver/hash create mode 100644 repos/os/src/drivers/nic/include/drivers/nic/mode.h create mode 100644 repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h create mode 100644 repos/os/xsd/nic_driver_types.xsd 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 @@ + + + + + + + + + + +