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:
@@ -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,28 +779,21 @@ 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 (m_offset_msi_x.has_value())
|
||||||
if (count == 1)
|
{
|
||||||
return InterruptMechanism::PIN;
|
const uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02);
|
||||||
|
if (count <= (msg_ctrl & 0x7FF) + 1)
|
||||||
|
return InterruptMechanism::MSIX;
|
||||||
|
}
|
||||||
|
|
||||||
// MSI cannot be used without LAPIC
|
if (m_offset_msi.has_value())
|
||||||
return InterruptMechanism::NONE;
|
{
|
||||||
}
|
if (count == 1)
|
||||||
|
return InterruptMechanism::MSI;
|
||||||
if (m_offset_msi_x.has_value())
|
// FIXME: support multiple MSIs
|
||||||
{
|
}
|
||||||
const uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02);
|
|
||||||
if (count <= (msg_ctrl & 0x7FF) + 1)
|
|
||||||
return InterruptMechanism::MSIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_offset_msi.has_value())
|
|
||||||
{
|
|
||||||
if (count == 1)
|
|
||||||
return InterruptMechanism::MSI;
|
|
||||||
// FIXME: support multiple MSIs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user