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