On Sat, Mar 2, 2019 at 8:28 AM Waldemar Kozaczuk <[email protected]> wrote:
> 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 | 213 +++++++++++++++++++++++++++++++++++++++++ > drivers/virtio-mmio.hh | 151 +++++++++++++++++++++++++++++ > 4 files changed, 370 insertions(+) > create mode 100644 drivers/virtio-mmio.cc > create mode 100644 drivers/virtio-mmio.hh > > diff --git a/Makefile b/Makefile > index ff9f2ed3..b905063c 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 6db2f77c..5ac9af1f 100644 > --- a/arch/x64/arch-setup.cc > +++ b/arch/x64/arch-setup.cc > @@ -63,9 +63,11 @@ struct e820ent { > > 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); > } > > @@ -368,6 +370,9 @@ void arch_init_drivers() > pci::pci_device_enumeration(); > 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..181305e0 > --- /dev/null > +++ b/drivers/virtio-mmio.cc > @@ -0,0 +1,213 @@ > +// > +// Created by wkozaczuk on 12/25/18. > +// > The patch looks good, but can you please just replace this with our mode traditional copyright and license text? Thanks. (you can then just directly commit this patch yourself, if you want). + > +#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..422d86ad > --- /dev/null > +++ b/drivers/virtio-mmio.hh > @@ -0,0 +1,151 @@ > +// > +// Created by wkozaczuk on 12/25/18. > +// > + > +#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. > -- 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.
