1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2009 Corey Tabaka
3 // Copyright (c) 2015 Intel Corporation
4 // Copyright (c) 2016 Travis Geiselbrecht
5 //
6 // Use of this source code is governed by a MIT-style
7 // license that can be found in the LICENSE file or at
8 // https://opensource.org/licenses/MIT
9 
10 #if WITH_KERNEL_PCIE
11 
12 #include <dev/interrupt.h>
13 #include <dev/pcie_bus_driver.h>
14 #include <dev/pcie_platform.h>
15 #include <fbl/limits.h>
16 #include <inttypes.h>
17 #include <kernel/mutex.h>
18 #include <lk/init.h>
19 #include <string.h>
20 #include <trace.h>
21 #include <zircon/syscalls/pci.h>
22 #include <zircon/types.h>
23 
24 #include "platform_p.h"
25 
26 class X86PciePlatformSupport : public PciePlatformInterface {
27 public:
X86PciePlatformSupport()28     X86PciePlatformSupport()
29         : PciePlatformInterface(MsiSupportLevel::MSI) {}
AllocMsiBlock(uint requested_irqs,bool can_target_64bit,bool is_msix,msi_block_t * out_block)30     zx_status_t AllocMsiBlock(uint requested_irqs,
31                               bool can_target_64bit,
32                               bool is_msix,
33                               msi_block_t* out_block) override {
34         return msi_alloc_block(requested_irqs, can_target_64bit, is_msix, out_block);
35     }
36 
FreeMsiBlock(msi_block_t * block)37     void FreeMsiBlock(msi_block_t* block) override {
38         msi_free_block(block);
39     }
40 
RegisterMsiHandler(const msi_block_t * block,uint msi_id,int_handler handler,void * ctx)41     void RegisterMsiHandler(const msi_block_t* block,
42                             uint msi_id,
43                             int_handler handler,
44                             void* ctx) override {
45         msi_register_handler(block, msi_id, handler, ctx);
46     }
47 };
48 
49 X86PciePlatformSupport platform_pcie_support;
50 
lockdown_pcie_bus_regions(PcieBusDriver & pcie)51 static void lockdown_pcie_bus_regions(PcieBusDriver& pcie) {
52     // If we get to this point, something has gone Extremely Wrong.  Attempt to
53     // remove all possible allocatable bus addresses from the PCIe bus driver.
54     // This should *never* fail.  If it does, halt and catch fire, even in a
55     // release build.
56     zx_status_t res;
57     res = pcie.SubtractBusRegion(0x0, 0x10000, PciAddrSpace::PIO);
58     ASSERT(res == ZX_OK);
59 
60     res = pcie.SubtractBusRegion(0x0, fbl::numeric_limits<uint64_t>::max(), PciAddrSpace::MMIO);
61     ASSERT(res == ZX_OK);
62 }
63 
x86_pcie_init_hook(uint level)64 static void x86_pcie_init_hook(uint level) {
65     // Initialize the bus driver
66     zx_status_t res = PcieBusDriver::InitializeDriver(platform_pcie_support);
67     if (res != ZX_OK) {
68         TRACEF("Failed to initialize PCI bus driver (res = %d).  "
69                "PCI will be non-functional.\n",
70                res);
71         return;
72     }
73 
74     auto pcie = PcieBusDriver::GetDriver();
75     DEBUG_ASSERT(pcie != nullptr);
76 
77     // Compute the initial set of PIO/MMIO bus regions which PCIe is allowed to
78     // allocate to devices for BAR windows.
79     //
80     // TODO(johngro) : do a better job of computing the valid initial PIO
81     // regions we are permitted to use.  Right now, we just hardcode it.
82     constexpr uint64_t pcie_pio_base = 0x8000;
83     constexpr uint64_t pcie_pio_size = 0x10000 - pcie_pio_base;
84 
85     res = pcie->AddBusRegion(pcie_pio_base, pcie_pio_size, PciAddrSpace::PIO);
86     if (res != ZX_OK) {
87         TRACEF("WARNING - Failed to add initial PCIe PIO region "
88                "[%" PRIx64 ", %" PRIx64 ") to bus driver! (res %d)\n",
89                pcie_pio_base, pcie_pio_base + pcie_pio_size, res);
90     }
91 
92     // TODO(johngro) : Right now, we add only the low memory (< 4GB) region to
93     // the allocatable set and then subtract out everything else.  Someday, we
94     // should really add in the entire 64-bit address space as a starting point.
95     //
96     // Also, we may want to consider unconditionally subtracting out the region
97     // from [0xFEC00000, 4 << 30).  x86/64 architecture specific registers tend
98     // to live here and it would be Very Bad to allow PCI to allocate BARs in
99     // this region.  In theory, this region should be listed in the e820 map
100     // given to us by the bootloader/BIOS, but bootloaders have been known to
101     // make mistakes in the past.
102     constexpr uint64_t pcie_mmio_base = 0x0;
103     constexpr uint64_t pcie_mmio_size = 0x100000000;
104     res = pcie->AddBusRegion(pcie_mmio_base, pcie_mmio_size, PciAddrSpace::MMIO);
105     if (res != ZX_OK) {
106         TRACEF("WARNING - Failed to add initial PCIe MMIO region "
107                "[%" PRIx64 ", %" PRIx64 ") to bus driver! (res %d)\n",
108                pcie_mmio_base, pcie_mmio_base + pcie_mmio_size, res);
109         return;
110     }
111 
112     res = enumerate_e820([](uint64_t base, uint64_t size, bool is_mem, void* ctx) -> void {
113         DEBUG_ASSERT(ctx != nullptr);
114         auto pcie = reinterpret_cast<PcieBusDriver*>(ctx);
115         zx_status_t res;
116 
117         res = pcie->SubtractBusRegion(base, size, PciAddrSpace::MMIO);
118         if (res != ZX_OK) {
119             // Woah, this is Very Bad!  If we failed to prohibit the PCIe bus
120             // driver from using a region of the MMIO bus we are in a pretty
121             // dangerous situation.  For now, log a message, then attempt to
122             // lockdown the bus.
123             TRACEF("FATAL ERROR - Failed to subtract PCIe MMIO region "
124                    "[%" PRIx64 ", %" PRIx64 ") from bus driver! (res %d)\n",
125                    base, base + size, res);
126             lockdown_pcie_bus_regions(*pcie);
127         }
128     },
129                          pcie.get());
130 
131     if (res != ZX_OK) {
132         // Woah, this is Very Bad!  If we failed to prohibit the PCIe bus
133         // driver from using a region of the MMIO bus we are in a pretty
134         // dangerous situation.  For now, log a message, then attempt to
135         // lockdown the bus.
136         TRACEF("FATAL ERROR - Failed to enumerate e820 (res = %d)\n", res);
137         lockdown_pcie_bus_regions(*pcie);
138     }
139 }
140 
141 LK_INIT_HOOK(x86_pcie_init, x86_pcie_init_hook, LK_INIT_LEVEL_PLATFORM);
142 
143 #endif // WITH_KERNEL_PCIE
144