From ab8b77406dd8a45ed6f6c4ae5b0a1c2c5020b7c1 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 11 Oct 2023 22:18:58 +0300 Subject: [PATCH] Kernel: PCI can now get interrupts for devices --- kernel/include/kernel/InterruptController.h | 2 + kernel/include/kernel/PCI.h | 5 ++ kernel/kernel/PCI.cpp | 76 +++++++++++++++++---- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/kernel/include/kernel/InterruptController.h b/kernel/include/kernel/InterruptController.h index d8496604..06ed0a8a 100644 --- a/kernel/include/kernel/InterruptController.h +++ b/kernel/include/kernel/InterruptController.h @@ -35,6 +35,8 @@ namespace Kernel static void initialize(bool force_pic); static InterruptController& get(); + bool is_using_apic() const { return m_using_apic; } + void enter_acpi_mode(); private: diff --git a/kernel/include/kernel/PCI.h b/kernel/include/kernel/PCI.h index 46691da1..79098096 100644 --- a/kernel/include/kernel/PCI.h +++ b/kernel/include/kernel/PCI.h @@ -75,6 +75,8 @@ namespace Kernel::PCI uint8_t header_type() const { return m_header_type; } + BAN::ErrorOr get_irq(); + BAN::ErrorOr> allocate_bar_region(uint8_t bar_num); void enable_bus_mastering(); @@ -105,6 +107,9 @@ namespace Kernel::PCI uint8_t m_prog_if; uint8_t m_header_type; + + BAN::Optional m_offset_msi; + BAN::Optional m_offset_msi_x; }; class PCIManager diff --git a/kernel/kernel/PCI.cpp b/kernel/kernel/PCI.cpp index a052beb2..68b73f1f 100644 --- a/kernel/kernel/PCI.cpp +++ b/kernel/kernel/PCI.cpp @@ -15,6 +15,11 @@ #define CONFIG_DATA 0xCFC #define PCI_REG_COMMAND 0x04 +#define PCI_REG_STATUS 0x06 +#define PCI_REG_CAPABILITIES 0x34 +#define PCI_REG_IRQ_LINE 0x3C +#define PCI_REG_IRQ_PIN 0x44 + #define PCI_CMD_IO_SPACE (1 << 0) #define PCI_CMD_MEM_SPACE (1 << 1) #define PCI_CMD_BUS_MASTER (1 << 2) @@ -187,10 +192,9 @@ namespace Kernel::PCI { ASSERT(device.header_type() == 0x00); - uint32_t command_status = device.read_dword(0x04); - // disable io/mem space while reading bar - device.write_dword(0x04, command_status & ~3); + uint16_t command = device.read_word(PCI_REG_COMMAND); + device.write_word(PCI_REG_COMMAND, command & ~(PCI_CMD_IO_SPACE | PCI_CMD_MEM_SPACE)); uint8_t offset = 0x10 + bar_num * 4; @@ -232,9 +236,9 @@ namespace Kernel::PCI auto region = BAN::UniqPtr::adopt(region_ptr); TRY(region->initialize()); - // restore old command register and enable correct IO/MEM - command_status |= (type == BarType::IO) ? 1 : 2; - device.write_dword(0x04, command_status); + // restore old command register and enable correct IO/MEM space + command |= (type == BarType::IO) ? PCI_CMD_IO_SPACE : PCI_CMD_MEM_SPACE; + device.write_word(PCI_REG_COMMAND, command); #if DEBUG_PCI dprintln("created BAR region for PCI {}:{}.{}", @@ -373,19 +377,67 @@ namespace Kernel::PCI void PCI::Device::enumerate_capabilites() { - uint16_t status = read_word(0x06); + uint16_t status = read_word(PCI_REG_STATUS); if (!(status & (1 << 4))) return; - uint8_t capabilities = read_byte(0x34) & 0xFC; - while (capabilities) + uint8_t capability_offset = read_byte(PCI_REG_CAPABILITIES) & 0xFC; + while (capability_offset) { - uint16_t next = read_word(capabilities); - dprintln(" cap {2H}", next & 0xFF); - capabilities = (next >> 8) & 0xFC; + uint16_t capability_info = read_word(capability_offset); + + switch (capability_info & 0xFF) + { + case 0x05: + m_offset_msi = capability_offset; + dprintln("{}:{}.{} has MSI", m_bus, m_dev, m_func); + break; + case 0x11: + m_offset_msi_x = capability_offset; + dprintln("{}:{}.{} has MSI-X", m_bus, m_dev, m_func); + break; + default: + break; + } + + capability_offset = (capability_info >> 8) & 0xFC; } } + BAN::ErrorOr PCI::Device::get_irq() + { + // Legacy PIC just uses the interrupt line field + if (!InterruptController::get().is_using_apic()) + return read_byte(PCI_REG_IRQ_LINE); + + // TODO: use MSI and MSI-X if supported + + if (m_offset_msi.has_value()) + { + } + + if (m_offset_msi_x.has_value()) + { + } + + for (uint8_t irq_pin = 1; irq_pin <= 4; irq_pin++) + { + acpi_resource_t dest; + auto err = lai_pci_route_pin(&dest, 0, m_bus, m_dev, m_func, irq_pin); + if (err != LAI_ERROR_NONE) + { + dprintln("{}", lai_api_error_to_string(err)); + continue; + } + + write_byte(PCI_REG_IRQ_PIN, irq_pin); + return dest.base; + } + + dwarnln("Could not allocate interrupt for PCI {}:{}.{}", m_bus, m_dev, m_func); + return BAN::Error::from_errno(ENOTSUP); + } + void PCI::Device::set_command_bits(uint16_t mask) { write_dword(PCI_REG_COMMAND, read_dword(PCI_REG_COMMAND) | mask);