diff --git a/kernel/include/kernel/APIC.h b/kernel/include/kernel/APIC.h index 4a0a687d..5d81543d 100644 --- a/kernel/include/kernel/APIC.h +++ b/kernel/include/kernel/APIC.h @@ -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 reserve_irq(uint8_t irq) override; + virtual BAN::ErrorOr reserve_irq(uint8_t irq, bool shared) override; virtual BAN::Optional 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 reserve_gsi(uint32_t gsi); + BAN::ErrorOr 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 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 }; }; diff --git a/kernel/include/kernel/InterruptController.h b/kernel/include/kernel/InterruptController.h index 6f11c7b6..44fbad04 100644 --- a/kernel/include/kernel/InterruptController.h +++ b/kernel/include/kernel/InterruptController.h @@ -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 reserve_irq(uint8_t irq) = 0; + virtual BAN::ErrorOr reserve_irq(uint8_t irq, bool shared = false) = 0; virtual BAN::Optional get_free_irq() = 0; bool is_using_apic() const { return m_using_apic; } diff --git a/kernel/include/kernel/PIC.h b/kernel/include/kernel/PIC.h index d93e0d89..5744c488 100644 --- a/kernel/include/kernel/PIC.h +++ b/kernel/include/kernel/PIC.h @@ -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 reserve_irq(uint8_t irq) override; + virtual BAN::ErrorOr reserve_irq(uint8_t irq, bool shared) override; virtual BAN::Optional 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; }; diff --git a/kernel/kernel/APIC.cpp b/kernel/kernel/APIC.cpp index 5a23848c..cb93bfc7 100644 --- a/kernel/kernel/APIC.cpp +++ b/kernel/kernel/APIC.cpp @@ -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 APIC::reserve_irq(uint8_t irq) + BAN::ErrorOr 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 APIC::reserve_gsi(uint32_t gsi) + BAN::ErrorOr 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 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 {}; } diff --git a/kernel/kernel/PIC.cpp b/kernel/kernel/PIC.cpp index 344fd98f..b9fea420 100644 --- a/kernel/kernel/PIC.cpp +++ b/kernel/kernel/PIC.cpp @@ -2,8 +2,6 @@ #include #include -#include - #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 PIC::reserve_irq(uint8_t irq) + BAN::ErrorOr 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 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 {}; }