This patch refactors virtio layer in order to support modern PCI and mmio devices. It is also a step 0 on the list of tasks to make OSv boot on AWS firecracker.
This patch introduces virtio_device class that abstracts virtio transport layer. This way virtio_driver class and its specializations can interact with host device without being tied to a specific transport (PCI, mmio, etc). The virtio_pci_device and virtio_legacy_pci_device classes define specializations of virtio_device for PCI transport. This patch moves all PCI-related logic from virtio_driver to virtio_legacy_pci_device class and effectively makes virtio driver classes transport agnostic. Follow-up patches will provide implementations of virtio_modern_pci_device and future virtio_mmio_device. The interrupt_factory struct defines a mechanism used by virtio drivers to specify particulars of creating/registering proper interrupt logic for each transport. Additionally this patch slightly changes a sequence of initialization steps so that it conforms to the list specified by virtio specification (please see section 3.1 of http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html). Finally this patch also removes some unused code. Signed-off-by: Waldemar Kozaczuk <[email protected]> --- Makefile | 1 + drivers/pci-generic.cc | 25 +++++- drivers/virtio-blk.cc | 37 +++++--- drivers/virtio-blk.hh | 6 +- drivers/virtio-device.hh | 86 ++++++++++++++++++ drivers/virtio-net.cc | 62 +++++++------ drivers/virtio-net.hh | 12 ++- drivers/virtio-pci-device.cc | 170 +++++++++++++++++++++++++++++++++++ drivers/virtio-pci-device.hh | 143 +++++++++++++++++++++++++++++ drivers/virtio-rng.cc | 25 ++++-- drivers/virtio-rng.hh | 7 +- drivers/virtio-scsi.cc | 40 +++++---- drivers/virtio-scsi.hh | 6 +- drivers/virtio-vring.cc | 15 ++-- drivers/virtio-vring.hh | 6 +- drivers/virtio.cc | 150 +++++++------------------------ drivers/virtio.hh | 91 +++---------------- drivers/vmw-pvscsi.hh | 4 +- 18 files changed, 598 insertions(+), 288 deletions(-) create mode 100644 drivers/virtio-device.hh create mode 100644 drivers/virtio-pci-device.cc create mode 100644 drivers/virtio-pci-device.hh diff --git a/Makefile b/Makefile index ea126f10..4002b29c 100644 --- a/Makefile +++ b/Makefile @@ -819,6 +819,7 @@ drivers += $(libtsm) drivers += drivers/vga.o drivers/kbd.o drivers/isa-serial.o drivers += arch/$(arch)/pvclock-abi.o drivers += drivers/virtio.o +drivers += drivers/virtio-pci-device.o drivers += drivers/virtio-vring.o drivers += drivers/virtio-net.o drivers += drivers/vmxnet3.o diff --git a/drivers/pci-generic.cc b/drivers/pci-generic.cc index 61e7a0f5..20fa87f4 100644 --- a/drivers/pci-generic.cc +++ b/drivers/pci-generic.cc @@ -16,6 +16,8 @@ #include "drivers/pci-function.hh" #include "drivers/pci-bridge.hh" #include "drivers/pci-device.hh" +#include "drivers/virtio.hh" +#include "drivers/virtio-pci-device.hh" namespace pci { @@ -93,11 +95,28 @@ bool check_bus(u16 bus) break; } - if (!device_manager::instance()->register_device(dev)) { + hw_device *dev_to_register = dev; + // + // Create virtio_device if vendor is VIRTIO_VENDOR_ID + if (dev->get_vendor_id() == virtio::VIRTIO_VENDOR_ID) { + if (auto pci_dev = dynamic_cast<device*>(dev)) { + dev_to_register = virtio::create_virtio_pci_device(pci_dev); + if (!dev_to_register) { + pci_e("Error: couldn't create virtio pci device %02x:%02x.%x", + bus, slot, func); + delete dev; + } + } + else + pci_e("Error: expected regular pci device %02x:%02x.%x", + bus, slot, func); + } + + if (dev_to_register && !device_manager::instance()->register_device(dev_to_register)) { pci_e("Error: couldn't register device %02x:%02x.%x", - bus, slot, func); + bus, slot, func); //TODO: Need to beautify it as multiple instances of the device may exist - delete dev; + delete dev_to_register; } // test for multiple functions diff --git a/drivers/virtio-blk.cc b/drivers/virtio-blk.cc index a3f7898d..6b439ada 100644 --- a/drivers/virtio-blk.cc +++ b/drivers/virtio-blk.cc @@ -10,7 +10,6 @@ #include "drivers/virtio.hh" #include "drivers/virtio-blk.hh" -#include "drivers/pci-device.hh" #include <osv/interrupt.hh> #include <osv/mempool.hh> @@ -24,6 +23,7 @@ #include <osv/sched.hh> #include "osv/trace.hh" +#include "osv/aligned_new.hh" #include <osv/device.h> #include <osv/bio.h> @@ -100,7 +100,7 @@ struct driver blk_driver = { bool blk::ack_irq() { - auto isr = virtio_conf_readb(VIRTIO_PCI_ISR); + auto isr = _dev.read_and_ack_isr(); auto queue = get_virt_queue(0); if (isr) { @@ -112,33 +112,44 @@ bool blk::ack_irq() } -blk::blk(pci::device& pci_dev) - : virtio_driver(pci_dev), _ro(false) +blk::blk(virtio_device& virtio_dev) + : virtio_driver(virtio_dev), _ro(false) { _driver_name = "virtio-blk"; _id = _instance++; virtio_i("VIRTIO BLK INSTANCE %d", _id); + // Steps 4 & 5 - negotiate and confirm features setup_features(); read_config(); + // Step 7 - generic init of virtqueues + probe_virt_queues(); + //register the single irq callback for the block sched::thread* t = sched::thread::make([this] { this->req_done(); }, sched::thread::attr().name("virtio-blk")); t->start(); auto queue = get_virt_queue(0); - if (pci_dev.is_msix()) { - _msi.easy_register({ { 0, [=] { queue->disable_interrupts(); }, t } }); - } else { - _irq.reset(new pci_interrupt(pci_dev, - [=] { return ack_irq(); }, - [=] { t->wake(); })); - } + + interrupt_factory int_factory; + int_factory.register_msi_bindings = [queue, t](interrupt_manager &msi) { + msi.easy_register( {{ 0, [=] { queue->disable_interrupts(); }, t }}); + }; + + int_factory.create_pci_interrupt = [this,t](pci::device &pci_dev) { + return new pci_interrupt( + pci_dev, + [=] { return this->ack_irq(); }, + [=] { t->wake(); }); + }; + _dev.register_interrupt(int_factory); // Enable indirect descriptor queue->set_use_indirect(true); + // Step 8 add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK); struct blk_priv* prv; @@ -164,7 +175,7 @@ blk::~blk() void blk::read_config() { //read all of the block config (including size, mce, topology,..) in one shot - virtio_conf_read(virtio_pci_config_offset(), &_config, sizeof(_config)); + virtio_conf_read(0, &_config, sizeof(_config)); trace_virtio_blk_read_config_capacity(_config.capacity); @@ -308,7 +319,7 @@ u32 blk::get_driver_features() hw_driver* blk::probe(hw_device* dev) { - return virtio::probe<blk, VIRTIO_BLK_DEVICE_ID>(dev); + return virtio::probe<blk, VIRTIO_ID_BLOCK>(dev); } } diff --git a/drivers/virtio-blk.hh b/drivers/virtio-blk.hh index 17cf4d18..b707b5bf 100644 --- a/drivers/virtio-blk.hh +++ b/drivers/virtio-blk.hh @@ -8,7 +8,7 @@ #ifndef VIRTIO_BLK_DRIVER_H #define VIRTIO_BLK_DRIVER_H #include "drivers/virtio.hh" -#include "drivers/pci-device.hh" +#include "drivers/virtio-device.hh" #include <osv/bio.h> namespace virtio { @@ -31,7 +31,6 @@ public: }; enum { - VIRTIO_BLK_DEVICE_ID = 0x1001, VIRTIO_BLK_ID_BYTES = 20, /* ID string length */ /* @@ -118,7 +117,7 @@ public: u8 status; }; - explicit blk(pci::device& dev); + explicit blk(virtio_device& dev); virtual ~blk(); virtual std::string get_name() const { return _driver_name; } @@ -157,7 +156,6 @@ private: bool _ro; // This mutex protects parallel make_request invocations mutex _lock; - std::unique_ptr<pci_interrupt> _irq; }; } diff --git a/drivers/virtio-device.hh b/drivers/virtio-device.hh new file mode 100644 index 00000000..93f65a4b --- /dev/null +++ b/drivers/virtio-device.hh @@ -0,0 +1,86 @@ +/* + * 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_DEVICE_HH +#define VIRTIO_DEVICE_HH + +#include <osv/types.h> +#include <osv/pci.hh> +#include <osv/interrupt.hh> +#include <osv/msi.hh> +#include "virtio-vring.hh" +#include "device.hh" + +using namespace hw; + +#define VIRTIO_ALIGN(x,alignment) ((x + (alignment-1)) & ~(alignment-1)) + +namespace virtio { + +// Allows virtio drivers specify how to instantiate interrupts or +// register msi bindings. The associated virtio-device will +// use adequate functor to create correct interrupt in order +// to register it. +struct interrupt_factory { + std::function<void(interrupt_manager &)> register_msi_bindings = nullptr; + std::function<pci_interrupt *(pci::device &)> create_pci_interrupt = nullptr; + std::function<gsi_edge_interrupt *()> create_gsi_edge_interrupt = nullptr; +}; + +// Defines virtio transport abstraction used by virtio-driver +// to communicate with virtio device. The specializations of this +// include virtio pci device (legacy and modern) as well +// as virtio mmio device. This abstraction allows virtio driver +// not be tied to any specific transport (pci, mmio). +// +// From the specification: +// "Each device consists of the following parts: +// - Device status field - see section 2.1 of the spec +// - Feature bits - see section 2.2 of the spec +// - Device Configuration space - see section 2.3 of the spec +// - One or more virtqueues" - see section 2.4 of the spec +// +// For details please VIRTIO 1.0 specification here - +// http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html. +class virtio_device : public hw_device { +public: + virtual ~virtio_device() {}; + + virtual void init() = 0; + + virtual unsigned get_irq() = 0; + virtual u8 read_and_ack_isr() = 0; + virtual void register_interrupt(interrupt_factory irq_factory) = 0; + + virtual void select_queue(int queue) = 0; + virtual u16 get_queue_size() = 0; + virtual void setup_queue(vring *queue) = 0; + virtual void kick_queue(int queue) = 0; + + virtual u64 get_available_features() = 0; + virtual bool get_available_feature_bit(int bit) = 0; + + virtual void set_enabled_features(u64 features) = 0; + virtual u64 get_enabled_features() = 0; + virtual bool get_enabled_feature_bit(int bit) = 0; + + // From the spec: + // "The device status field provides a simple low-level indication of + // the completed steps of the device initialization sequence". + virtual u8 get_status() = 0; + virtual void set_status(u8 status) = 0; + + virtual u8 read_config(u32 offset) = 0; + virtual void dump_config() = 0; + + virtual bool is_modern() = 0; + virtual size_t get_vring_alignment() = 0; +}; + +} + +#endif //VIRTIO_DEVICE_HH diff --git a/drivers/virtio-net.cc b/drivers/virtio-net.cc index b820c36c..3f75cea5 100644 --- a/drivers/virtio-net.cc +++ b/drivers/virtio-net.cc @@ -10,7 +10,6 @@ #include "drivers/virtio.hh" #include "drivers/virtio-net.hh" -#include "drivers/pci-device.hh" #include <osv/interrupt.hh> #include <osv/mempool.hh> @@ -217,7 +216,7 @@ void net::fill_qstats(const struct txq& txq, struct if_data* out_data) const bool net::ack_irq() { - auto isr = virtio_conf_readb(VIRTIO_PCI_ISR); + auto isr = _dev.read_and_ack_isr(); if (isr) { _rxq.vqueue->disable_interrupts(); @@ -225,24 +224,31 @@ bool net::ack_irq() } else { return false; } - } -net::net(pci::device& dev) - : virtio_driver(dev), - _rxq(get_virt_queue(0), [this] { this->receiver(); }), - _txq(this, get_virt_queue(1)) +void net::init() { - sched::thread* poll_task = _rxq.poll_task.get(); + // Steps 4 & 5 - negotiate and confirm features + setup_features(); + read_config(); - poll_task->set_priority(sched::thread::priority_infinity); + // Step 7 - generic init of virtqueues + probe_virt_queues(); +} +net::net(virtio_device& dev) + : virtio_driver(dev), + _pre_init(this), + _rxq(get_virt_queue(0), [this] { this->receiver(); }), + _txq(this, get_virt_queue(1)) +{ _driver_name = "virtio-net"; virtio_i("VIRTIO NET INSTANCE"); _id = _instance++; - setup_features(); - read_config(); + sched::thread* poll_task = _rxq.poll_task.get(); + + poll_task->set_priority(sched::thread::priority_infinity); _hdr_size = _mergeable_bufs ? sizeof(net_hdr_mrg_rxbuf) : sizeof(net_hdr); @@ -290,19 +296,25 @@ net::net(pci::device& dev) ether_ifattach(_ifn, _config.mac); - if (dev.is_msix()) { - _msi.easy_register({ - { 0, [&] { _rxq.vqueue->disable_interrupts(); }, poll_task }, - { 1, [&] { _txq.vqueue->disable_interrupts(); }, nullptr } - }); - } else { - _irq.reset(new pci_interrupt(dev, - [=] { return this->ack_irq(); }, - [=] { poll_task->wake(); })); - } + interrupt_factory int_factory; + int_factory.register_msi_bindings = [this,poll_task](interrupt_manager &msi) { + msi.easy_register({ + { 0, [&] { this->_rxq.vqueue->disable_interrupts(); }, poll_task }, + { 1, [&] { this->_txq.vqueue->disable_interrupts(); }, nullptr } + }); + }; + + int_factory.create_pci_interrupt = [this,poll_task](pci::device &pci_dev) { + return new pci_interrupt( + pci_dev, + [=] { return this->ack_irq(); }, + [=] { poll_task->wake(); }); + }; + _dev.register_interrupt(int_factory); fill_rx_ring(); + // Step 8 add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK); } @@ -324,7 +336,7 @@ net::~net() void net::read_config() { //read all of the net config in one shot - virtio_conf_read(virtio_pci_config_offset(), &_config, sizeof(_config)); + virtio_conf_read(0, &_config, sizeof(_config)); if (get_guest_feature_bit(VIRTIO_NET_F_MAC)) net_i("The mac addr of the device is %x:%x:%x:%x:%x:%x", @@ -856,12 +868,12 @@ u32 net::get_driver_features() hw_driver* net::probe(hw_device* dev) { - if (auto pci_dev = dynamic_cast<pci::device*>(dev)) { - if (pci_dev->get_id() == hw_device_id(VIRTIO_VENDOR_ID, VIRTIO_NET_DEVICE_ID)) { + if (auto virtio_dev = dynamic_cast<virtio_device*>(dev)) { + if (virtio_dev->get_id() == hw_device_id(VIRTIO_VENDOR_ID, VIRTIO_ID_NET)) { if (opt_maxnic && maxnic-- <= 0) { return nullptr; } else { - return aligned_new<net>(*pci_dev); + return aligned_new<net>(*virtio_dev); } } } diff --git a/drivers/virtio-net.hh b/drivers/virtio-net.hh index ad323e69..adc93b39 100644 --- a/drivers/virtio-net.hh +++ b/drivers/virtio-net.hh @@ -53,7 +53,6 @@ public: }; enum { - VIRTIO_NET_DEVICE_ID = 0x1000, VIRTIO_NET_S_LINK_UP = 1, /* Link is up */ VIRTIO_NET_S_ANNOUNCE = 2, /* Announcement is needed */ VIRTIO_NET_OK = 0, @@ -204,8 +203,9 @@ public: u16 virtqueue_pairs; }; - explicit net(pci::device& dev); + explicit net(virtio_device& dev); virtual ~net(); + void init(); virtual std::string get_name() const { return _driver_name; } void read_config(); @@ -269,8 +269,6 @@ private: u32 _hdr_size; - std::unique_ptr<pci_interrupt> _irq; - struct rxq_stats { u64 rx_packets; /* if_ipackets */ u64 rx_bytes; /* if_ibytes */ @@ -299,6 +297,12 @@ private: wakeup_stats tx_wakeup_stats; }; + struct pre_init { + explicit pre_init(virtio::net *_net) { + _net->init(); + } + } _pre_init; + /* Single Rx queue object */ struct rxq { rxq(vring* vq, std::function<void ()> poll_func) diff --git a/drivers/virtio-pci-device.cc b/drivers/virtio-pci-device.cc new file mode 100644 index 00000000..bfd20a04 --- /dev/null +++ b/drivers/virtio-pci-device.cc @@ -0,0 +1,170 @@ +/* + * 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 "drivers/virtio-pci-device.hh" + +namespace virtio { + +virtio_pci_device::virtio_pci_device(pci::device *dev) + : virtio_device() + , _dev(dev) + , _msi(dev) +{ +} + +virtio_pci_device::~virtio_pci_device() { + delete _dev; +} + +void virtio_pci_device::dump_config() +{ + u8 B, D, F; + _dev->get_bdf(B, D, F); + + virtio_d("version:%s, type id:%d, pci device [%x:%x.%x] vid:id=%x:%x", + get_version(), get_type_id(), + (u16)B, (u16)D, (u16)F, + _dev->get_vendor_id(), + _dev->get_device_id()); +} + +void virtio_pci_device::init() +{ + bool status = parse_pci_config(); + assert(status); + + _dev->set_bus_master(true); + + _dev->msix_enable(); +} + +void virtio_pci_device::register_interrupt(interrupt_factory irq_factory) +{ + if (irq_factory.register_msi_bindings && _dev->is_msix()) { + irq_factory.register_msi_bindings(_msi); + } else { + _irq.reset(irq_factory.create_pci_interrupt(*_dev)); + } +} + +virtio_legacy_pci_device::virtio_legacy_pci_device(pci::device *dev) + : virtio_pci_device(dev) +{ +} + +void virtio_legacy_pci_device::kick_queue(int queue) +{ + virtio_conf_writew(VIRTIO_PCI_QUEUE_NOTIFY, queue); +} + +void virtio_legacy_pci_device::setup_queue(vring *queue) +{ + if (_dev->is_msix()) { + // Setup queue_id:entry_id 1:1 correlation... + virtio_conf_writew(VIRTIO_MSI_QUEUE_VECTOR, queue->index()); + if (virtio_conf_readw(VIRTIO_MSI_QUEUE_VECTOR) != queue->index()) { + virtio_e("Setting MSIx entry for queue %d failed.", queue->index()); + return; + } + } + // Tell host about pfn + // TODO: Yak, this is a bug in the design, on large memory we'll have PFNs > 32 bit + // Dor to notify Rusty + virtio_conf_writel(VIRTIO_PCI_QUEUE_PFN, (u32)(queue->get_paddr() >> VIRTIO_PCI_QUEUE_ADDR_SHIFT)); +} + +void virtio_legacy_pci_device::select_queue(int queue) +{ + virtio_conf_writew(VIRTIO_PCI_QUEUE_SEL, queue); +} + +u16 virtio_legacy_pci_device::get_queue_size() +{ + return virtio_conf_readw(VIRTIO_PCI_QUEUE_NUM); +} + +bool virtio_legacy_pci_device::get_virtio_config_bit(u32 offset, int bit) +{ + return (virtio_conf_readl(offset) & (1 << bit)) != 0; +} + +u64 virtio_legacy_pci_device::get_available_features() +{ + return virtio_conf_readl(VIRTIO_PCI_HOST_FEATURES); +} + +bool virtio_legacy_pci_device::get_available_feature_bit(int bit) +{ + return get_virtio_config_bit(VIRTIO_PCI_HOST_FEATURES, bit); +} + +void virtio_legacy_pci_device::set_enabled_features(u64 features) +{ + virtio_conf_writel(VIRTIO_PCI_GUEST_FEATURES, (u32)features); +} + +u64 virtio_legacy_pci_device::get_enabled_features() +{ + return virtio_conf_readl(VIRTIO_PCI_GUEST_FEATURES); +} + +bool virtio_legacy_pci_device::get_enabled_feature_bit(int bit) +{ + return get_virtio_config_bit(VIRTIO_PCI_GUEST_FEATURES, bit); +} + +u8 virtio_legacy_pci_device::get_status() +{ + return virtio_conf_readb(VIRTIO_PCI_STATUS); +} + +void virtio_legacy_pci_device::set_status(u8 status) +{ + virtio_conf_writeb(VIRTIO_PCI_STATUS, status); +} + +u8 virtio_legacy_pci_device::read_config(u32 offset) +{ + auto conf_start = _dev->is_msix_enabled()? 24 : 20; + return _bar1->readb(conf_start + offset); +} + +u8 virtio_legacy_pci_device::read_and_ack_isr() +{ + return virtio_conf_readb(VIRTIO_PCI_ISR); +} + +bool virtio_legacy_pci_device::parse_pci_config() +{ + // Test whether bar1 is present + _bar1 = _dev->get_bar(1); + if (_bar1 == nullptr) { + return false; + } + + // Check ABI version + u8 rev = _dev->get_revision_id(); + if (rev != VIRTIO_PCI_LEGACY_ABI_VERSION) { + virtio_e("Wrong virtio revision=%x", rev); + return false; + } + + // Check device ID + u16 dev_id = _dev->get_device_id(); + if ((dev_id < VIRTIO_PCI_LEGACY_ID_MIN) || (dev_id > VIRTIO_PCI_LEGACY_ID_MAX)) { + virtio_e("Wrong virtio dev id %x", dev_id); + return false; + } + + return true; +} + +virtio_device* create_virtio_pci_device(pci::device *dev) { + return new virtio_legacy_pci_device(dev); +} + +} diff --git a/drivers/virtio-pci-device.hh b/drivers/virtio-pci-device.hh new file mode 100644 index 00000000..42b14934 --- /dev/null +++ b/drivers/virtio-pci-device.hh @@ -0,0 +1,143 @@ +/* + * 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_PCI_DEVICE_HH +#define VIRTIO_PCI_DEVICE_HH + +#include <osv/pci.hh> +#include <osv/interrupt.hh> +#include <osv/msi.hh> + +#include "virtio-device.hh" +#include "virtio.hh" + +namespace virtio { + +enum VIRTIO_PCI_CONFIG { + /* A 32-bit r/o bitmask of the features supported by the host */ + VIRTIO_PCI_HOST_FEATURES = 0, + /* A 32-bit r/o bitmask of the features supported by the host */ + VIRTIO_PCI_HOST_FEATURES_HIGH = 1, + /* A 32-bit r/w bitmask of features activated by the guest */ + VIRTIO_PCI_GUEST_FEATURES = 4, + /* A 32-bit r/w PFN for the currently selected queue */ + VIRTIO_PCI_QUEUE_PFN = 8, + /* A 16-bit r/o queue size for the currently selected queue */ + VIRTIO_PCI_QUEUE_NUM = 12, + /* A 16-bit r/w queue selector */ + VIRTIO_PCI_QUEUE_SEL = 14, + /* A 16-bit r/w queue notifier */ + VIRTIO_PCI_QUEUE_NOTIFY = 16, + /* An 8-bit device status register. */ + VIRTIO_PCI_STATUS = 18, + /* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ + VIRTIO_PCI_ISR = 19, + /* The bit of the ISR which indicates a device configuration change. */ + VIRTIO_PCI_ISR_CONFIG = 0x2, + /* MSI-X registers: only enabled if MSI-X is enabled. */ + /* A 16-bit vector for configuration changes. */ + VIRTIO_MSI_CONFIG_VECTOR = 20, + /* A 16-bit vector for selected queue notifications. */ + VIRTIO_MSI_QUEUE_VECTOR = 22, + /* Vector value used to disable MSI for queue */ + VIRTIO_MSI_NO_VECTOR = 0xffff, + /* Virtio ABI version, this must match exactly */ + VIRTIO_PCI_LEGACY_ABI_VERSION = 0, + /* How many bits to shift physical queue address written to QUEUE_PFN. + * 12 is historical, and due to x86 page size. */ + VIRTIO_PCI_QUEUE_ADDR_SHIFT = 12, + /* The alignment to use between consumer and producer parts of vring. + * x86 pagesize again. */ + VIRTIO_PCI_VRING_ALIGN = 4096, + VIRTIO_PCI_LEGACY_ID_MIN = 0x1000, + VIRTIO_PCI_LEGACY_ID_MAX = 0x103f, +}; + +class virtio_pci_device : public virtio_device { +public: + explicit virtio_pci_device(pci::device *dev); + ~virtio_pci_device(); + + virtual const char *get_version() = 0; + virtual u16 get_type_id() = 0; + + virtual hw_device_id get_id() { return hw_device_id(VIRTIO_VENDOR_ID,get_type_id()); } + virtual void print() { _dev->print(); } + virtual void reset() { _dev->reset(); } + + bool is_attached() { return _dev->is_attached(); } + void set_attached() { _dev->set_attached(); } + + virtual void dump_config(); + virtual void init(); + virtual void register_interrupt(interrupt_factory irq_factory); + + virtual unsigned get_irq() { return 0; } + size_t get_vring_alignment() { return VIRTIO_PCI_VRING_ALIGN; } + +protected: + virtual bool parse_pci_config() = 0; + + pci::device *_dev; + interrupt_manager _msi; + std::unique_ptr<pci_interrupt> _irq; +}; + +class virtio_legacy_pci_device : public virtio_pci_device { +public: + explicit virtio_legacy_pci_device(pci::device *dev); + ~virtio_legacy_pci_device() {} + + virtual const char *get_version() { return "legacy"; } + virtual u16 get_type_id() { return _dev->get_subsystem_id(); }; + + virtual void select_queue(int queue); + virtual u16 get_queue_size(); + virtual void setup_queue(vring *queue); + virtual void kick_queue(int queue); + + virtual u64 get_available_features(); + virtual bool get_available_feature_bit(int bit); + + virtual void set_enabled_features(u64 features); + virtual u64 get_enabled_features(); + virtual bool get_enabled_feature_bit(int bit); + + virtual u8 get_status(); + virtual void set_status(u8 status); + + virtual u8 read_config(u32 offset); + virtual u8 read_and_ack_isr(); + + virtual bool is_modern() { return false; }; +protected: + virtual bool parse_pci_config(); + +private: + // Access the virtio conf address space set by pci bar 1 + bool get_virtio_config_bit(u32 offset, int bit); + + // Access virtio config space + u8 virtio_conf_readb(u32 offset) { return _bar1->readb(offset);}; + u16 virtio_conf_readw(u32 offset) { return _bar1->readw(offset);}; + u32 virtio_conf_readl(u32 offset) { return _bar1->readl(offset);}; + void virtio_conf_writeb(u32 offset, u8 val) { _bar1->writeb(offset, val);}; + void virtio_conf_writew(u32 offset, u16 val) { _bar1->writew(offset, val);}; + void virtio_conf_writel(u32 offset, u32 val) { _bar1->writel(offset, val);}; + + pci::bar* _bar1; +}; + +// Creates appropriate instance of virtio_pci_device +// by reading configuration from PCI device +virtio_device* create_virtio_pci_device(pci::device *dev); + +} + +#endif //VIRTIO_PCI_DEVICE_HH \ No newline at end of file diff --git a/drivers/virtio-rng.cc b/drivers/virtio-rng.cc index d506cb2a..48331ebd 100644 --- a/drivers/virtio-rng.cc +++ b/drivers/virtio-rng.cc @@ -36,13 +36,28 @@ static int virtio_rng_read(void *buf, int size) } namespace virtio { -rng::rng(pci::device& pci_dev) - : virtio_driver(pci_dev) - , _irq(pci_dev, [&] { return ack_irq(); }, [&] { handle_irq(); }) +rng::rng(virtio_device& dev) + : virtio_driver(dev) , _thread(sched::thread::make([&] { worker(); }, sched::thread::attr().name("virtio-rng"))) { + // Steps 4 & 5 - negotiate and confirm features + setup_features(); + + // Step 7 - generic init of virtqueues + probe_virt_queues(); + + interrupt_factory int_factory; + int_factory.create_pci_interrupt = [this](pci::device &pci_dev) { + return new pci_interrupt( + pci_dev, + [=] { return this->ack_irq(); }, + [=] { this->handle_irq(); }); + }; + _dev.register_interrupt(int_factory); + _queue = get_virt_queue(0); + // Step 8 add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK); _thread->start(); @@ -79,7 +94,7 @@ void rng::handle_irq() bool rng::ack_irq() { - return virtio_conf_readb(VIRTIO_PCI_ISR); + return _dev.read_and_ack_isr(); } void rng::worker() @@ -124,7 +139,7 @@ void rng::refill() hw_driver* rng::probe(hw_device* dev) { - return virtio::probe<rng, VIRTIO_RNG_DEVICE_ID>(dev); + return virtio::probe<rng, VIRTIO_ID_RNG>(dev); } } diff --git a/drivers/virtio-rng.hh b/drivers/virtio-rng.hh index 1790665b..ad3165fb 100644 --- a/drivers/virtio-rng.hh +++ b/drivers/virtio-rng.hh @@ -22,11 +22,7 @@ namespace virtio { class rng : public virtio_driver, randomdev::hw_rng { public: - enum { - VIRTIO_RNG_DEVICE_ID = 0x1005, - }; - - explicit rng(pci::device& dev); + explicit rng(virtio_device& dev); virtual ~rng(); virtual std::string get_name() const { return "virtio-rng"; } @@ -44,7 +40,6 @@ private: static const size_t _pool_size = 64; std::vector<char> _entropy; - pci_interrupt _irq; std::unique_ptr<sched::thread> _thread; condvar _producer; condvar _consumer; diff --git a/drivers/virtio-scsi.cc b/drivers/virtio-scsi.cc index f3f94bd8..ff92eef1 100644 --- a/drivers/virtio-scsi.cc +++ b/drivers/virtio-scsi.cc @@ -12,7 +12,6 @@ #include <osv/mempool.hh> #include <osv/sched.hh> #include <osv/interrupt.hh> -#include "drivers/pci-device.hh" #include "drivers/virtio.hh" #include "drivers/virtio-scsi.hh" #include "drivers/scsi-common.hh" @@ -129,7 +128,7 @@ void scsi::add_lun(u16 target, u16 lun) bool scsi::ack_irq() { - auto isr = virtio_conf_readb(VIRTIO_PCI_ISR); + auto isr = _dev.read_and_ack_isr(); auto queue = get_virt_queue(VIRTIO_SCSI_QUEUE_REQ); if (isr) { @@ -141,37 +140,47 @@ bool scsi::ack_irq() } -scsi::scsi(pci::device& dev) +scsi::scsi(virtio_device& dev) : virtio_driver(dev) { _driver_name = "virtio-scsi"; _id = _instance++; + // Steps 4 & 5 - negotiate and confirm features setup_features(); read_config(); + // Step 7 - generic init of virtqueues + probe_virt_queues(); + //register the single irq callback for the block sched::thread* t = sched::thread::make([this] { this->req_done(); }, sched::thread::attr().name("virtio-scsi")); t->start(); auto queue = get_virt_queue(VIRTIO_SCSI_QUEUE_REQ); - if (dev.is_msix()) { - _msi.easy_register({ - { VIRTIO_SCSI_QUEUE_CTRL, nullptr, nullptr }, - { VIRTIO_SCSI_QUEUE_EVT, nullptr, nullptr }, - { VIRTIO_SCSI_QUEUE_REQ, [=] { queue->disable_interrupts(); }, t }, + interrupt_factory int_factory; + int_factory.register_msi_bindings = [queue,t](interrupt_manager &msi) { + msi.easy_register({ + { VIRTIO_SCSI_QUEUE_CTRL, nullptr, nullptr }, + { VIRTIO_SCSI_QUEUE_EVT, nullptr, nullptr }, + { VIRTIO_SCSI_QUEUE_REQ, [=] { queue->disable_interrupts(); }, t }, }); - } else { - _irq.reset(new pci_interrupt(dev, - [=] { return this->ack_irq(); }, - [=] { t->wake(); })); - } + }; + + int_factory.create_pci_interrupt = [this,t](pci::device &pci_dev) { + return new pci_interrupt( + pci_dev, + [=] { return this->ack_irq(); }, + [=] { t->wake(); }); + }; + _dev.register_interrupt(int_factory); // Enable indirect descriptor queue->set_use_indirect(true); + // Step 8 add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK); scan(); @@ -184,7 +193,7 @@ scsi::~scsi() void scsi::read_config() { - virtio_conf_read(virtio_pci_config_offset(), &_config, sizeof(_config)); + virtio_conf_read(0, &_config, sizeof(_config)); config.max_lun = _config.max_lun; config.max_target = _config.max_target; } @@ -245,7 +254,6 @@ u32 scsi::get_driver_features() hw_driver* scsi::probe(hw_device* dev) { - return virtio::probe<scsi, VIRTIO_SCSI_DEVICE_ID>(dev); + return virtio::probe<scsi, VIRTIO_ID_SCSI>(dev); } - } diff --git a/drivers/virtio-scsi.hh b/drivers/virtio-scsi.hh index 205ccb40..de93129b 100644 --- a/drivers/virtio-scsi.hh +++ b/drivers/virtio-scsi.hh @@ -8,7 +8,6 @@ #ifndef VIRTIO_SCSI_DRIVER_H #define VIRTIO_SCSI_DRIVER_H #include "drivers/virtio.hh" -#include "drivers/pci-device.hh" #include "drivers/scsi-common.hh" #include <osv/bio.h> #include <osv/types.h> @@ -34,7 +33,6 @@ public: VIRTIO_SCSI_CDB_SIZE = 32, VIRTIO_SCSI_SENSE_SIZE = 96, VIRTIO_SCSI_SECTOR_SIZE = 512, - VIRTIO_SCSI_DEVICE_ID = 0x1004, }; enum scsi_res_code { @@ -145,7 +143,7 @@ public: }; - scsi(pci::device& dev); + scsi(virtio_device& dev); ~scsi(); virtual std::string get_name() const { return _driver_name; } @@ -174,8 +172,6 @@ private: std::string _driver_name; scsi_config _config; - std::unique_ptr<pci_interrupt> _irq; - //maintains the virtio instance number for multiple drives static int _instance; int _id; diff --git a/drivers/virtio-vring.cc b/drivers/virtio-vring.cc index 5f89fb5d..3f0e7e25 100644 --- a/drivers/virtio-vring.cc +++ b/drivers/virtio-vring.cc @@ -38,12 +38,13 @@ TRACEPOINT(trace_vring_get_buf_ret, "vring=%p _avail_count %d", void*, int); namespace virtio { - vring::vring(virtio_driver* const dev, u16 num, u16 q_index) + vring::vring(virtio_driver* const driver, u16 num, u16 q_index) { - _dev = dev; + _driver = driver; _q_index = q_index; // Alloc enough pages for the vring... - unsigned sz = VIRTIO_ALIGN(vring::get_size(num, VIRTIO_PCI_VRING_ALIGN)); + size_t alignment = driver->get_vring_alignment(); + size_t sz = VIRTIO_ALIGN(vring::get_size(num, alignment), alignment); _vring_ptr = memory::alloc_phys_contiguous_aligned(sz, 4096); memset(_vring_ptr, 0, sz); @@ -53,7 +54,7 @@ namespace virtio { _desc = (vring_desc*)_vring_ptr; _avail = (vring_avail*)(_vring_ptr + num * sizeof(vring_desc)); _used = (vring_used*)(((unsigned long)&_avail->_ring[num] + - sizeof(u16) + VIRTIO_PCI_VRING_ALIGN - 1) & ~(VIRTIO_PCI_VRING_ALIGN - 1)); + sizeof(u16) + alignment - 1) & ~(alignment - 1)); // initialize the next pointer within the available ring for (int i = 0; i < num; i++) _desc[i]._next = i + 1; @@ -102,7 +103,7 @@ namespace virtio { inline bool vring::use_indirect(int desc_needed) { return _use_indirect && - _dev->get_indirect_buf_cap() && + _driver->get_indirect_buf_cap() && // don't let the posting fail due to low available buffers number (desc_needed > _avail_count || // no need to use indirect for a single descriptor @@ -280,7 +281,7 @@ namespace virtio { vring::kick() { bool kicked = true; - if (_dev->get_event_idx_cap()) { + if (_driver->get_event_idx_cap()) { std::atomic_thread_fence(std::memory_order_seq_cst); @@ -310,7 +311,7 @@ namespace virtio { // and _avail_added_since_kick might wrap around due to this bulking. // if (kicked || (_avail_added_since_kick >= (u16)(~0) / 2)) { - _dev->kick(_q_index); + _driver->kick(_q_index); _avail_added_since_kick = 0; return true; } diff --git a/drivers/virtio-vring.hh b/drivers/virtio-vring.hh index af8691ca..74c4d8a1 100644 --- a/drivers/virtio-vring.hh +++ b/drivers/virtio-vring.hh @@ -122,7 +122,7 @@ class virtio_driver; class vring { public: - vring(virtio_driver* const dev, u16 num, u16 q_index); + vring(virtio_driver* const driver, u16 num, u16 q_index); virtual ~vring(); u64 get_paddr(); @@ -183,6 +183,8 @@ class virtio_driver; // Total number of descriptors in ring int size() {return _num;} + u16 index() {return _q_index; } + // Use memory order acquire when there are prior updates to local variables that must // be seen by the reading threads void set_used_event(u16 event, std::memory_order order) {_used_event->store(event, order);}; @@ -240,7 +242,7 @@ class virtio_driver; private: // Up pointer - virtio_driver* _dev; + virtio_driver* _driver; u16 _q_index; // The physical of the physical address handed to the virtio device void* _vring_ptr; diff --git a/drivers/virtio.cc b/drivers/virtio.cc index ffb5967d..ef929820 100644 --- a/drivers/virtio.cc +++ b/drivers/virtio.cc @@ -12,58 +12,50 @@ #include <osv/debug.h> #include "osv/trace.hh" -using namespace pci; - TRACEPOINT(trace_virtio_wait_for_queue, "queue(%p) have_elements=%d", void*, int); namespace virtio { int virtio_driver::_disk_idx = 0; -virtio_driver::virtio_driver(pci::device& dev) +virtio_driver::virtio_driver(virtio_device& dev) : hw_driver() , _dev(dev) - , _msi(&dev) , _num_queues(0) - , _bar1(nullptr) , _cap_indirect_buf(false) { for (unsigned i = 0; i < max_virtqueues_nr; i++) { _queues[i] = nullptr; } - bool status = parse_pci_config(); - assert(status == true); - - _dev.set_bus_master(true); - - _dev.msix_enable(); - //make sure the queue is reset - reset_host_side(); + // Initialize device + _dev.init(); - // Acknowledge device - add_dev_status(VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); + // Step 1 - make sure the device is reset + reset_device(); - // Generic init of virtqueues - probe_virt_queues(); + // Steps 2 & 3 - acknowledge device + add_dev_status(VIRTIO_CONFIG_S_ACKNOWLEDGE); + add_dev_status(VIRTIO_CONFIG_S_DRIVER); } virtio_driver::~virtio_driver() { - reset_host_side(); + reset_device(); free_queues(); } void virtio_driver::setup_features() { - u32 dev_features = get_device_features(); - u32 drv_features = this->get_driver_features(); + // Step 4 - negotiate features + u64 dev_features = get_device_features(); + u64 drv_features = this->get_driver_features(); - u32 subset = dev_features & drv_features; + u64 subset = dev_features & drv_features; //notify the host about the features in used according //to the virtio spec - for (int i = 0; i < 32; i++) + for (int i = 0; i < 64; i++) if (subset & (1 << i)) virtio_d("%s: found feature intersec of bit %d", __FUNCTION__, i); @@ -78,46 +70,14 @@ void virtio_driver::setup_features() void virtio_driver::dump_config() { - u8 B, D, F; - _dev.get_bdf(B, D, F); - _dev.dump_config(); - virtio_d("%s [%x:%x.%x] vid:id=%x:%x", get_name().c_str(), - (u16)B, (u16)D, (u16)F, - _dev.get_vendor_id(), - _dev.get_device_id()); virtio_d(" virtio features: "); - for (int i = 0; i < 32; i++) + for (int i = 0; i < 64; i++) virtio_d(" %d ", get_device_feature_bit(i)); } -bool virtio_driver::parse_pci_config() -{ - // Test whether bar1 is present - _bar1 = _dev.get_bar(1); - if (_bar1 == nullptr) { - return false; - } - - // Check ABI version - u8 rev = _dev.get_revision_id(); - if (rev != VIRTIO_PCI_ABI_VERSION) { - virtio_e("Wrong virtio revision=%x", rev); - return false; - } - - // Check device ID - u16 dev_id = _dev.get_device_id(); - if ((dev_id < VIRTIO_PCI_ID_MIN) || (dev_id > VIRTIO_PCI_ID_MAX)) { - virtio_e("Wrong virtio dev id %x", dev_id); - return false; - } - - return true; -} - -void virtio_driver::reset_host_side() +void virtio_driver::reset_device() { set_dev_status(0); } @@ -134,7 +94,7 @@ void virtio_driver::free_queues() bool virtio_driver::kick(int queue) { - virtio_conf_writew(VIRTIO_PCI_QUEUE_NOTIFY, queue); + _dev.kick_queue(queue); return true; } @@ -149,8 +109,8 @@ void virtio_driver::probe_virt_queues() } // Read queue size - virtio_conf_writew(VIRTIO_PCI_QUEUE_SEL, _num_queues); - qsize = virtio_conf_readw(VIRTIO_PCI_QUEUE_NUM); + _dev.select_queue(_num_queues); + qsize = _dev.get_queue_size(); if (0 == qsize) { break; } @@ -159,22 +119,10 @@ void virtio_driver::probe_virt_queues() vring* queue = new vring(this, qsize, _num_queues); _queues[_num_queues] = queue; - if (_dev.is_msix()) { - // Setup queue_id:entry_id 1:1 correlation... - virtio_conf_writew(VIRTIO_MSI_QUEUE_VECTOR, _num_queues); - if (virtio_conf_readw(VIRTIO_MSI_QUEUE_VECTOR) != _num_queues) { - virtio_e("Setting MSIx entry for queue %d failed.", _num_queues); - return; - } - } - + // Activate queue + _dev.setup_queue(queue); _num_queues++; - // Tell host about pfn - // TODO: Yak, this is a bug in the design, on large memory we'll have PFNs > 32 bit - // Dor to notify Rusty - virtio_conf_writel(VIRTIO_PCI_QUEUE_PFN, (u32)(queue->get_paddr() >> VIRTIO_PCI_QUEUE_ADDR_SHIFT)); - // Debug print virtio_d("Queue[%d] -> size %d, paddr %x", (_num_queues-1), qsize, queue->get_paddr()); @@ -212,82 +160,46 @@ void virtio_driver::wait_for_queue(vring* queue, bool (vring::*pred)() const) }); } -u32 virtio_driver::get_device_features() +u64 virtio_driver::get_device_features() { - return virtio_conf_readl(VIRTIO_PCI_HOST_FEATURES); + return _dev.get_available_features(); } bool virtio_driver::get_device_feature_bit(int bit) { - return get_virtio_config_bit(VIRTIO_PCI_HOST_FEATURES, bit); + return _dev.get_available_feature_bit(bit); } -void virtio_driver::set_guest_features(u32 features) +void virtio_driver::set_guest_features(u64 features) { - virtio_conf_writel(VIRTIO_PCI_GUEST_FEATURES, features); -} - -void virtio_driver::set_guest_feature_bit(int bit, bool on) -{ - set_virtio_config_bit(VIRTIO_PCI_GUEST_FEATURES, bit, on); -} - -u32 virtio_driver::get_guest_features() -{ - return virtio_conf_readl(VIRTIO_PCI_GUEST_FEATURES); + _dev.set_enabled_features(features); } bool virtio_driver::get_guest_feature_bit(int bit) { - return get_virtio_config_bit(VIRTIO_PCI_GUEST_FEATURES, bit); + return _dev.get_enabled_feature_bit(bit); } - u8 virtio_driver::get_dev_status() { - return virtio_conf_readb(VIRTIO_PCI_STATUS); + return _dev.get_status(); } void virtio_driver::set_dev_status(u8 status) { - virtio_conf_writeb(VIRTIO_PCI_STATUS, status); + _dev.set_status(status); } void virtio_driver::add_dev_status(u8 status) { - set_dev_status(get_dev_status() | status); -} - -void virtio_driver::del_dev_status(u8 status) -{ - set_dev_status(get_dev_status() & ~status); -} - -bool virtio_driver::get_virtio_config_bit(u32 offset, int bit) -{ - return virtio_conf_readl(offset) & (1 << bit); -} - -void virtio_driver::set_virtio_config_bit(u32 offset, int bit, bool on) -{ - u32 val = virtio_conf_readl(offset); - u32 newval = ( val & ~(1 << bit) ) | ((int)(on)<<bit); - virtio_conf_writel(offset, newval); -} - -void virtio_driver::virtio_conf_write(u32 offset, void* buf, int length) -{ - u8* ptr = reinterpret_cast<u8*>(buf); - for (int i = 0; i < length; i++) - _bar1->writeb(offset + i, ptr[i]); + _dev.set_status(get_dev_status() | status); } void virtio_driver::virtio_conf_read(u32 offset, void* buf, int length) { unsigned char* ptr = reinterpret_cast<unsigned char*>(buf); for (int i = 0; i < length; i++) - ptr[i] = _bar1->readb(offset + i); + ptr[i] = _dev.read_config(offset + i); } } - diff --git a/drivers/virtio.hh b/drivers/virtio.hh index b918de28..4718f286 100644 --- a/drivers/virtio.hh +++ b/drivers/virtio.hh @@ -9,14 +9,9 @@ #define VIRTIO_DRIVER_H #include "driver.hh" -#include <osv/pci.hh> #include "drivers/driver.hh" #include "drivers/virtio-vring.hh" -#include "drivers/pci-function.hh" -#include "drivers/pci-device.hh" -#include "drivers/virtio-vring.hh" -#include <osv/interrupt.hh> -#include <osv/msi.hh> +#include "drivers/virtio-device.hh" namespace virtio { @@ -42,52 +37,15 @@ enum VIRTIO_CONFIG { /* The Host publishes the avail index for which it expects a kick * at the end of the used ring. Guest should ignore the used->flags field. */ VIRTIO_RING_F_EVENT_IDX = 29, - + /* Version bit that can be used to detect legacy vs modern devices */ + VIRTIO_F_VERSION_1 = 32, /* Do we get callbacks when the ring is completely used, even if we've * suppressed them? */ VIRTIO_F_NOTIFY_ON_EMPTY = 24, - /* A 32-bit r/o bitmask of the features supported by the host */ - VIRTIO_PCI_HOST_FEATURES = 0, - /* A 32-bit r/w bitmask of features activated by the guest */ - VIRTIO_PCI_GUEST_FEATURES = 4, - /* A 32-bit r/w PFN for the currently selected queue */ - VIRTIO_PCI_QUEUE_PFN = 8, - /* A 16-bit r/o queue size for the currently selected queue */ - VIRTIO_PCI_QUEUE_NUM = 12, - /* A 16-bit r/w queue selector */ - VIRTIO_PCI_QUEUE_SEL = 14, - /* A 16-bit r/w queue notifier */ - VIRTIO_PCI_QUEUE_NOTIFY = 16, - /* An 8-bit device status register. */ - VIRTIO_PCI_STATUS = 18, - /* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ - VIRTIO_PCI_ISR = 19, - /* The bit of the ISR which indicates a device configuration change. */ - VIRTIO_PCI_ISR_CONFIG = 0x2, - /* MSI-X registers: only enabled if MSI-X is enabled. */ - /* A 16-bit vector for configuration changes. */ - VIRTIO_MSI_CONFIG_VECTOR = 20, - /* A 16-bit vector for selected queue notifications. */ - VIRTIO_MSI_QUEUE_VECTOR = 22, - /* Vector value used to disable MSI for queue */ - VIRTIO_MSI_NO_VECTOR = 0xffff, - /* Virtio ABI version, this must match exactly */ - VIRTIO_PCI_ABI_VERSION = 0, - /* How many bits to shift physical queue address written to QUEUE_PFN. - * 12 is historical, and due to x86 page size. */ - VIRTIO_PCI_QUEUE_ADDR_SHIFT = 12, - /* The alignment to use between consumer and producer parts of vring. - * x86 pagesize again. */ - VIRTIO_PCI_VRING_ALIGN = 4096, - }; enum { VIRTIO_VENDOR_ID = 0x1af4, - VIRTIO_PCI_ID_MIN = 0x1000, - VIRTIO_PCI_ID_MAX = 0x103f, VIRTIO_ID_NET = 1, VIRTIO_ID_BLOCK = 2, @@ -100,25 +58,17 @@ enum { VIRTIO_ID_RPROC_SERIAL = 11, }; -#define VIRTIO_ALIGN(x) ((x + (VIRTIO_PCI_VRING_ALIGN-1)) & ~(VIRTIO_PCI_VRING_ALIGN-1)) - const unsigned max_virtqueues_nr = 64; class virtio_driver : public hw_driver { public: - explicit virtio_driver(pci::device& dev); + explicit virtio_driver(virtio_device& dev); virtual ~virtio_driver(); virtual std::string get_name() const = 0; virtual void dump_config(); - // The remaining space is defined by each driver as the per-driver - // configuration space - int virtio_pci_config_offset() {return (_dev.is_msix_enabled())? 24 : 20;} - - bool parse_pci_config(); - void probe_virt_queues(); vring* get_virt_queue(unsigned idx); @@ -126,35 +76,21 @@ public: void wait_for_queue(vring* queue, bool (vring::*pred)() const); // guest/host features physical access - u32 get_device_features(); + u64 get_device_features(); bool get_device_feature_bit(int bit); - void set_guest_features(u32 features); - void set_guest_feature_bit(int bit, bool on); - u32 get_guest_features(); + void set_guest_features(u64 features); bool get_guest_feature_bit(int bit); // device status u8 get_dev_status(); void set_dev_status(u8 status); void add_dev_status(u8 status); - void del_dev_status(u8 status); - - // Access the virtio conf address space set by pci bar 1 - bool get_virtio_config_bit(u32 offset, int bit); - void set_virtio_config_bit(u32 offset, int bit, bool on); // Access virtio config space void virtio_conf_read(u32 offset, void* buf, int length); - void virtio_conf_write(u32 offset, void* buf, int length); - u8 virtio_conf_readb(u32 offset) { return _bar1->readb(offset);}; - u16 virtio_conf_readw(u32 offset) { return _bar1->readw(offset);}; - u32 virtio_conf_readl(u32 offset) { return _bar1->readl(offset);}; - void virtio_conf_writeb(u32 offset, u8 val) { _bar1->writeb(offset, val);}; - void virtio_conf_writew(u32 offset, u16 val) { _bar1->writew(offset, val);}; - void virtio_conf_writel(u32 offset, u32 val) { _bar1->writel(offset, val);}; bool kick(int queue); - void reset_host_side(); + void reset_device(); void free_queues(); bool get_indirect_buf_cap() {return _cap_indirect_buf;} @@ -162,17 +98,16 @@ public: bool get_event_idx_cap() {return _cap_event_idx;} void set_event_idx_cap(bool on) {_cap_event_idx = on;} - pci::device& pci_device() { return _dev; } + size_t get_vring_alignment() { return _dev.get_vring_alignment();} + protected: // Actual drivers should implement this on top of the basic ring features virtual u32 get_driver_features() { return 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX; } void setup_features(); protected: - pci::device& _dev; - interrupt_manager _msi; + virtio_device& _dev; vring* _queues[max_virtqueues_nr]; u32 _num_queues; - pci::bar* _bar1; bool _cap_indirect_buf; bool _cap_event_idx = false; static int _disk_idx; @@ -181,9 +116,9 @@ protected: template <typename T, u16 ID> hw_driver* probe(hw_device* dev) { - if (auto pci_dev = dynamic_cast<pci::device*>(dev)) { - if (pci_dev->get_id() == hw_device_id(VIRTIO_VENDOR_ID, ID)) { - return new T(*pci_dev); + if (auto virtio_dev = dynamic_cast<virtio_device*>(dev)) { + if (virtio_dev->get_id() == hw_device_id(VIRTIO_VENDOR_ID, ID)) { + return new T(*virtio_dev); } } return nullptr; diff --git a/drivers/vmw-pvscsi.hh b/drivers/vmw-pvscsi.hh index ecfe962b..12caff59 100644 --- a/drivers/vmw-pvscsi.hh +++ b/drivers/vmw-pvscsi.hh @@ -8,11 +8,13 @@ #ifndef VMW_PVSCSI_DRIVER_H #define VMW_PVSCSI_DRIVER_H -#include "drivers/virtio.hh" +#include "drivers/driver.hh" #include "drivers/pci-device.hh" #include "drivers/scsi-common.hh" #include <osv/bio.h> #include <osv/types.h> +#include <osv/interrupt.hh> +#include <osv/msi.hh> namespace vmw { -- 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.
