From 5facdd3da6b0b96486e23c50603a597c5fe9e79f Mon Sep 17 00:00:00 2001
From: Christopher Anderson <cja@google.com>
Date: Wed, 14 Nov 2018 19:27:01 -0800
Subject: [PATCH] [upci] Implement ecam mapping

The pci bus driver will now obtain platform information from
pciroot and map an ecam into its address space if one exists.

Test: Checked config dump of 00:00.0, and verified kpci behavior
has not regressed;

Change-Id: I50c393c543de0323671b623c4deebe445305b727
---
 .../banjo/ddk-protocol-pciroot/pciroot.banjo  |  1 +
 system/dev/bus/acpi/pci.cpp                   | 21 +++++--
 system/dev/bus/acpi/pciroot.cpp               |  4 +-
 system/dev/bus/pci/bus.cpp                    | 58 +++++++++++++++++--
 system/dev/bus/pci/bus.h                      |  7 ++-
 system/dev/bus/pci/common.h                   | 10 ++++
 system/dev/bus/pci/config.cpp                 | 21 ++++++-
 system/dev/bus/pci/config.h                   |  2 +-
 system/public/zircon/hw/pci.h                 |  4 +-
 9 files changed, 109 insertions(+), 19 deletions(-)
 create mode 100644 system/dev/bus/pci/common.h

diff --git a/system/banjo/ddk-protocol-pciroot/pciroot.banjo b/system/banjo/ddk-protocol-pciroot/pciroot.banjo
index 5cba7b028ad..cbbb28bfaf2 100644
--- a/system/banjo/ddk-protocol-pciroot/pciroot.banjo
+++ b/system/banjo/ddk-protocol-pciroot/pciroot.banjo
@@ -22,6 +22,7 @@ struct PciEcam {
     uint16 segment_group;
     uint8 start_bus_num;
     uint8 end_bus_num;
+    handle vmo_handle;
 };
 
 /// This structure is the primary means of passing PCI platform information
diff --git a/system/dev/bus/acpi/pci.cpp b/system/dev/bus/acpi/pci.cpp
index c4d7234d4c4..b70b56a418e 100644
--- a/system/dev/bus/acpi/pci.cpp
+++ b/system/dev/bus/acpi/pci.cpp
@@ -382,12 +382,23 @@ zx_status_t pci_init(zx_device_t* parent,
     pci_mcfg_allocation_t mcfg_alloc;
     status = pci_get_segment_mcfg_alloc(dev_ctx->info.segment_group, &mcfg_alloc);
     if (status == ZX_OK) {
-        dev_ctx->info.ecam.base_address = mcfg_alloc.base_address;
-        dev_ctx->info.ecam.segment_group = mcfg_alloc.segment_group;
-        dev_ctx->info.ecam.start_bus_num = mcfg_alloc.start_bus_num;
-        dev_ctx->info.ecam.end_bus_num = mcfg_alloc.end_bus_num;
+        auto& ecam = dev_ctx->info.ecam;
+        ecam.base_address = mcfg_alloc.base_address;
+        ecam.segment_group = mcfg_alloc.segment_group;
+        ecam.start_bus_num = mcfg_alloc.start_bus_num;
+        ecam.end_bus_num = mcfg_alloc.end_bus_num;
         dev_ctx->info.has_ecam = true;
 
+        // The bus driver needs a VMO representing the entire ecam region so it can map it in
+        size_t ecam_size = (ecam.end_bus_num - ecam.start_bus_num) * PCIE_ECAM_BYTES_PER_BUS;
+        status = zx_vmo_create_physical(get_root_resource(), ecam.base_address, ecam_size,
+                                        &ecam.vmo_handle);
+        if (status != ZX_OK) {
+            zxlogf(ERROR, "couldn't create VMO for ecam, mmio cfg will not work: %d!\n", status);
+            return status;
+        }
+
+        // Default to configuration in the mcfg allocation if we didn't find a _BBN entry
         if (!found_bbn) {
             dev_ctx->info.base_bus_number = mcfg_alloc.start_bus_num;
         }
@@ -405,7 +416,7 @@ zx_status_t pci_init(zx_device_t* parent,
                kLogTag, dev_ctx->name);
     }
 
-    if (driver_get_log_flags() & DDK_LOG_TRACE) {
+    if (zxlog_level_enabled(TRACE)) {
         auto& pi = dev_ctx->info;
         printf("%s %s { acpi_obj(%p), bbn(%u), seg(%u), ecam(%u) }\n", kLogTag,
                dev_ctx->name, dev_ctx->acpi_object, pi.base_bus_number, pi.segment_group,
diff --git a/system/dev/bus/acpi/pciroot.cpp b/system/dev/bus/acpi/pciroot.cpp
index faf88dcd018..181dfaf3997 100644
--- a/system/dev/bus/acpi/pciroot.cpp
+++ b/system/dev/bus/acpi/pciroot.cpp
@@ -197,7 +197,9 @@ static zx_status_t pciroot_op_get_bti(void* context, uint32_t bdf, uint32_t inde
 
 #ifdef ENABLE_USER_PCI
 static zx_status_t pciroot_op_get_pci_platform_info(void* ctx, pci_platform_info_t* info) {
-    return ZX_ERR_NOT_SUPPORTED;
+    pciroot_ctx_t* pciroot = static_cast<pciroot_ctx_t*>(ctx);
+    *info = pciroot->info;
+    return ZX_OK;
 }
 
 static zx_status_t pciroot_op_get_pci_irq_info(void* ctx, pci_irq_info_t* info) {
diff --git a/system/dev/bus/pci/bus.cpp b/system/dev/bus/pci/bus.cpp
index 12b6bdd3159..bebe4f24cec 100644
--- a/system/dev/bus/pci/bus.cpp
+++ b/system/dev/bus/pci/bus.cpp
@@ -1,50 +1,96 @@
+// Copyright 2018 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+//
 #include "bus.h"
+#include "common.h"
 #include "config.h"
 
 #include <ddk/debug.h>
 #include <ddk/device.h>
+#include <ddk/mmio-buffer.h>
 #include <fbl/alloc_checker.h>
 
 namespace pci {
 
+// Creates the PCI bus driver instance and attempts initialization.
 zx_status_t Bus::Create(zx_device_t* parent) {
     zx_status_t status;
     pciroot_protocol_t pciroot;
     if ((status = device_get_protocol(parent, ZX_PROTOCOL_PCIROOT, &pciroot)) != ZX_OK) {
-        zxlogf(ERROR, "%s: failed to obtain pciroot protocol: %d\n", "pci_bus", status);
+        pci_errorf("failed to obtain pciroot protocol: %d!\n", status);
         return status;
     }
 
     fbl::AllocChecker ac;
     Bus* bus = new (&ac) Bus(parent, &pciroot);
     if (!ac.check()) {
-        zxlogf(ERROR, "%s: failed to allocate PciBus object.\n", "pci_bus");
+        pci_errorf("failed to allocate bus object.\n");
         return ZX_ERR_NO_MEMORY;
     }
 
-    if ((status = bus->Init()) != ZX_OK) {
-        zxlogf(ERROR, "%s: failed to initialize bus driver: %d\n", "pci_bus", status);
+    if ((status = bus->Initialize()) != ZX_OK) {
+        pci_errorf("failed to initialize bus driver: %d!\n", status);
         return status;
     }
 
     return bus->DdkAdd("pci");
 }
 
-zx_status_t Bus::Init() {
+// Maps a vmo as an mmio_buffer to be used as this Bus driver's ECAM region
+// for config space access.
+zx_status_t Bus::MapEcam(void) {
+    ZX_DEBUG_ASSERT(info_.has_ecam);
+
+    size_t size;
+    zx_status_t status = zx_vmo_get_size(info_.ecam.vmo_handle, &size);
+    if (status != ZX_OK) {
+        pci_errorf("couldn't get ecam vmo size: %d!\n", status);
+        return status;
+    }
+
+    status = mmio_buffer_init(&ecam_, 0, size, info_.ecam.vmo_handle, ZX_CACHE_POLICY_UNCACHED);
+    if (status != ZX_OK) {
+        pci_errorf("couldn't map ecam vmo: %d!\n", status);
+        return status;
+    }
+
+    pci_infof("ecam for segment %u mapped at %p (size: %#zx)\n", info_.segment_group,
+              ecam_.vaddr, ecam_.size);
+    return ZX_OK;
+}
+
+zx_status_t Bus::Initialize() {
     // Temporarily dump the config of bdf 00:00.0 to show proxy config
     // is working properly.
     pci_bdf_t bdf = {0, 0, 0};
     pciroot_protocol_t proto = {};
     GetProto(&proto);
 
-    auto cfg = ProxyConfig::Create(bdf, &proto);
+    zx_status_t status = GetPciPlatformInfo(&info_);
+    if (status != ZX_OK) {
+        pci_errorf("failed to obtain platform information: %d!\n", status);
+        return status;
+    }
+
+    if (info_.has_ecam) {
+        MapEcam();
+    }
+
+    auto cfg = MmioConfig::Create(bdf, ecam_.vaddr);
     if (cfg) {
         cfg->DumpConfig(PCI_BASE_CONFIG_SIZE);
     }
+
     return ZX_OK;
 }
 
 void Bus::DdkRelease() {
+    if (info_.has_ecam) {
+        mmio_buffer_release(&ecam_);
+    }
     delete this;
 }
 
diff --git a/system/dev/bus/pci/bus.h b/system/dev/bus/pci/bus.h
index 1263c0fb1cd..046634a029b 100644
--- a/system/dev/bus/pci/bus.h
+++ b/system/dev/bus/pci/bus.h
@@ -5,6 +5,7 @@
 #define ZIRCON_SYSTEM_DEV_BUS_PCI_BUS_H_
 
 #include <ddk/device.h>
+#include <ddk/mmio-buffer.h>
 #include <ddktl/device.h>
 #include <ddktl/protocol/pciroot.h>
 
@@ -17,13 +18,17 @@ class Bus : public PciBusType,
             public ddk::PcirootProtocolProxy {
 public:
     static zx_status_t Create(zx_device_t* parent);
-    zx_status_t Init(void);
     void DdkRelease();
 
 private:
+    // Utility methods for the bus driver
+    zx_status_t Initialize(void);
+    zx_status_t MapEcam(void);
     // Our constructor exists to fulfill the mixin constructors
     explicit Bus(zx_device_t* parent, const pciroot_protocol_t* proto)
         : PciBusType(parent), PcirootProtocolProxy(proto) {}
+    pci_platform_info_t info_;
+    mmio_buffer_t ecam_;
 };
 
 } // namespace pci
diff --git a/system/dev/bus/pci/common.h b/system/dev/bus/pci/common.h
new file mode 100644
index 00000000000..b53a016b295
--- /dev/null
+++ b/system/dev/bus/pci/common.h
@@ -0,0 +1,10 @@
+#ifndef ZIRCON_SYSTEM_DEV_BUS_PCI_COMMON_H_
+#define ZIRCON_SYSTEM_DEV_BUS_PCI_COMMON_H_
+
+#include <ddk/debug.h>
+
+#define pci_tracef(...) zxlogf(TRACE, "pci: " __VA_ARGS__)
+#define pci_errorf(...) zxlogf(ERROR, "pci: " __VA_ARGS__)
+#define pci_infof(...) zxlogf(INFO, "pci: " __VA_ARGS__)
+
+#endif // ZIRCON_SYSTEM_DEV_BUS_PCI_COMMON_H_
diff --git a/system/dev/bus/pci/config.cpp b/system/dev/bus/pci/config.cpp
index f3ceb7fa6ba..2f723a08cba 100644
--- a/system/dev/bus/pci/config.cpp
+++ b/system/dev/bus/pci/config.cpp
@@ -1,10 +1,11 @@
-// Copyright 2017 The Fuchsia Authors
+// Copyright 2018 The Fuchsia Authors
 //
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file or at
 // https://opensource.org/licenses/MIT
 
 #include "config.h"
+#include "common.h"
 
 #include <assert.h>
 #include <ddk/debug.h>
@@ -55,12 +56,26 @@ constexpr PciReg32 Config::kBridgeExpansionRomAddress;
 constexpr PciReg16 Config::kBridgeControl;
 
 // MMIO Config Implementation
-fbl::RefPtr<Config> MmioConfig::Create(pci_bdf_t bdf, zx_paddr_t base) {
+fbl::RefPtr<Config> MmioConfig::Create(pci_bdf_t bdf, void* ecam_base) {
+    ZX_DEBUG_ASSERT(bdf.device_id < PCI_MAX_DEVICES_PER_BUS);
+    ZX_DEBUG_ASSERT(bdf.function_id < PCI_MAX_FUNCTIONS_PER_DEVICE);
+
+    // Find the offset into the ecam region for the given bdf address. Every bus
+    // has 32 devices, every device has 8 functions, and each function has an
+    // extended config space of 4096 bytes.
+    zx_paddr_t bdf_start = reinterpret_cast<zx_paddr_t>(ecam_base);
+    bdf_start += bdf.bus_id * PCIE_ECAM_BYTES_PER_BUS;
+    bdf_start += bdf.device_id * PCI_MAX_FUNCTIONS_PER_DEVICE * PCIE_EXTENDED_CONFIG_SIZE;
+    bdf_start += bdf.function_id * PCIE_EXTENDED_CONFIG_SIZE;
+
+    pci_infof("created mmio cfg for bdf %02x:%02x.%1x (base: %#" PRIxPTR ")\n",
+              bdf.bus_id, bdf.device_id, bdf.function_id, bdf_start);
     fbl::AllocChecker ac;
-    fbl::RefPtr<Config> cfg = AdoptRef(new (&ac) MmioConfig(bdf, base));
+    fbl::RefPtr<Config> cfg = AdoptRef(new (&ac) MmioConfig(bdf, bdf_start));
     if (!ac.check()) {
         zxlogf(ERROR, "failed to allocate memory for PciConfig!\n");
     }
+
     return cfg;
 }
 
diff --git a/system/dev/bus/pci/config.h b/system/dev/bus/pci/config.h
index 36885522174..2205eae4db2 100644
--- a/system/dev/bus/pci/config.h
+++ b/system/dev/bus/pci/config.h
@@ -147,7 +147,7 @@ protected:
 // ecam and can be directly accessed with standard IO operations.t
 class MmioConfig final : public Config {
 public:
-    static fbl::RefPtr<Config> Create(pci_bdf_t bdf, zx_paddr_t base);
+    static fbl::RefPtr<Config> Create(pci_bdf_t bdf, void* base);
     uint8_t Read(const PciReg8 addr) const final;
     uint16_t Read(const PciReg16 addr) const final;
     uint32_t Read(const PciReg32 addr) const final;
diff --git a/system/public/zircon/hw/pci.h b/system/public/zircon/hw/pci.h
index 432b1a4a1ef..06c7023e63f 100644
--- a/system/public/zircon/hw/pci.h
+++ b/system/public/zircon/hw/pci.h
@@ -23,7 +23,7 @@ typedef struct pci_bdf {
 // guard, but remove it after the transition.
 #ifndef WITH_KERNEL_PCIE
 
-#define PCI_MAX_BUSSES (256u)
+#define PCI_MAX_BUSES (256u)
 #define PCI_MAX_DEVICES_PER_BUS (32u)
 #define PCI_MAX_FUNCTIONS_PER_DEVICE (8u)
 #define PCI_MAX_FUNCTIONS_PER_BUS (PCI_MAX_DEVICES_PER_BUS * PCI_MAX_FUNCTIONS_PER_DEVICE)
@@ -31,7 +31,7 @@ typedef struct pci_bdf {
 #define PCI_STANDARD_CONFIG_HDR_SIZE (64u)
 #define PCI_BASE_CONFIG_SIZE (256u)
 #define PCIE_EXTENDED_CONFIG_SIZE (4096u)
-#define PCIE_ECAM_BYTE_PER_BUS (PCIE_EXTENDED_CONFIG_SIZE * PCI_MAX_FUNCTIONS_PER_BUS)
+#define PCIE_ECAM_BYTES_PER_BUS (PCIE_EXTENDED_CONFIG_SIZE * PCI_MAX_FUNCTIONS_PER_BUS)
 
 #define PCI_BAR_REGS_PER_BRIDGE (2u)
 #define PCI_BAR_REGS_PER_DEVICE (6u)
-- 
GitLab