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.
