wip

Change-Id: I7c3a5ff2fdc5779df0cbbdf279ccaec6be29bf88
diff --git a/system/dev/bus/pci/protocol.c b/system/dev/bus/pci/protocol.c
index c61f170..2df280f 100644
--- a/system/dev/bus/pci/protocol.c
+++ b/system/dev/bus/pci/protocol.c
@@ -72,7 +72,7 @@
 }
 
 static uint8_t kpci_get_next_capability(void* ctx, uint8_t offset, uint8_t type) {
-    uint8_t cap_offset = offset;
+    uint8_t cap_offset = (uint8_t)kpci_config_read(ctx, offset + 1, 8);
     uint8_t limit = 64;
 
     // Walk the capability list looking for the type requested, starting at the offset
@@ -80,7 +80,9 @@
     // that causes us to iterate forever otherwise.
     while (cap_offset != 0 && limit--) {
         uint8_t type_id = (uint8_t)kpci_config_read(ctx, cap_offset, 8);
+        printf("kpci_get_next_capability loop offset %u, type %u\n", cap_offset, type_id);
         if (type_id == type) {
+            printf("kpci_get_next_capability: returning %u\n", cap_offset);
             return cap_offset;
         }
 
@@ -92,10 +94,11 @@
     return 0;
 }
 
-static mx_status_t pci_get_resource(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res) {
+static mx_status_t kpci_get_resource(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res) {
     mx_status_t status = MX_OK;
 
     if (!out_res || res_id >= PCI_RESOURCE_COUNT) {
+        printf("????\n");
         return MX_ERR_INVALID_ARGS;
     }
 
@@ -139,7 +142,7 @@
     }
 
     mx_pci_resource_t resource;
-    mx_status_t status = pci_get_resource(ctx, res_id, &resource);
+    mx_status_t status = kpci_get_resource(ctx, res_id, &resource);
     if (status != MX_OK) {
         return status;
     }
@@ -229,6 +232,7 @@
     .enable_bus_master = kpci_enable_bus_master,
     .enable_pio = kpci_enable_pio,
     .reset_device = kpci_reset_device,
+    .get_resource = kpci_get_resource,
     .map_resource = kpci_map_resource,
     .map_interrupt = kpci_map_interrupt,
     .query_irq_mode_caps = kpci_query_irq_mode_caps,
diff --git a/system/dev/bus/virtio/device.cpp b/system/dev/bus/virtio/device.cpp
index 94db9d8..a5e0aac 100644
--- a/system/dev/bus/virtio/device.cpp
+++ b/system/dev/bus/virtio/device.cpp
@@ -7,6 +7,7 @@
 #include <assert.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -17,7 +18,7 @@
 
 #include "trace.h"
 
-#define LOCAL_TRACE 0
+#define LOCAL_TRACE 1
 
 namespace virtio {
 
@@ -31,15 +32,31 @@
     LTRACE_ENTRY;
 }
 
+static void ReadVirtioCap(pci_protocol_t* pci, uint8_t offset, virtio_pci_cap& cap) {
+    cap.cap_vndr     = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_vndr)));
+    cap.cap_next     = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_next)));
+    cap.cap_len      = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_len)));
+    cap.cfg_type     = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cfg_type)));
+    cap.bar          = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, bar)));
+    cap.padding[0]   = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding)));
+    cap.padding[1]   = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding) + 1));
+    cap.padding[2]   = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding) + 2));
+    cap.offset      = pci_config_read32(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, offset)));
+    cap.length      = pci_config_read32(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, length)));
+}
+
 mx_status_t Device::MapBar(uint8_t i) {
+    LTRACEF("bar %u\n", i);
     if (bar_[i].mmio_handle != MX_HANDLE_INVALID)
         return MX_OK;
 
     uint64_t sz;
     mx_handle_t tmp_handle;
 
+    TRACE;
     mx_status_t r = pci_map_resource(&pci_, PCI_RESOURCE_BAR_0 + i, MX_CACHE_POLICY_UNCACHED_DEVICE,
                                      (void**)&bar_[i].mmio_base, &sz, &tmp_handle);
+    TRACE;
     if (r != MX_OK) {
         VIRTIO_ERROR("cannot map io %d\n", bar_[i].mmio_handle.get());
         return r;
@@ -50,8 +67,7 @@
     return MX_OK;
 }
 
-mx_status_t Device::Bind(pci_protocol_t* pci,
-                         mx_handle_t pci_config_handle, const pci_config_t* pci_config) {
+mx_status_t Device::Bind(pci_protocol_t* pci, mx_pcie_device_info_t info) {
     LTRACE_ENTRY;
 
     mxtl::AutoLock lock(&lock_);
@@ -59,8 +75,7 @@
 
     // save off handles to things
     memcpy(&pci_, pci, sizeof(pci_protocol_t));
-    pci_config_handle_.reset(pci_config_handle);
-    pci_config_ = pci_config;
+    info_ = info;
 
     // enable bus mastering
     mx_status_t r;
@@ -89,101 +104,63 @@
     LTRACEF("irq handle %u\n", irq_handle_.get());
 
     // try to parse capabilities
-    if (pci_config_->status & PCI_STATUS_NEW_CAPS) {
-        LTRACEF("pci config capabilities_ptr 0x%x\n", pci_config_->capabilities_ptr);
+    for (uint8_t off = pci_get_first_capability(&pci_, kPciCapIdVendor);
+            off != 0;
+            off = pci_get_next_capability(&pci_, off, kPciCapIdVendor)) {
+        virtio_pci_cap cap;
 
-        size_t off = pci_config_->capabilities_ptr;
-        for (int i = 0; i < 64; i++) { // only loop so many times in case things out of whack
-            virtio_pci_cap *cap;
-
-            if (off > PAGE_SIZE) {
-                VIRTIO_ERROR("capability pointer is out of whack %zu\n", off);
-                return MX_ERR_INVALID_ARGS;
-            }
-
-            cap = (virtio_pci_cap *)(((uintptr_t)pci_config_) + off);
-            LTRACEF("cap %p: type %#hhx next %#hhx len %#hhx cfg_type %#hhx bar %#hhx offset %#x length %#x\n",
-                    cap, cap->cfg_type, cap->cap_next, cap->cap_len, cap->cfg_type, cap->bar, cap->offset, cap->length);
-
-            if (cap->cap_vndr == 0x9) { // vendor specific capability
-                switch (cap->cfg_type) {
-                    case VIRTIO_PCI_CAP_COMMON_CFG: {
-                        MapBar(cap->bar);
-                        mmio_regs_.common_config = (volatile virtio_pci_common_cfg*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
-                        LTRACEF("common_config %p\n", mmio_regs_.common_config);
-                        break;
-                    }
-                    case VIRTIO_PCI_CAP_NOTIFY_CFG: {
-                        MapBar(cap->bar);
-                        mmio_regs_.notify_base = (volatile uint16_t*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
-                        LTRACEF("notify_base %p\n", mmio_regs_.notify_base);
-                        mmio_regs_.notify_mul = ((virtio_pci_notify_cap *) cap)->notify_off_multiplier;
-                        LTRACEF("notify_mul %x\n", mmio_regs_.notify_mul);
-                        break;
-                    }
-                    case VIRTIO_PCI_CAP_ISR_CFG: {
-                        MapBar(cap->bar);
-                        mmio_regs_.isr_status = (volatile uint32_t*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
-                        LTRACEF("isr_status %p\n", mmio_regs_.isr_status);
-                        break;
-                    }
-                    case VIRTIO_PCI_CAP_DEVICE_CFG: {
-                        MapBar(cap->bar);
-                        mmio_regs_.device_config = (volatile void*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
-                        LTRACEF("device_config %p\n", mmio_regs_.device_config);
-                        break;
-                    }
-                    case VIRTIO_PCI_CAP_PCI_CFG: {
-                        // will be pointing at bar0, which we'll map below anyway
-                        break;
-                    }
-                }
-            }
-
-            off = cap->cap_next;
-            if (cap->cap_next == 0)
+        ReadVirtioCap(&pci_, off, cap);
+        LTRACEF("cap type %#hhx next %#hhx len %#hhx cfg_type %#hhx bar %#hhx "
+                "offset %#x length %#x\n", cap.cap_vndr, cap.cap_next, cap.cap_len, cap.cfg_type, cap.bar, cap.offset, cap.length);
+        switch (cap.cfg_type) {
+            case VIRTIO_PCI_CAP_COMMON_CFG: {
+                MapBar(cap.bar);
+                mmio_regs_.common_config = (volatile virtio_pci_common_cfg*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
+                LTRACEF("common_config %p\n", mmio_regs_.common_config);
                 break;
+            }
+            case VIRTIO_PCI_CAP_NOTIFY_CFG: {
+                MapBar(cap.bar);
+                mmio_regs_.notify_base = (volatile uint16_t*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
+                uint8_t notify_mul_off = static_cast<uint8_t>(off + offsetof(virtio_pci_notify_cap, notify_off_multiplier));
+                mmio_regs_.notify_mul = pci_config_read32(&pci_, notify_mul_off);
+                LTRACEF("notify_base %p\n", mmio_regs_.notify_base);
+                LTRACEF("notify_mul %x\n", mmio_regs_.notify_mul);
+                break;
+            }
+            case VIRTIO_PCI_CAP_ISR_CFG: {
+                MapBar(cap.bar);
+                mmio_regs_.isr_status = (volatile uint32_t*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
+                LTRACEF("isr_status %p\n", mmio_regs_.isr_status);
+                break;
+            }
+            case VIRTIO_PCI_CAP_DEVICE_CFG: {
+                MapBar(cap.bar);
+                mmio_regs_.device_config = (volatile void*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
+                LTRACEF("device_config %p\n", mmio_regs_.device_config);
+                break;
+            }
         }
     }
 
-    // if we've found mmio pointers to everything from the capability structure, then skip mapping bar0, since we don't
-    // need legacy pio access from BAR0
-    if (!(mmio_regs_.common_config && mmio_regs_.notify_base && mmio_regs_.isr_status && mmio_regs_.device_config)) {
-        // transitional devices have a single PIO window at BAR0
-        if (pci_config_->base_addresses[0] & 0x1) {
-            // look at BAR0, which should be a PIO memory window
-            bar0_pio_base_ = pci_config->base_addresses[0];
-            LTRACEF("BAR0 address %#x\n", bar0_pio_base_);
-            if ((bar0_pio_base_ & 0x1) == 0) {
-                VIRTIO_ERROR("bar 0 does not appear to be PIO (address %#x, aborting\n", bar0_pio_base_);
+    // if we've found mmio pointers to everything from the capability structure,
+    // then skip mapping bar0, since we don't need legacy pio access from BAR0
+    if (!(mmio_regs_.common_config && mmio_regs_.notify_base &&
+                mmio_regs_.isr_status && mmio_regs_.device_config)) {
+        mx_pci_resource_t bar0;
+        r = MapBar(0);
+        if (r == MX_OK) {
+            LTRACEF("bar_[0].mmio_base %p\n", bar_[0].mmio_base);
+        } else if (r == MX_ERR_NOT_FOUND) {
+            r = pci_get_resource(&pci_, PCI_RESOURCE_BAR_0, &bar0);
+            if (r != MX_OK || bar0.type != PCI_RESOURCE_TYPE_PIO) {
+                VIRTIO_ERROR("failed to get PIO BAR0: %d\n", r);
                 return -1;
             }
-
-            bar0_pio_base_ &= ~1;
-            if (bar0_pio_base_ > 0xffff) {
-                bar0_pio_base_ = 0;
-
-                r = MapBar(0);
-                if (r != MX_OK) {
-                    VIRTIO_ERROR("cannot mmap io %d\n", r);
-                    return r;
-                }
-
-                LTRACEF("bar_[0].mmio_base %p\n", bar_[0].mmio_base);
-            } else {
-                // this is probably PIO
-                r = mx_mmap_device_io(get_root_resource(), bar0_pio_base_, bar0_size_);
-                if (r != MX_OK) {
-                    VIRTIO_ERROR("failed to access PIO range %#x, length %#xw\n", bar0_pio_base_, bar0_size_);
-                    return r;
-                }
-            }
-
-            // enable pio access
-            if ((r = pci_enable_pio(&pci_, true)) < 0) {
-                VIRTIO_ERROR("cannot enable PIO %d\n", r);
-                return -1;
-            }
+            bar0_pio_base_ = static_cast<uint32_t>(bar0.pio_addr);
+            LTRACEF("Using PIO bar0, base: %d\n", bar0_pio_base_);
+        } else {
+            LTRACEF("Failed to do the bar0 thing\n");
         }
     }
 
diff --git a/system/dev/bus/virtio/device.h b/system/dev/bus/virtio/device.h
index 6cfe4a9..da626bb 100644
--- a/system/dev/bus/virtio/device.h
+++ b/system/dev/bus/virtio/device.h
@@ -23,7 +23,7 @@
     mx_device_t* bus_device() { return bus_device_; }
     mx_device_t* device() { return device_; }
 
-    virtual mx_status_t Bind(pci_protocol_t*, mx_handle_t pci_config_handle, const pci_config_t*);
+    virtual mx_status_t Bind(pci_protocol_t*, mx_pcie_device_info_t info);
     virtual mx_status_t Init() = 0;
     virtual void Unbind();
     virtual void Release();
@@ -60,9 +60,8 @@
 
     // handles to pci bits
     pci_protocol_t pci_ = { nullptr, nullptr };
-    mx::handle pci_config_handle_ = {};
-    const pci_config_t* pci_config_ = nullptr;
     mx::handle irq_handle_ = {};
+    mx_pcie_device_info_t info_;
 
     // bar0 memory map or PIO
     uint32_t bar0_pio_base_ = 0;
diff --git a/system/dev/bus/virtio/virtio_driver.cpp b/system/dev/bus/virtio/virtio_driver.cpp
index 87db212..68d3bbf 100644
--- a/system/dev/bus/virtio/virtio_driver.cpp
+++ b/system/dev/bus/virtio/virtio_driver.cpp
@@ -10,18 +10,15 @@
 #include <ddk/device.h>
 #include <ddk/driver.h>
 #include <ddk/protocol/pci.h>
-
 #include <mxtl/alloc_checker.h>
 #include <mxtl/unique_ptr.h>
-
 #include <magenta/compiler.h>
 #include <magenta/types.h>
-
 #include "block.h"
 #include "device.h"
+#include "trace.h"
 #include "ethernet.h"
 #include "gpu.h"
-#include "trace.h"
 
 #define LOCAL_TRACE 0
 
@@ -38,22 +35,18 @@
         return -1;
     }
 
-    const pci_config_t* config;
-    size_t config_size;
-    mx_handle_t config_handle = MX_HANDLE_INVALID;
-    status = pci_map_resource(&pci, PCI_RESOURCE_CONFIG, MX_CACHE_POLICY_UNCACHED_DEVICE,
-                                   (void**)&config, &config_size, &config_handle);
+    mx_pcie_device_info_t info;
+    status = pci_get_device_info(&pci, &info);
     if (status != MX_OK) {
-        TRACEF("failed to grab config handle\n");
         return status;
     }
 
     LTRACEF("pci %p\n", &pci);
-    LTRACEF("0x%x:0x%x\n", config->vendor_id, config->device_id);
+    LTRACEF("0x%x:0x%x\n", info.vendor_id, info.device_id);
 
-    // TODO: Make symbols for these constants and reuse in the BIND protocol.
+    // XXX TODO: Make symbols for these constants and reuse in the BIND protocol.
     mxtl::unique_ptr<virtio::Device> vd = nullptr;
-    switch (config->device_id) {
+    switch (info.device_id) {
     case 0x1000:
         LTRACEF("found net device\n");
         vd.reset(new virtio::EthernetDevice(device));
@@ -73,7 +66,7 @@
     }
 
     LTRACEF("calling Bind on driver\n");
-    status = vd->Bind(&pci, config_handle, config);
+    status = vd->Bind(&pci, info);
     if (status != MX_OK)
         return status;
 
diff --git a/system/ulib/ddk/include/ddk/protocol/pci.h b/system/ulib/ddk/include/ddk/protocol/pci.h
index 5e9fb7d..21650cc 100644
--- a/system/ulib/ddk/include/ddk/protocol/pci.h
+++ b/system/ulib/ddk/include/ddk/protocol/pci.h
@@ -38,7 +38,31 @@
     kPciCfgCapabilitiesPtr = 0x34,
 };
 
+enum pci_cap_types {
+    kPciCapIdNull = 0x00,
+    kPciCapIdPciPwrMgmt = 0x01,
+    kPciCapIdAgp = 0x02,
+    kPciCapIdVpd = 0x03,
+    kPciCapIdMsi = 0x05,
+    kPciCapIdPcix = 0x07,
+    kPciCapIdHypertransport = 0x08,
+    kPciCapIdVendor = 0x09,
+    kPciCapIdDebugPort = 0x0A,
+    kPciCapIdCompactPciCrc = 0x0B,
+    kPciCapIdPciHotplug = 0x0C,
+    kPciCapIdPciBridgeSubsystemVid = 0x0D,
+    kPciCapIdAgp8x = 0x0E,
+    kPciCapIdSecureDevice = 0x0F,
+    kPciCapIdPciExpress = 0x10,
+    kPciCapIdMsix = 0x11,
+    kPciCapIdSataDataNdxCfg = 0x12,
+    kPciCapIdAdvancedFeatures = 0x13,
+    PciCapIdEnhancedAllocation = 0x14,
+};
+
+
 typedef struct pci_protocol_ops {
+    mx_status_t (*get_resource)(void* ctx, uint32_t res_id,  mx_pci_resource_t* out_res);
     mx_status_t (*map_resource)(void* ctx, uint32_t res_id, uint32_t cache_policy,
                                 void** vaddr, size_t* size, mx_handle_t* out_handle);
     mx_status_t (*enable_bus_master)(void* ctx, bool enable);
@@ -59,6 +83,11 @@
     void* ctx;
 } pci_protocol_t;
 
+static inline mx_status_t pci_get_resource(pci_protocol_t* pci, uint32_t res_id,
+                                           mx_pci_resource_t* out_info) {
+    return pci->ops->get_resource(pci->ctx, res_id, out_info);
+}
+
 static inline mx_status_t pci_map_resource(pci_protocol_t* pci, uint32_t res_id,
                                            uint32_t cache_policy, void** vaddr, size_t* size,
                                            mx_handle_t* out_handle) {
@@ -114,11 +143,10 @@
 }
 
 static uint8_t pci_get_first_capability(pci_protocol_t* pci, uint8_t type) {
-    // TODO(cja): This will need to change when config reads are limited to outside
-    // header space. Perhaps the Caps ptr can be placed in the structure returned by
-    // get_device_info?
-    uint8_t offset = pci_config_read8(pci, kPciCfgCapabilitiesPtr);
-    return pci_get_next_capability(pci, offset, type);
+    // the next_capability method will always look at the second byte next
+    // pointer to fetch the next capability. By offsetting the CapPtr field
+    // by -1 we can pretend we're working with a normal capability entry
+    return pci_get_next_capability(pci, kPciCfgCapabilitiesPtr - 1u, type);
 }
 
 __END_CDECLS;