blob: e7b98971e049621b9a810b76284f3f36b6826124 [file] [log] [blame]
// 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/pc/bootloader.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;
}
for (const zbi_mem_range_t& range : bootloader.memory_ranges) {
zx_status_t result = pcie->SubtractBusRegion(range.paddr, range.length, PciAddrSpace::MMIO);
if (result != 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",
range.paddr, range.paddr + range.length, result);
lockdown_pcie_bus_regions(*pcie);
return;
}
}
}
LK_INIT_HOOK(x86_pcie_init, x86_pcie_init_hook, LK_INIT_LEVEL_PLATFORM)
#endif // WITH_KERNEL_PCIE