This patch refactors virtio layer in order to support
modern PCI and mmio devices. It is essentially a step 0
in the list of tasks to make OSv boot on AWS firecracker.
This patch introduces virtio_device class that abstracts
virtio transport layer. That way virtio_driver class and
its specializations can interact with host without being
tied to a specific transport (PCI, mmio, etc).
The virtio_pci_device, virtio_legacy_pci_device and virtio_modern_pci_device
classes define specializations of virtio_device for PCI transport.
This patch effectively moves all PCI-related logic from virtio_driver
to virtio_legacy_pci_device classi and effectively virtio drivers
transport agnostic. Followup 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.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
Makefile | 1 +
drivers/device.hh | 2 +
drivers/pci-generic.cc | 25 +++-
drivers/virtio-assign.cc | 40 ++-----
drivers/virtio-blk.cc | 29 +++--
drivers/virtio-blk.hh | 5 +-
drivers/virtio-device.hh | 78 ++++++++++++
drivers/virtio-net.cc | 38 +++---
drivers/virtio-net.hh | 4 +-
drivers/virtio-pci-device.cc | 223 +++++++++++++++++++++++++++++++++++
drivers/virtio-pci-device.hh | 150 +++++++++++++++++++++++
drivers/virtio-rng.cc | 16 ++-
drivers/virtio-rng.hh | 3 +-
drivers/virtio-scsi.cc | 32 ++---
drivers/virtio-scsi.hh | 5 +-
drivers/virtio-vring.cc | 15 +--
drivers/virtio-vring.hh | 4 +-
drivers/virtio.cc | 120 +++++--------------
drivers/virtio.hh | 87 ++------------
drivers/vmw-pvscsi.hh | 4 +-
20 files changed, 611 insertions(+), 270 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 68043485..7a88c56a 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/virtio-assign.o
diff --git a/drivers/device.hh b/drivers/device.hh
index 3882470a..2fbd0afd 100644
--- a/drivers/device.hh
+++ b/drivers/device.hh
@@ -34,6 +34,8 @@ namespace hw {
&& _device_id == other._device_id;
}
+ bool is_vendor(u16 vendor_id) { return _vendor_id == vendor_id; }
+
private:
u16 _vendor_id;
u16 _device_id;
diff --git a/drivers/pci-generic.cc b/drivers/pci-generic.cc
index 61e7a0f5..821111c3 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,24 @@ bool check_bus(u16 bus)
break;
}
- if (!device_manager::instance()->register_device(dev)) {
- pci_e("Error: couldn't register device %02x:%02x.%x",
- bus, slot, func);
- //TODO: Need to beautify it as multiple instances of the device may exist
- delete dev;
+ if (auto pci_dev = dynamic_cast<device*>(dev)) {
+ if (pci_dev->get_id().is_vendor(virtio::VIRTIO_VENDOR_ID)) {
+ auto virtio_dev = virtio::create_virtio_device(pci_dev);
+ if (!device_manager::instance()->register_device(virtio_dev)) {
+ pci_e("Error: couldn't register virtio pci device %02x:%02x.%x",
+ bus, slot, func);
+ //TODO: Need to beautify it as multiple instances of the device may exist
+ delete dev;
+ }
+ }
+ }
+ else {
+ if (!device_manager::instance()->register_device(dev)) {
+ pci_e("Error: couldn't register device %02x:%02x.%x",
+ bus, slot, func);
+ //TODO: Need to beautify it as multiple instances of the device may exist
+ delete dev;
+ }
}
// test for multiple functions
diff --git a/drivers/virtio-assign.cc b/drivers/virtio-assign.cc
index 8a128d5e..427811b6 100644
--- a/drivers/virtio-assign.cc
+++ b/drivers/virtio-assign.cc
@@ -7,8 +7,10 @@
#include <osv/virtio-assign.hh>
+#include <drivers/virtio-device.hh>
#include <drivers/virtio-net.hh>
+
// Currently we support only one assigned virtio device, but more could
// easily be added later.
static osv::assigned_virtio *the_assigned_virtio_device = nullptr;
@@ -23,8 +25,8 @@ assigned_virtio *assigned_virtio::get() {
class impl : public osv::assigned_virtio, public virtio::virtio_driver {
public:
static hw_driver* probe_net(hw_device* dev);
- explicit impl(pci::device& dev)
- : virtio_driver(dev)
+ explicit impl(virtio::virtio_device& virtio_dev)
+ : virtio_driver(virtio_dev)
{
assert(!the_assigned_virtio_device);
the_assigned_virtio_device = this;
@@ -48,9 +50,8 @@ public:
virtual u32 queue_size(int queue) override
{
- virtio_conf_writew(virtio::VIRTIO_PCI_QUEUE_SEL, queue);
- return virtio_conf_readw(virtio::VIRTIO_PCI_QUEUE_NUM);
-
+ _dev.select_queue(queue);
+ return _dev.get_queue_size();
}
virtual u32 init_features(u32 driver_features) override
{
@@ -70,34 +71,13 @@ public:
auto *ht = &_hack_threads.back(); // assumes object won't move later
handler = [ht] { ht->wake(); };
- // OSv's generic virtio driver has already set the device to msix, and set
- // the VIRTIO_MSI_QUEUE_VECTOR of its queue to its number.
- assert(_dev.is_msix());
- virtio_conf_writew(virtio::VIRTIO_PCI_QUEUE_SEL, queue);
- assert(virtio_conf_readw(virtio::VIRTIO_MSI_QUEUE_VECTOR) == queue);
- if (!_dev.is_msix_enabled()) {
- _dev.msix_enable();
- }
- auto vectors = _msi.request_vectors(1);
- assert(vectors.size() == 1);
- auto vec = vectors[0];
- // TODO: in _msi.easy_register() we also have code for moving the
- // interrupt's affinity to where the handling thread is. We should
- // probably do this here too.
- _msi.assign_isr(vec, handler);
- auto ok = _msi.setup_entry(queue, vec);
- assert(ok);
- vec->msix_unmask_entries();
+ _dev.register_interrupt(queue,handler);
}
virtual void set_queue_pfn(int queue, u64 phys) override
{
- virtio_conf_writew(virtio::VIRTIO_PCI_QUEUE_SEL, queue);
- // Tell host about pfn
- u64 pfn = phys >> virtio::VIRTIO_PCI_QUEUE_ADDR_SHIFT;
- // A bug in virtio's design... on large memory, this can actually happen
- assert(pfn <= std::numeric_limits<u32>::max());
- virtio_conf_writel(virtio::VIRTIO_PCI_QUEUE_PFN, (u32)pfn);
+ _dev.select_queue(queue);
+ _dev.activate_queue(phys);
}
virtual void set_driver_ok() override
@@ -107,7 +87,7 @@ public:
virtual void conf_read(void *buf, int length) override
{
- virtio_conf_read(virtio_pci_config_offset(), buf, length);
+ virtio_conf_read(_dev.config_offset(), buf, length);
}
private:
diff --git a/drivers/virtio-blk.cc b/drivers/virtio-blk.cc
index a3f7898d..5f9f2028 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>
@@ -100,7 +99,7 @@ struct driver blk_driver = {
bool blk::ack_irq()
{
- auto isr = virtio_conf_readb(VIRTIO_PCI_ISR);
+ auto isr = _dev.ack_irq();
auto queue = get_virt_queue(0);
if (isr) {
@@ -112,8 +111,8 @@ 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";
@@ -128,13 +127,19 @@ blk::blk(pci::device& pci_dev)
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);
@@ -164,7 +169,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(_dev.config_offset(), &_config, sizeof(_config));
trace_virtio_blk_read_config_capacity(_config.capacity);
diff --git a/drivers/virtio-blk.hh b/drivers/virtio-blk.hh
index 17cf4d18..8e814501 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 {
@@ -118,7 +118,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 +157,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..e6e29155
--- /dev/null
+++ b/drivers/virtio-device.hh
@@ -0,0 +1,78 @@
+/*
+ * 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 thise
+// 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).
+class virtio_device : public hw_device {
+public:
+ virtual ~virtio_device() {};
+
+ virtual void init() = 0;
+
+ virtual u8 ack_irq() = 0;
+ virtual void register_interrupt(interrupt_factory irq_factory) = 0;
+ virtual void register_interrupt(unsigned int queue, std::function<void(void)> handler) = 0;
+
+ virtual void select_queue(int queue) = 0;
+ virtual u16 get_queue_size() = 0;
+ virtual void setup_queue(int queue) = 0;
+ virtual void activate_queue(vring *queue) = 0;
+ virtual void activate_queue(u64 phys) = 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 void set_enabled_feature_bit(int bit, bool on) = 0;
+ virtual u64 get_enabled_features() = 0;
+ virtual bool get_enabled_feature_bit(int bit) = 0;
+
+ virtual u8 get_status() = 0;
+ virtual void set_status(u8 status) = 0;
+
+ virtual u8 read_config(u32 offset) = 0;
+ virtual void write_config(u32 offset, u8 byte) = 0;
+ virtual int config_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..3528613d 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.ack_irq();
if (isr) {
_rxq.vqueue->disable_interrupts();
@@ -228,7 +227,7 @@ bool net::ack_irq()
}
-net::net(pci::device& dev)
+net::net(virtio_device& dev)
: virtio_driver(dev),
_rxq(get_virt_queue(0), [this] { this->receiver(); }),
_txq(this, get_virt_queue(1))
@@ -290,16 +289,21 @@ 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();
@@ -324,7 +328,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(_dev.config_offset(), &_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 +860,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_NET_DEVICE_ID)) {
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..e9f6dd0d 100644
--- a/drivers/virtio-net.hh
+++ b/drivers/virtio-net.hh
@@ -204,7 +204,7 @@ public:
u16 virtqueue_pairs;
};
- explicit net(pci::device& dev);
+ explicit net(virtio_device& dev);
virtual ~net();
virtual std::string get_name() const { return _driver_name; }
@@ -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 */
diff --git a/drivers/virtio-pci-device.cc b/drivers/virtio-pci-device.cc
new file mode 100644
index 00000000..a3ccfe26
--- /dev/null
+++ b/drivers/virtio-pci-device.cc
@@ -0,0 +1,223 @@
+/*
+ * 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;
+}
+
+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(int queue)
+{
+ if (_dev->is_msix()) {
+ // Setup queue_id:entry_id 1:1 correlation...
+ virtio_conf_writew(VIRTIO_MSI_QUEUE_VECTOR, queue);
+ if (virtio_conf_readw(VIRTIO_MSI_QUEUE_VECTOR) != queue) {
+ virtio_e("Setting MSIx entry for queue %d failed.", queue);
+ return;
+ }
+ }
+}
+
+void virtio_legacy_pci_device::activate_queue(vring *queue)
+{
+ // 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::activate_queue(u64 phys)
+{
+ // Tell host about pfn
+ u64 pfn = phys >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+ // A bug in virtio's design... on large memory, this can actually happen
+ assert(pfn <= std::numeric_limits<u32>::max());
+ virtio_conf_writel(VIRTIO_PCI_QUEUE_PFN, (u32)pfn);
+}
+
+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);
+}
+
+void virtio_legacy_pci_device::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);
+}
+
+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);
+}
+
+void virtio_legacy_pci_device::set_enabled_feature_bit(int bit, bool on)
+{
+ set_virtio_config_bit(VIRTIO_PCI_GUEST_FEATURES, bit, on);
+}
+
+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)
+{
+ return _bar1->readb(offset);
+}
+
+void virtio_legacy_pci_device::write_config(u32 offset, u8 byte)
+{
+ _bar1->writeb(offset, byte);
+}
+
+void virtio_legacy_pci_device::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());
+}
+
+void virtio_legacy_pci_device::init()
+{
+ bool status = parse_pci_config();
+ assert(status);
+
+ _dev->set_bus_master(true);
+
+ _dev->msix_enable();
+}
+
+void virtio_legacy_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));
+ }
+}
+
+void virtio_legacy_pci_device::register_interrupt(unsigned int queue, std::function<void(void)> handler)
+{
+ // OSv's generic virtio driver has already set the device to msix, and set
+ // the VIRTIO_MSI_QUEUE_VECTOR of its queue to its number.
+ assert(_dev->is_msix());
+ virtio_conf_writew(virtio::VIRTIO_PCI_QUEUE_SEL, queue);
+ assert(virtio_conf_readw(virtio::VIRTIO_MSI_QUEUE_VECTOR) == queue);
+ if (!_dev->is_msix_enabled()) {
+ _dev->msix_enable();
+ }
+ auto vectors = _msi.request_vectors(1);
+ assert(vectors.size() == 1);
+ auto vec = vectors[0];
+ // TODO: in _msi.easy_register() we also have code for moving the
+ // interrupt's affinity to where the handling thread is. We should
+ // probably do this here too.
+ _msi.assign_isr(vec, handler);
+ auto ok = _msi.setup_entry(queue, vec);
+ assert(ok);
+ vec->msix_unmask_entries();
+}
+
+u8 virtio_legacy_pci_device::ack_irq()
+{
+ 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_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;
+}
+
+virtio_device* create_virtio_device(pci::device *dev) {
+ //TODO Read PCI device configuration to create instance
+ // of virtio_modern_pci_device or virtio_legacy_pci_device
+ 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..76827fc8
--- /dev/null
+++ b/drivers/virtio-pci-device.hh
@@ -0,0 +1,150 @@
+/*
+ * 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"
+
+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_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_ID_MIN = 0x1000,
+ VIRTIO_PCI_ID_MAX = 0x103f,
+};
+
+class virtio_pci_device : public virtio_device {
+public:
+ explicit virtio_pci_device(pci::device *dev);
+ ~virtio_pci_device();
+
+ virtual hw_device_id get_id() { return _dev->get_id(); }
+ virtual void print() { _dev->print(); }
+ virtual void reset() { _dev->reset(); }
+
+ bool is_attached() { return _dev->is_attached(); }
+ void set_attached() { _dev->set_attached(); }
+
+ size_t get_vring_alignment() { return VIRTIO_PCI_VRING_ALIGN; }
+
+protected:
+ 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 void init();
+ virtual void register_interrupt(interrupt_factory irq_factory);
+ virtual void register_interrupt(unsigned int queue, std::function<void(void)> handler);
+
+ virtual void select_queue(int queue);
+ virtual u16 get_queue_size();
+ virtual void setup_queue(int queue);
+ virtual void activate_queue(vring *queue);
+ virtual void activate_queue(u64 phys);
+ 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 void set_enabled_feature_bit(int bit, bool on);
+ 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 void write_config(u32 offset, u8 byte);
+ virtual void dump_config();
+ virtual u8 ack_irq();
+
+ // The remaining space is defined by each driver as the per-driver
+ // configuration space
+ virtual int config_offset() { return (_dev->is_msix_enabled())? 24 : 20;}
+
+ virtual bool is_modern() { return false; };
+private:
+ bool parse_pci_config();
+
+ // 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
+ 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;
+};
+
+class virtio_modern_pci_device : public virtio_pci_device {
+public:
+ explicit virtio_modern_pci_device(pci::device *dev);
+ ~virtio_modern_pci_device();
+
+ virtual bool is_modern() { return true; };
+};
+
+// Creates instance of virtio_modern_pci_device or virtio_legacy_pci_device
+// by reading configuration from PCI device
+virtio_device* create_virtio_device(pci::device *dev);
+
+}
+
+#endif //VIRTIO_PCI_DEVICE_HH
diff --git a/drivers/virtio-rng.cc b/drivers/virtio-rng.cc
index d506cb2a..1e13c3e4 100644
--- a/drivers/virtio-rng.cc
+++ b/drivers/virtio-rng.cc
@@ -36,11 +36,19 @@ 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")))
{
+ 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);
add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK);
@@ -79,7 +87,7 @@ void rng::handle_irq()
bool rng::ack_irq()
{
- return virtio_conf_readb(VIRTIO_PCI_ISR);
+ return _dev.ack_irq();
}
void rng::worker()
diff --git a/drivers/virtio-rng.hh b/drivers/virtio-rng.hh
index 1790665b..e704fea4 100644
--- a/drivers/virtio-rng.hh
+++ b/drivers/virtio-rng.hh
@@ -26,7 +26,7 @@ public:
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 +44,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..b053ae7e 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.ack_irq();
auto queue = get_virt_queue(VIRTIO_SCSI_QUEUE_REQ);
if (isr) {
@@ -141,7 +140,7 @@ bool scsi::ack_irq()
}
-scsi::scsi(pci::device& dev)
+scsi::scsi(virtio_device& dev)
: virtio_driver(dev)
{
@@ -157,17 +156,22 @@ scsi::scsi(pci::device& dev)
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);
@@ -184,7 +188,7 @@ scsi::~scsi()
void scsi::read_config()
{
- virtio_conf_read(virtio_pci_config_offset(), &_config, sizeof(_config));
+ virtio_conf_read(_dev.config_offset(), &_config, sizeof(_config));
config.max_lun = _config.max_lun;
config.max_target = _config.max_target;
}
diff --git a/drivers/virtio-scsi.hh b/drivers/virtio-scsi.hh
index 205ccb40..30c3d9e0 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>
@@ -145,7 +144,7 @@ public:
};
- scsi(pci::device& dev);
+ scsi(virtio_device& dev);
~scsi();
virtual std::string get_name() const { return _driver_name; }
@@ -174,8 +173,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..6aa6398c 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();
@@ -240,7 +240,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..acda7610 100644
--- a/drivers/virtio.cc
+++ b/drivers/virtio.cc
@@ -12,31 +12,24 @@
#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();
+ //initialize device
+ _dev.init();
//make sure the queue is reset
reset_host_side();
@@ -56,14 +49,14 @@ virtio_driver::~virtio_driver()
void virtio_driver::setup_features()
{
- u32 dev_features = get_device_features();
- u32 drv_features = this->get_driver_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,45 +71,13 @@ 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()
{
set_dev_status(0);
@@ -134,7 +95,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 +110,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 +120,11 @@ 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;
- }
- }
+ _dev.setup_queue(_num_queues);
+ _dev.activate_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 +162,68 @@ 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);
+ _dev.set_enabled_features(features);
}
void virtio_driver::set_guest_feature_bit(int bit, bool on)
{
- set_virtio_config_bit(VIRTIO_PCI_GUEST_FEATURES, bit, on);
+ _dev.set_enabled_feature_bit(bit, on);
}
-u32 virtio_driver::get_guest_features()
+u64 virtio_driver::get_guest_features()
{
- return virtio_conf_readl(VIRTIO_PCI_GUEST_FEATURES);
+ return _dev.get_enabled_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);
+ _dev.set_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);
+ _dev.set_status(get_dev_status() & ~status);
}
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.write_config(offset + i, ptr[i]);
}
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..aa8ccd7f 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,11 +76,11 @@ 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_features(u64 features);
void set_guest_feature_bit(int bit, bool on);
- u32 get_guest_features();
+ u64 get_guest_features();
bool get_guest_feature_bit(int bit);
// device status
@@ -139,19 +89,9 @@ public:
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();
@@ -162,17 +102,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 +120,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