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 "kutil/memory.h"
#include "acpi_tables.h" #include "acpi_tables.h"
#include "apic.h"
#include "assert.h" #include "assert.h"
#include "device_manager.h" #include "device_manager.h"
#include "interrupts.h"
#include "log.h" #include "log.h"
#include "memory.h"
static const char expected_signature[] = "RSD PTR "; 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) : device_manager::device_manager(const void *root_table) :
m_local_apic(nullptr), m_lapic(nullptr),
m_io_apic(nullptr), m_ioapics(nullptr),
m_global_interrupt_base(0) m_num_ioapics(0)
{ {
kassert(root_table != 0, "ACPI root table pointer is null."); 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)); 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 static void
put_sig(char *into, uint32_t type) put_sig(char *into, uint32_t type)
{ {
@@ -117,8 +126,11 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
void void
device_manager::load_apic(const acpi_apic *apic) device_manager::load_apic(const acpi_apic *apic)
{ {
m_local_apic = reinterpret_cast<uint32_t *>(apic->local_address); uint32_t *local = reinterpret_cast<uint32_t *>(apic->local_address);
log::info(logs::devices, " APIC local address %lx", 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 *p = apic->controller_data;
uint8_t const *end = p + acpi_table_entries(apic, 1); 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 case 0: // Local APIC
break; break;
case 1: // I/O APIC case 1: { // I/O APIC
m_io_apic = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4)); uint32_t *io_apic = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
m_global_interrupt_base = kutil::read_from<uint32_t>(p+8); uint32_t base = kutil::read_from<uint32_t>(p+8);
log::info(logs::devices, " IO APIC address %lx base %d", log::info(logs::devices, " IO APIC address %lx base %d", io_apic, base);
m_io_apic, m_global_interrupt_base); }
break; break;
case 2: // Interrupt source override 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); (kutil::read_from<uint16_t>(p+8) >> 2) & 0x3);
break; 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", 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+2),
kutil::read_from<uint8_t>(p+5), kutil::read_from<uint8_t>(p+5),
kutil::read_from<uint16_t>(p+3) & 0x3, kutil::read_from<uint16_t>(p+3) & 0x3,
(kutil::read_from<uint16_t>(p+3) >> 2) & 0x3); (kutil::read_from<uint16_t>(p+3) >> 2) & 0x3);
}
break; break;
default: default:
@@ -160,4 +179,7 @@ device_manager::load_apic(const acpi_apic *apic)
p += length; p += length;
} }
// m_lapic->enable_timer(isr::isrTimer, 128, 3000000);
m_lapic->enable();
} }

View File

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

View File

@@ -1,51 +1,54 @@
ISR ( 0, isr0); ISR ( 0, isr0)
ISR ( 1, isr1); ISR ( 1, isr1)
ISR ( 2, isr2); ISR ( 2, isr2)
ISR ( 3, isr3); ISR ( 3, isr3)
ISR ( 4, isr4); ISR ( 4, isr4)
ISR ( 5, isr5); ISR ( 5, isr5)
ISR ( 6, isr6); ISR ( 6, isr6)
ISR ( 7, isr7); ISR ( 7, isr7)
EISR( 8, isr8); EISR( 8, isr8)
ISR ( 9, isr9); ISR ( 9, isr9)
EISR(10, isr10); EISR(10, isr10)
EISR(11, isr11); EISR(11, isr11)
EISR(12, isr12); EISR(12, isr12)
EISR(13, isr13); EISR(13, isr13)
EISR(14, isr14); EISR(14, isr14)
ISR (15, isr15); ISR (15, isr15)
ISR (16, isr16); ISR (16, isr16)
ISR (17, isr17); ISR (17, isr17)
ISR (18, isr18); ISR (18, isr18)
ISR (19, isr19); ISR (19, isr19)
ISR (20, isr20); ISR (20, isr20)
ISR (21, isr21); ISR (21, isr21)
ISR (22, isr22); ISR (22, isr22)
ISR (23, isr23); ISR (23, isr23)
ISR (24, isr24); ISR (24, isr24)
ISR (25, isr25); ISR (25, isr25)
ISR (26, isr26); ISR (26, isr26)
ISR (27, isr27); ISR (27, isr27)
ISR (28, isr28); ISR (28, isr28)
ISR (29, isr29); ISR (29, isr29)
ISR (30, isr30); ISR (30, isr30)
ISR (31, isr31); ISR (31, isr31)
IRQ (64, 0, irq0); IRQ (64, 0, irq0)
IRQ (65, 1, irq1); IRQ (65, 1, irq1)
IRQ (66, 2, irq2); IRQ (66, 2, irq2)
IRQ (67, 3, irq3); IRQ (67, 3, irq3)
IRQ (68, 4, irq4); IRQ (68, 4, irq4)
IRQ (69, 5, irq5); IRQ (69, 5, irq5)
IRQ (70, 6, irq6); IRQ (70, 6, irq6)
IRQ (71, 7, irq7); IRQ (71, 7, irq7)
IRQ (72, 8, irq8); IRQ (72, 8, irq8)
IRQ (73, 9, irq9); IRQ (73, 9, irq9)
IRQ (74, 10, irq10); IRQ (74, 10, irq10)
IRQ (75, 11, irq11); IRQ (75, 11, irq11)
IRQ (76, 12, irq12); IRQ (76, 12, irq12)
IRQ (77, 13, irq13); IRQ (77, 13, irq13)
IRQ (78, 14, irq14); IRQ (78, 14, irq14)
IRQ (79, 15, irq15); 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 "console.h"
#include "interrupts.h" #include "interrupts.h"
#include "log.h" #include "log.h"
#include "memory_pages.h"
enum class gdt_flags : uint8_t enum class gdt_flags : uint8_t
{ {
@@ -59,7 +60,6 @@ idt_descriptor g_idt_table[256];
table_ptr g_gdtr; table_ptr g_gdtr;
table_ptr g_idtr; table_ptr g_idtr;
struct registers; struct registers;
extern "C" { extern "C" {
@@ -72,9 +72,9 @@ extern "C" {
void isr_handler(registers); void isr_handler(registers);
void irq_handler(registers); void irq_handler(registers);
#define ISR(i, name) extern void name () #define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name () #define EISR(i, name) extern void name ();
#define IRQ(i, q, name) extern void name () #define IRQ(i, q, name) extern void name ();
#include "interrupt_isrs.inc" #include "interrupt_isrs.inc"
#undef IRQ #undef IRQ
#undef EISR #undef EISR
@@ -143,8 +143,6 @@ interrupts_init()
idt_write(); idt_write();
log::info(logs::interrupt, "Interrupts enabled."); log::info(logs::interrupt, "Interrupts enabled.");
gdt_dump(g_gdtr);
idt_dump(g_idtr);
} }
struct registers struct registers
@@ -162,6 +160,20 @@ isr_handler(registers regs)
{ {
console *cons = console::get(); 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->set_color(9);
cons->puts("\nReceived ISR interrupt:\n"); cons->puts("\nReceived ISR interrupt:\n");
cons->set_color(); cons->set_color();
@@ -191,6 +203,9 @@ isr_handler(registers regs)
print_reg("esp", regs.user_esp); print_reg("esp", regs.user_esp);
print_reg(" ss", regs.ss); print_reg(" ss", regs.ss);
while(1) asm("hlt"); while(1) asm("hlt");
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
} }
void void

View File

@@ -1,4 +1,21 @@
#pragma once #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" { extern "C" {
void interrupts_enable(); void interrupts_enable();

View File

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

View File

@@ -191,33 +191,6 @@ page_manager::init(
// allocated, full of page_block structs. Eventually hand // allocated, full of page_block structs. Eventually hand
// control of that to a slab allocator. // 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(); consolidate_blocks();
page_block::dump(m_used, "used", true); page_block::dump(m_used, "used", true);
@@ -238,10 +211,31 @@ page_manager::init(
} }
void 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; addr_t *p = reinterpret_cast<addr_t *>(pointer);
m_marked_pointer_lengths[m_marked_pointer_count++] = length; 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 * page_block *

View File

@@ -40,11 +40,10 @@ public:
/// \arg count The number of pages to unmap /// \arg count The number of pages to unmap
void unmap_pages(addr_t address, size_t count); void unmap_pages(addr_t address, size_t count);
/// Mark a pointer and range to be offset-mapped. This pointer will /// Offset-map a pointer. No physical pages will be mapped.
/// automatically get updated once page_manager::init() is called.
/// \arg pointer Pointer to a pointer to the memory area to 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 /// \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: private:
friend void memory_initialize_managers(const void *, size_t, size_t); 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 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 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; page_manager(const page_manager &) = delete;
}; };