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