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.
This commit is contained in:
Reto Buerki 2015-02-17 09:03:12 +01:00 committed by Christian Helmuth
parent 557c96a9cb
commit a21959fc26
4 changed files with 185 additions and 12 deletions

View File

@ -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

View File

@ -14,6 +14,8 @@
#ifndef _PIC_H_
#define _PIC_H_
#include <board.h>
#include <util/mmio.h>
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<Ioregsel>(IOREDTBL + 2 * i);
write<Iowin>(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<Ioregsel>(IOREDTBL + (2 * (vector -
Board::VECTOR_REMAP_BASE)));
uint32_t val = read<Iowin>();
if (set) {
val |= 1 << IRTE_BIT_MASK;
} else {
val &= ~(1 << IRTE_BIT_MASK);
}
write<Iowin>(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<Isr>(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) { }
};

View File

@ -0,0 +1,67 @@
#include <port_io.h>
#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<Svr::APIC_enable>(1);
}
bool Pic::take_request(unsigned &irq)
{
irq = get_lowest_bit();
if (!irq) {
return false;
}
irq -= 1;
return true;
}
void Pic::finish_request()
{
write<EOI>(0);
}
void Pic::unmask(unsigned const i, unsigned)
{
_ioapic.toggle_mask(i, false);
}
void Pic::mask(unsigned const i)
{
_ioapic.toggle_mask(i, true);
}

View File

@ -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