| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // Copyright (c) 2015 Intel Corporation |
| // Copyright (c) 2016 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #if WITH_KERNEL_PCIE |
| |
| #include <inttypes.h> |
| #include <string.h> |
| #include <trace.h> |
| #include <zircon/syscalls/pci.h> |
| #include <zircon/types.h> |
| |
| #include <dev/interrupt.h> |
| #include <dev/pcie_bus_driver.h> |
| #include <dev/pcie_platform.h> |
| #include <kernel/mutex.h> |
| #include <ktl/limits.h> |
| #include <lk/init.h> |
| |
| #include "platform_p.h" |
| |
| class X86PciePlatformSupport : public PciePlatformInterface { |
| public: |
| X86PciePlatformSupport() : PciePlatformInterface(MsiSupportLevel::MSI) {} |
| zx_status_t AllocMsiBlock(uint requested_irqs, bool can_target_64bit, bool is_msix, |
| msi_block_t* out_block) override { |
| return msi_alloc_block(requested_irqs, can_target_64bit, is_msix, out_block); |
| } |
| |
| void FreeMsiBlock(msi_block_t* block) override { msi_free_block(block); } |
| |
| void RegisterMsiHandler(const msi_block_t* block, uint msi_id, int_handler handler, |
| void* ctx) override { |
| msi_register_handler(block, msi_id, handler, ctx); |
| } |
| }; |
| |
| X86PciePlatformSupport platform_pcie_support; |
| |
| static void lockdown_pcie_bus_regions(PcieBusDriver& pcie) { |
| // If we get to this point, something has gone Extremely Wrong. Attempt to |
| // remove all possible allocatable bus addresses from the PCIe bus driver. |
| // This should *never* fail. If it does, halt and catch fire, even in a |
| // release build. |
| zx_status_t res; |
| res = pcie.SubtractBusRegion(0x0, 0x10000, PciAddrSpace::PIO); |
| ASSERT(res == ZX_OK); |
| |
| res = pcie.SubtractBusRegion(0x0, ktl::numeric_limits<uint64_t>::max(), PciAddrSpace::MMIO); |
| ASSERT(res == ZX_OK); |
| } |
| |
| static void x86_pcie_init_hook(uint level) { |
| // Initialize the bus driver |
| zx_status_t res = PcieBusDriver::InitializeDriver(platform_pcie_support); |
| if (res != ZX_OK) { |
| TRACEF( |
| "Failed to initialize PCI bus driver (res = %d). " |
| "PCI will be non-functional.\n", |
| res); |
| return; |
| } |
| |
| auto pcie = PcieBusDriver::GetDriver(); |
| DEBUG_ASSERT(pcie != nullptr); |
| |
| // Compute the initial set of PIO/MMIO bus regions which PCIe is allowed to |
| // allocate to devices for BAR windows. |
| // |
| // TODO(johngro) : do a better job of computing the valid initial PIO |
| // regions we are permitted to use. Right now, we just hardcode it. |
| constexpr uint64_t pcie_pio_base = 0x8000; |
| constexpr uint64_t pcie_pio_size = 0x10000 - pcie_pio_base; |
| |
| res = pcie->AddBusRegion(pcie_pio_base, pcie_pio_size, PciAddrSpace::PIO); |
| if (res != ZX_OK) { |
| TRACEF( |
| "WARNING - Failed to add initial PCIe PIO region " |
| "[%" PRIx64 ", %" PRIx64 ") to bus driver! (res %d)\n", |
| pcie_pio_base, pcie_pio_base + pcie_pio_size, res); |
| } |
| |
| // TODO(johngro) : Right now, we add only the low memory (< 4GB) region to |
| // the allocatable set and then subtract out everything else. Someday, we |
| // should really add in the entire 64-bit address space as a starting point. |
| // |
| // Also, we may want to consider unconditionally subtracting out the region |
| // from [0xFEC00000, 4 << 30). x86/64 architecture specific registers tend |
| // to live here and it would be Very Bad to allow PCI to allocate BARs in |
| // this region. In theory, this region should be listed in the e820 map |
| // given to us by the bootloader/BIOS, but bootloaders have been known to |
| // make mistakes in the past. |
| constexpr uint64_t pcie_mmio_base = 0x0; |
| constexpr uint64_t pcie_mmio_size = 0x100000000; |
| res = pcie->AddBusRegion(pcie_mmio_base, pcie_mmio_size, PciAddrSpace::MMIO); |
| if (res != ZX_OK) { |
| TRACEF( |
| "WARNING - Failed to add initial PCIe MMIO region " |
| "[%" PRIx64 ", %" PRIx64 ") to bus driver! (res %d)\n", |
| pcie_mmio_base, pcie_mmio_base + pcie_mmio_size, res); |
| return; |
| } |
| |
| res = enumerate_e820( |
| [](uint64_t base, uint64_t size, bool is_mem, void* ctx) -> void { |
| DEBUG_ASSERT(ctx != nullptr); |
| auto pcie = reinterpret_cast<PcieBusDriver*>(ctx); |
| zx_status_t res; |
| |
| res = pcie->SubtractBusRegion(base, size, PciAddrSpace::MMIO); |
| if (res != ZX_OK) { |
| // Woah, this is Very Bad! If we failed to prohibit the PCIe bus |
| // driver from using a region of the MMIO bus we are in a pretty |
| // dangerous situation. For now, log a message, then attempt to |
| // lockdown the bus. |
| TRACEF( |
| "FATAL ERROR - Failed to subtract PCIe MMIO region " |
| "[%" PRIx64 ", %" PRIx64 ") from bus driver! (res %d)\n", |
| base, base + size, res); |
| lockdown_pcie_bus_regions(*pcie); |
| } |
| }, |
| pcie.get()); |
| |
| if (res != ZX_OK) { |
| // Woah, this is Very Bad! If we failed to prohibit the PCIe bus |
| // driver from using a region of the MMIO bus we are in a pretty |
| // dangerous situation. For now, log a message, then attempt to |
| // lockdown the bus. |
| TRACEF("FATAL ERROR - Failed to enumerate e820 (res = %d)\n", res); |
| lockdown_pcie_bus_regions(*pcie); |
| } |
| } |
| |
| LK_INIT_HOOK(x86_pcie_init, x86_pcie_init_hook, LK_INIT_LEVEL_PLATFORM) |
| |
| #endif // WITH_KERNEL_PCIE |