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 26class X86PciePlatformSupport : public PciePlatformInterface { 27public: 28 X86PciePlatformSupport() 29 : PciePlatformInterface(MsiSupportLevel::MSI) {} 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 37 void FreeMsiBlock(msi_block_t* block) override { 38 msi_free_block(block); 39 } 40 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 49X86PciePlatformSupport platform_pcie_support; 50 51static 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 64static 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 141LK_INIT_HOOK(x86_pcie_init, x86_pcie_init_hook, LK_INIT_LEVEL_PLATFORM); 142 143#endif // WITH_KERNEL_PCIE 144