Adds implementation of virtio mmio devices
needed to support block and networking devices
on firecracker.

Signed-off-by: Waldemar Kozaczuk <[email protected]>
---
 Makefile               |   1 +
 arch/x64/arch-setup.cc |   5 +
 drivers/virtio-mmio.cc | 216 +++++++++++++++++++++++++++++++++++++++++
 drivers/virtio-mmio.hh | 154 +++++++++++++++++++++++++++++
 4 files changed, 376 insertions(+)
 create mode 100644 drivers/virtio-mmio.cc
 create mode 100644 drivers/virtio-mmio.hh

diff --git a/Makefile b/Makefile
index 377e93e9..3f560807 100644
--- a/Makefile
+++ b/Makefile
@@ -821,6 +821,7 @@ drivers += arch/$(arch)/pvclock-abi.o
 drivers += drivers/virtio.o
 drivers += drivers/virtio-pci-device.o
 drivers += drivers/virtio-vring.o
+drivers += drivers/virtio-mmio.o
 drivers += drivers/virtio-net.o
 drivers += drivers/vmxnet3.o
 drivers += drivers/vmxnet3-queues.o
diff --git a/arch/x64/arch-setup.cc b/arch/x64/arch-setup.cc
index 7350714c..9317dd25 100644
--- a/arch/x64/arch-setup.cc
+++ b/arch/x64/arch-setup.cc
@@ -24,9 +24,11 @@
 
 osv_multiboot_info_type* osv_multiboot_info;
 
+#include "drivers/virtio-mmio.hh"
 void parse_cmdline(multiboot_info_type& mb)
 {
     auto p = reinterpret_cast<char*>(mb.cmdline);
+    virtio::parse_mmio_device_configuration(p);
     osv::parse_cmdline(p);
 }
 
@@ -246,6 +248,9 @@ void arch_init_drivers()
         boot_time.event("pci enumerated");
     }
 
+    // Register any parsed virtio-mmio devices
+    virtio::register_mmio_devices(device_manager::instance());
+
     // Initialize all drivers
     hw::driver_manager* drvman = hw::driver_manager::instance();
     drvman->register_driver(virtio::blk::probe);
diff --git a/drivers/virtio-mmio.cc b/drivers/virtio-mmio.cc
new file mode 100644
index 00000000..12bf3413
--- /dev/null
+++ b/drivers/virtio-mmio.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <vector>
+#include <osv/debug.hh>
+#include "virtio-mmio.hh"
+
+namespace virtio {
+
+// This implements virtio-io mmio device (transport layer, modeled after PSI).
+// Read here - 
https://www.kraxel.org/virtio/virtio-v1.0-cs03-virtio-gpu.html#x1-1080002
+hw_device_id mmio_device::get_id()
+{
+    return hw_device_id(0x1af4, _device_id);
+}
+
+u8 mmio_device::get_status()
+{
+    return mmio_getl(_addr_mmio + VIRTIO_MMIO_STATUS) & 0xff;
+}
+
+void mmio_device::set_status(u8 status)
+{
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_STATUS, status);
+}
+
+u64 mmio_device::get_available_features()
+{
+    u64 features;
+
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DEVICE_FEATURES_SEL, 1);
+    features = mmio_getl(_addr_mmio + VIRTIO_MMIO_DEVICE_FEATURES);
+    features <<= 32;
+
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
+    features |= mmio_getl(_addr_mmio + VIRTIO_MMIO_DEVICE_FEATURES);
+
+    return features;
+}
+
+void mmio_device::set_enabled_features(u64 features)
+{
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DRIVER_FEATURES_SEL, 1);
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DRIVER_FEATURES, (u32)(features >> 32));
+
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_DRIVER_FEATURES, (u32)features);
+}
+
+void mmio_device::kick_queue(int queue_num)
+{
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_NOTIFY, queue_num);
+}
+
+void mmio_device::select_queue(int queue_num)
+{
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_SEL, queue_num);
+    assert(!mmio_getl(_addr_mmio + VIRTIO_MMIO_QUEUE_READY));
+}
+
+u16 mmio_device::get_queue_size()
+{
+    return mmio_getl(_addr_mmio + VIRTIO_MMIO_QUEUE_NUM_MAX) & 0xffff;
+}
+
+void mmio_device::setup_queue(vring* queue)
+{
+    // Set size
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_NUM, queue->size());
+    //
+    // Pass addresses
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_DESC_LOW, 
(u32)queue->get_desc_addr());
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_DESC_HIGH, 
(u32)(queue->get_desc_addr() >> 32));
+
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_AVAIL_LOW, 
(u32)queue->get_avail_addr());
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_AVAIL_HIGH, 
(u32)(queue->get_avail_addr() >> 32));
+
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_USED_LOW, 
(u32)queue->get_used_addr());
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_USED_HIGH, 
(u32)(queue->get_used_addr() >> 32));
+}
+
+void mmio_device::activate_queue(int queue)
+{   //
+    // Make it ready
+    select_queue(queue);
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_QUEUE_READY, 1 );
+}
+
+u8 mmio_device::read_and_ack_isr()
+{
+    unsigned long status = mmio_getl(_addr_mmio + 
VIRTIO_MMIO_INTERRUPT_STATUS);
+    mmio_setl(_addr_mmio + VIRTIO_MMIO_INTERRUPT_ACK, status);
+    return (status & VIRTIO_MMIO_INT_VRING);
+}
+
+u8 mmio_device::read_config(u32 offset)
+{
+    return mmio_getb(_addr_mmio + VIRTIO_MMIO_CONFIG + offset);
+}
+
+void mmio_device::register_interrupt(interrupt_factory irq_factory)
+{
+    _irq.reset(irq_factory.create_gsi_edge_interrupt());
+}
+
+bool mmio_device::parse_config()
+{
+    _addr_mmio = mmio_map(_dev_info._address, _dev_info._size);
+
+    u32 magic = mmio_getl(_addr_mmio + VIRTIO_MMIO_MAGIC_VALUE);
+    if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
+        return false;
+    }
+
+    // Check device version
+    u32 version = mmio_getl(_addr_mmio + VIRTIO_MMIO_VERSION);
+    if (version < 1 || version > 2) {
+        debugf( "Version %ld not supported!\n", version);
+        return false;
+    }
+
+    _device_id = mmio_getl(_addr_mmio + VIRTIO_MMIO_DEVICE_ID);
+    if (_device_id == 0) {
+        //
+        // virtio-mmio device with an ID 0 is a (dummy) placeholder
+        // with no function. End probing now with no error reported.
+        debug( "Dummy virtio-mmio device detected!\n");
+        return false;
+    }
+    _vendor_id = mmio_getl(_addr_mmio + VIRTIO_MMIO_VENDOR_ID);
+
+    debugf("Detected virtio-mmio device: (%ld,%ld)\n", _device_id, _vendor_id);
+    return true;
+}
+
+#define VIRTIO_MMIO_DEVICE_CMDLINE_PREFIX "virtio_mmio.device="
+static mmio_device_info* parse_mmio_device_info(char *cmdline)
+{   //
+    // Parse virtio mmio device information from command line
+    // appended/prepended by VMMs like firecracker. After successfully
+    // parsing any found mmio device info, remove it from the commandline.
+    //
+    // [virtio_mmio.]device=<size>@<baseaddr>:<irq>[:<id>]
+    char *prefix_pos = strstr(cmdline,VIRTIO_MMIO_DEVICE_CMDLINE_PREFIX);
+    if (!prefix_pos)
+        return nullptr;
+
+    u64 size = 0;
+    char *size_pos = prefix_pos + strlen(VIRTIO_MMIO_DEVICE_CMDLINE_PREFIX);
+    if (sscanf(size_pos,"%ld", &size) != 1)
+        return nullptr;
+
+    char *at_pos = strstr(size_pos,"@");
+    if (!at_pos)
+        return nullptr;
+
+    switch(*(at_pos - 1)) {
+        case 'k':
+        case 'K':
+            size *= 1024;
+            break;
+        case 'm':
+        case 'M':
+            size *= (1024 * 1024);
+            break;
+        default:
+            break;
+    }
+
+    u64 irq = 0, address = 0;
+    if (sscanf(at_pos, "@%lli:%u", &address, &irq) != 2)
+        return nullptr;
+
+    // Find first white-character or null as an end of device description
+    auto desc_end_pos = at_pos;
+    while (*desc_end_pos != 0 && !isspace(*desc_end_pos))
+        desc_end_pos++;
+
+    // Remove conf info part from cmdline by copying over remaining part
+    do {
+       *prefix_pos = *desc_end_pos++;
+    } while (*prefix_pos++);
+
+    return new mmio_device_info(address, size, irq);
+}
+
+static std::vector<struct mmio_device_info> *mmio_device_info_entries = 0;
+
+void parse_mmio_device_configuration(char *cmdline)
+{   //
+    // We are assuming the mmio devices information is appended to the
+    // command line (at least it is the case with the firecracker) so
+    // once we parse those we strip it away so only plain OSv command line is 
left
+    mmio_device_info_entries = new std::vector<struct mmio_device_info>();
+    for( auto device_info = parse_mmio_device_info(cmdline); device_info != 
nullptr; device_info = parse_mmio_device_info(cmdline))
+        mmio_device_info_entries->push_back(*device_info);
+}
+
+void register_mmio_devices(hw::device_manager *dev_manager)
+{
+    for (auto info : *mmio_device_info_entries) {
+        auto device = new mmio_device(info);
+        if (device->parse_config()) {
+            dev_manager->register_device(device);
+        }
+        else {
+            delete device;
+        }
+    }
+}
+
+}
diff --git a/drivers/virtio-mmio.hh b/drivers/virtio-mmio.hh
new file mode 100644
index 00000000..024190ac
--- /dev/null
+++ b/drivers/virtio-mmio.hh
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef VIRTIO_MMIO_DEVICE_HH
+#define VIRTIO_MMIO_DEVICE_HH
+
+#include <osv/types.h>
+#include <osv/mmio.hh>
+#include "virtio-device.hh"
+#include "virtio-vring.hh"
+
+using namespace hw;
+
+/* Magic value ("virt" string) - Read Only */
+#define VIRTIO_MMIO_MAGIC_VALUE                0x000
+
+/* Virtio device version - Read Only */
+#define VIRTIO_MMIO_VERSION            0x004
+
+/* Virtio device ID - Read Only */
+#define VIRTIO_MMIO_DEVICE_ID          0x008
+
+/* Virtio vendor ID - Read Only */
+#define VIRTIO_MMIO_VENDOR_ID          0x00c
+
+/* Bitmask of the features supported by the device (host)
+ * (32 bits per set) - Read Only */
+#define VIRTIO_MMIO_DEVICE_FEATURES    0x010
+
+/* Device (host) features set selector - Write Only */
+#define VIRTIO_MMIO_DEVICE_FEATURES_SEL        0x014
+
+/* Bitmask of features activated by the driver (guest)
+ * (32 bits per set) - Write Only */
+#define VIRTIO_MMIO_DRIVER_FEATURES    0x020
+
+/* Activated features set selector - Write Only */
+#define VIRTIO_MMIO_DRIVER_FEATURES_SEL        0x024
+
+/* Queue selector - Write Only */
+#define VIRTIO_MMIO_QUEUE_SEL          0x030
+
+/* Maximum size of the currently selected queue - Read Only */
+#define VIRTIO_MMIO_QUEUE_NUM_MAX      0x034
+
+/* Queue size for the currently selected queue - Write Only */
+#define VIRTIO_MMIO_QUEUE_NUM          0x038
+
+/* Ready bit for the currently selected queue - Read Write */
+#define VIRTIO_MMIO_QUEUE_READY                0x044
+
+/* Queue notifier - Write Only */
+#define VIRTIO_MMIO_QUEUE_NOTIFY       0x050
+
+/* Interrupt status - Read Only */
+#define VIRTIO_MMIO_INTERRUPT_STATUS   0x060
+
+/* Interrupt acknowledge - Write Only */
+#define VIRTIO_MMIO_INTERRUPT_ACK      0x064
+
+/* Device status register - Read Write */
+#define VIRTIO_MMIO_STATUS             0x070
+
+/* Selected queue's Descriptor Table address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_DESC_LOW     0x080
+#define VIRTIO_MMIO_QUEUE_DESC_HIGH    0x084
+
+/* Selected queue's Available Ring address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_AVAIL_LOW    0x090
+#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH   0x094
+
+/* Selected queue's Used Ring address, 64 bits in two halves */
+#define VIRTIO_MMIO_QUEUE_USED_LOW     0x0a0
+#define VIRTIO_MMIO_QUEUE_USED_HIGH    0x0a4
+
+/* Configuration atomicity value */
+#define VIRTIO_MMIO_CONFIG_GENERATION  0x0fc
+
+/* The config space is defined by each driver as
+ * the per-driver configuration space - Read Write */
+#define VIRTIO_MMIO_CONFIG             0x100
+
+#define VIRTIO_MMIO_INT_VRING          (1 << 0)
+#define VIRTIO_MMIO_INT_CONFIG         (1 << 1)
+
+namespace virtio {
+
+struct mmio_device_info {
+    mmio_device_info(u64 address, u64 size, unsigned int irq) :
+        _address(address), _size(size), _irq_no(irq) {}
+
+    u64 _address;
+    u64 _size;
+    unsigned int _irq_no;
+};
+
+class mmio_device : public virtio_device {
+public:
+    mmio_device(mmio_device_info dev_info) :
+        _dev_info(dev_info), _vendor_id(0), _device_id(0), _addr_mmio(0) {}
+
+    virtual ~mmio_device() {}
+
+    virtual hw_device_id get_id();
+
+    virtual void init() {}
+    virtual void print() {}
+    virtual void reset() {}
+
+    virtual unsigned get_irq() { return _dev_info._irq_no; }
+    virtual u8 read_and_ack_isr();
+    virtual void register_interrupt(interrupt_factory irq_factory);
+
+    virtual void select_queue(int queue);
+    virtual u16 get_queue_size();
+    virtual void setup_queue(vring *queue);
+    virtual void activate_queue(int queue);
+    virtual void kick_queue(int queue);
+
+    virtual u64 get_available_features();
+    virtual void set_enabled_features(u64 features);
+
+    virtual u8 get_status();
+    virtual void set_status(u8 status);
+
+    virtual u8 read_config(u32 offset);
+
+    virtual void dump_config() {}
+
+    virtual bool is_modern() { return true; };
+    virtual size_t get_vring_alignment() { return PAGE_SIZE; }
+
+    bool parse_config();
+
+private:
+    mmio_device_info _dev_info;
+    //u64 _id;
+    u16 _vendor_id;
+    u16 _device_id;
+
+    mmioaddr_t _addr_mmio;
+    std::unique_ptr<gsi_edge_interrupt> _irq;
+};
+
+void parse_mmio_device_configuration(char *cmdline);
+void register_mmio_devices(hw::device_manager *dev_manager);
+
+}
+
+#endif //VIRTIO_MMIO_DEVICE_HH
-- 
2.19.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to