diff --git a/repos/os/include/virtio/mmio_device.h b/repos/os/include/virtio/mmio_device.h
new file mode 100644
index 0000000000..2217dd4bb6
--- /dev/null
+++ b/repos/os/include/virtio/mmio_device.h
@@ -0,0 +1,201 @@
+/*
+ * \brief VirtIO MMIO device
+ * \author Piotr Tworek
+ * \date 2019-09-27
+ */
+
+/*
+ * 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 _INCLUDE__VIRTIO__MMIO_DEVICE_H_
+#define _INCLUDE__VIRTIO__MMIO_DEVICE_H_
+
+#include
+#include
+#include
+#include
+
+
+namespace Virtio {
+ using namespace Genode;
+ class Device;
+}
+
+
+class Virtio::Device : Genode::Attached_dataspace, Genode::Mmio
+{
+ public:
+
+ struct Invalid_device : Genode::Exception { };
+
+ enum Status : uint8_t
+ {
+ RESET = 0,
+ ACKNOWLEDGE = 1 << 0,
+ DRIVER = 1 << 1,
+ DRIVER_OK = 1 << 2,
+ FEATURES_OK = 1 << 3,
+ FAILED = 1 << 7,
+ };
+
+ enum Access_size : uint8_t
+ {
+ ACCESS_8BIT,
+ ACCESS_16BIT,
+ ACCESS_32BIT,
+ };
+
+ private:
+
+ enum { VIRTIO_MMIO_MAGIC = 0x74726976 };
+
+ /**
+ * Some of the registers are actually 8 bits wide, but according to
+ * section 4.2.2.2 of VIRTIO 1.0 spec "The driver MUST use only 32 bit
+ * wide and aligned reads and writes".
+ */
+ struct Magic : Register<0x000, 32> { };
+ struct Version : Register<0x004, 32> { };
+ struct DeviceID : Register<0x008, 32> { };
+ struct VendorID : Register<0x00C, 32> { };
+ struct DeviceFeatures : Register<0x010, 32> { };
+ struct DeviceFeaturesSel : Register<0x014, 32> { };
+ struct DriverFeatures : Register<0x020, 32> { };
+ struct DriverFeaturesSel : Register<0x024, 32> { };
+ struct QueueSel : Register<0x030, 32> { };
+ struct QueueNumMax : Register<0x034, 32> { };
+ struct QueueNum : Register<0x038, 32> { };
+ struct QueueReady : Register<0x044, 32> { };
+ struct QueueNotify : Register<0x050, 32> { };
+ struct InterruptStatus : Register<0x060, 32> { };
+ struct InterruptAck : Register<0x064, 32> { };
+ struct StatusReg : Register<0x070, 32> { };
+ struct QueueDescLow : Register<0x080, 32> { };
+ struct QueueDescHigh : Register<0x084, 32> { };
+ struct QueueAvailLow : Register<0x090, 32> { };
+ struct QueueAvailHigh : Register<0x094, 32> { };
+ struct QueueUsedLow : Register<0x0A0, 32> { };
+ struct QueueUsedHigh : Register<0x0A4, 32> { };
+ struct ConfigGeneration : Register<0x0FC, 32> { };
+
+ /**
+ * Different views on device configuration space. According to the
+ * VIRTIO 1.0 spec 64 bit wide registers are supposed to be read as
+ * two 32 bit values.
+ */
+ struct Config_8 : Register_array<0x100, 8, 256, 8> { };
+ struct Config_16 : Register_array<0x100, 16, 128, 16> { };
+ struct Config_32 : Register_array<0x100, 32, 64, 32> { };
+
+ /*
+ * Noncopyable
+ */
+ Device(Device const &) = delete;
+ Device &operator = (Device const &) = delete;
+
+ public:
+
+ Device(Genode::Env &env,
+ Genode::Io_mem_dataspace_capability io_mem_ds,
+ size_t offset)
+ : Attached_dataspace(env.rm(), io_mem_ds)
+ , Mmio((addr_t)local_addr() + offset)
+ {
+ if (read() != VIRTIO_MMIO_MAGIC) {
+ throw Invalid_device(); }
+ }
+
+ uint32_t vendor_id() { return read(); }
+ uint32_t device_id() { return read(); }
+ uint8_t get_status() { return read() & 0xff; }
+
+ bool set_status(uint8_t status)
+ {
+ write(status);
+ return read() == status;
+ }
+
+ uint32_t get_features(uint32_t selection)
+ {
+ write(selection);
+ return read();
+ }
+
+ void set_features(uint32_t selection, uint32_t features)
+ {
+ write(selection);
+ write(features);
+ }
+
+ uint8_t get_config_generation() {
+ return read() & 0xff; }
+
+ uint16_t get_max_queue_size(uint16_t queue_index)
+ {
+ write(queue_index);
+ if (read() != 0) {
+ return 0; }
+ return read();
+ }
+
+ uint32_t read_config(uint8_t offset, Access_size size)
+ {
+ switch (size) {
+ case ACCESS_8BIT: return read(offset);
+ case ACCESS_16BIT: return read(offset >> 1);
+ case ACCESS_32BIT: return read(offset >> 2);
+ }
+ return 0;
+ }
+
+ void write_config(uint8_t offset, Access_size size, uint32_t value)
+ {
+ switch (size) {
+ case ACCESS_8BIT: write(value, offset); break;
+ case ACCESS_16BIT: write(value, (offset >> 1)); break;
+ case ACCESS_32BIT: write(value, (offset >> 2)); break;
+ }
+ }
+
+ bool configure_queue(uint16_t queue_index,
+ Virtio::Queue_description desc)
+ {
+ write(queue_index);
+
+ if (read() != 0)
+ return false;
+
+ write(desc.size);
+
+ uint64_t addr = desc.desc;
+ write((uint32_t)addr);
+ write((uint32_t)(addr >> 32));
+
+ addr = desc.avail;
+ write((uint32_t)addr);
+ write((uint32_t)(addr >> 32));
+
+ addr = desc.used;
+ write((uint32_t)addr);
+ write((uint32_t)(addr >> 32));
+
+ write(1);
+ return read() != 0;
+ }
+
+ void notify_buffers_available(uint16_t queue_index) {
+ write(queue_index); }
+
+ uint32_t read_isr()
+ {
+ uint32_t isr = read();
+ write(isr);
+ return isr;
+ }
+};
+
+#endif /* _INCLUDE__VIRTIO__MMIO_DEVICE_H_ */
diff --git a/repos/os/include/virtio/pci_device.h b/repos/os/include/virtio/pci_device.h
new file mode 100644
index 0000000000..4c53d6b43c
--- /dev/null
+++ b/repos/os/include/virtio/pci_device.h
@@ -0,0 +1,282 @@
+/*
+ * \brief VirtIO PCI device
+ * \author Piotr Tworek
+ * \date 2019-09-27
+ */
+
+/*
+ * 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 _INCLUDE__VIRTIO__PCI_DEVICE_H_
+#define _INCLUDE__VIRTIO__PCI_DEVICE_H_
+
+#include
+#include
+#include
+
+namespace Virtio {
+ using namespace Genode;
+ struct Device_mmio;
+ class Device;
+}
+
+struct Virtio::Device_mmio : public Attached_mmio
+{
+ struct DeviceFeatureSelect : Register<0x00, 32> { };
+ struct DeviceFeature : Register<0x04, 32> { };
+ struct DriverFeatureSelect : Register<0x08, 32> { };
+ struct DriverFeature : Register<0x0C, 32> { };
+ struct MsiXConfig : Register<0x10, 16> { };
+ struct NumQueues : Register<0x12, 16> { };
+ struct DeviceStatus : Register<0x14, 8> { };
+ struct ConfigGeneration : Register<0x15, 8> { };
+ struct QueueSelect : Register<0x16, 16> { };
+ struct QueueSize : Register<0x18, 16> { };
+ struct QueueMsixVector : Register<0x1A, 16> { };
+ struct QueueEnable : Register<0x1C, 16> { };
+ struct QueueNotifyOff : Register<0x1E, 16> { };
+ struct QueueDescLow : Register<0x20, 32> { };
+ struct QueueDescHigh : Register<0x24, 32> { };
+ struct QueueAvailLow : Register<0x28, 32> { };
+ struct QueueAvailHigh : Register<0x2C, 32> { };
+ struct QueueUsedLow : Register<0x30, 32> { };
+ struct QueueUsedHigh : Register<0x34, 32> { };
+
+ struct Config_8 : Register_array<0x0, 8, 256, 8> { };
+ struct Config_16 : Register_array<0x0, 16, 128, 16> { };
+ struct Config_32 : Register_array<0x0, 32, 64, 32> { };
+
+ struct IrqReason : Register<0x0, 32> { };
+
+ Device_mmio(Genode::Env &env,
+ Genode::addr_t base,
+ Genode::size_t size)
+ : Attached_mmio(env, base, size, false) { }
+};
+
+class Virtio::Device
+{
+ public:
+
+ struct Configuration_failed : Genode::Exception { };
+
+ enum Status : uint8_t
+ {
+ RESET = 0,
+ ACKNOWLEDGE = 1 << 0,
+ DRIVER = 1 << 1,
+ DRIVER_OK = 1 << 2,
+ FEATURES_OK = 1 << 3,
+ FAILED = 1 << 7,
+ };
+
+ enum Access_size : uint8_t
+ {
+ ACCESS_8BIT,
+ ACCESS_16BIT,
+ ACCESS_32BIT,
+ };
+
+ private:
+
+ /*
+ * Noncopyable
+ */
+ Device(Device const &) = delete;
+ Device &operator = (Device const &) = delete;
+
+ enum {
+ VIRTIO_PCI_BASE_ID = 0x1040,
+ VIRTIO_MSI_NO_VECTOR = 0xffff
+ };
+
+ Genode::Env &_env;
+ Platform::Device_client &_device;
+ uint32_t _notify_offset_multiplier = 0;
+ Genode::Constructible _cfg_common { };
+ Genode::Constructible _dev_config { };
+ Genode::Constructible _notify { };
+ Genode::Constructible _isr { };
+
+
+ void _configure()
+ {
+ typedef Platform::Device Pdev;
+
+ enum { PCI_STATUS = 0x6, PCI_CAPABILITIES = 0x34, };
+
+ auto status = _device.config_read(PCI_STATUS, Pdev::ACCESS_16BIT);
+ if (!(status & 0x10)) {
+ error("PCI capabilities missing according to device status!");
+ throw Configuration_failed();
+ }
+
+ auto addr = _device.config_read(PCI_CAPABILITIES, Pdev::ACCESS_8BIT);
+ addr &= 0xFC;
+
+ while (addr) {
+ enum { ID_VNDR = 0x09 };
+ enum { CAP_ID = 0, CAP_LIST_NEXT = 1 };
+
+ auto const cap_id = _device.config_read(addr + CAP_ID, Pdev::ACCESS_8BIT);
+ auto const cap_next = _device.config_read(addr + CAP_LIST_NEXT, Pdev::ACCESS_8BIT);
+
+ if (cap_id == ID_VNDR) {
+ enum { CFG_TYPE = 0x3, BAR = 0x4, OFFSET = 0x8,
+ LENGTH = 0xC, NOTIFY_OFFSET_MULT = 0x10 };
+ enum { COMMON_CFG = 1, NOTIFY_CFG = 2, ISR_CFG = 3,
+ DEVICE_CFG = 4, PCI_CFG = 5 };
+
+ auto const cfg_type = _device.config_read(addr + CFG_TYPE, Pdev::ACCESS_8BIT);
+ auto const bar = _device.config_read(addr + BAR, Pdev::ACCESS_8BIT);
+ auto const off = _device.config_read(addr + OFFSET, Pdev::ACCESS_32BIT);
+ auto const len = _device.config_read(addr + LENGTH, Pdev::ACCESS_32BIT);
+
+ if (cfg_type == COMMON_CFG) {
+ auto const r = _device.resource(bar);
+ _cfg_common.construct(_env, r.base() + off, len);
+ } else if (cfg_type == DEVICE_CFG) {
+ auto const r = _device.resource(bar);
+ _dev_config.construct(_env, r.base() + off, len);
+ } else if (cfg_type == NOTIFY_CFG) {
+ _notify_offset_multiplier = _device.config_read(
+ addr + NOTIFY_OFFSET_MULT, Pdev::ACCESS_32BIT);
+ auto const r = _device.resource(bar);
+ _notify.construct(_env, r.base() + off, len);
+ } else if (cfg_type == ISR_CFG) {
+ auto const r = _device.resource(bar);
+ _isr.construct(_env, r.base() + off, len);
+ }
+ }
+
+ addr = cap_next;
+ }
+
+ if (!_cfg_common.constructed() || !_dev_config.constructed() ||
+ !_notify.constructed() || !_isr.constructed()) {
+ error("Required VirtIO PCI capabilities not found!");
+ throw Configuration_failed();
+ }
+
+ _cfg_common->write(VIRTIO_MSI_NO_VECTOR);
+ }
+
+ public:
+
+ Device(Genode::Env &env,
+ Platform::Device_client device)
+ : _env(env), _device(device)
+ {
+ _configure();
+ }
+
+ uint32_t vendor_id() { return _device.vendor_id(); }
+ uint32_t device_id() {
+ return _device.device_id() - VIRTIO_PCI_BASE_ID; }
+
+ uint8_t get_status() {
+ return _cfg_common->read(); }
+
+ bool set_status(uint8_t status)
+ {
+ _cfg_common->write(status);
+ return _cfg_common->read() == status;
+ }
+
+ uint32_t get_features(uint32_t selection)
+ {
+ _cfg_common->write(selection);
+ return _cfg_common->read();
+ }
+
+ void set_features(uint32_t selection, uint32_t features)
+ {
+ _cfg_common->write(selection);
+ _cfg_common->write(features);
+ }
+
+ uint8_t get_config_generation() {
+ return _cfg_common->read(); }
+
+ uint16_t get_max_queue_size(uint16_t queue_index)
+ {
+ _cfg_common->write(queue_index);
+ return _cfg_common->read();
+ }
+
+ uint32_t read_config(uint8_t offset, Access_size size)
+ {
+ switch (size) {
+ case Device::ACCESS_8BIT:
+ return _dev_config->read(offset);
+ case Device::ACCESS_16BIT:
+ return _dev_config->read(offset >> 1);
+ case Device::ACCESS_32BIT:
+ return _dev_config->read(offset >> 2);
+ }
+ return 0;
+ }
+
+ void write_config(uint8_t offset, Access_size size, uint32_t value)
+ {
+ switch (size) {
+ case Device::ACCESS_8BIT:
+ _dev_config->write(value, offset);
+ break;
+ case Device::ACCESS_16BIT:
+ _dev_config->write(value, offset >> 1);
+ break;
+ case Device::ACCESS_32BIT:
+ _dev_config->write(value, offset >> 2);
+ break;
+ }
+ }
+
+ bool configure_queue(uint16_t queue_index, Virtio::Queue_description desc)
+ {
+ _cfg_common->write(queue_index);
+
+ if (_cfg_common->read()) {
+ warning("VirtIO queues can't be re-configured after being enabled!");
+ return false;
+ }
+
+ _cfg_common->write(VIRTIO_MSI_NO_VECTOR);
+ if (_cfg_common->read() != VIRTIO_MSI_NO_VECTOR) {
+ error("Failed to disable MSI-X for queue ", queue_index);
+ return false;
+ }
+
+ _cfg_common->write(desc.size);
+
+ uint64_t addr = desc.desc;
+ _cfg_common->write((uint32_t)addr);
+ _cfg_common->write((uint32_t)(addr >> 32));
+
+ addr = desc.avail;
+ _cfg_common->write((uint32_t)addr);
+ _cfg_common->write((uint32_t)(addr >> 32));
+
+ addr = desc.used;
+ _cfg_common->write((uint32_t)addr);
+ _cfg_common->write((uint32_t)(addr >> 32));
+ _cfg_common->write(1);
+ return _cfg_common->read() != 0;
+ }
+
+ void notify_buffers_available(uint16_t queue_index) {
+ _cfg_common->write(queue_index);
+ auto const offset = _cfg_common->read();
+ auto const addr = (offset * _notify_offset_multiplier >> 1) + 1;
+ _notify->local_addr()[addr] = queue_index;
+ }
+
+ uint32_t read_isr() {
+ return _isr->read(); }
+};
+
+#endif /* _INCLUDE__VIRTIO__PCI_DEVICE_H_ */
diff --git a/repos/os/include/virtio/queue.h b/repos/os/include/virtio/queue.h
new file mode 100644
index 0000000000..397996884e
--- /dev/null
+++ b/repos/os/include/virtio/queue.h
@@ -0,0 +1,382 @@
+/*
+ * \brief VirtIO queue implementation
+ * \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.
+ */
+
+#ifndef _INCLUDE__VIRTIO__QUEUE_H_
+#define _INCLUDE__VIRTIO__QUEUE_H_
+
+#include
+#include
+#include
+#include
+
+namespace Virtio
+{
+ template class Queue;
+ struct Queue_default_traits;
+ struct Queue_description;
+}
+
+
+struct Virtio::Queue_description
+{
+ /**
+ * Physical address of the descriptor table.
+ */
+ Genode::addr_t desc;
+
+ /**
+ * Physical address of the available descriptor ring.
+ */
+ Genode::addr_t avail;
+
+ /**
+ * Physcical address of the used descriptor ring.
+ */
+ Genode::addr_t used;
+
+ /**
+ * The size of the descriptor table (number of elements).
+ */
+ Genode::uint16_t size;
+};
+
+
+struct Queue_default_traits
+{
+ /**
+ * The queue is only supposed to be written to by the device.
+ */
+ static const bool device_write_only = false;
+
+ /**
+ * Each queue event has additional data payload associated with it.
+ */
+ static const bool has_data_payload = false;
+};
+
+
+/**
+ * This class implements VirtIO queue interface as defined in section 2.4 of VirtIO 1.0 specification.
+ */
+template
+class Virtio::Queue
+{
+ private:
+
+ /*
+ * Noncopyable
+ */
+ Queue(Queue const &);
+ Queue &operator = (Queue const &);
+
+ protected:
+
+ typedef HEADER_TYPE Header_type;
+
+ struct Descriptor
+ {
+ enum Flags : Genode::uint16_t
+ {
+ NEXT = 1,
+ WRITE = 2,
+ };
+
+ Genode::uint64_t addr;
+ Genode::uint32_t len;
+ Genode::uint16_t flags;
+ Genode::uint16_t next;
+ } __attribute__((packed));
+
+ struct Avail
+ {
+ enum Flags : Genode::uint16_t { NO_INTERRUPT = 1 };
+ Genode::uint16_t flags;
+ Genode::uint16_t idx;
+ Genode::uint16_t ring[];
+ /* Genode::uint16_t used_event; */
+ } __attribute__((packed));
+
+ struct Used
+ {
+ Genode::uint16_t flags;
+ Genode::uint16_t idx;
+ struct {
+ Genode::uint32_t id;
+ Genode::uint32_t len;
+ } ring[];
+ /* Genode::uint16_t avail_event; */
+ } __attribute__((packed));
+
+ Genode::uint16_t const _queue_size;
+ Genode::uint16_t const _buffer_size;
+ Genode::Attached_ram_dataspace _ram_ds;
+ Descriptor *_desc_table = nullptr;
+ Avail *_avail = nullptr;
+ Used *_used = nullptr;
+ Genode::addr_t _buffer_phys_base = 0;
+ Genode::addr_t _buffer_local_base = 0;
+ Genode::uint16_t _last_used_idx = 0;
+ Queue_description _description { 0, 0, 0, 0 };
+
+
+ /* As defined in section 2.4 of VIRTIO 1.0 specification. */
+ static Genode::size_t _desc_size(Genode::uint16_t queue_size) {
+ return 16 * queue_size; }
+ static Genode::size_t _avail_size(Genode::uint16_t queue_size) {
+ return 6 + 2 * queue_size; }
+ static Genode::size_t _used_size(Genode::uint16_t queue_size) {
+ return 6 + 8 * queue_size; }
+
+ Genode::uint16_t _check_buffer_size(Genode::uint16_t buffer_size)
+ {
+ /**
+ * Each buffer in the queue should be big enough to hold
+ * at least VirtIO header.
+ */
+ if (buffer_size < sizeof(Header_type))
+ throw Invalid_buffer_size();
+ return buffer_size;
+ }
+
+ static Genode::size_t _ds_size(Genode::uint16_t queue_size,
+ Genode::uint16_t buffer_size)
+ {
+ Genode::size_t size = _desc_size(queue_size) + _avail_size(queue_size);
+ size = Genode::align_natural(size);
+ /* See section 2.4 of VirtIO 1.0 specification */
+ size += _used_size(queue_size);
+ size = Genode::align_natural(size);
+ return size + (queue_size * Genode::align_natural(buffer_size));
+ }
+
+ void _init_tables()
+ {
+ using namespace Genode;
+
+ Dataspace_client ram_ds_client(_ram_ds.cap());
+
+ uint8_t const *base_phys = (uint8_t *)ram_ds_client.phys_addr();
+ uint8_t const *base_local = _ram_ds.local_addr();
+
+ size_t const avail_offset = _desc_size(_queue_size);
+ size_t const used_offset = align_natural(avail_offset + _avail_size(_queue_size));
+ size_t const buff_offset = align_natural(used_offset + _used_size(_queue_size));
+
+ _desc_table = (Descriptor *)base_local;
+ _avail = (Avail *)(base_local + avail_offset);
+ _used = (Used *)(base_local + used_offset);
+ _buffer_local_base = (addr_t)(base_local + buff_offset);
+ _buffer_phys_base = (addr_t)(base_phys + buff_offset);
+
+ _description.desc = (addr_t)base_phys;
+ _description.avail = (addr_t)(base_phys + avail_offset);
+ _description.used = (addr_t)(base_phys + used_offset);
+ _description.size = _queue_size;
+ }
+
+ void _fill_descriptor_table()
+ {
+ const Genode::uint16_t flags =
+ TRAITS::device_write_only ? Descriptor::Flags::WRITE : 0;
+
+ for (Genode::uint16_t idx = 0; idx < _queue_size; idx++) {
+ _desc_table[idx] = Descriptor {
+ _buffer_phys_base + idx * Genode::align_natural(_buffer_size),
+ _buffer_size, flags, 0 };
+ _avail->ring[idx] = idx;
+ }
+
+ /* Expose all available buffers to the device. */
+ if (TRAITS::device_write_only) {
+ _avail->flags = 0;
+ _avail->idx = _queue_size;
+ }
+ }
+
+ Genode::uint16_t _avail_capacity() const
+ {
+ auto const used_idx = _used->idx;
+ auto const avail_idx = _avail->idx;
+ if (avail_idx >= used_idx) {
+ return _queue_size - avail_idx + used_idx;
+ } else {
+ return used_idx - avail_idx;
+ }
+ }
+
+ void *_buffer_local_addr(Descriptor const *d) {
+ return (void *)(_buffer_local_base + (d->addr - _buffer_phys_base)); }
+
+ public:
+
+ struct Invalid_buffer_size : Genode::Exception { };
+
+ Queue_description const description() const { return _description; }
+
+ bool has_used_buffers() const { return _last_used_idx != _used->idx; }
+
+ void ack_all_transfers() { _last_used_idx = _used->idx;}
+
+ Genode::size_t size() const { return _ds_size(_queue_size, _buffer_size); }
+
+ bool write_data(Header_type const &header,
+ char const *data,
+ Genode::size_t data_size,
+ bool request_irq = true)
+ {
+ static_assert(!TRAITS::device_write_only);
+ static_assert(TRAITS::has_data_payload);
+
+ const int req_desc_count = 1 + (sizeof(header) + data_size) / _buffer_size;
+
+ if (req_desc_count > _avail_capacity())
+ return false;
+
+ Genode::uint16_t avail_idx = _avail->idx;
+ auto *desc = &_desc_table[avail_idx % _queue_size];
+
+ Genode::memcpy(_buffer_local_addr(desc), (void *)&header, sizeof(header));
+ desc->len = sizeof(header);
+
+ Genode::size_t len = Genode::min(_buffer_size - sizeof(header), data_size);
+ Genode::memcpy((char *)_buffer_local_addr(desc) + desc->len, data, len);
+ desc->len += len;
+
+ len = data_size + sizeof(header) - desc->len;
+
+ avail_idx++;
+
+ if (len == 0) {
+ desc->flags = 0;
+ desc->next = 0;
+ _avail->flags = request_irq ? 0 : Avail::Flags::NO_INTERRUPT;
+ _avail->idx = avail_idx;
+ return true;
+ }
+
+ desc->flags = Descriptor::Flags::NEXT;
+ desc->next = avail_idx % _queue_size;
+
+ Genode::size_t data_offset = desc->len;
+ do {
+ desc = &_desc_table[avail_idx % _queue_size];
+ avail_idx++;
+
+ Genode::size_t write_len = Genode::min(_buffer_size, len);
+ Genode::memcpy((char *)_buffer_local_addr(desc), data + data_offset, write_len);
+
+ desc->len = write_len;
+ desc->flags = len > 0 ? Descriptor::Flags::NEXT : 0;
+ desc->next = len > 0 ? (avail_idx % _queue_size) : 0;
+
+ len -= write_len;
+ data_offset += desc->len;
+ } while (len > 0);
+
+ _avail->flags = request_irq ? 0 : Avail::Flags::NO_INTERRUPT;
+ _avail->idx = avail_idx;
+
+ return true;
+ }
+
+ bool write_data(Header_type const &header, bool request_irq = true)
+ {
+ static_assert(!TRAITS::device_write_only);
+ static_assert(!TRAITS::has_data_payload);
+
+ if (_avail_capacity() == 0)
+ return false;
+
+ Genode::uint16_t avail_idx = _avail->idx;
+ auto *desc = &_desc_table[avail_idx % _queue_size];
+
+ Genode::memcpy(_buffer_local_addr(desc), (void *)&header, sizeof(header));
+ desc->len = sizeof(header);
+ desc->flags = 0;
+ desc->next = 0;
+ _avail->flags = request_irq ? 0 : Avail::Flags::NO_INTERRUPT;
+ _avail->idx = ++avail_idx;
+
+ return true;
+ }
+
+ template
+ void read_data(FN const &fn)
+ {
+ static_assert(TRAITS::has_data_payload);
+
+ if (!has_used_buffers())
+ return;
+
+ Genode::uint16_t const idx = _last_used_idx % _queue_size;
+ Genode::uint32_t const len = _used->ring[idx].len;
+
+ auto const *desc = &_desc_table[idx];
+ char const *desc_data = (char *)_buffer_local_addr(desc);
+ Header_type const &header = *((Header_type *)(desc_data));
+ char const *data = desc_data + sizeof(Header_type);
+ Genode::size_t const data_size = len - sizeof(Header_type);
+
+ if (fn(header, data, data_size)) {
+ _last_used_idx++;
+ _avail->idx = _avail->idx + 1;
+ }
+ }
+
+ Header_type read_data()
+ {
+ static_assert(!TRAITS::has_data_payload);
+
+ if (!has_used_buffers())
+ return Header_type();
+
+ Genode::uint16_t const idx = _last_used_idx % _queue_size;
+
+ auto const *desc = &_desc_table[idx];
+ char const *desc_data = (char *)_buffer_local_addr(desc);
+ Header_type const &header = *((Header_type *)(desc_data));
+
+ _last_used_idx++;
+ _avail->idx = _avail->idx + 1;
+
+ return header;
+ }
+
+ void print(Genode::Output& output) const
+ {
+ Genode::print(output, "avail idx: ");
+ Genode::print(output, _avail->idx);
+ Genode::print(output, ", used idx = ");
+ Genode::print(output, _used->idx);
+ Genode::print(output, ", last seen used idx = ");
+ Genode::print(output, _last_used_idx);
+ Genode::print(output, ", capacity = ");
+ Genode::print(output, _avail_capacity());
+ Genode::print(output, ", size = ");
+ Genode::print(output, _queue_size);
+ }
+
+ Queue(Genode::Ram_allocator &ram,
+ Genode::Region_map &rm,
+ Genode::uint16_t queue_size,
+ Genode::uint16_t buffer_size)
+ : _queue_size(queue_size),
+ _buffer_size(_check_buffer_size(buffer_size)),
+ _ram_ds(ram, rm, _ds_size(queue_size, buffer_size), Genode::UNCACHED)
+ {
+ _init_tables();
+ _fill_descriptor_table();
+ }
+};
+
+#endif /* _INCLUDE__VIRTIO__QUEUE_H_ */
diff --git a/repos/os/recipes/api/virtio/content.mk b/repos/os/recipes/api/virtio/content.mk
new file mode 100644
index 0000000000..cad815aa0e
--- /dev/null
+++ b/repos/os/recipes/api/virtio/content.mk
@@ -0,0 +1,3 @@
+MIRRORED_FROM_REP_DIR := include/virtio
+
+include $(REP_DIR)/recipes/api/session.inc
diff --git a/repos/os/recipes/api/virtio/hash b/repos/os/recipes/api/virtio/hash
new file mode 100644
index 0000000000..5d4f4b38fa
--- /dev/null
+++ b/repos/os/recipes/api/virtio/hash
@@ -0,0 +1 @@
+2020-07-03 60db929b95df6142a34d6579a61c72592f7bdbcc