Kernel: Fix PCI PIN interrupt allocation and handling

We now use the correct PIN interrupt from the interrupt line field
instead of writing to it, this had no effect :D

PIN interrupts are now mapped as shared and level triggered, this
properly allows PIN interrupt sharing with multiple devices
This commit is contained in:
2026-07-20 10:45:32 +03:00
parent 0d390e2246
commit 0b53fba991
+58 -26
View File
@@ -45,6 +45,36 @@ namespace Kernel::PCI
}; };
static_assert(sizeof(MSIXEntry) == 16); static_assert(sizeof(MSIXEntry) == 16);
class PCIPinInterrupt : public Interruptable
{
public:
PCIPinInterrupt(uint8_t irq)
{
Interruptable::set_irq(irq);
InterruptController::get().enable_irq(irq, true);
}
void handle_irq() override
{
SpinLockGuard _(m_lock);
for (auto* interruptable : m_interruptables)
interruptable->handle_irq();
}
BAN::ErrorOr<void> add_interruptable(Interruptable* interruptable)
{
SpinLockGuard _(m_lock);
TRY(m_interruptables.push_back(interruptable));
return {};
}
private:
SpinLock m_lock;
BAN::Vector<Interruptable*> m_interruptables;
};
static BAN::HashMap<uint8_t, BAN::UniqPtr<PCIPinInterrupt>> s_pci_pin_interrupts;
static uint32_t get_device_io_address(uint8_t bus, uint8_t dev, uint8_t func) static uint32_t get_device_io_address(uint8_t bus, uint8_t dev, uint8_t func)
{ {
return 0x80000000 return 0x80000000
@@ -427,9 +457,6 @@ namespace Kernel::PCI
void PCI::Device::enable_interrupt(uint8_t index, Interruptable& interruptable) void PCI::Device::enable_interrupt(uint8_t index, Interruptable& interruptable)
{ {
const uint8_t irq = get_interrupt(index);
interruptable.set_irq(irq);
const auto disable_msi = const auto disable_msi =
[this]() [this]()
{ {
@@ -450,24 +477,33 @@ namespace Kernel::PCI
write_word(*m_offset_msi_x + 0x02, msg_ctrl); write_word(*m_offset_msi_x + 0x02, msg_ctrl);
}; };
const uint8_t irq = get_interrupt(index);
switch (m_interrupt_mechanism) switch (m_interrupt_mechanism)
{ {
case InterruptMechanism::NONE: case InterruptMechanism::NONE:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
case InterruptMechanism::PIN: case InterruptMechanism::PIN:
{
enable_pin_interrupts(); enable_pin_interrupts();
disable_msi(); disable_msi();
disable_msi_x(); disable_msi_x();
if (!InterruptController::get().is_using_apic()) // TODO: allow failing
write_byte(PCI_REG_IRQ_LINE, irq); auto it = s_pci_pin_interrupts.find(irq);
InterruptController::get().enable_irq(irq); if (it == s_pci_pin_interrupts.end())
it = MUST(s_pci_pin_interrupts.insert(irq, MUST(BAN::UniqPtr<PCIPinInterrupt>::create(irq))));
MUST(it->value->add_interruptable(&interruptable));
break; break;
}
case InterruptMechanism::MSI: case InterruptMechanism::MSI:
{ {
disable_pin_interrupts(); disable_pin_interrupts();
disable_msi_x(); disable_msi_x();
interruptable.set_irq(irq);
uint16_t msg_ctrl = read_word(*m_offset_msi + 0x02); uint16_t msg_ctrl = read_word(*m_offset_msi + 0x02);
msg_ctrl &= ~(0x07 << 4); // Only one interrupt msg_ctrl &= ~(0x07 << 4); // Only one interrupt
msg_ctrl |= 1u << 0; // Enable msg_ctrl |= 1u << 0; // Enable
@@ -495,6 +531,8 @@ namespace Kernel::PCI
disable_pin_interrupts(); disable_pin_interrupts();
disable_msi(); disable_msi();
interruptable.set_irq(irq);
uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02); uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02);
msg_ctrl |= 1 << 15; // Enable msg_ctrl |= 1 << 15; // Enable
write_word(*m_offset_msi_x + 0x02, msg_ctrl); write_word(*m_offset_msi_x + 0x02, msg_ctrl);
@@ -548,7 +586,7 @@ namespace Kernel::PCI
debug_guard.disable(); debug_guard.disable();
auto& apic = static_cast<APIC&>(InterruptController::get()); auto& apic = static_cast<APIC&>(InterruptController::get());
return TRY(apic.reserve_gsi(gsi_value)); return TRY(apic.reserve_gsi(gsi_value, true));
} }
else else
{ {
@@ -587,11 +625,8 @@ namespace Kernel::PCI
if (irq_mask & (1 << irq)) if (irq_mask & (1 << irq))
break; break;
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error()) if (auto ret = InterruptController::get().reserve_irq(irq, true); ret.is_error())
{
dwarnln("FIXME: irq sharing");
return ret.release_error(); return ret.release_error();
}
return irq; return irq;
} }
@@ -621,11 +656,8 @@ namespace Kernel::PCI
debug_guard.disable(); debug_guard.disable();
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error()) if (auto ret = InterruptController::get().reserve_irq(irq, true); ret.is_error())
{
dwarnln("FIXME: irq sharing");
return ret.release_error(); return ret.release_error();
}
return irq; return irq;
} }
@@ -747,16 +779,8 @@ namespace Kernel::PCI
const auto mechanism = const auto mechanism =
[this, count]() -> InterruptMechanism [this, count]() -> InterruptMechanism
{ {
if (!InterruptController::get().is_using_apic()) if (InterruptController::get().is_using_apic())
{ {
// FIXME: support multiple PIN interrupts
if (count == 1)
return InterruptMechanism::PIN;
// MSI cannot be used without LAPIC
return InterruptMechanism::NONE;
}
if (m_offset_msi_x.has_value()) if (m_offset_msi_x.has_value())
{ {
const uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02); const uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02);
@@ -770,6 +794,7 @@ namespace Kernel::PCI
return InterruptMechanism::MSI; return InterruptMechanism::MSI;
// FIXME: support multiple MSIs // FIXME: support multiple MSIs
} }
}
if (count == 1) if (count == 1)
return InterruptMechanism::PIN; return InterruptMechanism::PIN;
@@ -790,10 +815,17 @@ namespace Kernel::PCI
case InterruptMechanism::NONE: case InterruptMechanism::NONE:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
case InterruptMechanism::PIN: case InterruptMechanism::PIN:
if (!InterruptController::get().is_using_apic()) if (InterruptController::get().is_using_apic())
return InterruptController::get().get_free_irq(); {
if (auto ret = find_intx_interrupt(); !ret.is_error()) if (auto ret = find_intx_interrupt(); !ret.is_error())
return ret.release_value(); return ret.release_value();
}
else
{
if (const uint8_t irq = read_byte(PCI_REG_IRQ_LINE); irq != 0xFF)
if (!InterruptController::get().reserve_irq(irq, true).is_error())
return irq;
}
return {}; return {};
case InterruptMechanism::MSI: case InterruptMechanism::MSI:
case InterruptMechanism::MSIX: case InterruptMechanism::MSIX: