From a21959fc26f4841117c9e7b6f8d66b57a923ebc6 Mon Sep 17 00:00:00 2001 From: Reto Buerki Date: Tue, 17 Feb 2015 09:03:12 +0100 Subject: [PATCH] hw_x86_64: Implement APIC-based PIC driver The implementation initializes the Local APIC (LAPIC) of CPU 0 in xapic mode (mmio register access) and uses the I/O APIC to remap, mask and unmask hardware IRQs. The remapping offset of IRQs is 48. Also initialize the legacy PIC and mask all interrupts in order to disable it. For more information about LAPIC and I/O APIC see Intel SDM Vol. 3A, chapter 10 and the Intel 82093AA I/O Advanced Programmable Interrupt Controller (IOAPIC) specification Set bit 9 in the RFLAGS register of user CPU context to enable interrupts on kernel- to usermode switch. --- repos/base-hw/lib/mk/x86_64/core.mk | 1 + repos/base-hw/src/core/include/spec/x86/pic.h | 124 ++++++++++++++++-- repos/base-hw/src/core/spec/x86/pic.cc | 67 ++++++++++ .../src/core/spec/x86_64/mode_transition.s | 5 +- 4 files changed, 185 insertions(+), 12 deletions(-) create mode 100644 repos/base-hw/src/core/spec/x86/pic.cc 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