Add initial classes representing APIC

This commit is contained in:
Justin C. Miller
2018-05-01 01:03:19 -07:00
parent 6c3bbaa686
commit 428e4563d0
10 changed files with 382 additions and 158 deletions

106
src/kernel/apic.cpp Normal file
View File

@@ -0,0 +1,106 @@
#include "apic.h"
#include "assert.h"
#include "interrupts.h"
#include "log.h"
#include "memory_pages.h"
static uint32_t
apic_read(uint32_t *apic, uint16_t offset)
{
return *(apic + offset/sizeof(uint32_t));
}
static void
apic_write(uint32_t *apic, uint16_t offset, uint32_t value)
{
*(apic + offset/sizeof(uint32_t)) = value;
}
apic::apic(uint32_t *base) :
m_base(base)
{
g_page_manager.map_offset_pointer(reinterpret_cast<void **>(&m_base), 0x1000);
}
lapic::lapic(uint32_t *base, isr spurious) :
apic(base)
{
apic_write(m_base, 0xf0, static_cast<uint32_t>(spurious));
log::info(logs::interrupt, "LAPIC created, base %lx", m_base);
}
void
lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
{
switch (divisor) {
case 1: divisor = 11; break;
case 2: divisor = 0; break;
case 4: divisor = 1; break;
case 8: divisor = 2; break;
case 16: divisor = 3; break;
case 32: divisor = 8; break;
case 64: divisor = 9; break;
case 128: divisor = 10; break;
default:
kassert(0, "Invalid divisor passed to lapic::enable_timer");
}
apic_write(m_base, 0x3e0, divisor);
apic_write(m_base, 0x380, count);
uint32_t lvte = static_cast<uint8_t>(vector);
if (repeat)
lvte |= 0x20000;
log::debug(logs::interrupt, "Enabling APIC timer with isr %d.", vector);
apic_write(m_base, 0x320, lvte);
}
void
lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
{
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
uint16_t off = num ? 0x360 : 0x350;
uint32_t lvte = static_cast<uint8_t>(vector);
uint16_t polarity = flags & 0x3;
if (polarity == 3)
lvte |= (1 << 13);
uint16_t trigger = (flags >> 2) & 0x3;
if (trigger == 3)
lvte |= (1 << 15);
apic_write(m_base, off, lvte);
log::debug(logs::interrupt, "APIC LINT%d enabled as %s %d %s-triggered, active %s.",
num, nmi ? "NMI" : "ISR", vector,
polarity == 3 ? "level" : "edge",
trigger == 3 ? "low" : "high");
}
void
lapic::enable()
{
apic_write(m_base, 0xf0,
apic_read(m_base, 0xf0) | 0x100);
log::debug(logs::interrupt, "LAPIC enabled!");
}
void
lapic::disable()
{
apic_write(m_base, 0xf0,
apic_read(m_base, 0xf0) & ~0x100);
log::debug(logs::interrupt, "LAPIC disabled.");
}
ioapic::ioapic(uint32_t *base, uint32_t base_gsr) :
apic(base),
m_base_gsr(base_gsr)
{
}

69
src/kernel/apic.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
/// \file apic.h
/// Classes to control both local and I/O APICs.
#include <stdint.h>
enum class isr : uint8_t;
/// Base class for other APIC types
class apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
apic(uint32_t *base);
protected:
uint32_t *m_base;
};
/// Controller for processor-local APICs
class lapic :
public apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg spurious Vector of the spurious interrupt handler
lapic(uint32_t *base, isr spurious);
/// Enable interrupts for the LAPIC timer.
/// \arg vector Interrupt vector the timer should use
/// \arg divisor The frequency divisor of the bus Hz (power of 2, <= 128)
/// \arg count The count of ticks before an interrupt
/// \arg repeat If false, this timer is one-off, otherwise repeating
void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true);
/// Enable interrupts for the LAPIC LINT0 pin.
/// \arg num Local interrupt number (0 or 1)
/// \arg vector Interrupt vector LINT0 should use
/// \arg nmi Whether this interrupt is NMI delivery mode
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
void enable(); ///< Enable servicing of interrupts
void disable(); ///< Disable (temporarily) servicing of interrupts
};
/// Controller for I/O APICs
class ioapic :
public apic
{
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);
uint32_t get_base_gsr() const { return m_base_gsr; }
uint32_t get_num_gsr() const { return m_num_gsr; }
private:
uint32_t m_base_gsr;
uint32_t m_num_gsr;
};

View File

@@ -3,9 +3,12 @@
#include "kutil/memory.h"
#include "acpi_tables.h"
#include "apic.h"
#include "assert.h"
#include "device_manager.h"
#include "interrupts.h"
#include "log.h"
#include "memory.h"
static const char expected_signature[] = "RSD PTR ";
@@ -50,9 +53,9 @@ acpi_table_header::validate(uint32_t expected_type) const
}
device_manager::device_manager(const void *root_table) :
m_local_apic(nullptr),
m_io_apic(nullptr),
m_global_interrupt_base(0)
m_lapic(nullptr),
m_ioapics(nullptr),
m_num_ioapics(0)
{
kassert(root_table != 0, "ACPI root table pointer is null.");
@@ -77,6 +80,12 @@ device_manager::device_manager(const void *root_table) :
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
}
ioapic *
device_manager::get_ioapic(int i)
{
return (i < m_num_ioapics) ? m_ioapics + i : nullptr;
}
static void
put_sig(char *into, uint32_t type)
{
@@ -117,8 +126,11 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
void
device_manager::load_apic(const acpi_apic *apic)
{
m_local_apic = reinterpret_cast<uint32_t *>(apic->local_address);
log::info(logs::devices, " APIC local address %lx", apic->local_address);
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);
uint8_t const *p = apic->controller_data;
uint8_t const *end = p + acpi_table_entries(apic, 1);
@@ -131,11 +143,11 @@ device_manager::load_apic(const acpi_apic *apic)
case 0: // Local APIC
break;
case 1: // I/O APIC
m_io_apic = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
m_global_interrupt_base = kutil::read_from<uint32_t>(p+8);
log::info(logs::devices, " IO APIC address %lx base %d",
m_io_apic, m_global_interrupt_base);
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
@@ -146,12 +158,19 @@ device_manager::load_apic(const acpi_apic *apic)
(kutil::read_from<uint16_t>(p+8) >> 2) & 0x3);
break;
case 4: // Interrupt source override
case 4: {// LAPIC NMI
uint8_t cpu = kutil::read_from<uint8_t>(p + 2);
uint8_t num = kutil::read_from<uint8_t>(p + 5);
uint16_t flags = kutil::read_from<uint16_t>(p + 3);
m_lapic->enable_lint(num, num == 0 ? isr::isrLINT0 : isr::isrLINT1, true, flags);
log::info(logs::devices, " LAPIC NMI Proc %d LINT%d Pol %d Tri %d",
kutil::read_from<uint8_t>(p+2),
kutil::read_from<uint8_t>(p+5),
kutil::read_from<uint16_t>(p+3) & 0x3,
(kutil::read_from<uint16_t>(p+3) >> 2) & 0x3);
}
break;
default:
@@ -160,4 +179,7 @@ device_manager::load_apic(const acpi_apic *apic)
p += length;
}
// m_lapic->enable_timer(isr::isrTimer, 128, 3000000);
m_lapic->enable();
}

View File

@@ -3,6 +3,9 @@
struct acpi_xsdt;
struct acpi_apic;
class lapic;
class ioapic;
class device_manager
{
public:
@@ -11,14 +14,13 @@ public:
device_manager() = delete;
device_manager(const device_manager &) = delete;
uint8_t * local_apic() const;
uint8_t * io_apic() const;
lapic * get_lapic() { return m_lapic; }
ioapic * get_ioapic(int i);
private:
uint32_t *m_local_apic;
uint32_t *m_io_apic;
uint32_t m_global_interrupt_base;
lapic *m_lapic;
ioapic *m_ioapics;
int m_num_ioapics;
void load_xsdt(const acpi_xsdt *xsdt);
void load_apic(const acpi_apic *apic);

View File

@@ -1,51 +1,54 @@
ISR ( 0, isr0);
ISR ( 1, isr1);
ISR ( 2, isr2);
ISR ( 3, isr3);
ISR ( 4, isr4);
ISR ( 5, isr5);
ISR ( 6, isr6);
ISR ( 7, isr7);
EISR( 8, isr8);
ISR ( 9, isr9);
EISR(10, isr10);
EISR(11, isr11);
EISR(12, isr12);
EISR(13, isr13);
EISR(14, isr14);
ISR (15, isr15);
ISR (16, isr16);
ISR (17, isr17);
ISR (18, isr18);
ISR (19, isr19);
ISR (20, isr20);
ISR (21, isr21);
ISR (22, isr22);
ISR (23, isr23);
ISR (24, isr24);
ISR (25, isr25);
ISR (26, isr26);
ISR (27, isr27);
ISR (28, isr28);
ISR (29, isr29);
ISR (30, isr30);
ISR (31, isr31);
ISR ( 0, isr0)
ISR ( 1, isr1)
ISR ( 2, isr2)
ISR ( 3, isr3)
ISR ( 4, isr4)
ISR ( 5, isr5)
ISR ( 6, isr6)
ISR ( 7, isr7)
EISR( 8, isr8)
ISR ( 9, isr9)
EISR(10, isr10)
EISR(11, isr11)
EISR(12, isr12)
EISR(13, isr13)
EISR(14, isr14)
ISR (15, isr15)
ISR (16, isr16)
ISR (17, isr17)
ISR (18, isr18)
ISR (19, isr19)
ISR (20, isr20)
ISR (21, isr21)
ISR (22, isr22)
ISR (23, isr23)
ISR (24, isr24)
ISR (25, isr25)
ISR (26, isr26)
ISR (27, isr27)
ISR (28, isr28)
ISR (29, isr29)
ISR (30, isr30)
ISR (31, isr31)
IRQ (64, 0, irq0);
IRQ (65, 1, irq1);
IRQ (66, 2, irq2);
IRQ (67, 3, irq3);
IRQ (68, 4, irq4);
IRQ (69, 5, irq5);
IRQ (70, 6, irq6);
IRQ (71, 7, irq7);
IRQ (72, 8, irq8);
IRQ (73, 9, irq9);
IRQ (74, 10, irq10);
IRQ (75, 11, irq11);
IRQ (76, 12, irq12);
IRQ (77, 13, irq13);
IRQ (78, 14, irq14);
IRQ (79, 15, irq15);
IRQ (64, 0, irq0)
IRQ (65, 1, irq1)
IRQ (66, 2, irq2)
IRQ (67, 3, irq3)
IRQ (68, 4, irq4)
IRQ (69, 5, irq5)
IRQ (70, 6, irq6)
IRQ (71, 7, irq7)
IRQ (72, 8, irq8)
IRQ (73, 9, irq9)
IRQ (74, 10, irq10)
IRQ (75, 11, irq11)
IRQ (76, 12, irq12)
IRQ (77, 13, irq13)
IRQ (78, 14, irq14)
IRQ (79, 15, irq15)
ISR (0xff, isrSpurious);
ISR (0x7c, isrTimer)
ISR (0x7d, isrLINT0)
ISR (0x7e, isrLINT1)
ISR (0x7f, isrSpurious)

View File

@@ -5,6 +5,7 @@
#include "console.h"
#include "interrupts.h"
#include "log.h"
#include "memory_pages.h"
enum class gdt_flags : uint8_t
{
@@ -59,7 +60,6 @@ idt_descriptor g_idt_table[256];
table_ptr g_gdtr;
table_ptr g_idtr;
struct registers;
extern "C" {
@@ -72,9 +72,9 @@ extern "C" {
void isr_handler(registers);
void irq_handler(registers);
#define ISR(i, name) extern void name ()
#define EISR(i, name) extern void name ()
#define IRQ(i, q, name) extern void name ()
#define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name ();
#define IRQ(i, q, name) extern void name ();
#include "interrupt_isrs.inc"
#undef IRQ
#undef EISR
@@ -143,8 +143,6 @@ interrupts_init()
idt_write();
log::info(logs::interrupt, "Interrupts enabled.");
gdt_dump(g_gdtr);
idt_dump(g_idtr);
}
struct registers
@@ -162,6 +160,20 @@ isr_handler(registers regs)
{
console *cons = console::get();
switch (static_cast<isr>(regs.interrupt & 0xff)) {
case isr::isrTimer:
cons->puts("\nTICK\n");
break;
case isr::isrLINT0:
cons->puts("\nLINT0\n");
break;
case isr::isrLINT1:
cons->puts("\nLINT1\n");
break;
default:
cons->set_color(9);
cons->puts("\nReceived ISR interrupt:\n");
cons->set_color();
@@ -191,6 +203,9 @@ isr_handler(registers regs)
print_reg("esp", regs.user_esp);
print_reg(" ss", regs.ss);
while(1) asm("hlt");
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
}
void

View File

@@ -1,4 +1,21 @@
#pragma once
/// \file interrupts.h
/// Free functions and definitions related to interrupt service vectors
/// Enum of all defined ISR/IRQ vectors
enum class isr : uint8_t
{
#define ISR(i, name) name = i,
#define EISR(i, name) name = i,
#define IRQ(i, q, name) name = i,
#include "interrupt_isrs.inc"
#undef IRQ
#undef EISR
#undef ISR
max
};
extern "C" {
void interrupts_enable();

View File

@@ -48,6 +48,7 @@ kernel_main(popcorn_data *header)
cons->puts(GIT_VERSION " booting...\n");
log::init(cons);
log::enable(logs::interrupt, log::level::debug);
cpu_id cpu;
@@ -62,13 +63,14 @@ kernel_main(popcorn_data *header)
cpu_info.ecx_bit(21) ? "yes" : "no");
page_manager *pager = new (&g_page_manager) page_manager;
pager->mark_offset_pointer(&header->frame_buffer, header->frame_buffer_length);
memory_initialize_managers(
header->memory_map,
header->memory_map_length,
header->memory_map_desc_size);
pager->map_offset_pointer(&header->frame_buffer, header->frame_buffer_length);
interrupts_init();
interrupts_enable();

View File

@@ -191,33 +191,6 @@ page_manager::init(
// allocated, full of page_block structs. Eventually hand
// control of that to a slab allocator.
page_table *pml4 = get_pml4();
// Fix up the offset-marked pointers
for (unsigned i = 0; i < m_marked_pointer_count; ++i) {
addr_t *p = reinterpret_cast<addr_t *>(m_marked_pointers[i]);
addr_t v = *p + page_offset;
addr_t c = (m_marked_pointer_lengths[i] / page_size) + 1;
// TODO: cleanly search/split this as a block out of used/free if possible
page_block *block = get_block();
// TODO: page-align
block->physical_address = *p;
block->virtual_address = v;
block->count = c;
block->flags =
page_block_flags::used |
page_block_flags::mapped |
page_block_flags::mmio;
log::info(logs::memory, "Fixing up pointer %lx [%3d] -> %lx", *p, c, v);
m_used = page_block::insert(m_used, block);
page_in(pml4, *p, v, c);
*p = v;
}
consolidate_blocks();
page_block::dump(m_used, "used", true);
@@ -238,10 +211,31 @@ page_manager::init(
}
void
page_manager::mark_offset_pointer(void **pointer, size_t length)
page_manager::map_offset_pointer(void **pointer, size_t length)
{
m_marked_pointers[m_marked_pointer_count] = pointer;
m_marked_pointer_lengths[m_marked_pointer_count++] = length;
addr_t *p = reinterpret_cast<addr_t *>(pointer);
addr_t v = *p + page_offset;
addr_t c = ((length - 1) / page_size) + 1;
// TODO: cleanly search/split this as a block out of used/free if possible
page_block *block = get_block();
// TODO: page-align
block->physical_address = *p;
block->virtual_address = v;
block->count = c;
block->flags =
page_block_flags::used |
page_block_flags::mapped |
page_block_flags::mmio;
log::info(logs::memory, "Fixing up pointer %lx [%3d] -> %lx", *p, c, v);
m_used = page_block::insert(m_used, block);
page_table *pml4 = get_pml4();
page_in(pml4, *p, v, c);
*p = v;
}
page_block *

View File

@@ -40,11 +40,10 @@ public:
/// \arg count The number of pages to unmap
void unmap_pages(addr_t address, size_t count);
/// Mark a pointer and range to be offset-mapped. This pointer will
/// automatically get updated once page_manager::init() is called.
/// Offset-map a pointer. No physical pages will be mapped.
/// \arg pointer Pointer to a pointer to the memory area to be mapped
/// \arg length Length of the memory area to be mapped
void mark_offset_pointer(void **pointer, size_t length);
void map_offset_pointer(void **pointer, size_t length);
private:
friend void memory_initialize_managers(const void *, size_t, size_t);
@@ -137,11 +136,6 @@ private:
page_block *m_block_cache; ///< Cache of unused page_block structs
free_page_header *m_page_cache; ///< Cache of free pages to use for tables
static const unsigned marked_pointer_max = 16;
unsigned m_marked_pointer_count;
void **m_marked_pointers[marked_pointer_max];
size_t m_marked_pointer_lengths[marked_pointer_max];
page_manager(const page_manager &) = delete;
};