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