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 
9 #pragma once
10 
11 #include <assert.h>
12 #include <zircon/compiler.h>
13 #include <zircon/errors.h>
14 #include <dev/pcie_bus_driver.h>
15 #include <dev/pcie_caps.h>
16 #include <dev/pci_common.h>
17 #include <dev/pcie_irqs.h>
18 #include <dev/pcie_ref_counted.h>
19 #include <dev/pci_config.h>
20 #include <kernel/spinlock.h>
21 #include <fbl/algorithm.h>
22 #include <fbl/macros.h>
23 #include <fbl/mutex.h>
24 #include <fbl/ref_ptr.h>
25 #include <ktl/unique_ptr.h>
26 #include <sys/types.h>
27 
28 /* Fwd decls */
29 class  PcieBusDriver;
30 class  PcieUpstreamNode;
31 
32 /*
33  * struct used to fetch information about a config
34  */
35 struct pci_config_info_t {
36     uint64_t size = 0;
37     uint64_t base_addr = 0;
38     bool     is_mmio;
39 };
40 
41 /*
42  * struct used to fetch information about a configured base address register
43  */
44 struct pcie_bar_info_t {
45     uint64_t size = 0;
46     uint64_t bus_addr = 0;
47     bool     is_mmio;
48     bool     is_64bit;
49     bool     is_prefetchable;
50     uint     first_bar_reg;
51     RegionAllocator::Region::UPtr allocation;
52 };
53 
54 /*
55  * Base used to manage the relationship between a PCIe device/function and its
56  * associated driver.  During a bus scan/probe operation, all drivers will have
57  * their registered probe methods called until a driver claims a device.  A
58  * driver may claim a device by returning a pointer to a driver-managed
59  * pcie_device_state struct, with the driver owned fields filled out.
60  */
61 class PcieDevice {
62 public:
63     using CapabilityList = fbl::SinglyLinkedList<ktl::unique_ptr<PciStdCapability>>;
64     static fbl::RefPtr<PcieDevice> Create(PcieUpstreamNode& upstream, uint dev_id, uint func_id);
65     virtual ~PcieDevice();
66 
67     // Disallow copying, assigning and moving.
68     DISALLOW_COPY_ASSIGN_AND_MOVE(PcieDevice);
69 
70     // Require that derived classes implement ref counting.
71     PCIE_REQUIRE_REFCOUNTED;
72 
73     fbl::RefPtr<PcieUpstreamNode> GetUpstream();
74 
75     zx_status_t  Claim();
76     void         Unclaim();
77     virtual void Unplug();
78 
79     /*
80      * Trigger a function level reset (if possible)
81      */
82     zx_status_t DoFunctionLevelReset();
83 
84     /*
85      * Modify bits in the device's command register (in the device config space),
86      * clearing the bits specified by clr_bits and setting the bits specified by set
87      * bits.  Specifically, the operation will be applied as...
88      *
89      * WR(cmd, (RD(cmd) & ~clr) | set)
90      *
91      * @param clr_bits The mask of bits to be cleared.
92      * @param clr_bits The mask of bits to be set.
93      * @return A zx_status_t indicating success or failure of the operation.
94      */
95     zx_status_t ModifyCmd(uint16_t clr_bits, uint16_t set_bits);
96 
97     /*
98      * Enable or disable bus mastering in a device's configuration.
99      *
100      * @param enable If true, allow the device to access main system memory as a bus
101      * master.
102      * @return A zx_status_t indicating success or failure of the operation.
103      */
EnableBusMaster(bool enabled)104     inline zx_status_t EnableBusMaster(bool enabled) {
105         if (enabled && disabled_)
106             return ZX_ERR_BAD_STATE;
107 
108         return ModifyCmd(enabled ? 0 : PCI_COMMAND_BUS_MASTER_EN,
109                          enabled ? PCI_COMMAND_BUS_MASTER_EN : 0);
110     }
111 
112     /*
113      * Enable or disable PIO access in a device's configuration.
114      *
115      * @param enable If true, allow the device to access its PIO mapped registers.
116      * @return A zx_status_t indicating success or failure of the operation.
117      */
EnablePio(bool enabled)118     inline zx_status_t EnablePio(bool enabled) {
119         if (enabled && disabled_)
120             return ZX_ERR_BAD_STATE;
121 
122         return ModifyCmd(enabled ? 0 : PCI_COMMAND_IO_EN,
123                          enabled ? PCI_COMMAND_IO_EN : 0);
124     }
125 
126     /*
127      * Enable or disable MMIO access in a device's configuration.
128      *
129      * @param enable If true, allow the device to access its MMIO mapped registers.
130      * @return A zx_status_t indicating success or failure of the operation.
131      */
EnableMmio(bool enabled)132     inline zx_status_t EnableMmio(bool enabled) {
133         if (enabled && disabled_)
134             return ZX_ERR_BAD_STATE;
135 
136         return ModifyCmd(enabled ? 0 : PCI_COMMAND_MEM_EN,
137                          enabled ? PCI_COMMAND_MEM_EN : 0);
138     }
139 
140 
141     /*
142      * Return information about the requested base address register, if it has been
143      * allocated.  Otherwise, return NULL.
144      *
145      * @param bar_ndx The index of the BAR register to fetch info for.
146      *
147      * @return A pointer to the BAR info, including where in the bus address space
148      * the BAR window has been mapped, or NULL if the BAR window does not exist or
149      * has not been allocated.
150      */
GetBarInfo(uint bar_ndx)151     const pcie_bar_info_t* GetBarInfo(uint bar_ndx) const {
152         if (bar_ndx >= bar_count_)
153             return nullptr;
154 
155         DEBUG_ASSERT(bar_ndx < fbl::count_of(bars_));
156 
157         const pcie_bar_info_t* ret = &bars_[bar_ndx];
158         return (!disabled_ && (ret->allocation != nullptr)) ? ret : nullptr;
159     }
160 
161     /**
162      * Query the number of IRQs which are supported for a given IRQ mode by a given
163      * device.
164      *
165      * @param mode The IRQ mode to query capabilities for.
166      * @param out_caps A pointer to structure which, upon success, will hold the
167      * capabilities of the selected IRQ mode.
168      *
169      * @return A zx_status_t indicating the success or failure of the operation.
170      */
171     zx_status_t QueryIrqModeCapabilities(pcie_irq_mode_t mode,
172                                          pcie_irq_mode_caps_t* out_caps) const;
173 
174     /**
175      * Fetch details about the currently configured IRQ mode.
176      *
177      * @param out_info A pointer to the structure which (upon success) will hold
178      * info about the currently configured IRQ mode.  @see pcie_irq_mode_info_t for
179      * more details.
180      *
181      * @return A zx_status_t indicating the success or failure of the operation.
182      * Status codes may include (but are not limited to)...
183      *
184      * ++ ZX_ERR_UNAVAILABLE
185      *    The device has become unplugged and is waiting to be released.
186      */
187     zx_status_t GetIrqMode(pcie_irq_mode_info_t* out_info) const;
188 
189     /**
190      * Configure the base IRQ mode, requesting a specific number of vectors and
191      * sharing mode in the process.
192      *
193      * Devices are not permitted to transition from an active mode (anything but
194      * DISABLED) to a different active mode.  They must first transition to
195      * DISABLED, then request the new mode.
196      *
197      * Transitions to the DISABLED state will automatically mask and un-register all
198      * IRQ handlers, and return all allocated resources to the system pool.  IRQ
199      * dispatch may continue to occur for unmasked IRQs during a transition to
200      * DISABLED, but is guaranteed not to occur after the call to pcie_set_irq_mode
201      * has completed.
202      *
203      * @param mode The requested mode.
204      * @param requested_irqs The number of individual IRQ vectors the device would
205      * like to use.
206      *
207      * @return A zx_status_t indicating the success or failure of the operation.
208      * Status codes may include (but are not limited to)...
209      *
210      * ++ ZX_ERR_UNAVAILABLE
211      *    The device has become unplugged and is waiting to be released.
212      * ++ ZX_ERR_BAD_STATE
213      *    The device cannot transition into the selected mode at this point in time
214      *    due to the mode it is currently in.
215      * ++ ZX_ERR_NOT_SUPPORTED
216      *    ++ The chosen mode is not supported by the device
217      *    ++ The device supports the chosen mode, but does not support the number of
218      *       IRQs requested.
219      * ++ ZX_ERR_NO_RESOURCES
220      *    The system is unable to allocate sufficient system IRQs to satisfy the
221      *    number of IRQs and exclusivity mode requested the device driver.
222      */
223     zx_status_t SetIrqMode(pcie_irq_mode_t mode, uint requested_irqs);
224 
225     /**
226      * Set the current IRQ mode to PCIE_IRQ_MODE_DISABLED
227      *
228      * Convenience function.  @see SetIrqMode for details.
229      */
SetIrqModeDisabled()230     void SetIrqModeDisabled() {
231         /* It should be impossible to fail a transition to the DISABLED state,
232          * regardless of the state of the system.  ASSERT this in debug builds */
233         __UNUSED zx_status_t result;
234 
235         result = SetIrqMode(PCIE_IRQ_MODE_DISABLED, 0);
236 
237         DEBUG_ASSERT(result == ZX_OK);
238     }
239 
240     /**
241      * Register an IRQ handler for the specified IRQ ID.
242      *
243      * @param irq_id The ID of the IRQ to register.
244      * @param handler A pointer to the handler function to call when the IRQ is
245      * received.  Pass NULL to automatically mask the IRQ and unregister the
246      * handler.
247      * @param ctx A user supplied context pointer to pass to a registered handler.
248      *
249      * @return A zx_status_t indicating the success or failure of the operation.
250      * Status codes may include (but are not limited to)...
251      *
252      * ++ ZX_ERR_UNAVAILABLE
253      *    The device has become unplugged and is waiting to be released.
254      * ++ ZX_ERR_BAD_STATE
255      *    The device is in DISABLED IRQ mode.
256      * ++ ZX_ERR_INVALID_ARGS
257      *    The irq_id parameter is out of range for the currently configured mode.
258      */
259     zx_status_t RegisterIrqHandler(uint irq_id, pcie_irq_handler_fn_t handler, void* ctx);
260 
261     /**
262      * Mask or unmask the specified IRQ for the given device.
263      *
264      * @param irq_id The ID of the IRQ to mask or unmask.
265      * @param mask If true, mask (disable) the IRQ.  Otherwise, unmask it.
266      *
267      * @return A zx_status_t indicating the success or failure of the operation.
268      * Status codes may include (but are not limited to)...
269      *
270      * ++ ZX_ERR_UNAVAILABLE
271      *    The device has become unplugged and is waiting to be released.
272      * ++ ZX_ERR_BAD_STATE
273      *    Attempting to mask or unmask an IRQ while in the DISABLED mode or with no
274      *    handler registered.
275      * ++ ZX_ERR_INVALID_ARGS
276      *    The irq_id parameter is out of range for the currently configured mode.
277      * ++ ZX_ERR_NOT_SUPPORTED
278      *    The device is operating in MSI mode, but neither the PCI device nor the
279      *    platform interrupt controller support masking the MSI vector.
280      */
281     zx_status_t MaskUnmaskIrq(uint irq_id, bool mask);
282 
SetQuirksDone()283     void SetQuirksDone() { quirks_done_ = true; }
284 
285     /**
286      * Convenience functions.  @see MaskUnmaskIrq for details.
287      */
MaskIrq(uint irq_id)288     zx_status_t MaskIrq(uint irq_id)   { return MaskUnmaskIrq(irq_id, true); }
UnmaskIrq(uint irq_id)289     zx_status_t UnmaskIrq(uint irq_id) { return MaskUnmaskIrq(irq_id, false); }
290 
config()291     const PciConfig*     config()      const { return cfg_; }
config_phys()292     paddr_t              config_phys() const { return cfg_phys_; }
driver()293     PcieBusDriver&       driver()            { return bus_drv_; }
294 
plugged_in()295     bool     plugged_in()     const { return plugged_in_; }
disabled()296     bool     disabled()       const { return disabled_; }
quirks_done()297     bool     quirks_done()    const { return quirks_done_; }
298 
is_bridge()299     bool     is_bridge()      const { return is_bridge_; }
is_pcie()300     bool     is_pcie()        const { return (pcie_ != nullptr); }
vendor_id()301     uint16_t vendor_id()      const { return vendor_id_; }
device_id()302     uint16_t device_id()      const { return device_id_; }
class_id()303     uint8_t  class_id()       const { return class_id_; }
subclass()304     uint8_t  subclass()       const { return subclass_; }
prog_if()305     uint8_t  prog_if()        const { return prog_if_; }
rev_id()306     uint8_t  rev_id()         const { return rev_id_; }
bus_id()307     uint     bus_id()         const { return bus_id_; }
dev_id()308     uint     dev_id()         const { return dev_id_; }
func_id()309     uint     func_id()        const { return func_id_; }
bar_count()310     uint     bar_count()      const { return bar_count_; }
legacy_irq_pin()311     uint8_t  legacy_irq_pin() const { return irq_.legacy.pin; }
capabilities()312     const    CapabilityList& capabilities() const { return caps_.detected; }
313     // TODO(cja): This doesn't really make sense in a pcie capability optional world.
314     // It is only used by bridge and debug code, so it might make sense to just have those check if
315     // the device is pcie first, then use dev->pcie()->devtype().
pcie_device_type()316     pcie_device_type_t pcie_device_type() const {
317         if (pcie_)
318             return pcie_->devtype();
319         else
320             return PCIE_DEVTYPE_UNKNOWN;
321     }
322 
323     // TODO(johngro) : make these protected.  They are currently only visible
324     // because of debug code.
dev_lock()325     fbl::Mutex* dev_lock() { return &dev_lock_; }
326 
327     // Dump some information about the device
328     virtual void Dump() const;
329 
330 protected:
331     friend class PcieUpstreamNode;
332     friend class PcieBusDriver;  // TODO(johngro): remove this.  Currently used for IRQ swizzle.
333     PcieDevice(PcieBusDriver& bus_drv, uint bus_id, uint dev_id, uint func_id, bool is_bridge);
334 
335     void ModifyCmdLocked(uint16_t clr_bits, uint16_t set_bits);
AssignCmdLocked(uint16_t value)336     void AssignCmdLocked(uint16_t value) { ModifyCmdLocked(0xFFFF, value); }
337 
338     // Initialization and probing.
339     zx_status_t Init(PcieUpstreamNode& upstream);
340     zx_status_t InitLocked(PcieUpstreamNode& upstream);
341     zx_status_t ProbeBarsLocked();
342     zx_status_t ProbeBarLocked(uint bar_id);
343     zx_status_t ProbeCapabilitiesLocked();
344     zx_status_t ParseStdCapabilitiesLocked();
345     zx_status_t ParseExtCapabilitiesLocked();
346     zx_status_t MapPinToIrqLocked(fbl::RefPtr<PcieUpstreamNode>&& upstream);
347     zx_status_t InitLegacyIrqStateLocked(PcieUpstreamNode& upstream);
348 
349     // BAR allocation
350     virtual zx_status_t AllocateBars();
351     zx_status_t         AllocateBarsLocked();
352     zx_status_t         AllocateBarLocked(pcie_bar_info_t& info);
353 
354     // Disable a device, and anything downstream of it.  The device will
355     // continue to enumerate, but users will only be able to access config (and
356     // only in a read only fashion).  BAR windows, bus mastering, and interrupts
357     // will all be disabled.
358     virtual void Disable();
359     void         DisableLocked();
360 
361     PcieBusDriver& bus_drv_;        // Reference to our bus driver state.
362     const PciConfig*         cfg_ = nullptr;  // Pointer to the memory mapped ECAM (kernel vaddr)
363     paddr_t        cfg_phys_ = 0;   // The physical address of the device's ECAM
364     SpinLock       cmd_reg_lock_;   // Protection for access to the command register.
365     const bool     is_bridge_;      // True if this device is also a bridge
366     const uint     bus_id_;         // The bus ID this bridge/device exists on
367     const uint     dev_id_;         // The device ID of this bridge/device
368     const uint     func_id_;        // The function ID of this bridge/device
369     uint16_t       vendor_id_;      // The device's vendor ID, as read from config
370     uint16_t       device_id_;      // The device's device ID, as read from config
371     uint8_t        class_id_;       // The device's class ID, as read from config.
372     uint8_t        subclass_;       // The device's subclass, as read from config.
373     uint8_t        prog_if_;        // The device's programming interface (from cfg)
374     uint8_t        rev_id_;         // The device's revision ID (from cfg)
375 
376     fbl::RefPtr<PcieUpstreamNode> upstream_;  // The upstream node in the device graph.
377 
378     /* State related to lifetime management */
379     mutable fbl::Mutex dev_lock_;
380     bool          plugged_in_  = false;
381     bool          disabled_    = false;
382     bool          quirks_done_ = false;
383 
384     /* Info about the BARs computed and cached during the initial setup/probe,
385      * indexed by starting BAR register index */
386     pcie_bar_info_t bars_[PCIE_MAX_BAR_REGS];
387     const uint bar_count_;
388 
389 private:
390     friend class SharedLegacyIrqHandler;
391 
392     // Top level internal IRQ support.
393     zx_status_t QueryIrqModeCapabilitiesLocked(pcie_irq_mode_t mode,
394                                                pcie_irq_mode_caps_t* out_caps) const;
395     zx_status_t GetIrqModeLocked(pcie_irq_mode_info_t* out_info) const;
396     zx_status_t SetIrqModeLocked(pcie_irq_mode_t mode, uint requested_irqs);
397     zx_status_t RegisterIrqHandlerLocked(uint irq_id, pcie_irq_handler_fn_t handler, void* ctx);
398     zx_status_t MaskUnmaskIrqLocked(uint irq_id, bool mask);
399 
400     // Internal Legacy IRQ support.
401     zx_status_t MaskUnmaskLegacyIrq(bool mask);
402     zx_status_t EnterLegacyIrqMode(uint requested_irqs);
403     void        LeaveLegacyIrqMode();
404 
405     // Internal MSI IRQ support.
SetMsiEnb(bool enb)406     void SetMsiEnb(bool enb) {
407         DEBUG_ASSERT(irq_.msi);
408         DEBUG_ASSERT(irq_.msi->is_valid());
409         cfg_->Write(irq_.msi->ctrl_reg(),
410                 PCIE_CAP_MSI_CTRL_SET_ENB(enb, cfg_->Read(irq_.msi->ctrl_reg())));
411     }
412 
413     bool        MaskUnmaskMsiIrqLocked(uint irq_id, bool mask);
414     zx_status_t MaskUnmaskMsiIrq(uint irq_id, bool mask);
415     void        MaskAllMsiVectors();
416     void        SetMsiTarget(uint64_t tgt_addr, uint32_t tgt_data);
417     void        FreeMsiBlock();
418     void        SetMsiMultiMessageEnb(uint requested_irqs);
419     void        LeaveMsiIrqMode();
420     zx_status_t EnterMsiIrqMode(uint requested_irqs);
421 
422     void        MsiIrqHandler(pcie_irq_handler_state_t& hstate);
423     static interrupt_eoi MsiIrqHandlerThunk(void *arg);
424 
425     // Common Internal IRQ support.
426     void        ResetCommonIrqBookkeeping();
427     zx_status_t AllocIrqHandlers(uint requested_irqs, bool is_masked);
428 
429     /* Capabilities */
430     // TODO(cja): organize capabilities into their own structure
431     struct Capabilities {
432         CapabilityList detected;
433     } caps_;
434     /* PCI Express Capabilities (Standard Capability 0x10) if present */
435     PciCapPcie* pcie_ = nullptr;
436     /* PCI Advanced Capabilities (Standard Capability 0x13) if present */
437     PciCapAdvFeatures* pci_af_ = nullptr;
438 
439     // IRQ configuration and handling state
440     struct {
441         /* Shared state */
442         pcie_irq_mode_t           mode = PCIE_IRQ_MODE_DISABLED;
443         pcie_irq_handler_state_t  singleton_handler;
444         pcie_irq_handler_state_t* handlers = nullptr;
445         uint                      handler_count = 0;
446         uint                      registered_handler_count = 0;
447 
448         /* Legacy IRQ state */
449         struct {
450             // TODO(johngro): clean up the messy list_node initialization below
451             // by converting to fbl intrusive lists.
452             uint8_t pin = 0;
453             uint    irq_id = static_cast<uint>(-1);
454             struct list_node shared_handler_node = { nullptr, nullptr};
455             fbl::RefPtr<SharedLegacyIrqHandler> shared_handler;
456         } legacy;
457 
458         PciCapMsi* msi = nullptr;
459         /* TODO(johngro) : Add MSI-X state */
460         struct { } msi_x;
461     } irq_;
462 };
463