diff --git a/kernel/include/kernel/PCI.h b/kernel/include/kernel/PCI.h index d8b2322b..f7fb1945 100644 --- a/kernel/include/kernel/PCI.h +++ b/kernel/include/kernel/PCI.h @@ -66,11 +66,13 @@ namespace Kernel::PCI }; public: - Device() = default; + Device(uint8_t bus, uint8_t dev, uint8_t func) + : m_bus(bus) + , m_dev(dev) + , m_func(func) + { } - void set_location(uint8_t bus, uint8_t dev, uint8_t func); void initialize(paddr_t pcie_paddr); - bool is_valid() const { return m_is_valid; } uint32_t read_dword(uint8_t) const; uint16_t read_word(uint8_t) const; @@ -124,10 +126,9 @@ namespace Kernel::PCI BAN::ErrorOr find_intx_interrupt(); private: - bool m_is_valid { false }; - uint8_t m_bus { 0 }; - uint8_t m_dev { 0 }; - uint8_t m_func { 0 }; + const uint8_t m_bus { 0 }; + const uint8_t m_dev { 0 }; + const uint8_t m_func { 0 }; vaddr_t m_mmio_config { 0 }; @@ -161,11 +162,8 @@ namespace Kernel::PCI template void for_each_device(F callback) { - for (auto& bus : m_buses) - for (auto& dev : bus) - for (auto& func : dev) - if (func.is_valid()) - callback(func); + for (auto& dev : m_devices) + callback(dev); }; uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset); @@ -179,19 +177,22 @@ namespace Kernel::PCI BAN::Optional reserve_msi(); private: - PCIManager() : m_bus_pcie_paddr(0) {} - void check_function(uint8_t bus, uint8_t dev, uint8_t func); - void check_device(uint8_t bus, uint8_t dev); - void check_bus(uint8_t bus); - void check_all_buses(); + struct PCIeInfo + { + paddr_t bus_paddr[256]; + }; + + PCIManager() = default; + void check_function(const PCIeInfo&, uint8_t bus, uint8_t dev, uint8_t func); + void check_device(const PCIeInfo&, uint8_t bus, uint8_t dev); + void check_bus(const PCIeInfo&, uint8_t bus); + void check_all_buses(const PCIeInfo&); void initialize_impl(); private: static constexpr uint8_t m_msi_count = IRQ_MSI_END - IRQ_MSI_BASE; - using PCIBus = BAN::Array, 32>; - BAN::Array m_buses; - BAN::Array m_bus_pcie_paddr; - bool m_is_pcie { false }; + + BAN::Vector m_devices; SpinLock m_reserved_msi_lock; BAN::Array m_reserved_msi_bitmap; diff --git a/kernel/kernel/PCI.cpp b/kernel/kernel/PCI.cpp index 3c386d0c..3bd0d974 100644 --- a/kernel/kernel/PCI.cpp +++ b/kernel/kernel/PCI.cpp @@ -45,34 +45,58 @@ namespace Kernel::PCI }; static_assert(sizeof(MSIXEntry) == 16); + static uint32_t get_device_io_address(uint8_t bus, uint8_t dev, uint8_t func) + { + return 0x80000000 + | (static_cast(bus) << 16) + | (static_cast(dev) << 11) + | (static_cast(func) << 8); + } + uint32_t PCIManager::read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) { - return m_buses[bus][dev][func].read_dword(offset); + ASSERT(offset % 4 == 0); + IO::outl(CONFIG_ADDRESS, get_device_io_address(bus, dev, func) | offset); + return IO::inl(CONFIG_DATA); } uint16_t PCIManager::read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) { - return m_buses[bus][dev][func].read_word(offset); + ASSERT(offset % 2 == 0); + const uint32_t dword = read_config_dword(bus, dev, func, offset & ~3); + return (dword >> ((offset & 3) * 8)) & 0xFFFF; } uint8_t PCIManager::read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) { - return m_buses[bus][dev][func].read_byte(offset); + const uint32_t dword = read_config_dword(bus, dev, func, offset & ~3); + return (dword >> ((offset & 3) * 8)) & 0xFF; } void PCIManager::write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value) { - m_buses[bus][dev][func].write_dword(offset, value); + ASSERT(offset % 4 == 0); + IO::outl(CONFIG_ADDRESS, get_device_io_address(bus, dev, func) | offset); + IO::outl(CONFIG_DATA, value); } void PCIManager::write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value) { - m_buses[bus][dev][func].write_word(offset, value); + ASSERT(offset % 2 == 0); + const uint32_t byte = (offset & 3) * 8; + uint32_t temp = read_config_dword(bus, dev, func, offset & ~3); + temp &= ~(0xFFFF << byte); + temp |= (uint32_t)value << byte; + write_config_dword(bus, dev, func, offset & ~3, temp); } void PCIManager::write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value) { - m_buses[bus][dev][func].write_byte(offset, value); + const uint32_t byte = (offset & 3) * 8; + uint32_t temp = read_config_dword(bus, dev, func, offset & ~3); + temp &= ~(0xFF << byte); + temp |= (uint32_t)value << byte; + write_config_dword(bus, dev, func, offset & ~3, temp); } static uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func) @@ -107,6 +131,9 @@ namespace Kernel::PCI }; static_assert(sizeof(BAAS) == 16); + auto* pcie_info = new PCIeInfo; + ASSERT(pcie_info); + if (auto* mcfg = ACPI::ACPI::get().get_header("MCFG", 0)) { const size_t count = (mcfg->length - 44) / 16; @@ -118,19 +145,15 @@ namespace Kernel::PCI continue; for (uint64_t bus = baas->bus_start; bus <= baas->bus_end; bus++) { - ASSERT(m_bus_pcie_paddr[bus] == 0); - m_bus_pcie_paddr[bus] = baas->addr + (bus << 20); + ASSERT(pcie_info->bus_paddr[bus] == 0); + pcie_info->bus_paddr[bus] = baas->addr + (bus << 20); } } - - m_is_pcie = true; } - for (size_t bus = 0; bus < m_buses.size(); bus++) - for (size_t dev = 0; dev < m_buses[bus].size(); dev++) - for (size_t func = 0; func < m_buses[bus][dev].size(); func++) - m_buses[bus][dev][func].set_location(bus, dev, func); - s_instance->check_all_buses(); + check_all_buses(*pcie_info); + + delete pcie_info; } PCIManager& PCIManager::get() @@ -139,43 +162,47 @@ namespace Kernel::PCI return *s_instance; } - void PCIManager::check_function(uint8_t bus, uint8_t dev, uint8_t func) + void PCIManager::check_function(const PCIeInfo& pcie_info, uint8_t bus, uint8_t dev, uint8_t func) { - auto& device = m_buses[bus][dev][func]; - const paddr_t pcie_paddr = m_is_pcie ? m_bus_pcie_paddr[bus] + (((paddr_t)dev << 15) | ((paddr_t)func << 12)) : 0; + const paddr_t pcie_paddr = pcie_info.bus_paddr[bus] ? pcie_info.bus_paddr[bus] + (((paddr_t)dev << 15) | ((paddr_t)func << 12)) : 0; + + MUST(m_devices.emplace_back(bus, dev, func)); + + auto& device = m_devices.back(); device.initialize(pcie_paddr); + if (device.class_code() == 0x06 && device.subclass() == 0x04) - check_bus(device.read_byte(0x19)); + check_bus(pcie_info, device.read_byte(0x19)); } - void PCIManager::check_device(uint8_t bus, uint8_t dev) + void PCIManager::check_device(const PCIeInfo& pcie_info, uint8_t bus, uint8_t dev) { if (get_vendor_id(bus, dev, 0) == INVALID_VENDOR) return; - check_function(bus, dev, 0); + check_function(pcie_info, bus, dev, 0); if (get_header_type(bus, dev, 0) & MULTI_FUNCTION) for (uint8_t func = 1; func < 8; func++) if (get_vendor_id(bus, dev, func) != INVALID_VENDOR) - check_function(bus, dev, func); + check_function(pcie_info, bus, dev, func); } - void PCIManager::check_bus(uint8_t bus) + void PCIManager::check_bus(const PCIeInfo& pcie_info, uint8_t bus) { for (uint8_t dev = 0; dev < 32; dev++) - check_device(bus, dev); + check_device(pcie_info, bus, dev); } - void PCIManager::check_all_buses() + void PCIManager::check_all_buses(const PCIeInfo& pcie_info) { if (get_header_type(0, 0, 0) & MULTI_FUNCTION) { for (int func = 0; func < 8 && get_vendor_id(0, 0, func) != INVALID_VENDOR; func++) - check_bus(func); + check_bus(pcie_info, func); } else { - check_bus(0); + check_bus(pcie_info, 0); } } @@ -264,32 +291,23 @@ namespace Kernel::PCI ); } - void PCI::Device::set_location(uint8_t bus, uint8_t dev, uint8_t func) - { - m_bus = bus; - m_dev = dev; - m_func = func; - } - void PCI::Device::initialize(paddr_t pcie_paddr) { - m_is_valid = true; - if (pcie_paddr) { - vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET); + const vaddr_t vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET); ASSERT(vaddr); PageTable::kernel().map_page_at(pcie_paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::MemoryType::Uncached); m_mmio_config = vaddr; } - uint32_t type = read_word(0x0A); + const uint32_t type = read_word(0x0A); m_class_code = (uint8_t)(type >> 8); m_subclass = (uint8_t)(type); m_prog_if = read_byte(0x09); m_header_type = read_byte(0x0E); - uint32_t device = read_dword(0x00); + const uint32_t device = read_dword(0x00); m_vendor_id = device & 0xFFFF; m_device_id = device >> 16; @@ -303,62 +321,44 @@ namespace Kernel::PCI uint32_t PCI::Device::read_dword(uint8_t offset) const { - ASSERT(offset % 4 == 0); if (m_mmio_config) return MMIO::read32(m_mmio_config + offset); - uint32_t config_addr = 0x80000000 | ((uint32_t)m_bus << 16) | ((uint32_t)m_dev << 11) | ((uint32_t)m_func << 8) | offset; - IO::outl(CONFIG_ADDRESS, config_addr); - return IO::inl(CONFIG_DATA); + return s_instance->read_config_dword(m_bus, m_dev, m_func, offset); } uint16_t PCI::Device::read_word(uint8_t offset) const { - ASSERT(offset % 2 == 0); if (m_mmio_config) return MMIO::read16(m_mmio_config + offset); - uint32_t dword = read_dword(offset & ~3); - return (dword >> ((offset & 3) * 8)) & 0xFFFF; + return s_instance->read_config_word(m_bus, m_dev, m_func, offset); } uint8_t PCI::Device::read_byte(uint8_t offset) const { if (m_mmio_config) return MMIO::read8(m_mmio_config + offset); - uint32_t dword = read_dword(offset & ~3); - return (dword >> ((offset & 3) * 8)) & 0xFF; + return s_instance->read_config_byte(m_bus, m_dev, m_func, offset); } void PCI::Device::write_dword(uint8_t offset, uint32_t value) { - ASSERT(offset % 4 == 0); if (m_mmio_config) return MMIO::write32(m_mmio_config + offset, value); - uint32_t config_addr = 0x80000000 | ((uint32_t)m_bus << 16) | ((uint32_t)m_dev << 11) | ((uint32_t)m_func << 8) | offset; - IO::outl(CONFIG_ADDRESS, config_addr); - IO::outl(CONFIG_DATA, value); + s_instance->write_config_dword(m_bus, m_dev, m_func, offset, value); } void PCI::Device::write_word(uint8_t offset, uint16_t value) { - ASSERT(offset % 2 == 0); if (m_mmio_config) return MMIO::write16(m_mmio_config + offset, value); - uint32_t byte = (offset & 3) * 8; - uint32_t temp = read_dword(offset & ~3); - temp &= ~(0xFFFF << byte); - temp |= (uint32_t)value << byte; - write_dword(offset & ~3, temp); + s_instance->write_config_word(m_bus, m_dev, m_func, offset, value); } void PCI::Device::write_byte(uint8_t offset, uint8_t value) { if (m_mmio_config) return MMIO::write8(m_mmio_config + offset, value); - uint32_t byte = (offset & 3) * 8; - uint32_t temp = read_dword(offset & ~3); - temp &= ~(0xFF << byte); - temp |= (uint32_t)value << byte; - write_dword(offset & ~3, temp); + s_instance->write_config_byte(m_bus, m_dev, m_func, offset, value); } BAN::ErrorOr> PCI::Device::allocate_bar_region(uint8_t bar_num)