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 <config> 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
This commit is contained in:
Martin Stein 2021-01-06 16:31:10 +01:00 committed by Norman Feske
parent 1d2649b49a
commit f6d195a9de
71 changed files with 3627 additions and 994 deletions

View File

@ -1,5 +1,7 @@
base
os
nic_session
uplink_session
nic_driver
platform_session
timer_session

View File

@ -12,14 +12,21 @@
* version 2.
*/
/* Genode */
/* Genode includes */
#include <base/component.h>
#include <base/env.h>
#include <base/heap.h>
#include <base/log.h>
#include <nic/component.h>
#include <nic/root.h>
#include <uplink_session/connection.h>
#include <base/attached_rom_dataspace.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
#include <drivers/nic/mode.h>
/* DDE iPXE includes */
#include <dde_ipxe/support.h>
#include <dde_ipxe/nic.h>
@ -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<Ipxe_session_component> 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<Ipxe_session_component> &root {
*new (_heap)
Nic::Root<Ipxe_session_component>(_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();

View File

@ -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 =

View File

@ -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

View File

@ -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)

View File

@ -3,4 +3,6 @@ os
platform_session
timer_session
nic_session
uplink_session
nic_driver
gpio_session

View File

@ -1,6 +1,8 @@
base
os
nic_session
uplink_session
nic_driver
usb_session
gpio_session
event_session

View File

@ -4,6 +4,8 @@ libc
libcrypto
libssl
nic_session
uplink_session
nic_driver
platform_session
report_session
timer_session

View File

@ -18,52 +18,6 @@ extern "C" {
#include <lxc.h>
};
void Session_component::_run_rx_task(void * args)
{
Rx_data *data = static_cast<Rx_data*>(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<Tx_data*>(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() }
{ }

View File

@ -18,43 +18,16 @@
#include <nic/component.h>
#include <root/component.h>
#include <lx_kit/scheduler.h>
/* local includes */
#include <linux_network_session_base.h>
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<Session_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();

View File

@ -279,7 +279,7 @@ struct Fec : public Genode::List<Fec>::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> mdio;
@ -319,8 +319,10 @@ static Genode::List<Fec> & 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;

View File

@ -11,11 +11,17 @@
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <base/component.h>
#include <base/heap.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* local includes */
#include <uplink_client.h>
#include <component.h>
/* 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> root { };
Genode::Constructible<Genode::Uplink_client> uplink_client { };
/* Linux task that handles the initialization */
Genode::Constructible<Lx::Task> 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; }
};

View File

@ -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

View File

@ -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 <uplink_client.h>
#include <lx_emul.h>
extern "C" {
#include <lxc.h>
};
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;
});
}

View File

@ -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 <drivers/nic/uplink_client_base.h>
/* local include */
#include <linux_network_session_base.h>
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_ */

View File

@ -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 <linux_network_session_base.h>
#include <lx_kit/scheduler.h>
#include <lx_emul.h>
void Linux_network_session_base::_run_tx_task(void * args)
{
Tx_data *data = static_cast<Tx_data*>(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<Rx_data*>(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();
}

View File

@ -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 <lx_kit/scheduler.h>
/* 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_ */

View File

@ -14,27 +14,54 @@
#ifndef _USB_NIC_COMPONENT_H_
#define _USB_NIC_COMPONENT_H_
/* Genode includes */
#include <base/log.h>
#include <nic/component.h>
#include <root/component.h>
/* Linux emulation environment includes */
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <linux/usb/usbnet.h>
#include <lx_emul/extern_c_end.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
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_ */

View File

@ -11,17 +11,24 @@
* version 2.
*/
/* Genode includes */
#include <base/rpc_server.h>
#include <base/snprintf.h>
#include <nic_session/nic_session.h>
#include <util/xml_node.h>
/* Linux emulation environment includes */
#include <lx_kit/env.h>
#include <lx_kit/malloc.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* local includes */
#include <usb_nic_component.h>
#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;

View File

@ -1,5 +1,5 @@
SRC_CC += main.cc
LIBS = base
LIBS = base nic_driver
CC_CXX_WARN_STRICT =

View File

@ -19,52 +19,6 @@ extern "C" {
#include <lxc.h>
};
void Session_component::_run_rx_task(void * args)
{
Rx_data *data = static_cast<Rx_data*>(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<Tx_data*>(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() }
{ }

View File

@ -19,43 +19,16 @@
#include <nic/component.h>
#include <root/component.h>
#include <lx_kit/scheduler.h>
/* local includes */
#include <fec_nic.h>
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;
};

View File

@ -15,20 +15,28 @@
#ifndef _SRC__DRIVERS__USB_MODEM__DRIVER_H_
#define _SRC__DRIVERS__USB_MODEM__DRIVER_H_
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <usb_session/connection.h>
#include <lx_kit/scheduler.h>
#include <util/reconstructible.h>
/* local includes */
#include <uplink_client.h>
#include <component.h>
#include <terminal.h>
/* Linux emulation environment includes */
#include <lx_kit/scheduler.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
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<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> 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> root { };
Genode::Constructible<Genode::Uplink_client> uplink_client { };
Terminal::Root terminal_root { env, heap };
Genode::Constructible<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> 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 *);
};

View File

@ -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 <fec_nic.h>
#include <lx_kit/scheduler.h>
#include <lx_emul.h>
void Fec_nic::_run_tx_task(void * args)
{
Tx_data *data = static_cast<Tx_data*>(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<Rx_data*>(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();
}

View File

@ -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 <lx_kit/scheduler.h>
/* 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_ */

View File

@ -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<Session_component*>(dev->session_component)->link_state(false);
reinterpret_cast<Fec_nic*>(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<Session_component*>(dev->session_component)->link_state(true);
reinterpret_cast<Fec_nic*>(dev->session_component)->link_state(true);
}
int netif_rx(struct sk_buff * skb)
{
if (skb->dev->session_component)
reinterpret_cast<Session_component*>(skb->dev->session_component)->receive(skb);
reinterpret_cast<Fec_nic*>(skb->dev->session_component)->receive(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;

View File

@ -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);

View File

@ -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

View File

@ -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 <uplink_client.h>
#include <lx_emul.h>
extern "C" {
#include <lxc.h>
};
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;
});
}

View File

@ -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 <drivers/nic/uplink_client_base.h>
/* local include */
#include <fec_nic.h>
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_ */

View File

@ -18,52 +18,6 @@ extern "C" {
#include <lxc.h>
};
void Session_component::_run_rx_task(void * args)
{
Rx_data *data = static_cast<Rx_data*>(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<Tx_data*>(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() }
{ }

View File

@ -18,43 +18,16 @@
#include <nic/component.h>
#include <root/component.h>
#include <lx_kit/scheduler.h>
/* local includes */
#include <linux_network_session_base.h>
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;
};

View File

@ -14,13 +14,22 @@
#ifndef _SRC__DRIVERS__USB_NET__DRIVER_H_
#define _SRC__DRIVERS__USB_NET__DRIVER_H_
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <usb_session/connection.h>
#include <util/reconstructible.h>
/* local includes */
#include <uplink_client.h>
#include <component.h>
/* Linux emulation environment includes */
#include <lx_kit/scheduler.h>
#include <component.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
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<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> 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> root { };
Genode::Constructible<Genode::Uplink_client> uplink_client { };
Genode::Constructible<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> 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 *);
};

View File

@ -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<Session_component*>(dev->session_component)->link_state(false);
reinterpret_cast<Linux_network_session_base*>(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<Session_component*>(dev->session_component)->link_state(true);
reinterpret_cast<Linux_network_session_base*>(dev->session_component)->
link_state(true);
}
int netif_rx(struct sk_buff * skb)
{
if (skb->dev->session_component)
reinterpret_cast<Session_component*>(skb->dev->session_component)->receive(skb);
reinterpret_cast<Linux_network_session_base*>(skb->dev->session_component)->
receive(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;

View File

@ -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);

View File

@ -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

View File

@ -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 <uplink_client.h>
#include <lx_emul.h>
extern "C" {
#include <lxc.h>
};
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;
});
}

View File

@ -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 <drivers/nic/uplink_client_base.h>
/* local include */
#include <linux_network_session_base.h>
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_ */

View File

@ -53,6 +53,9 @@
#include <util/interface.h>
#include <util/xml_node.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* rep includes */
#include <wifi/ctrl.h>
#include <wifi/rfkill.h>
@ -361,6 +364,8 @@ struct Wifi::Frontend
Genode::Attached_rom_dataspace _config_rom;
Genode::Signal_handler<Wifi::Frontend> _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_ */

View File

@ -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());
}
};

View File

@ -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

View File

@ -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());

View File

@ -17,6 +17,9 @@
/* Genode includes */
#include <base/allocator.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* local includes */
#include <lx_kit/scheduler.h>
@ -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);

View File

@ -20,6 +20,10 @@
#include <nic/component.h>
#include <root/component.h>
#include <util/xml_node.h>
#include <uplink_session/connection.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
/* local includes */
#include <lx.h>
@ -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<Wifi_session_component,
Genode::Single_client>
Genode::Single_client>,
public Wifi_nic
{
private:
@ -284,20 +345,19 @@ class Root : public Genode::Root_component<Wifi_session_component,
session = new (md_alloc())
Wifi_session_component(tx_buf_size, rx_buf_size,
*md_alloc(), _env, device);
*md_alloc(), _env, &Wifi_nic::device());
return session;
}
void _destroy_session(Wifi_session_component *session)
{
/* stop rx */
Root::instance->session = nullptr;
session = nullptr;
Genode::Root_component<Wifi_session_component, Genode::Single_client>::_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_component<Wifi_session_component,
_env(env)
{ }
void announce() { _env.parent().announce(_env.ep().manage(*this)); }
/**************
** Wifi_nic **
**************/
void activate() override
{
_env.parent().announce(_env.ep().manage(*this));
}
void handle_driver_rx_packet(struct sk_buff *skb) override
{
if (session) {
session->receive(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<Uplink_client> _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;
}

View File

@ -0,0 +1 @@
REP_INC_DIR += src/drivers/nic/include

View File

View File

@ -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 $@

View File

@ -0,0 +1 @@
2020-12-09-b 594a8cbf9fc6c84f60947a4b940540b5aa6e1486

View File

@ -2,3 +2,5 @@ base
os
platform_session
nic_session
uplink_session
nic_driver

View File

@ -2,3 +2,5 @@ base
base-linux
os
nic_session
uplink_session
nic_driver

View File

@ -2,4 +2,6 @@ base
os
virtio
nic_session
uplink_session
nic_driver
platform_session

View File

@ -1,3 +1,5 @@
base
os
nic_session
uplink_session
nic_driver

View File

@ -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 <util/xml_node.h>
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_ */

View File

@ -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 <net/mac_address.h>
#include <nic/packet_allocator.h>
#include <uplink_session/connection.h>
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<Uplink::Connection> _conn { };
Nic::Packet_allocator _conn_pkt_alloc { &_alloc };
Signal_handler<Uplink_client_base> _conn_rx_ready_to_ack_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_ready_to_ack };
Signal_handler<Uplink_client_base> _conn_rx_packet_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_packet_avail };
Signal_handler<Uplink_client_base> _conn_tx_ack_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ack_avail };
Signal_handler<Uplink_client_base> _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 <typename FUNC>
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_ */

View File

@ -15,6 +15,7 @@
#ifndef _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_
#define _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_
/* Genode includes */
#include <base/attached_dataspace.h>
#include <base/log.h>
#include <util/misc_math.h>
@ -22,15 +23,20 @@
#include <timer_session/connection.h>
#include <nic/component.h>
class Lan9118 : public Nic::Session_component
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
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<Lan9118> _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<Genode::uint32_t>()),
_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<Genode::uint32_t>() },
_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<Lan9118>,
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<Lan9118> { 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<Uplink_client>,
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<Uplink_client> { 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_ */

View File

@ -24,23 +24,28 @@
#include <platform_session/connection.h>
#include <root/component.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* driver code */
#include <lan9118.h>
class Root : public Genode::Root_component<Lan9118, Genode::Single_client>
using namespace Genode;
class Nic_root : public Root_component<Lan9118, Single_client>
{
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<Lan9118, Genode::Single_client>
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<Lan9118,
Genode::Single_client>(env.ep(), md_alloc),
_env(env) { }
Nic_root(Env &env,
Allocator &md_alloc,
Platform::Device_client &device)
:
Root_component<Lan9118, Genode::Single_client> { 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> _nic_root { };
Constructible<Uplink_client> _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 };
}

View File

@ -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)

View File

@ -32,6 +32,10 @@
#include <base/blockade.h>
#include <nic/root.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
#include <drivers/nic/mode.h>
/* Linux */
#include <errno.h>
#include <unistd.h>
@ -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<Uplink_client> _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<Linux_session_component> 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<Linux_session_component> &nic_root {
*new (_heap) Nic::Root<Linux_session_component>(_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;
}
}
};

View File

@ -1,4 +1,4 @@
TARGET = linux_nic_drv
REQUIRES = linux
LIBS = lx_hybrid
LIBS = lx_hybrid nic_driver
SRC_CC = main.cc

View File

@ -22,6 +22,9 @@
#include <timer_session/connection.h>
#include <nic/component.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
/* 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<Cadence_gem> _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>(
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_qbar>( _rx_buffer.phys_addr() );
write<Tx_qbar>( _tx_buffer.phys_addr() );
/* 3. Program the DMA Configuration register (gem.dma_cfg) */
write<Dma_config>( Dma_config::init() );
/*
* 4. Program the Network Control Register (gem.net_ctrl)
* Enable MDIO, transmitter and receiver
*/
write<Control>(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<Config::Gige_en>(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<Config::Gige_en>(0);
write<Config::Speed_100>(1);
rclk = 1 << 0;
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
log("Autonegotiation result: 100Mbit/s");
break;
case SPEED_10:
write<Config::Gige_en>(0);
write<Config::Speed_100>(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>(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<Interrupt_disable>(0x7FFFEFF);
/* Disable the receiver & transmitter */
write<Control>(0);
write<Control>(Control::Clear_statistics::bits(1));
write<Tx_status>(0xFF);
write<Rx_status>(0x0F);
write<Phy_maintenance>(0);
write<Rx_qbar>(0);
write<Tx_qbar>(0);
/* Clear the Hash registers for the mac address
* pointed by AddressPtr
*/
write<Hash_register>(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<Phy_maintenance::Data>();
}
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>(Control::start_tx());
}
Nic::Mac_address read_mac_address()
{
Nic::Mac_address mac;
uint32_t* const low_addr_pointer = reinterpret_cast<uint32_t*>(&mac.addr[0]);
uint16_t* const high_addr_pointer = reinterpret_cast<uint16_t*>(&mac.addr[4]);
*low_addr_pointer = read<Mac_addr_1::Low_addr>();
*high_addr_pointer = read<Mac_addr_1::High_addr>();
return mac;
}
template <typename RECEIVE_PKT,
typename HANDLE_ACKS>
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<Tx_status>();
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>(Interrupt_status::Rx_complete::bits(1));
}
else {
_handle_acks();
handle_acks();
}
/* handle Rx/Tx errors */
@ -540,7 +480,7 @@ namespace Genode
write<Control::Tx_en>(0);
write<Control::Rx_en>(0);
_tx_buffer.reset(*_tx.sink());
_tx_buffer.reset(_tx_buffer_sink);
_rx_buffer.reset();
write<Control::Tx_en>(1);
@ -556,7 +496,7 @@ namespace Genode
|| Tx_status::Tx_err_bufexh::get(txStatus)) {
write<Control::Tx_en>(0);
_tx_buffer.reset(*_tx.sink());
_tx_buffer.reset(_tx_buffer_sink);
write<Control::Tx_en>(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<void>()),
_tx_buf_region((addr_t)_rx_ds.local_addr<void>()),
_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>(
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_qbar>(_rx_buffer.phys_addr());
write<Tx_qbar>(_tx_buffer.phys_addr());
/* 3. Program the DMA Configuration register (gem.dma_cfg) */
write<Dma_config>( Dma_config::init() );
/*
* 4. Program the Network Control Register (gem.net_ctrl)
* Enable MDIO, transmitter and receiver
*/
write<Control>(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<Config::Gige_en>(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<Config::Gige_en>(0);
write<Config::Speed_100>(1);
rclk = 1 << 0;
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
log("Autonegotiation result: 100Mbit/s");
break;
case SPEED_10:
write<Config::Gige_en>(0);
write<Config::Speed_100>(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>(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<Interrupt_disable>(0x7FFFEFF);
/* Disable the receiver & transmitter */
write<Control>(0);
write<Control>(Control::Clear_statistics::bits(1));
write<Tx_status>(0xFF);
write<Rx_status>(0x0F);
write<Phy_maintenance>(0);
write<Rx_qbar>(0);
write<Tx_qbar>(0);
/* Clear the Hash registers for the mac address
* pointed by AddressPtr
*/
write<Hash_register>(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<Phy_maintenance::Data>();
}
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<const uint32_t*>(&mac.addr[0]);
const uint16_t* const high_addr_pointer = reinterpret_cast<const uint16_t*>(&mac.addr[4]);
@ -697,12 +683,158 @@ namespace Genode
write<Mac_addr_1::High_addr>(*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<Cadence_gem> _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>(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<uint32_t*>(&mac.addr[0]);
uint16_t* const high_addr_pointer = reinterpret_cast<uint16_t*>(&mac.addr[4]);
*low_addr_pointer = read<Mac_addr_1::Low_addr>();
*high_addr_pointer = read<Mac_addr_1::High_addr>();
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<Uplink_client> _irq_handler;
Constructible<Uplink_rx_buffer_source> _rx_buffer_source { };
Constructible<Uplink_tx_buffer_sink> _tx_buffer_sink { };
Constructible<Cadence_gem_base> _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_ */

View File

@ -18,6 +18,10 @@
#include <drivers/defs/zynq.h>
#include <nic/root.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* 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<Gem_session_component> nic_root{ _env, _heap };
Env &_env;
Heap _heap { _env.ram(), _env.rm() };
Constructible<Nic::Root<Gem_session_component> > _nic_root { };
Constructible<Uplink_client> _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;
}
}
};

View File

@ -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())
{

View File

@ -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)

View File

@ -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 */

View File

@ -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 <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/heap.h>
@ -23,35 +23,23 @@
#include <util/misc_math.h>
#include <util/register.h>
#include <virtio/queue.h>
#include <util/noncopyable.h>
/* NIC driver includes */
#include <drivers/nic/uplink_client_base.h>
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<Virtio_net_header, Rx_queue_traits> Rx_queue_type;
typedef Virtio::Queue<Virtio_net_header, Tx_queue_traits> Tx_queue_type;
typedef Genode::Signal_handler<Session_component> 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 <typename HANDLE_RX,
typename HANDLE_LINK_STATE>
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 <typename RECEIVE_PKT>
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<Session_component> 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<Session_component, Genode
Genode::Env &_env;
Virtio::Device &_device;
Attached_rom_dataspace &_config_rom;
Irq_session_capability _irq_cap;
Session_component *_create_session(const char *args) override
@ -502,11 +581,9 @@ class Virtio_nic::Root : public Genode::Root_component<Session_component, Genode
throw Genode::Insufficient_ram_quota();
}
Attached_rom_dataspace rom(_env, "config");
try {
return new (md_alloc()) Session_component(
_env, *md_alloc(), _device, _irq_cap, rom.xml(),
_env, *md_alloc(), _device, _irq_cap, _config_rom.xml(),
tx_buf_size, rx_buf_size);
} catch (...) { throw Service_denied(); }
}
@ -516,8 +593,102 @@ class Virtio_nic::Root : public Genode::Root_component<Session_component, Genode
Root(Env &env,
Allocator &md_alloc,
Virtio::Device &device,
Irq_session_capability irq_cap)
: Root_component<Session_component, Genode::Single_client>(env.ep(), md_alloc),
_env(env), _device(device), _irq_cap(irq_cap)
Irq_session_capability irq_cap,
Attached_rom_dataspace &config_rom)
:
Root_component<Session_component,
Genode::Single_client> { 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<Uplink_client> _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());
}
};

View File

@ -4,6 +4,7 @@
<xs:include schemaLocation="base_types.xsd"/>
<xs:include schemaLocation="net_types.xsd"/>
<xs:include schemaLocation="virtio_types.xsd"/>
<xs:include schemaLocation="nic_driver_types.xsd"/>
<xs:simpleType name="RxBufferSize">
<xs:restriction base="xs:unsignedShort">
@ -19,6 +20,7 @@
<xs:element name="config">
<xs:complexType>
<xs:attribute name="mode" type="Nic_driver_mode" />
<xs:attribute name="verbose" type="Boolean" />
<xs:attribute name="mac" type="Mac_address" />
<xs:attribute name="rx_queue_size" type="Virtio_queue_size" />

View File

@ -11,11 +11,16 @@
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/component.h>
#include <base/heap.h>
#include <platform_session/connection.h>
#include <virtio/mmio_device.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* 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<Virtio_nic::Root> root { };
Genode::Constructible<Genode::Uplink_client> 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); }
};

View File

@ -11,11 +11,16 @@
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/component.h>
#include <base/heap.h>
#include <platform_session/connection.h>
#include <virtio/pci_device.h>
/* NIC driver includes */
#include <drivers/nic/mode.h>
/* 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<Virtio_nic::Root> root { };
Genode::Constructible<Genode::Uplink_client> 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); }
};

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="Nic_driver_mode">
<xs:restriction base="xs:string">
<xs:enumeration value="nic_server" />
<xs:enumeration value="uplink_client" />
</xs:restriction>
</xs:simpleType><!-- Nic_driver_mode -->
</xs:schema>