diff --git a/repos/os/recipes/pkg/drivers_nic-virt_qemu/README b/repos/os/recipes/pkg/drivers_nic-virt_qemu/README
new file mode 100644
index 0000000000..b9ad938515
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_nic-virt_qemu/README
@@ -0,0 +1,3 @@
+
+ Device drivers needed for scenarios
+ using one network interface
diff --git a/repos/os/recipes/pkg/drivers_nic-virt_qemu/archives b/repos/os/recipes/pkg/drivers_nic-virt_qemu/archives
new file mode 100644
index 0000000000..9aae66c752
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_nic-virt_qemu/archives
@@ -0,0 +1,4 @@
+_/raw/drivers_nic-virt_qemu
+_/src/virtdev_rom
+_/src/platform_drv
+_/src/virtio_nic_drv
diff --git a/repos/os/recipes/pkg/drivers_nic-virt_qemu/hash b/repos/os/recipes/pkg/drivers_nic-virt_qemu/hash
new file mode 100644
index 0000000000..98a66e8f74
--- /dev/null
+++ b/repos/os/recipes/pkg/drivers_nic-virt_qemu/hash
@@ -0,0 +1 @@
+2020-07-01 45d2ce0a7e352afa423739b9c1de5435511f30b0
diff --git a/repos/os/recipes/raw/drivers_nic-virt_qemu/content.mk b/repos/os/recipes/raw/drivers_nic-virt_qemu/content.mk
new file mode 100644
index 0000000000..78a408f9d6
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_nic-virt_qemu/content.mk
@@ -0,0 +1,4 @@
+content: drivers.config
+
+drivers.config:
+ cp $(REP_DIR)/recipes/raw/drivers_nic-virt_qemu/$@ $@
diff --git a/repos/os/recipes/raw/drivers_nic-virt_qemu/drivers.config b/repos/os/recipes/raw/drivers_nic-virt_qemu/drivers.config
new file mode 100644
index 0000000000..6ee156fa50
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_nic-virt_qemu/drivers.config
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/recipes/raw/drivers_nic-virt_qemu/hash b/repos/os/recipes/raw/drivers_nic-virt_qemu/hash
new file mode 100644
index 0000000000..b420d1cb74
--- /dev/null
+++ b/repos/os/recipes/raw/drivers_nic-virt_qemu/hash
@@ -0,0 +1 @@
+2020-07-01 a13f65f854fef31aaca5c1183f4e80d47f7797ef
diff --git a/repos/os/recipes/src/virtio_nic_drv/content.mk b/repos/os/recipes/src/virtio_nic_drv/content.mk
new file mode 100644
index 0000000000..e61c9b01d1
--- /dev/null
+++ b/repos/os/recipes/src/virtio_nic_drv/content.mk
@@ -0,0 +1,2 @@
+SRC_DIR = src/drivers/nic/virtio
+include $(GENODE_DIR)/repos/base/recipes/src/content.inc
diff --git a/repos/os/recipes/src/virtio_nic_drv/hash b/repos/os/recipes/src/virtio_nic_drv/hash
new file mode 100644
index 0000000000..9d7a1d664a
--- /dev/null
+++ b/repos/os/recipes/src/virtio_nic_drv/hash
@@ -0,0 +1 @@
+2020-07-01-b 330640a1c3dddf5e18087e03155638fbd4bbd567
diff --git a/repos/os/recipes/src/virtio_nic_drv/used_apis b/repos/os/recipes/src/virtio_nic_drv/used_apis
new file mode 100644
index 0000000000..626bf71970
--- /dev/null
+++ b/repos/os/recipes/src/virtio_nic_drv/used_apis
@@ -0,0 +1,5 @@
+base
+os
+virtio
+nic_session
+platform_session
diff --git a/repos/os/src/drivers/nic/virtio/README b/repos/os/src/drivers/nic/virtio/README
new file mode 100644
index 0000000000..558e915789
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/README
@@ -0,0 +1,44 @@
+This directory contains the implementation of Genode's VirtIO NIC driver.
+
+Brief
+=====
+
+The driver implements virtual ethernet card driver as defined in section
+5.1 of Virtial I/O Device (VIRTIO) Version 1.0 specification. The device
+is exposed to the rest of the system via standard Genode NIC session
+interface.
+
+This driver does not require, or utilize any advanced VirtIO ethernet
+device features such as VLAN filtering, or checksum offloading.
+
+Configuration
+=============
+
+The following config illustrates how the driver is configured:
+
+!
+!
+!
+!
+!
+
+The rx_queue_size and tx_queue_size parameters define the maximum number
+buffers the transmit and receive virtio queues can hold. The buffers
+used by both queues are pre-allocated on device initialization. The
+rx_buffer_size and tx_buffer_size can be used to specify sizes of those
+pre-allocated buffers.
+
+The default values of the presented configuration parameters the driver
+uses were selected to minimize driver memory usage without negatively
+affecting performance. Testing was done on Qemu arm_v8a, arm_v8a, and
+x86_64 machines. These values might not be suitable for more advanted
+usage scenarios.
+
+The mac configuration parameter can be used to override the default
+device MAC address obtained from VirtIO configuration space.
+
+The driver does produce some additional logs when verbose parameter is set
+to true.
diff --git a/repos/os/src/drivers/nic/virtio/component.h b/repos/os/src/drivers/nic/virtio/component.h
new file mode 100644
index 0000000000..1f1b53e55f
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/component.h
@@ -0,0 +1,523 @@
+/*
+ * \brief VirtIO NIC driver component
+ * \author Piotr Tworek
+ * \date 2019-09-27
+ */
+
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/* Need to come before attached_rom_dataspace.h */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Virtio_nic {
+ using namespace Genode;
+ struct Main;
+ class Root;
+ class Session_component;
+}
+
+
+class Virtio_nic::Session_component : public Nic::Session_component
+{
+ 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;
+ };
+
+ /**
+ * See section 5.1.6 of VirtIO 1.0 specification.
+ */
+ struct Virtio_net_header
+ {
+ enum Flags : Genode::uint16_t
+ {
+ NEEDS_CSUM = 1,
+ };
+
+ enum GSO : Genode::uint16_t
+ {
+ NONE = 0,
+ TCPV4 = 1,
+ UDP = 3,
+ TCPV6 = 4,
+ ECN = 0x80,
+ };
+
+ uint8_t flags = 0;
+ uint8_t gso_type = GSO::NONE;
+ uint16_t hdr_len = 0;
+ uint16_t gso_size = 0;
+ uint16_t csum_start = 0;
+ uint16_t csum_offset = 0;
+ uint16_t num_buffers = 0;
+ };
+
+ /**
+ * VirtIO feature bits relevant to this VirtIO net driver implementation.
+ */
+ struct Features : Genode::Register<64>
+ {
+ struct CSUM : Bitfield<0, 1> { };
+ struct GUEST_CSUM : Bitfield<1, 1> { };
+ struct MTU : Bitfield<3, 1> { };
+ struct MAC : Bitfield<5, 1> { };
+ struct GSO : Bitfield<6, 1> { };
+ struct GUEST_TSO4 : Bitfield<7, 1> { };
+ struct GUEST_TSO6 : Bitfield<8, 1> { };
+ struct GUEST_ECN : Bitfield<9, 1> { };
+ struct GUEST_UFO : Bitfield<10, 1> { };
+ struct HOST_TSO4 : Bitfield<11, 1> { };
+ struct HOST_TSO6 : Bitfield<12, 1> { };
+ struct HOST_ECN : Bitfield<13, 1> { };
+ struct HOST_UFO : Bitfield<14, 1> { };
+ struct MRG_RXBUF : Bitfield<15, 1> { };
+ struct STATUS : Bitfield<16, 1> { };
+ struct CTRL_VQ : Bitfield<17, 1> { };
+ struct CTRL_RX : Bitfield<18, 1> { };
+ struct CTRL_VLAN : Bitfield<19, 1> { };
+ struct GUEST_ANNOUNCE : Bitfield<21, 1> { };
+ struct MQ : Bitfield<22, 1> { };
+ struct CTRL_MAC_ADDR : Bitfield<23, 1> { };
+ struct EVENT_IDX : Bitfield<29, 1> { };
+ struct VERSION_1 : Bitfield<32, 1> { };
+ };
+
+ /**
+ * See section 5.1.4 of VirtIO 1.0 specification.
+ */
+ enum { CONFIG_MAC_BASE = 0, CONFIG_STATUS = 6 };
+ enum { STATUS_LINK_UP = 1 << 0 };
+
+ /**
+ * Available VirtIO queue numbers, see section 5.1.2 of VirtIO 1.0 specification.
+ */
+ enum Vq_id : Genode::uint16_t { RX_VQ = 0, TX_VQ = 1 };
+
+ /**
+ * Each VirtIO queue contains fixed number of buffers. The most common size of the buffer
+ * is 1526 bytes (size of ethernet frame + Virtio_net_header). VirtIO queue size must be
+ * a power of 2. Each VirtIO queue needs some additional space for the descriptor table,
+ * available and used rings. The default VirtIO queue parameter values defined here have
+ * been selected to make Ram_dataspace used by both TX and RX VirtIO queues consume around
+ * 32Kb of RAM.
+ */
+ static const uint16_t DEFAULT_VQ_SIZE = 16;
+ static const uint16_t DEFAULT_VQ_BUF_SIZE = 2020;
+
+ struct Rx_queue_traits
+ {
+ static const bool device_write_only = true;
+ static const bool has_data_payload = true;
+ };
+
+ struct Tx_queue_traits
+ {
+ static const bool device_write_only = false;
+ static const bool has_data_payload = true;
+ };
+
+ typedef Virtio::Queue Rx_queue_type;
+ typedef Virtio::Queue Tx_queue_type;
+ typedef Genode::Signal_handler Signal_handler;
+
+
+ bool const _verbose;
+ Virtio::Device &_device;
+ Hardware_features const _hw_features;
+ 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()
+ {
+ using Status = Virtio::Device::Status;
+
+ if (!_device.set_status(Status::RESET)) {
+ error("Failed to reset the device!");
+ throw Device_init_failed();
+ }
+
+ if (!_device.set_status(Status::ACKNOWLEDGE)) {
+ error("Failed to acknowledge the device!");
+ throw Device_init_failed();
+ }
+
+ if (!_device.set_status(Status::DRIVER)) {
+ _device.set_status(Status::FAILED);
+ error("Device initialization failed!");
+ throw Device_init_failed();
+ }
+ }
+
+ static Nic::Mac_address _read_mac_address(Virtio::Device &device)
+ {
+ Nic::Mac_address mac = {};
+
+ /* See section 2.3.1 of VirtIO 1.0 spec for rationale. */
+ uint32_t before = 0, after = 0;
+ do {
+ before = device.get_config_generation();
+ for (size_t idx = 0; idx < sizeof(mac.addr); ++idx) {
+ mac.addr[idx] = device.read_config(
+ CONFIG_MAC_BASE + idx, Virtio::Device::ACCESS_8BIT);
+ }
+ after = device.get_config_generation();
+ } while (after != before);
+
+ return mac;
+ }
+
+ Hardware_features _init_hw_features(Xml_node const &xml)
+ {
+ _init_virtio_device();
+
+ using Status = Virtio::Device::Status;
+
+ const uint32_t low = _device.get_features(0);
+ const uint32_t high = _device.get_features(1);
+ const Features::access_t device_features = ((uint64_t)high << 32) | low;
+ Features::access_t driver_features = 0;
+
+ /* This driver does not support legacy VirtIO versions. */
+ if (!Features::VERSION_1::get(device_features)) {
+ error("Unsupprted VirtIO device version!");
+ throw Features_init_failed();
+ }
+ Features::VERSION_1::set(driver_features);
+
+ Hardware_features hw_features;
+
+ if (Features::MAC::get(device_features)) {
+ Features::MAC::set(driver_features);
+ hw_features.mac = _read_mac_address(_device);
+ }
+
+ hw_features.mac = xml.attribute_value("mac", hw_features.mac);
+
+ if (hw_features.mac == Nic::Mac_address()) {
+ error("HW mac address missing and not provided via config!");
+ throw Features_init_failed();
+ }
+
+ if (Features::STATUS::get(device_features)) {
+ Features::STATUS::set(driver_features);
+ hw_features.link_status_available = true;
+ }
+
+ _device.set_features(0, (uint32_t)driver_features);
+ _device.set_features(1, (uint32_t)(driver_features >> 32));
+
+ if (!_device.set_status(Status::FEATURES_OK)) {
+ _device.set_status(Status::FAILED);
+ error("Device feature negotiation failed!");
+ throw Features_init_failed();
+ }
+
+ return hw_features;
+ }
+
+ Genode::uint16_t _vq_size(Vq_id vq, Xml_node const &xml, char const *cfg_attr)
+ {
+ const uint16_t max_vq_size = _device.get_max_queue_size(vq);
+
+ if (max_vq_size == 0) {
+ error("VirtIO queue ", (int)vq, " is not available!");
+ throw Queue_init_failed();
+ }
+
+ const uint16_t vq_size = Genode::min(xml.attribute_value(cfg_attr, DEFAULT_VQ_SIZE),
+ max_vq_size);
+
+ if (_verbose)
+ log("VirtIO queue ", (int)vq, " size: ", vq_size, " (max: ", max_vq_size, ")");
+
+ return vq_size;
+ }
+
+ uint16_t _buf_size(Vq_id vq, Xml_node const &xml, char const *cfg_attr)
+ {
+ const uint16_t vq_buf_size = xml.attribute_value(cfg_attr, DEFAULT_VQ_BUF_SIZE );
+ if (_verbose)
+ log("VirtIO queue ", (int)vq, " buffer size: ", Number_of_bytes(vq_buf_size), "b");
+ return vq_buf_size;
+ }
+
+ void _setup_virtio_queues()
+ {
+ if (!_device.configure_queue(RX_VQ, _rx_vq.description())) {
+ error("Failed to initialize rx VirtIO queue!");
+ throw Queue_init_failed();
+ }
+
+ if (!_device.configure_queue(TX_VQ, _tx_vq.description())) {
+ error("Failed to initialize tx VirtIO queue!");
+ throw Queue_init_failed();
+ }
+
+ using Status = Virtio::Device::Status;
+ if (!_device.set_status(Status::DRIVER_OK)) {
+ _device.set_status(Status::FAILED);
+ error("Failed to initialize VirtIO queues!");
+ throw Queue_init_failed();
+ }
+ }
+
+ void _handle_irq()
+ {
+ const uint32_t reasons = _device.read_isr();
+
+ enum { IRQ_USED_RING_UPDATE = 1, IRQ_CONFIG_CHANGE = 2 };
+
+ if (_tx_vq.has_used_buffers())
+ _tx_vq.ack_all_transfers();
+
+ if (reasons & IRQ_USED_RING_UPDATE) {
+ _receive();
+ }
+
+ 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();
+ }
+
+ _irq.ack_irq();
+ }
+
+ 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()) {
+ 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;
+ }
+
+ void _receive()
+ {
+ 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);
+
+ /**
+ * Inform the device the buffers we've just consumed are ready
+ * to used again
+ */
+ _device.notify_buffers_available(RX_VQ);
+ }
+
+ void _handle_packet_stream() override
+ {
+ while (_rx.source()->ack_avail())
+ _rx.source()->release_packet(_rx.source()->get_acked_packet());
+
+ /* 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
+ {
+ /**
+ * According to docs when STATUS feature is not available or has not
+ * been negotiated the driver should assume the link is always active.
+ * See section 5.1.4.2 of VIRTIO 1.0 specification.
+ */
+ if (!_hw_features.link_status_available)
+ return true;
+
+ uint32_t before = 0, after = 0;
+ uint8_t status = 0;
+ do {
+ before = _device.get_config_generation();
+ status = _device.read_config(CONFIG_STATUS, Virtio::Device::ACCESS_8BIT);
+ after = _device.get_config_generation();
+ } while (after != before);
+
+ return status & STATUS_LINK_UP;
+ }
+
+ Nic::Mac_address mac_address() override { return _hw_features.mac; }
+
+ Session_component(Genode::Env &env,
+ Genode::Allocator &rx_block_md_alloc,
+ Virtio::Device &device,
+ Irq_session_capability irq_cap,
+ 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())
+ {
+ _setup_virtio_queues();
+ _irq.sigh(_irq_handler);
+ _irq.ack_irq();
+
+ _link_state_changed();
+
+ 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);
+ }
+};
+
+
+class Virtio_nic::Root : public Genode::Root_component
+{
+ private:
+
+ struct Device_not_found : Genode::Exception { };
+
+ /*
+ * Noncopyable
+ */
+ Root(Root const &) = delete;
+ Root &operator = (Root const &) = delete;
+
+ Genode::Env &_env;
+ Virtio::Device &_device;
+ Irq_session_capability _irq_cap;
+
+ Session_component *_create_session(const char *args) override
+ {
+ 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);
+
+ /*
+ * 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) {
+ error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size);
+ 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(),
+ tx_buf_size, rx_buf_size);
+ } catch (...) { throw Service_denied(); }
+ }
+
+ public:
+
+ Root(Env &env,
+ Allocator &md_alloc,
+ Virtio::Device &device,
+ Irq_session_capability irq_cap)
+ : Root_component(env.ep(), md_alloc),
+ _env(env), _device(device), _irq_cap(irq_cap)
+ { }
+};
diff --git a/repos/os/src/drivers/nic/virtio/config.xsd b/repos/os/src/drivers/nic/virtio/config.xsd
new file mode 100644
index 0000000000..9deae92320
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/config.xsd
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/src/drivers/nic/virtio/mmio_device.cc b/repos/os/src/drivers/nic/virtio/mmio_device.cc
new file mode 100644
index 0000000000..f8f27b3db0
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/mmio_device.cc
@@ -0,0 +1,86 @@
+/*
+ * \brief VirtIO MMIO NIC driver
+ * \author Piotr Tworek
+ * \date 2019-09-27
+ */
+
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "component.h"
+
+namespace Virtio_mmio_nic {
+ using namespace Genode;
+ struct Main;
+}
+
+struct Virtio_mmio_nic::Main
+{
+ struct Device_not_found : Genode::Exception { };
+
+ struct Device_info {
+ using String = Genode::String<64>;
+
+ String name {};
+ size_t io_mem_offset = 0;
+
+ Device_info(Platform::Connection & platform) {
+ bool found = false;
+ platform.with_xml([&] (Xml_node & xml) {
+ xml.for_each_sub_node("device", [&] (Xml_node node) {
+ if (found) return;
+
+ node.for_each_sub_node("property", [&] (Xml_node node) {
+ if ((node.attribute_value("name", String()) == "type") &&
+ (node.attribute_value("value", String()) == "nic")) {
+ found = true; }
+ });
+
+ if (!found) return;
+
+ name = node.attribute_value("name", String());
+
+ node.for_each_sub_node("io_mem", [&] (Xml_node node) {
+ io_mem_offset = node.attribute_value("page_offset", 0);
+ });
+ });
+ });
+ if (!found) {
+ error("No device was found");
+ throw Device_not_found();
+ }
+ }
+ };
+
+ 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() };
+
+ Main(Env &env)
+ try : env(env)
+ {
+ log("--- VirtIO MMIO NIC driver started ---");
+ env.parent().announce(env.ep().manage(root));
+ }
+ catch (...) { env.parent().exit(-1); }
+};
+
+
+void Component::construct(Genode::Env &env)
+{
+ static Virtio_mmio_nic::Main main(env);
+}
diff --git a/repos/os/src/drivers/nic/virtio/pci_device.cc b/repos/os/src/drivers/nic/virtio/pci_device.cc
new file mode 100644
index 0000000000..abef1ca5ea
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/pci_device.cc
@@ -0,0 +1,60 @@
+/*
+ * \brief VirtIO PCI NIC driver
+ * \author Piotr Tworek
+ * \date 2019-09-27
+ */
+
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "component.h"
+
+namespace Virtio_pci_nic {
+ using namespace Genode;
+ struct Main;
+}
+
+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) };
+
+ Platform::Device_capability find_platform_device()
+ {
+ Platform::Device_capability device_cap;
+ pci.with_upgrade([&] () { device_cap = pci.first_device(); });
+
+ if (!device_cap.valid()) throw Device_not_found();
+
+ return device_cap;
+ }
+
+ Main(Env &env)
+ try : env(env), platform_device(find_platform_device())
+ {
+ log("--- VirtIO PCI driver started ---");
+ env.parent().announce(env.ep().manage(root));
+ }
+ catch (...) { env.parent().exit(-1); }
+};
+
+
+void Component::construct(Genode::Env &env)
+{
+ static Virtio_pci_nic::Main main(env);
+}
diff --git a/repos/os/src/drivers/nic/virtio/spec/arm/target.mk b/repos/os/src/drivers/nic/virtio/spec/arm/target.mk
new file mode 100644
index 0000000000..73920eeb1e
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/spec/arm/target.mk
@@ -0,0 +1,3 @@
+REQUIRES = arm
+
+include $(REP_DIR)/src/drivers/nic/virtio/target_mmio.inc
diff --git a/repos/os/src/drivers/nic/virtio/spec/arm_64/target.mk b/repos/os/src/drivers/nic/virtio/spec/arm_64/target.mk
new file mode 100644
index 0000000000..ba17e4f3b6
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/spec/arm_64/target.mk
@@ -0,0 +1,3 @@
+REQUIRES = arm_64
+
+include $(REP_DIR)/src/drivers/nic/virtio/target_mmio.inc
diff --git a/repos/os/src/drivers/nic/virtio/spec/x86/target.mk b/repos/os/src/drivers/nic/virtio/spec/x86/target.mk
new file mode 100644
index 0000000000..e8445fab6e
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/spec/x86/target.mk
@@ -0,0 +1,8 @@
+TARGET = virtio_pci_nic
+REQUIRES = x86
+SRC_CC = pci_device.cc
+LIBS = base
+INC_DIR = $(REP_DIR)/src/drivers/nic/virtio
+CONFIG_XSD = ../../config.xsd
+
+vpath % $(REP_DIR)/src/drivers/nic/virtio
diff --git a/repos/os/src/drivers/nic/virtio/target_mmio.inc b/repos/os/src/drivers/nic/virtio/target_mmio.inc
new file mode 100644
index 0000000000..0723a56c05
--- /dev/null
+++ b/repos/os/src/drivers/nic/virtio/target_mmio.inc
@@ -0,0 +1,7 @@
+TARGET = virtio_mmio_nic
+SRC_CC = mmio_device.cc
+LIBS = base
+INC_DIR = $(REP_DIR)/src/drivers/nic/virtio
+CONFIG_XSD = ../../config.xsd
+
+vpath % $(REP_DIR)/src/drivers/nic/virtio
diff --git a/tool/run/depot.inc b/tool/run/depot.inc
index 7a25196308..0a70438fa8 100644
--- a/tool/run/depot.inc
+++ b/tool/run/depot.inc
@@ -487,7 +487,7 @@ proc drivers_nic_pkg { } {
if {[have_spec imx6q_sabrelite]} { return drivers_nic-imx6q_sabrelite }
if {[have_spec imx7d_sabre]} { return drivers_nic-imx7d_sabre }
if {[have_spec imx8q_evk]} { return drivers_nic-imx8q_evk }
-
+ if {[have_spec virt_qemu]} { return drivers_nic-virt_qemu }
puts stderr "drivers_nic package undefined for this build configuration"
exit 1
diff --git a/tool/run/qemu.inc b/tool/run/qemu.inc
index 7e20b37b0e..b67fca67c9 100644
--- a/tool/run/qemu.inc
+++ b/tool/run/qemu.inc
@@ -29,7 +29,12 @@ proc append_qemu_nic_args { { extra_netdev_args "" } } {
append qemu_args ",$extra_netdev_args"
}
- append qemu_args " -net nic,model=[qemu_nic_model],netdev=net0 "
+ if {[have_spec virt_qemu]} {
+ append qemu_args " -global virtio-mmio.force-legacy=false "
+ append qemu_args " -device virtio-net-device,bus=virtio-mmio-bus.0,netdev=net0 "
+ } else {
+ append qemu_args " -net nic,model=[qemu_nic_model],netdev=net0 "
+ }
}