Kernel: Allow allocating shared interrupts and setting trigger mode
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+29
-23
@@ -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,8 +510,8 @@ 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?
|
||||
@@ -528,14 +523,14 @@ namespace Kernel
|
||||
|
||||
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];
|
||||
@@ -609,12 +611,16 @@ namespace Kernel
|
||||
continue;
|
||||
|
||||
const uint8_t byte = gsi / 8;
|
||||
const uint8_t bit = gsi % 8;
|
||||
if (m_reserved_gsis[byte] & (1 << bit))
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
||||
+31
-19
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user