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 #pragma once 9 10 #include <dev/address_provider/address_provider.h> 11 #include <dev/pci_config.h> 12 #include <dev/pcie_platform.h> 13 #include <kernel/auto_lock.h> 14 #include <kernel/mutex.h> 15 #include <fbl/intrusive_single_list.h> 16 #include <fbl/intrusive_wavl_tree.h> 17 #include <fbl/macros.h> 18 #include <fbl/ref_counted.h> 19 #include <fbl/ref_ptr.h> 20 #include <region-alloc/region-alloc.h> 21 22 class SharedLegacyIrqHandler; 23 24 class PcieBridge; 25 class PcieDebugConsole; 26 class PcieDevice; 27 class PcieRoot; 28 class PcieUpstreamNode; 29 class PciConfig; 30 31 class PcieBusDriver : public fbl::RefCounted<PcieBusDriver> { 32 public: 33 // QuirkHandler 34 // 35 // Definition of a quirk handler hook. Quirks are behaviors which can be 36 // registered by platforms to deal with the sometimes odd (dare I say, 37 // quirky?) behavior of hardware detected on the PCI bus. All registered 38 // quirks handlers are executed whenever new hardware is discovered and 39 // probed, but before resource assignment has taken place. 40 // 41 // Once the system has been initialized and is ready to begin resource 42 // allocation, all quirks will be executed one final time will nullptr 43 // passed as the device argument. It is recommended that all quirks 44 // implementations use this final call as one last chance to make certain 45 // that the quirk has successfully done its job, and to log a warning/error 46 // if it has not. 47 // 48 // For example, if a platform has a quirk to deal with a particular oddness 49 // of a specific chipset, the quirk should use the final call as a chance to 50 // check to make sure that it saw a chipset device recognized and took 51 // appropriate action. If it didn't, it should log a warning informing the 52 // maintainers to come back and update the quirk to take the appropriate 53 // actions (if any) for the new chipset. 54 using QuirkHandler = void (*)(const fbl::RefPtr<PcieDevice>& device); 55 56 ~PcieBusDriver(); 57 platform()58 PciePlatformInterface& platform() const { return platform_; } 59 60 const PciConfig* GetConfig(uint bus_id, 61 uint dev_id, 62 uint func_id, 63 paddr_t* out_cfg_phys = nullptr); 64 65 // Address space (PIO and MMIO) allocation management 66 // 67 // Note: Internally, regions held for MMIO address space allocation are 68 // tracked in two different allocators; one for <4GB allocations usable by 69 // 32-bit or 64-bit BARs, and one for >4GB allocations usable only by 64-bit 70 // BARs. 71 // 72 // Users of Add/SubtractBusRegion are permitted to supply regions which span 73 // the 4GB mark in the MMIO address space, but their operation will be 74 // internally split into two different operations executed against the two 75 // different allocators. The low memory portion of the operation will be 76 // executed first. In the case that the first of the split operations 77 // succeeds but the second fails, the first operation will not be rolled 78 // back. If this behavior is unacceptable, users should be sure to submit 79 // only MMIO address space operations which target regions either entirely 80 // above or entirely below the 4GB mark. AddBusRegion(uint64_t base,uint64_t size,PciAddrSpace aspace)81 zx_status_t AddBusRegion(uint64_t base, uint64_t size, PciAddrSpace aspace) { 82 return AddSubtractBusRegion(base, size, aspace, true); 83 } 84 SubtractBusRegion(uint64_t base,uint64_t size,PciAddrSpace aspace)85 zx_status_t SubtractBusRegion(uint64_t base, uint64_t size, PciAddrSpace aspace) { 86 return AddSubtractBusRegion(base, size, aspace, false); 87 } 88 89 // Add a root bus to the driver and attempt to scan it for devices. 90 zx_status_t AddRoot(fbl::RefPtr<PcieRoot>&& root); 91 92 // A PcieAddressProvider translates a BDF address to an address that the 93 // system can use to access ECAMs. 94 zx_status_t SetAddressTranslationProvider(ktl::unique_ptr<PcieAddressProvider> provider); 95 96 // Start the driver 97 // 98 // Notes about startup: 99 // Before starting the bus driver, platforms must add all of the resources 100 // to be used by the driver during operation. Once started, the set of 101 // resources used by the driver may not be modified. Resources which must 102 // be supplied include... 103 // 104 // ++ ECAM regions for memory mapped config sections. See AddEcamRegion 105 // ++ Bus regions for both MMIO and PIO bus access. See (Add|Subtract)BusRegion 106 // ++ Roots. See AddRoot 107 // 108 // Resources may be added in any order. 109 // 110 // Once all of the resources have been added, StartBusDriver will scan for 111 // devices under each of the added roots, run all registered quirks and 112 // attempt to allocated bus/IRQ resources for discovered devices. 113 // 114 zx_status_t StartBusDriver(); 115 116 // Rescan looking for new devices 117 zx_status_t RescanDevices(); 118 119 // TODO(johngro) : Remove this someday. Getting the "Nth" device is not a 120 // concept which is going to carry over well to the world of hot-pluggable 121 // devices. 122 fbl::RefPtr<PcieDevice> GetNthDevice(uint32_t index); 123 124 // Topology related stuff 125 void LinkDeviceToUpstream(PcieDevice& dev, PcieUpstreamNode& upstream); 126 void UnlinkDeviceFromUpstream(PcieDevice& dev); 127 fbl::RefPtr<PcieUpstreamNode> GetUpstream(PcieDevice& dev); 128 fbl::RefPtr<PcieDevice> GetDownstream(PcieUpstreamNode& upstream, uint ndx); 129 fbl::RefPtr<PcieDevice> GetRefedDevice(uint bus_id, uint dev_id, uint func_id); 130 131 // Bus region allocation region_bookkeeping()132 const RegionAllocator::RegionPool::RefPtr& region_bookkeeping() const { 133 return region_bookkeeping_; 134 } pf_mmio_regions()135 RegionAllocator& pf_mmio_regions() { return pf_mmio_regions_; } mmio_lo_regions()136 RegionAllocator& mmio_lo_regions() { return mmio_lo_regions_; } mmio_hi_regions()137 RegionAllocator& mmio_hi_regions() { return mmio_hi_regions_; } pio_regions()138 RegionAllocator& pio_regions() { return pio_regions_; } 139 140 // TODO(johngro) : Make this private when we can. 141 fbl::RefPtr<SharedLegacyIrqHandler> FindLegacyIrqHandler(uint irq_id); 142 // TODO(johngro) : end TODO section 143 144 // Disallow copying, assigning and moving. 145 DISALLOW_COPY_ASSIGN_AND_MOVE(PcieBusDriver); 146 GetDriver()147 static fbl::RefPtr<PcieBusDriver> GetDriver() { 148 fbl::AutoLock lock(&driver_lock_); 149 return driver_; 150 } 151 152 void DisableBus(); 153 static zx_status_t InitializeDriver(PciePlatformInterface& platform); 154 static void ShutdownDriver(); 155 156 // Debug/ASSERT routine, used by devices and bridges to assert that the 157 // rescan lock is currently being held. RescanLockIsHeld()158 bool RescanLockIsHeld() const { return bus_rescan_lock_.IsHeld(); }; 159 160 private: 161 friend class PcieDebugConsole; 162 static constexpr size_t REGION_BOOKKEEPING_SLAB_SIZE = 16 << 10; 163 static constexpr size_t REGION_BOOKKEEPING_MAX_MEM = 128 << 10; 164 165 using RootCollection = fbl::WAVLTree<uint, fbl::RefPtr<PcieRoot>>; 166 using ForeachRootCallback = bool (*)(const fbl::RefPtr<PcieRoot>& root, void* ctx); 167 using ForeachDeviceCallback = bool (*)(const fbl::RefPtr<PcieDevice>& dev, 168 void* ctx, uint level); 169 170 enum class State { 171 NOT_STARTED = 0, 172 STARTING_SCANNING = 1, 173 STARTING_RUNNING_QUIRKS = 2, 174 STARTING_RESOURCE_ALLOCATION = 3, 175 OPERATIONAL = 4, 176 }; 177 178 explicit PcieBusDriver(PciePlatformInterface& platform); 179 180 bool AdvanceState(State expected, State next); 181 bool IsNotStarted(bool allow_quirks_phase = false) const; IsOperational()182 bool IsOperational() const { smp_mb(); return state_ == State::OPERATIONAL; } 183 184 zx_status_t AllocBookkeeping(); 185 void ForeachRoot(ForeachRootCallback cbk, void* ctx); 186 void ForeachDevice(ForeachDeviceCallback cbk, void* ctx); 187 bool ForeachDownstreamDevice(const fbl::RefPtr<PcieUpstreamNode>& upstream, 188 uint level, 189 ForeachDeviceCallback cbk, 190 void* ctx); 191 zx_status_t AddSubtractBusRegion(uint64_t base, uint64_t size, 192 PciAddrSpace aspace, bool add_op); 193 194 // IRQ support. Implementation in pcie_irqs.cpp 195 void ShutdownIrqs(); 196 197 static void RunQuirks(const fbl::RefPtr<PcieDevice>& device); 198 199 State state_ = State::NOT_STARTED; 200 fbl::Mutex bus_topology_lock_; 201 fbl::Mutex bus_rescan_lock_; 202 mutable fbl::Mutex start_lock_; 203 RootCollection roots_; 204 fbl::SinglyLinkedList<fbl::RefPtr<PciConfig>> configs_; 205 206 RegionAllocator::RegionPool::RefPtr region_bookkeeping_; 207 RegionAllocator pf_mmio_regions_; 208 RegionAllocator mmio_lo_regions_; 209 RegionAllocator mmio_hi_regions_; 210 RegionAllocator pio_regions_; 211 212 ktl::unique_ptr<PcieAddressProvider> addr_provider_; 213 214 fbl::Mutex legacy_irq_list_lock_; 215 fbl::SinglyLinkedList<fbl::RefPtr<SharedLegacyIrqHandler>> legacy_irq_list_; 216 PciePlatformInterface& platform_; 217 218 static fbl::RefPtr<PcieBusDriver> driver_; 219 static fbl::Mutex driver_lock_; 220 }; 221