From: Waldemar Kozaczuk <[email protected]>
Committer: Waldemar Kozaczuk <[email protected]>
Branch: master
Add virtio mmio implementation
Adds implementation of virtio mmio devices
needed to support block and networking devices
on firecracker.
Signed-off-by: Waldemar Kozaczuk <[email protected]>
---
diff --git a/Makefile b/Makefile
--- 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
--- 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
--- a/drivers/virtio-mmio.cc
+++ 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
--- a/drivers/virtio-mmio.hh
+++ 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
--
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.