From fb859bc848e0adb8957c58d2725c826e593a69f5 Mon Sep 17 00:00:00 2001
From: Guy Zana <guy@cloudius-systems.com>
Date: Tue, 15 Jan 2013 22:33:48 +0200
Subject: [PATCH] Added pci config capability parsing and moved the virtual
 call to earlyInitChecks() to the base class

---
 drivers/driver.cc            |  74 ++++++++++++++++++
 drivers/driver.hh            |  18 +++++
 drivers/pci.hh               |  29 +++++++
 drivers/virtio-vring.hh.orig | 141 +++++++++++++++++++++++++++++++++++
 drivers/virtio.cc            |   4 -
 5 files changed, 262 insertions(+), 4 deletions(-)
 create mode 100644 drivers/virtio-vring.hh.orig

diff --git a/drivers/driver.cc b/drivers/driver.cc
index 87543331f..abbf22450 100644
--- a/drivers/driver.cc
+++ b/drivers/driver.cc
@@ -85,6 +85,12 @@ bool
 Driver::Init(Device* dev) {
     if (!dev) return false;
 
+    if (!earlyInitChecks()) {
+        return false;
+    }
+
+    parse_pci_config();
+
     debug(fmt("Driver:Init %x:%x") % _vid % _id);
 
     setBusMaster(true);
@@ -116,6 +122,74 @@ Driver::getFunc() {
     return _func;
 }
 
+bool Driver::parse_pci_config(void)
+{
+    // Parse capabilities
+    bool parse_ok = parse_pci_capabilities();
+
+    return parse_ok;
+}
+
+bool Driver::parse_pci_capabilities(void)
+{
+    // FIXME: check pci device type (act differently if bridge)
+    u8 capabilities_base = pci_readb(PCI_CAPABILITIES_PTR);
+    u8 off = capabilities_base;
+
+    while (off != 0) {
+        if (off > 255) {
+            return (false);
+        }
+
+        u8 capability = pci_readb(off + PCI_CAP_OFF_ID);
+        switch (capability) {
+        case PCI_CAP_MSIX:
+            _have_msix = true;
+            debug(fmt("Have MSI-X!"));
+            break;
+        }
+
+        off = pci_readb(off + PCI_CAP_OFF_NEXT);
+    }
+
+    return true;
+}
+
+bool Driver::parse_pci_msix(void)
+{
+    return true;
+}
+
+u8 Driver::pci_readb(u8 offset)
+{
+    return read_pci_config_byte(_bus, _slot, _func, offset);
+}
+
+u16 Driver::pci_readw(u8 offset)
+{
+    return read_pci_config_word(_bus, _slot, _func, offset);
+}
+
+u32 Driver::pci_readl(u8 offset)
+{
+    return read_pci_config(_bus, _slot, _func, offset);
+}
+
+void Driver::pci_writeb(u8 offset, u8 val)
+{
+    write_pci_config_byte(_bus, _slot, _func, offset, val);
+}
+
+void Driver::pci_writew(u8 offset, u16 val)
+{
+    write_pci_config_word(_bus, _slot, _func, offset, val);
+}
+
+void Driver::pci_writel(u8 offset, u32 val)
+{
+    write_pci_config(_bus, _slot, _func, offset, val);
+}
+
 void Driver::dumpConfig() const {
     debug(fmt("Driver vid:id= %x:%x") % _vid % _id);
 }
diff --git a/drivers/driver.hh b/drivers/driver.hh
index 777999a4f..69dd006ca 100644
--- a/drivers/driver.hh
+++ b/drivers/driver.hh
@@ -29,6 +29,8 @@ public:
     virtual void dumpConfig() const;
     virtual bool Init(Device *d);
 
+    bool parse_pci_config(void);
+
     friend std::ostream& operator <<(std::ostream& out, const Driver &d);
     struct equal {
       bool operator()(const Driver* d1, const Driver* d2) const
@@ -60,11 +62,27 @@ protected:
     bool allocateBARs();
     virtual bool earlyInitChecks();
 
+    // Parsing of extra capabilities
+    virtual bool parse_pci_capabilities(void);
+    virtual bool parse_pci_msix(void);
+
+    // Access to PCI address space
+    virtual u8 pci_readb(u8 offset);
+    virtual u16 pci_readw(u8 offset);
+    virtual u32 pci_readl(u8 offset);
+    virtual void pci_writeb(u8 offset, u8 val);
+    virtual void pci_writew(u8 offset, u16 val);
+    virtual void pci_writel(u8 offset, u32 val);
+
     u16 _id;
     u16 _vid;
     bool _present;
     u8  _bus, _slot, _func;
     Bar* _bars[6];
+
+    // MSI-X
+    bool _have_msix;
+
 };
 
 #endif
diff --git a/drivers/pci.hh b/drivers/pci.hh
index e35c28904..9d4ad96ac 100644
--- a/drivers/pci.hh
+++ b/drivers/pci.hh
@@ -37,6 +37,35 @@ using processor::outl;
 	    PCI_CAPABILITIES_PTR = 0x34,
 	};
 
+	/* Capability Register Offsets */
+	enum pci_capabilities_offsets {
+	    PCI_CAP_OFF_ID      = 0x0,
+	    PCI_CAP_OFF_NEXT    = 0x1
+	};
+
+	enum pci_capabilities {
+	    PCI_CAP_PM          = 0x01,    // PCI Power Management
+	    PCI_CAP_AGP         = 0x02,    // AGP
+	    PCI_CAP_VPD         = 0x03,    // Vital Product Data
+	    PCI_CAP_SLOTID      = 0x04,    // Slot Identification
+	    PCI_CAP_MSI         = 0x05,    // Message Signaled Interrupts
+	    PCI_CAP_CHSWP       = 0x06,    // CompactPCI Hot Swap
+	    PCI_CAP_PCIX        = 0x07,    // PCI-X
+	    PCI_CAP_HT          = 0x08,    // HyperTransport
+	    PCI_CAP_VENDOR      = 0x09,    // Vendor Unique
+	    PCI_CAP_DEBUG       = 0x0a,    // Debug port
+	    PCI_CAP_CRES        = 0x0b,    // CompactPCI central resource control
+	    PCI_CAP_HOTPLUG     = 0x0c,    // PCI Hot-Plug
+	    PCI_CAP_SUBVENDOR   = 0x0d,    // PCI-PCI bridge subvendor ID
+	    PCI_CAP_AGP8X       = 0x0e,    // AGP 8x
+	    PCI_CAP_SECDEV      = 0x0f,    // Secure Device
+	    PCI_CAP_EXPRESS     = 0x10,    // PCI Express
+	    PCI_CAP_MSIX        = 0x11,    // MSI-X
+	    PCI_CAP_SATA        = 0x12,    // SATA
+	    PCI_CAP_PCIAF       = 0x13     // PCI Advanced Features
+	};
+
+
 	u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset);
     u16 read_pci_config_word(u8 bus, u8 slot, u8 func, u8 offset);
 	u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset);
diff --git a/drivers/virtio-vring.hh.orig b/drivers/virtio-vring.hh.orig
new file mode 100644
index 000000000..bcca69066
--- /dev/null
+++ b/drivers/virtio-vring.hh.orig
@@ -0,0 +1,141 @@
+#ifndef VIRTIO_VRING_H
+#define VIRTIO_VRING_H
+
+#include "types.hh"
+#include "drivers/virtio.hh"
+
+namespace virtio {
+
+    // Buffer descriptors in the ring
+    class vring_desc {
+    public:
+        enum {
+            // This marks a buffer as continuing via the next field.
+            VRING_DESC_F_NEXT=1,
+            // This marks a buffer as write-only (otherwise read-only).
+            VRING_DESC_F_WRITE=2,
+            // This means the buffer contains a list of buffer descriptors.
+            VRING_DESC_F_INDIRECT=4
+        };
+
+        u64 get_paddr(void);
+        u32 get_len(void) { return (_len); }
+        u16 next_idx(void) { return (_next); }
+
+        // flags
+        bool is_chained(void) { return ((_flags & VRING_DESC_F_NEXT) == VRING_DESC_F_NEXT); };
+        bool is_write(void) { return ((_flags & VRING_DESC_F_WRITE) == VRING_DESC_F_WRITE); };
+        bool is_indirect(void) { return ((_flags & VRING_DESC_F_INDIRECT) == VRING_DESC_F_INDIRECT); };
+        
+    private:
+        u64 _paddr;
+        u32 _len;
+        u16 _flags;
+        u16 _next;
+    };
+
+    // Guest to host
+    class vring_avail {
+    public:
+        enum {
+            // Mark that we do not need an interrupt for consuming a descriptor
+            // from the ring. Unrelieable so it's simply an optimization
+            VRING_AVAIL_F_NO_INTERRUPT=1
+        };
+
+        void disable_interrupt(void) { _flags |= VRING_AVAIL_F_NO_INTERRUPT; }
+        void enable_interrupt(void) { _flags = 0; }        
+       
+        u16 _flags;
+        // Where we put the next descriptor
+        u16 _idx;
+        // There may be no more entries than the queue size read from device
+        u16 _ring[];
+    };
+
+    class vring_used_elem {
+    public:
+        // Index of start of used vring_desc chain. (u32 for padding reasons)
+        u32 _id;
+        // Total length of the descriptor chain which was used (written to)
+        u32 _len;
+    };
+
+    // Host to guest
+    class vring_used {
+    public:
+
+        enum {
+            // The Host advise the Guest: don't kick me when
+            // you add a buffer.  It's unreliable, so it's simply an 
+            // optimization. Guest will still kick if it's out of buffers.
+            VRING_USED_F_NO_NOTIFY=1
+        };
+
+        void disable_interrupt(void) { _flags |= VRING_USED_F_NO_NOTIFY; }
+        void enable_interrupt(void) { _flags = 0; }
+        
+        u16 _flags;
+        u16 _idx;
+        vring_used_elem _used_elements[];
+    };
+
+    class vring {
+    public:
+
+        enum VRING_CONFIG {
+
+            /* We support indirect buffer descriptors */
+            VIRTIO_RING_F_INDIRECT_DESC = 28,
+
+            /* The Guest publishes the used index for which it expects an interrupt
+             * at the end of the avail ring. Host should ignore the avail->flags field. */
+            /* 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,
+        };    
+      
+        vring(unsigned int num);
+        virtual ~vring();
+
+<<<<<<< Updated upstream
+        u64 get_paddr(void);
+=======
+        // Add a buffer to the next free descriptor,
+        // Keep chaining until kick is called
+        bool add_buf(u64 addr, u32 len);
+        // Notify the device
+        void kick(void);
+
+        void * get_paddr(void);
+>>>>>>> Stashed changes
+        static unsigned get_size(unsigned int num, unsigned long align);
+
+        // The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX
+        // Assuming a given event_idx value from the other size, if
+        // we have just incremented index from old to new_idx,
+        // should we trigger an event?
+        static int need_event(u16 event_idx, u16 new_idx, u16 old);
+
+    private:
+        // The physical of the physical address handed to the virtio device
+        void* _vring_ptr;
+        
+        // Total number of descriptors in ring
+        unsigned int _num;
+        // Flat list of chained descriptors
+        vring_desc *_desc;
+        // Available for host consumption
+        vring_avail *_avail;
+        // Available for guest consumption
+        vring_used *_used;
+
+        // next free idx
+        u16 free_avail_idx;
+    };
+
+
+}
+
+#endif // VIRTIO_VRING_H
+
diff --git a/drivers/virtio.cc b/drivers/virtio.cc
index d05f92c00..38e4ab660 100644
--- a/drivers/virtio.cc
+++ b/drivers/virtio.cc
@@ -52,10 +52,6 @@ namespace virtio {
 
     bool virtio_driver::Init(Device* dev)
     {
-        if (!earlyInitChecks()) {
-            return false;
-        }
-
         if (!Driver::Init(dev)) {
             return (false);
         }
-- 
GitLab