Add initial IO APIC support

- IO APIC vector mapping
- Legacy PIC disable
- Real interrupts happening
This commit is contained in:
Justin C. Miller
2018-05-02 16:46:37 -07:00
parent 428e4563d0
commit 59700b07db
11 changed files with 317 additions and 62 deletions

View File

@@ -6,17 +6,30 @@
static uint32_t
apic_read(uint32_t *apic, uint16_t offset)
apic_read(uint32_t volatile *apic, uint16_t offset)
{
return *(apic + offset/sizeof(uint32_t));
}
static void
apic_write(uint32_t *apic, uint16_t offset, uint32_t value)
apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value)
{
*(apic + offset/sizeof(uint32_t)) = value;
}
static uint32_t
ioapic_read(uint32_t volatile *base, uint8_t reg)
{
*base = reg;
return *(base + 4);
}
static void
ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
{
*base = reg;
*(base + 4) = value;
}
apic::apic(uint32_t *base) :
m_base(base)
@@ -99,8 +112,83 @@ lapic::disable()
}
ioapic::ioapic(uint32_t *base, uint32_t base_gsr) :
ioapic::ioapic(uint32_t *base, uint32_t base_gsi) :
apic(base),
m_base_gsr(base_gsr)
m_base_gsi(base_gsi)
{
uint32_t id = ioapic_read(m_base, 0);
uint32_t version = ioapic_read(m_base, 1);
m_id = (id >> 24) & 0xff;
m_version = version & 0xff;
m_num_gsi = (version >> 16) & 0xff;
log::debug(logs::interrupt, "IOAPIC %d loaded, version %d, GSIs %d-%d",
m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1));
for (uint8_t i = 0; i < m_num_gsi; ++i) {
uint16_t flags = (i < 0x10) ? 0 : 0xf;
isr vector = isr::irq0 + i;
redirect(i, vector, flags, true);
}
}
void
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
{
uint64_t entry = static_cast<uint64_t>(vector);
uint16_t polarity = flags & 0x3;
if (polarity == 3)
entry |= (1 << 13);
uint16_t trigger = (flags >> 2) & 0x3;
if (trigger == 3)
entry |= (1 << 15);
if (masked)
entry |= (1 << 16);
ioapic_write(m_base, (2 * irq) + 0x10, static_cast<uint32_t>(entry & 0xffffffff));
ioapic_write(m_base, (2 * irq) + 0x11, static_cast<uint32_t>(entry >> 32));
}
void
ioapic::mask(uint8_t irq, bool masked)
{
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
if (masked)
entry |= (1 << 16);
else
entry &= ~(1 << 16);
ioapic_write(m_base, (2 * irq) + 0x10, entry);
}
void
ioapic::dump_redirs() const
{
log::debug(logs::interrupt, "IOAPIC %d redirections:", m_id);
for (uint8_t i = 0; i < m_num_gsi; ++i) {
uint64_t low = ioapic_read(m_base, 0x10 + (2 *i));
uint64_t high = ioapic_read(m_base, 0x10 + (2 *i));
uint64_t redir = low | (high << 32);
if (redir == 0) continue;
uint8_t vector = redir & 0xff;
uint8_t mode = (redir >> 8) & 0x7;
uint8_t dest_mode = (redir >> 11) & 0x1;
uint8_t polarity = (redir >> 13) & 0x1;
uint8_t trigger = (redir >> 15) & 0x1;
uint8_t mask = (redir >> 16) & 0x1;
uint8_t dest = (redir >> 56) & 0xff;
log::debug(logs::interrupt, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x",
m_base_gsi + i, vector,
polarity ? "low" : "high",
trigger ? "level" : "edge",
mask ? "masked" : "",
dest_mode,
dest);
}
}

View File

@@ -56,14 +56,37 @@ class ioapic :
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg base_gsr Starting global system interrupt number of this IOAPIC
ioapic(uint32_t *base, uint32_t base_gsr);
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
ioapic(uint32_t *base, uint32_t base_gsi);
uint32_t get_base_gsr() const { return m_base_gsr; }
uint32_t get_num_gsr() const { return m_num_gsr; }
uint32_t get_base_gsi() const { return m_base_gsi; }
uint32_t get_num_gsi() const { return m_num_gsi; }
/// Set a redirection entry.
/// TODO: pick CPU
/// \arg source Source interrupt number
/// \arg vector Interrupt vector that should be used
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
/// \arg masked Whether the iterrupt should be suppressed
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
/// \arg irq The IOAPIC-local irq number
/// \arg masked Whether to suppress this interrupt
void mask(uint8_t irq, bool masked);
/// Mask all interrupts on this IOAPIC.
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
/// Unmask all interrupts on this IOAPIC.
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
void dump_redirs() const;
private:
uint32_t m_base_gsr;
uint32_t m_num_gsr;
};
uint32_t m_base_gsi;
uint32_t m_num_gsi;
uint8_t m_id;
uint8_t m_version;
};

View File

@@ -54,11 +54,12 @@ acpi_table_header::validate(uint32_t expected_type) const
device_manager::device_manager(const void *root_table) :
m_lapic(nullptr),
m_ioapics(nullptr),
m_num_ioapics(0)
{
kassert(root_table != 0, "ACPI root table pointer is null.");
kutil::memset(m_ioapics, 0, sizeof(m_ioapics));
const acpi1_rsdp *acpi1 =
reinterpret_cast<const acpi1_rsdp *>(root_table);
@@ -83,7 +84,7 @@ device_manager::device_manager(const void *root_table) :
ioapic *
device_manager::get_ioapic(int i)
{
return (i < m_num_ioapics) ? m_ioapics + i : nullptr;
return (i < m_num_ioapics) ? m_ioapics[i] : nullptr;
}
static void
@@ -128,34 +129,46 @@ device_manager::load_apic(const acpi_apic *apic)
{
uint32_t *local = reinterpret_cast<uint32_t *>(apic->local_address);
m_lapic = new (kalloc(sizeof(lapic))) lapic(local, isr::isrSpurious);
m_lapic->enable_lint(0, isr::isrLINT0, false, 0);
m_lapic->enable_lint(1, isr::isrLINT1, false, 0);
m_lapic = new lapic(local, isr::isrSpurious);
size_t count = acpi_table_entries(apic, 1);
uint8_t const *p = apic->controller_data;
uint8_t const *end = p + acpi_table_entries(apic, 1);
uint8_t const *end = p + count;
// Pass one: set up IOAPIC objcts
while (p < end) {
const uint8_t type = p[0];
const uint8_t length = p[1];
if (type == 1) {
uint32_t *base = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
uint32_t base_gsr = kutil::read_from<uint32_t>(p+8);
m_ioapics[m_num_ioapics++] = new ioapic(base, base_gsr);
}
p += length;
}
// Pass two: configure APIC objects
p = apic->controller_data;
while (p < end) {
const uint8_t type = p[0];
const uint8_t length = p[1];
switch (type) {
case 0: // Local APIC
case 1: // I/O APIC
break;
case 1: { // I/O APIC
uint32_t *io_apic = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
uint32_t base = kutil::read_from<uint32_t>(p+8);
log::info(logs::devices, " IO APIC address %lx base %d", io_apic, base);
}
break;
case 2: { // Interrupt source override
uint8_t source = kutil::read_from<uint8_t>(p+3);
isr gsi = isr::irq0 + kutil::read_from<uint32_t>(p+4);
uint16_t flags = kutil::read_from<uint16_t>(p+8);
case 2: // Interrupt source override
log::info(logs::devices, " Intr source override IRQ %d -> %d Pol %d Tri %d",
kutil::read_from<uint8_t>(p+3),
kutil::read_from<uint32_t>(p+4),
kutil::read_from<uint16_t>(p+8) & 0x3,
(kutil::read_from<uint16_t>(p+8) >> 2) & 0x3);
source, gsi, (flags & 0x3), ((flags >> 2) & 0x3));
// TODO: in a multiple-IOAPIC system this might be elsewhere
m_ioapics[0]->redirect(source, static_cast<isr>(gsi), flags, true);
}
break;
case 4: {// LAPIC NMI
@@ -181,5 +194,14 @@ device_manager::load_apic(const acpi_apic *apic)
}
// m_lapic->enable_timer(isr::isrTimer, 128, 3000000);
for (uint8_t i = 0; i < m_ioapics[0]->get_num_gsi(); ++i) {
switch (i) {
case 2: break;
default: m_ioapics[0]->mask(i, false);
}
}
m_ioapics[0]->dump_redirs();
m_lapic->enable();
}

View File

@@ -18,8 +18,10 @@ public:
ioapic * get_ioapic(int i);
private:
static const int max_ioapics = 16;
lapic *m_lapic;
ioapic *m_ioapics;
ioapic *m_ioapics[16];
int m_num_ioapics;
void load_xsdt(const acpi_xsdt *xsdt);

View File

@@ -47,8 +47,33 @@ IRQ (76, 12, irq12)
IRQ (77, 13, irq13)
IRQ (78, 14, irq14)
IRQ (79, 15, irq15)
IRQ (80, 16, irq16)
IRQ (81, 17, irq17)
IRQ (82, 18, irq18)
IRQ (83, 19, irq19)
IRQ (84, 20, irq20)
IRQ (85, 21, irq21)
IRQ (86, 22, irq22)
ISR (0x7c, isrTimer)
ISR (0x7d, isrLINT0)
ISR (0x7e, isrLINT1)
ISR (0x7f, isrSpurious)
ISR (0xf0, isrIgnore0)
ISR (0xf1, isrIgnore1)
ISR (0xf2, isrIgnore2)
ISR (0xf3, isrIgnore3)
ISR (0xf4, isrIgnore4)
ISR (0xf5, isrIgnore5)
ISR (0xf6, isrIgnore6)
ISR (0xf7, isrIgnore7)
ISR (0xf8, isrIgnore8)
ISR (0xf9, isrIgnore9)
ISR (0xfa, isrIgnoreA)
ISR (0xfb, isrIgnoreB)
ISR (0xfc, isrIgnoreC)
ISR (0xfd, isrIgnoreD)
ISR (0xfe, isrIgnoreE)
ISR (0xff, isrIgnoreF)

View File

@@ -1,9 +1,11 @@
#include <stddef.h>
#include <stdint.h>
#include "kutil/enum_bitfields.h"
#include "kutil/memory.h"
#include "console.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "memory_pages.h"
@@ -20,13 +22,7 @@ enum class gdt_flags : uint8_t
res_1 = 0x10
};
inline gdt_flags
operator | (gdt_flags lhs, gdt_flags rhs)
{
return static_cast<gdt_flags>(
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
IS_BITFIELD(gdt_flags);
struct gdt_descriptor
{
@@ -84,6 +80,29 @@ extern "C" {
void idt_dump(const table_ptr &table);
void gdt_dump(const table_ptr &table);
isr
operator+(const isr &lhs, int rhs)
{
using under_t = std::underlying_type<isr>::type;
return static_cast<isr>(static_cast<under_t>(lhs) + rhs);
}
uint8_t
get_irq(unsigned vector)
{
switch (vector) {
#define ISR(i, name)
#define EISR(i, name)
#define IRQ(i, q, name) case i : return q;
#include "interrupt_isrs.inc"
#undef IRQ
#undef EISR
#undef ISR
default: return 0xff;
}
}
void
set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags)
{
@@ -111,6 +130,30 @@ set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
g_idt_table[i].reserved = 0;
}
void
disable_legacy_pic()
{
static const uint16_t PIC1 = 0x20;
static const uint16_t PIC2 = 0xa0;
// Mask all interrupts
outb(0xa1, 0xff);
outb(0x21, 0xff);
// Start initialization sequence
outb(PIC1, 0x11); io_wait();
outb(PIC2, 0x11); io_wait();
// Remap into ignore ISRs
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
// Tell PICs about each other
outb(PIC1+1, 0x04); io_wait();
outb(PIC2+1, 0x02); io_wait();
}
void
interrupts_init()
{
@@ -142,6 +185,8 @@ interrupts_init()
#undef ISR
idt_write();
disable_legacy_pic();
log::info(logs::interrupt, "Interrupts enabled.");
}
@@ -173,6 +218,28 @@ isr_handler(registers regs)
cons->puts("\nLINT1\n");
break;
case isr::isrIgnore0:
case isr::isrIgnore1:
case isr::isrIgnore2:
case isr::isrIgnore3:
case isr::isrIgnore4:
case isr::isrIgnore5:
case isr::isrIgnore6:
case isr::isrIgnore7:
case isr::isrIgnore8:
case isr::isrIgnore9:
case isr::isrIgnoreA:
case isr::isrIgnoreB:
case isr::isrIgnoreC:
case isr::isrIgnoreD:
case isr::isrIgnoreE:
case isr::isrIgnoreF:
/*
cons->printf("\nIGNORED PIC INTERRUPT %d\n",
(regs.interrupt % 0xff) - 0xf0);
*/
break;
default:
cons->set_color(9);
cons->puts("\nReceived ISR interrupt:\n");
@@ -212,14 +279,20 @@ void
irq_handler(registers regs)
{
console *cons = console::get();
uint8_t irq = get_irq(regs.interrupt);
cons->set_color(9);
cons->puts("\nReceived IRQ interrupt:\n");
switch (irq) {
case 2:
cons->set_color(11);
cons->puts(".");
cons->set_color();
break;
print_reg("ISR", regs.interrupt);
print_reg("ERR", regs.errorcode);
cons->puts("\n");
default:
cons->set_color(11);
cons->printf("\nReceived IRQ interrupt: %d (vec %d)\n",
irq, regs.interrupt);
cons->set_color();
print_reg(" ds", regs.ds);
print_reg("rdi", regs.rdi);
@@ -240,6 +313,9 @@ irq_handler(registers regs)
while(1) asm("hlt");
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
}
void
gdt_dump(const table_ptr &table)

View File

@@ -14,9 +14,11 @@ enum class isr : uint8_t
#undef EISR
#undef ISR
max
_zero = 0
};
isr operator+(const isr &lhs, int rhs);
extern "C" {
void interrupts_enable();
void interrupts_disable();

View File

@@ -30,3 +30,8 @@ wrmsr(uint64_t addr, uint64_t value)
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
}
void
io_wait()
{
outb(0x80, 0);
}

View File

@@ -24,6 +24,9 @@ uint64_t rdmsr(uint64_t addr);
/// \arg value The value to write
void wrmsr(uint64_t addr, uint64_t value);
/// Pause briefly by doing IO to port 0x80
void io_wait();
}
const uint16_t COM1 = 0x03f8;

View File

@@ -72,9 +72,9 @@ kernel_main(popcorn_data *header)
pager->map_offset_pointer(&header->frame_buffer, header->frame_buffer_length);
interrupts_init();
interrupts_enable();
device_manager devices(header->acpi_table);
interrupts_enable();
// int x = 1 / 0;
// __asm__ __volatile__("int $15");

View File

@@ -44,3 +44,12 @@ void memory_initialize_managers(const void *memory_map, size_t map_length, size_
/// \returns A pointer to the allocated memory, or nullptr if
/// allocation failed.
inline void * kalloc(size_t length) { return g_kernel_memory_manager.allocate(length); }
/// Free kernel space memory.
/// \arg p The pointer to free
inline void kfree(void *p) { g_kernel_memory_manager.free(p); }
inline void * operator new (size_t n) { return g_kernel_memory_manager.allocate(n); }
inline void * operator new[] (size_t n) { return g_kernel_memory_manager.allocate(n); }
inline void operator delete (void *p) { return g_kernel_memory_manager.free(p); }
inline void operator delete[] (void *p){ return g_kernel_memory_manager.free(p); }