mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
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:
parent
557c96a9cb
commit
a21959fc26
@ -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
|
||||
|
@ -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) { }
|
||||
};
|
||||
|
67
repos/base-hw/src/core/spec/x86/pic.cc
Normal file
67
repos/base-hw/src/core/spec/x86/pic.cc
Normal 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);
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user