Kernel: Allow allocating shared interrupts and setting trigger mode

This commit is contained in:
2026-07-20 10:28:18 +03:00
parent d46d7a5123
commit a3b7a758eb
5 changed files with 74 additions and 54 deletions

View File

@@ -12,10 +12,10 @@ namespace Kernel
{
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual void enable_irq(uint8_t, bool level_triggered) override;
virtual bool is_in_service(uint8_t) override;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq) override;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq, bool shared) override;
virtual BAN::Optional<uint8_t> get_free_irq() override;
virtual void initialize_multiprocessor() override;
@@ -23,7 +23,7 @@ namespace Kernel
virtual void broadcast_ipi() override;
virtual void enable() override;
BAN::ErrorOr<uint8_t> reserve_gsi(uint32_t gsi);
BAN::ErrorOr<uint8_t> reserve_gsi(uint32_t gsi, bool shared = false);
void initialize_timer();
void set_timer_dealine(uint64_t ns);
@@ -71,7 +71,8 @@ namespace Kernel
Kernel::vaddr_t m_local_apic_vaddr = 0;
BAN::Vector<IOAPIC> m_io_apics;
uint8_t m_irq_overrides[0x100] {};
uint8_t m_reserved_gsis[m_irq_count / 8] {};
uint8_t m_used_gsis[m_irq_count / 8] {};
uint8_t m_excl_gsis[m_irq_count / 8] {};
uint64_t m_lapic_timer_frequency_hz { 0 };
};

View File

@@ -14,7 +14,7 @@ namespace Kernel
virtual ~InterruptController() {}
virtual void eoi(uint8_t) = 0;
virtual void enable_irq(uint8_t) = 0;
virtual void enable_irq(uint8_t, bool level_triggered = false) = 0;
virtual bool is_in_service(uint8_t) = 0;
static void initialize(bool force_pic);
@@ -26,7 +26,7 @@ namespace Kernel
virtual void broadcast_ipi() = 0;
virtual void enable() = 0;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq) = 0;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq, bool shared = false) = 0;
virtual BAN::Optional<uint8_t> get_free_irq() = 0;
bool is_using_apic() const { return m_using_apic; }

View File

@@ -10,10 +10,10 @@ namespace Kernel
{
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual void enable_irq(uint8_t, bool level_triggered) override;
virtual bool is_in_service(uint8_t) override;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq) override;
virtual BAN::ErrorOr<void> reserve_irq(uint8_t irq, bool shared) override;
virtual BAN::Optional<uint8_t> get_free_irq() override;
virtual void initialize_multiprocessor() override;
@@ -29,7 +29,8 @@ namespace Kernel
private:
SpinLock m_lock;
uint16_t m_reserved_irqs { 1u << 2 };
uint16_t m_used_irqs { 1u << 2 };
uint16_t m_excl_irqs { 1u << 2 };
friend class InterruptController;
};

View File

@@ -487,17 +487,12 @@ namespace Kernel
write_to_local_apic(LAPIC_EIO_REG, 0);
}
void APIC::enable_irq(uint8_t irq)
void APIC::enable_irq(uint8_t irq, bool level_triggered)
{
SpinLockGuard _(m_lock);
const uint32_t gsi = m_irq_overrides[irq];
{
int byte = gsi / 8;
int bit = gsi % 8;
ASSERT(m_reserved_gsis[byte] & (1 << bit));
}
ASSERT(m_used_gsis[gsi / 8] & (1 << (gsi % 8)));
IOAPIC* ioapic = nullptr;
for (IOAPIC& io : m_io_apics)
@@ -515,27 +510,27 @@ namespace Kernel
RedirectionEntry redir;
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + pin * 2);
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + pin * 2 + 1);
ASSERT(redir.mask); // TODO: handle overlapping interrupts
redir.trigger_mode = level_triggered;
redir.vector = IRQ_VECTOR_BASE + irq;
redir.mask = 0;
// FIXME: distribute IRQs more evenly?
redir.destination = Kernel::Processor::bsp_id().as_u32();
ioapic->write(IOAPIC_REDIRS + pin * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + pin * 2 + 1, redir.hi_dword);
ioapic->write(IOAPIC_REDIRS + pin * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + pin * 2 + 1, redir.hi_dword);
}
bool APIC::is_in_service(uint8_t irq)
{
uint32_t dword = (irq + IRQ_VECTOR_BASE) / 32;
uint32_t bit = (irq + IRQ_VECTOR_BASE) % 32;
const uint32_t dword = (irq + IRQ_VECTOR_BASE) / 32;
const uint32_t bit = (irq + IRQ_VECTOR_BASE) % 32;
uint32_t isr = read_from_local_apic(LAPIC_IS_REG + dword * 0x10);
const uint32_t isr = read_from_local_apic(LAPIC_IS_REG + dword * 0x10);
return isr & (1 << bit);
}
BAN::ErrorOr<void> APIC::reserve_irq(uint8_t irq)
BAN::ErrorOr<void> APIC::reserve_irq(uint8_t irq, bool shared)
{
SpinLockGuard _(m_lock);
@@ -557,20 +552,26 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
}
int byte = gsi / 8;
int bit = gsi % 8;
if (m_reserved_gsis[byte] & (1 << bit))
const uint8_t byte = gsi / 8;
const uint8_t mask = 1 << (gsi % 8);
if (!shared && (m_excl_gsis[byte] & mask))
{
dwarnln("GSI {} is already reserved (IRQ {})", gsi, irq);
return BAN::Error::from_errno(EFAULT);
dwarnln("GSI {} is already reserved as exclusive", gsi);
return BAN::Error::from_errno(EINVAL);
}
m_reserved_gsis[byte] |= 1 << bit;
m_used_gsis[byte] |= mask;
if (!shared)
m_excl_gsis[byte] |= mask;
return {};
}
// FIXME: rewrite gsi and vector reserving
// this is a hack to allow direct GSI reservation
BAN::ErrorOr<uint8_t> APIC::reserve_gsi(uint32_t gsi)
BAN::ErrorOr<uint8_t> APIC::reserve_gsi(uint32_t gsi, bool shared)
{
size_t irq = 0;
for (; irq < 0x100; irq++)
@@ -583,7 +584,7 @@ namespace Kernel
return BAN::Error::from_errno(ENOTSUP);
}
TRY(reserve_irq(irq));
TRY(reserve_irq(irq, shared));
return irq;
}
@@ -591,6 +592,7 @@ namespace Kernel
BAN::Optional<uint8_t> APIC::get_free_irq()
{
SpinLockGuard _(m_lock);
for (uint8_t irq = 0; irq < m_irq_count; irq++)
{
const uint8_t gsi = m_irq_overrides[irq];
@@ -608,13 +610,17 @@ namespace Kernel
if (!ioapic)
continue;
const uint8_t byte = gsi / 8;
const uint8_t bit = gsi % 8;
if (m_reserved_gsis[byte] & (1 << bit))
const uint8_t byte = gsi / 8;
const uint8_t mask = 1 << (gsi % 8);
if (m_used_gsis[byte] & mask)
continue;
m_reserved_gsis[byte] |= 1 << bit;
m_used_gsis[byte] |= mask;
m_excl_gsis[byte] |= mask;
return irq;
}
return {};
}

View File

@@ -2,8 +2,6 @@
#include <kernel/IO.h>
#include <kernel/PIC.h>
#include <string.h>
#define PIC1_CMD 0x20
#define PIC1_DATA 0x21
#define PIC2_CMD 0xA0
@@ -76,48 +74,62 @@ namespace Kernel
IO::outb(PIC1_CMD, PIC_EOI);
}
void PIC::enable_irq(uint8_t irq)
void PIC::enable_irq(uint8_t irq, bool level_triggered)
{
SpinLockGuard _(m_lock);
ASSERT(irq < 16);
ASSERT(m_reserved_irqs & (1 << irq));
if (level_triggered)
dprintln("no level triggered IRQs with PIC, using edge triggered");
uint16_t port = PIC1_DATA;
if(irq >= 8)
{
port = PIC2_DATA;
SpinLockGuard _(m_lock);
ASSERT(irq < 16);
ASSERT(m_used_irqs & (1 << irq));
const uint16_t port = [&irq]() -> uint16_t {
if (irq < 8)
return PIC1_DATA;
irq -= 8;
}
return PIC2_DATA;
}();
IO::outb(port, IO::inb(port) & ~(1 << irq));
}
BAN::ErrorOr<void> PIC::reserve_irq(uint8_t irq)
BAN::ErrorOr<void> PIC::reserve_irq(uint8_t irq, bool shared)
{
if (irq >= 16)
{
dwarnln("PIC only supports 16 irqs");
return BAN::Error::from_errno(EFAULT);
}
SpinLockGuard _(m_lock);
if (m_reserved_irqs & (1 << irq))
if (!shared && (m_excl_irqs & (1 << irq)))
{
dwarnln("irq {} is already reserved", irq);
return BAN::Error::from_errno(EFAULT);
dwarnln("IRQ {} is already reserved as exclusive", irq);
return BAN::Error::from_errno(EINVAL);
}
m_reserved_irqs |= 1 << irq;
m_used_irqs |= 1 << irq;
if (!shared)
m_excl_irqs |= 1 << irq;
return {};
}
BAN::Optional<uint8_t> PIC::get_free_irq()
{
SpinLockGuard _(m_lock);
for (int irq = 0; irq < 16; irq++)
for (uint8_t irq = 0; irq < 16; irq++)
{
if (m_reserved_irqs & (1 << irq))
if (m_used_irqs & (1 << irq))
continue;
m_reserved_irqs |= 1 << irq;
m_used_irqs |= 1 << irq;
m_excl_irqs |= 1 << irq;
return irq;
}
return {};
}