1// Copyright 2016 The Fuchsia Authors 2// Copyright (c) 2016, Google, Inc. All rights reserved 3// 4// Use of this source code is governed by a MIT-style 5// license that can be found in the LICENSE file or at 6// https://opensource.org/licenses/MIT 7 8#include <assert.h> 9#include <zircon/compiler.h> 10#include <debug.h> 11#include <err.h> 12#include <inttypes.h> 13#include <lk/init.h> 14#include <fbl/limits.h> 15#include <dev/interrupt.h> 16#include <string.h> 17#include <trace.h> 18#include <platform.h> 19#include <vm/vm.h> 20 21#include <dev/pci_config.h> 22#include <dev/pcie_bridge.h> 23 24#include <fbl/alloc_checker.h> 25 26using fbl::AutoLock; 27 28#define LOCAL_TRACE 0 29 30PcieBridge::PcieBridge(PcieBusDriver& bus_drv, uint bus_id, uint dev_id, uint func_id, uint mbus_id) 31 : PcieDevice(bus_drv, bus_id, dev_id, func_id, true), 32 PcieUpstreamNode(bus_drv, PcieUpstreamNode::Type::BRIDGE, mbus_id) { 33 /* Assign the driver-wide region pool to this bridge's allocators. */ 34 DEBUG_ASSERT(driver().region_bookkeeping() != nullptr); 35 pf_mmio_regions_.SetRegionPool(driver().region_bookkeeping()); 36 mmio_lo_regions_.SetRegionPool(driver().region_bookkeeping()); 37 mmio_hi_regions_.SetRegionPool(driver().region_bookkeeping()); 38 pio_regions_.SetRegionPool(driver().region_bookkeeping()); 39} 40 41fbl::RefPtr<PcieDevice> PcieBridge::Create(PcieUpstreamNode& upstream, 42 uint dev_id, 43 uint func_id, 44 uint managed_bus_id) { 45 fbl::AllocChecker ac; 46 auto raw_bridge = new (&ac) PcieBridge(upstream.driver(), 47 upstream.managed_bus_id(), dev_id, func_id, 48 managed_bus_id); 49 if (!ac.check()) { 50 DEBUG_ASSERT(raw_bridge == nullptr); 51 TRACEF("Out of memory attemping to create PCIe bridge %02x:%02x.%01x.\n", 52 upstream.managed_bus_id(), dev_id, func_id); 53 return nullptr; 54 } 55 56 auto bridge = fbl::AdoptRef(static_cast<PcieDevice*>(raw_bridge)); 57 zx_status_t res = raw_bridge->Init(upstream); 58 if (res != ZX_OK) { 59 TRACEF("Failed to initialize PCIe bridge %02x:%02x.%01x. (res %d)\n", 60 upstream.managed_bus_id(), dev_id, func_id, res); 61 return nullptr; 62 } 63 64 return bridge; 65} 66 67zx_status_t PcieBridge::Init(PcieUpstreamNode& upstream) { 68 AutoLock dev_lock(&dev_lock_); 69 70 // Initialize the device portion of ourselves first. 71 zx_status_t res = PcieDevice::InitLocked(upstream); 72 if (res != ZX_OK) 73 return res; 74 75 // Sanity checks of bus allocation. 76 // 77 // TODO(johngro) : Strengthen sanity checks around bridge topology and 78 // handle the need to reconfigure bridge topology if a bridge happens to be 79 // misconfigured. Right now, we just assume that the BIOS/Bootloader has 80 // taken care of bridge configuration. In the short term, it would be good 81 // to add some protection against cycles in the bridge configuration which 82 // could lead to infinite recursion. 83 uint primary_id = cfg_->Read(PciConfig::kPrimaryBusId); 84 uint secondary_id = cfg_->Read(PciConfig::kSecondaryBusId); 85 86 if (primary_id == secondary_id) { 87 TRACEF("PCI-to-PCI bridge detected at %02x:%02x.%01x claims to be bridged to itsef " 88 "(primary %02x == secondary %02x)... skipping scan.\n", 89 bus_id_, dev_id_, func_id_, primary_id, secondary_id); 90 return ZX_ERR_BAD_STATE; 91 } 92 93 if (primary_id != bus_id_) { 94 TRACEF("PCI-to-PCI bridge detected at %02x:%02x.%01x has invalid primary bus id " 95 "(%02x)... skipping scan.\n", 96 bus_id_, dev_id_, func_id_, primary_id); 97 return ZX_ERR_BAD_STATE; 98 } 99 100 if (secondary_id != managed_bus_id()) { 101 TRACEF("PCI-to-PCI bridge detected at %02x:%02x.%01x has invalid secondary bus id " 102 "(%02x)... skipping scan.\n", 103 bus_id_, dev_id_, func_id_, secondary_id); 104 return ZX_ERR_BAD_STATE; 105 } 106 107 // Parse the state of its I/O and Memory windows. 108 res = ParseBusWindowsLocked(); 109 if (res != ZX_OK) 110 return res; 111 112 // Things went well, flag the device as plugged in and link ourselves up to 113 // the graph. 114 plugged_in_ = true; 115 driver().LinkDeviceToUpstream(*this, upstream); 116 117 // Release the device lock, then recurse and scan for downstream devices. 118 dev_lock.release(); 119 ScanDownstream(); 120 return res; 121} 122 123zx_status_t PcieBridge::ParseBusWindowsLocked() { 124 DEBUG_ASSERT(dev_lock_.IsHeld()); 125 126 // Parse the currently configured windows used to determine MMIO/PIO 127 // forwarding policy for this bridge. 128 // 129 // See The PCI-to-PCI Bridge Architecture Specification Revision 1.2, 130 // section 3.2.5 and chapter 4 for detail. 131 uint32_t base, limit; 132 133 // I/O window 134 base = cfg_->Read(PciConfig::kIoBase); 135 limit = cfg_->Read(PciConfig::kIoLimit); 136 137 supports_32bit_pio_ = ((base & 0xF) == 0x1) && ((base & 0xF) == (limit& 0xF)); 138 io_base_ = (base & ~0xF) << 8; 139 io_limit_ = (limit << 8) | 0xFFF; 140 if (supports_32bit_pio_) { 141 io_base_ |= static_cast<uint32_t>(cfg_->Read(PciConfig::kIoBaseUpper)) << 16; 142 io_limit_ |= static_cast<uint32_t>(cfg_->Read(PciConfig::kIoLimitUpper)) << 16; 143 } 144 145 // Non-prefetchable memory window 146 mem_base_ = (static_cast<uint32_t>(cfg_->Read(PciConfig::kMemoryBase)) << 16) 147 & ~0xFFFFF; 148 mem_limit_ = (static_cast<uint32_t>(cfg_->Read(PciConfig::kMemoryLimit)) << 16) 149 | 0xFFFFF; 150 151 // Prefetchable memory window 152 base = cfg_->Read(PciConfig::kPrefetchableMemoryBase); 153 limit = cfg_->Read(PciConfig::kPrefetchableMemoryLimit); 154 155 bool supports_64bit_pf_mem = ((base & 0xF) == 0x1) && ((base & 0xF) == (limit& 0xF)); 156 pf_mem_base_ = (base & ~0xF) << 16; 157 pf_mem_limit_ = (limit << 16) | 0xFFFFF; 158 if (supports_64bit_pf_mem) { 159 pf_mem_base_ |= 160 static_cast<uint64_t>(cfg_->Read(PciConfig::kPrefetchableMemoryBaseUpper)) << 32; 161 pf_mem_limit_ |= 162 static_cast<uint64_t>(cfg_->Read(PciConfig::kPrefetchableMemoryLimitUpper)) << 32; 163 } 164 165 return ZX_OK; 166} 167 168void PcieBridge::Dump() const { 169 PcieDevice::Dump(); 170 171 printf("\tbridge managed bus id %#02x\n", managed_bus_id()); 172 printf("\tio base %#x limit %#x\n", io_base(), io_limit()); 173 printf("\tmem base %#x limit %#x\n", mem_base(), mem_limit()); 174 printf("\tprefectable base %#" PRIx64 " limit %#" PRIx64 "\n", pf_mem_base(), pf_mem_limit()); 175} 176 177void PcieBridge::Unplug() { 178 PcieDevice::Unplug(); 179 PcieUpstreamNode::UnplugDownstream(); 180} 181 182zx_status_t PcieBridge::AllocateBars() { 183 AutoLock dev_lock(&dev_lock_); 184 185 // Start by making sure we can allocate our bridge windows. 186 zx_status_t res = AllocateBridgeWindowsLocked(); 187 if (res != ZX_OK) 188 return res; 189 190 // Now, attempt to allocate our device BARs. 191 res = PcieDevice::AllocateBarsLocked(); 192 if (res != ZX_OK) 193 return res; 194 195 // Great, we are good to go. Leave our device lock and attempt to allocate 196 // our downstream devices' resources. 197 dev_lock.release(); 198 PcieUpstreamNode::AllocateDownstreamBars(); 199 return ZX_OK; 200} 201 202zx_status_t PcieBridge::AllocateBridgeWindowsLocked() { 203 zx_status_t ret; 204 DEBUG_ASSERT(dev_lock_.IsHeld()); 205 206 // Hold a reference to our upstream node while we do this. If we cannot 207 // obtain a reference, then our upstream node has become unplugged and we 208 // should just fail out now. 209 auto upstream = GetUpstream(); 210 if (upstream == nullptr) 211 return ZX_ERR_UNAVAILABLE; 212 213 // We are configuring a bridge. We need to be able to allocate the MMIO and 214 // PIO regions this bridge is configured to manage. Currently, we don't 215 // support re-allocating a bridge's MMIO/PIO windows. 216 // 217 // TODO(johngro) : support dynamic configuration of bridge windows. Its 218 // going to be important when we need to support hot-plugging. See ZX-322 219 // 220 if (io_base_ <= io_limit_) { 221 uint64_t size = static_cast<uint64_t>(io_limit_) - io_base_ + 1; 222 ret = upstream->pio_regions().GetRegion({ .base = io_base_, .size = size }, pio_window_); 223 224 if (ret != ZX_OK) { 225 TRACEF("Failed to allocate bridge PIO window [0x%08x, 0x%08x]\n", io_base_, io_limit_); 226 return ret; 227 } 228 229 DEBUG_ASSERT(pio_window_ != nullptr); 230 pio_regions().AddRegion(*pio_window_); 231 } 232 233 if (mem_base_ <= mem_limit_) { 234 uint64_t size = mem_limit_ - mem_base_ + 1; 235 ret = upstream->mmio_lo_regions().GetRegion({ .base = mem_base_, .size = size }, 236 mmio_window_); 237 238 if (ret != ZX_OK) { 239 TRACEF("Failed to allocate bridge MMIO window [0x%08x, 0x%08x]\n", 240 mem_base_, mem_limit_); 241 return ret; 242 } 243 244 DEBUG_ASSERT(mmio_window_ != nullptr); 245 mmio_lo_regions().AddRegion(*mmio_window_); 246 } 247 248 if (pf_mem_base_ <= pf_mem_limit_) { 249 uint64_t size = pf_mem_limit_ - pf_mem_base_ + 1; 250 251 // Attempt to allocate out of the upstream's prefetchable region. 252 ret = upstream->pf_mmio_regions().GetRegion({ .base = pf_mem_base_, .size = size }, 253 pf_mmio_window_); 254 if (ret != ZX_OK) { 255 // We failed. If it's the root bridge try to allocate from its MMIO regions. 256 if (upstream->type() == PcieUpstreamNode::Type::ROOT) { 257 ret = upstream->mmio_lo_regions().GetRegion({ .base = pf_mem_base_, .size = size }, 258 pf_mmio_window_); 259 if (ret != ZX_OK) { 260 ret = upstream->mmio_hi_regions().GetRegion({ .base = pf_mem_base_, .size = size }, 261 pf_mmio_window_); 262 } 263 } 264 } 265 266 if (ret != ZX_OK) { 267 TRACEF("Failed to allocate bridge prefetcable MMIO window " 268 "[%#" PRIx64 ", %#" PRIx64 "]\n", 269 pf_mem_base_, pf_mem_limit_); 270 return ret; 271 } 272 273 DEBUG_ASSERT(pf_mmio_window_ != nullptr); 274 pf_mmio_regions().AddRegion(*pf_mmio_window_); 275 } 276 277 return ZX_OK; 278} 279 280void PcieBridge::Disable() { 281 DEBUG_ASSERT(!dev_lock_.IsHeld()); 282 283 // Immediately enter the device lock and enter the disabled state. We want 284 // to be outside of the device lock as we disable our downstream devices, 285 // but we don't want any new devices to be able to plug into us as we do so. 286 { 287 AutoLock dev_lock(&dev_lock_); 288 disabled_ = true; 289 } 290 291 // Start by disabling all of our downstream devices. This should prevent 292 // the from bothering us moving forward. Do not hold the device lock while 293 // we do this. 294 PcieUpstreamNode::DisableDownstream(); 295 296 // Enter the device lock again and finish shooting ourselves in the head. 297 { 298 AutoLock dev_lock(&dev_lock_); 299 300 // Disable the device portion of ourselves. 301 PcieDevice::DisableLocked(); 302 303 // Close all of our IO windows at the HW level and update the internal 304 // bookkeeping to indicate that they are closed. 305 cfg_->Write(PciConfig::kIoBase, 0xF0); 306 cfg_->Write(PciConfig::kIoLimit, 0); 307 cfg_->Write(PciConfig::kIoBaseUpper, 0); 308 cfg_->Write(PciConfig::kIoLimitUpper, 0); 309 310 cfg_->Write(PciConfig::kMemoryBase, 0xFFF0); 311 cfg_->Write(PciConfig::kMemoryLimit, 0); 312 313 cfg_->Write(PciConfig::kPrefetchableMemoryBase, 0xFFF0); 314 cfg_->Write(PciConfig::kPrefetchableMemoryLimit, 0); 315 cfg_->Write(PciConfig::kPrefetchableMemoryBaseUpper, 0); 316 cfg_->Write(PciConfig::kPrefetchableMemoryLimitUpper, 0); 317 318 pf_mem_limit_ = mem_limit_ = io_limit_ = 0u; 319 pf_mem_base_ = mem_base_ = io_base_ = 1u; 320 321 // Release our internal bookkeeping 322 mmio_lo_regions().Reset(); 323 mmio_hi_regions().Reset(); 324 pio_regions().Reset(); 325 326 mmio_window_.reset(); 327 pio_window_.reset(); 328 } 329} 330