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