diff --git a/repos/base-hw/lib/mk/x86_64/core.mk b/repos/base-hw/lib/mk/x86_64/core.mk index 6ea4d6672..0756f90f2 100644 --- a/repos/base-hw/lib/mk/x86_64/core.mk +++ b/repos/base-hw/lib/mk/x86_64/core.mk @@ -14,6 +14,7 @@ SRC_S += spec/x86_64/kernel/crt0.s SRC_S += spec/x86_64/crt0.s # add C++ sources +SRC_CC += spec/x86/pic.cc SRC_CC += spec/x86_64/kernel/thread_base.cc SRC_CC += spec/x86_64/idt.cc SRC_CC += spec/x86_64/tss.cc diff --git a/repos/base-hw/src/core/include/spec/x86/pic.h b/repos/base-hw/src/core/include/spec/x86/pic.h index d9ec4af98..951ebb458 100644 --- a/repos/base-hw/src/core/include/spec/x86/pic.h +++ b/repos/base-hw/src/core/include/spec/x86/pic.h @@ -14,6 +14,8 @@ #ifndef _PIC_H_ #define _PIC_H_ +#include +#include namespace Genode { @@ -24,14 +26,119 @@ namespace Genode } -class Genode::Pic +class Genode::Pic : public Mmio { + private: + + /* Registers */ + struct EOI : Register<0x0b0, 32, true> { }; + struct Svr : Register<0x0f0, 32> + { + struct APIC_enable : Bitfield<8, 1> { }; + }; + + /* + * ISR register, see Intel SDM Vol. 3A, section 10.8.4. Each of the 8 + * 32-bit ISR values is followed by 12 bytes of padding. + */ + struct Isr : Register_array<0x100, 32, 8 * 4, 32> { }; + + class Ioapic : public Mmio + { + private: + + uint8_t _irt_count; + + enum { + /* Number of Redirection Table entries */ + IRTE_COUNT = 0x17, + + IRTE_BIT_POL = 13, + IRTE_BIT_TRG = 15, + IRTE_BIT_MASK = 16, + + /* Register selectors */ + IOAPICVER = 0x01, + IOREDTBL = 0x10, + }; + + /* Create redirection table entry for given IRQ */ + uint64_t create_irt_entry(unsigned irq) + { + uint32_t entry = Board::VECTOR_REMAP_BASE + irq; + + if (irq > 15) { + /* Use level-triggered, high-active mode for non-legacy + * IRQs */ + entry |= 1 << IRTE_BIT_POL | 1 << IRTE_BIT_TRG; + } + return entry; + } + + public: + + Ioapic() : Mmio(Board::MMIO_IOAPIC_BASE) + { + /* Remap all supported IRQs */ + for (unsigned i = 0; i <= IRTE_COUNT; i++) { + write(IOREDTBL + 2 * i); + write(create_irt_entry(i)); + } + }; + + /* Set/unset mask bit of IRTE for given vector */ + void toggle_mask(unsigned const vector, bool const set) + { + if (vector < Board::VECTOR_REMAP_BASE || + vector > Board::VECTOR_REMAP_BASE + IRTE_COUNT) + return; + + write(IOREDTBL + (2 * (vector - + Board::VECTOR_REMAP_BASE))); + + uint32_t val = read(); + if (set) { + val |= 1 << IRTE_BIT_MASK; + } else { + val &= ~(1 << IRTE_BIT_MASK); + } + write(val); + } + + /* Registers */ + struct Ioregsel : Register<0x00, 32> { }; + struct Iowin : Register<0x10, 32> { }; + }; + + Ioapic _ioapic; + + /** + * Determine lowest pending interrupt in ISR register + * + * \return index of first ISR bit set starting at index one, zero if no + * bit is set. + */ + inline unsigned get_lowest_bit(void) + { + unsigned bit, vec_base = 0; + + for (unsigned i = 0; i < 8 * 4; i += 4) { + bit = __builtin_ffs(read(i)); + if (bit) { + return vec_base + bit; + } + vec_base += 32; + } + return 0; + } + public: enum { /* * FIXME: dummy ipi value on non-SMP platform, should be removed - * when SMP is an aspect of CPUs only compiled where necessary + * when SMP is an aspect of CPUs only compiled where + * necessary */ IPI = 255, NR_OF_IRQ = 256, @@ -40,24 +147,23 @@ class Genode::Pic /** * Constructor */ - Pic() { } + Pic(); - void init_cpu_local() { } + bool take_request(unsigned &irq); - bool take_request(unsigned &irq) { return false; } - - void finish_request() { } + void finish_request(); void mask() { } - void unmask(unsigned const i, unsigned) { } + void unmask(unsigned const i, unsigned); - void mask(unsigned const i) { } + void mask(unsigned const i); /* * Dummies */ + void init_cpu_local() { } bool is_ip_interrupt(unsigned, unsigned) { return false; } void trigger_ip_interrupt(unsigned) { } }; diff --git a/repos/base-hw/src/core/spec/x86/pic.cc b/repos/base-hw/src/core/spec/x86/pic.cc new file mode 100644 index 000000000..0931a0acb --- /dev/null +++ b/repos/base-hw/src/core/spec/x86/pic.cc @@ -0,0 +1,67 @@ +#include + +#include "pic.h" + +using namespace Genode; + +enum { + PIC_CMD_MASTER = 0x20, + PIC_CMD_SLAVE = 0xa0, + PIC_DATA_MASTER = 0x21, + PIC_DATA_SLAVE = 0xa1, +}; + +Pic::Pic() : Mmio(Board::MMIO_LAPIC_BASE) +{ + /* Start initialization sequence in cascade mode */ + outb(PIC_CMD_MASTER, 0x11); + outb(PIC_CMD_SLAVE, 0x11); + + /* ICW2: Master PIC vector offset (32) */ + outb(PIC_DATA_MASTER, 0x20); + /* ICW2: Slave PIC vector offset (40) */ + outb(PIC_DATA_SLAVE, 0x28); + + /* ICW3: Tell Master PIC that there is a slave PIC at IRQ2 */ + outb(PIC_DATA_MASTER, 4); + + /* ICW3: Tell Slave PIC its cascade identity */ + outb(PIC_DATA_SLAVE, 2); + + /* ICW4: Enable 8086 mode */ + outb(PIC_DATA_MASTER, 0x01); + outb(PIC_DATA_SLAVE, 0x01); + + /* Disable legacy pic */ + outb(PIC_DATA_SLAVE, 0xff); + outb(PIC_DATA_MASTER, 0xff); + + /* Set bit 8 of the APIC spurious vector register (SVR) */ + write(1); +} + +bool Pic::take_request(unsigned &irq) +{ + irq = get_lowest_bit(); + if (!irq) { + return false; + } + + irq -= 1; + return true; +} + +void Pic::finish_request() +{ + write(0); +} + +void Pic::unmask(unsigned const i, unsigned) +{ + _ioapic.toggle_mask(i, false); +} + +void Pic::mask(unsigned const i) +{ + _ioapic.toggle_mask(i, true); +} diff --git a/repos/base-hw/src/core/spec/x86_64/mode_transition.s b/repos/base-hw/src/core/spec/x86_64/mode_transition.s index 6ec74eb67..533b7dec1 100644 --- a/repos/base-hw/src/core/spec/x86_64/mode_transition.s +++ b/repos/base-hw/src/core/spec/x86_64/mode_transition.s @@ -204,9 +204,8 @@ pushq $0x23 pushq SP_OFFSET(%rax) - /* Set I/O privilege level to 3 */ - orq $0x3000, FLAGS_OFFSET(%rax) - btrq $9, FLAGS_OFFSET(%rax) /* XXX: Drop once interrupt handling is done */ + /* Set I/O privilege level to 3 and enable interrupts */ + orq $0x3200, FLAGS_OFFSET(%rax) pushq FLAGS_OFFSET(%rax) pushq $0x1b