// 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 #include #include #include #include #include #include #include #include #include #include #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, fbl::numeric_limits::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(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