mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-31 22:48:21 +00:00
332 lines
7.7 KiB
C
332 lines
7.7 KiB
C
#include <arch/apic.h>
|
|
#include <arch/acpi.h>
|
|
#include <kernel/vmm.h>
|
|
#include <kernel/pmm.h>
|
|
#include <arch/panic.h>
|
|
#include <stdio.h>
|
|
#include <arch/cls.h>
|
|
#include <arch/hpet.h>
|
|
|
|
#define APIC_REGISTER_ID 0x20
|
|
#define APIC_REGISTER_EOI 0xB0
|
|
#define APIC_REGISTER_SPURIOUS 0xF0
|
|
#define APIC_REGISTER_ICR_LO 0x300
|
|
#define APIC_REGISTER_ICR_HI 0x310
|
|
#define APIC_TIMER_LVT 0x320
|
|
#define APIC_THERMAL_LVT 0x330
|
|
#define APIC_PERFORMANCE_LVT 0x340
|
|
#define APIC_LINT0_LVT 0x350
|
|
#define APIC_LINT1_LVT 0x360
|
|
#define APIC_ERROR_LVT 0x370
|
|
#define APIC_TIMER_INITIALCOUNT 0x380
|
|
#define APIC_TIMER_COUNT 0x390
|
|
#define APIC_TIMER_DIVIDE 0x3E0
|
|
|
|
#define LVT_DELIVERY_NMI (0b100 << 8)
|
|
#define LVT_MASK (1 << 16)
|
|
|
|
#define TYPE_LAPIC 0
|
|
#define TYPE_IOAPIC 1
|
|
#define TYPE_OVERRIDE 2
|
|
#define TYPE_IOAPICNMI 3
|
|
#define TYPE_LAPICNMI 4
|
|
#define TYPE_ADDROVERRIDE 5
|
|
#define TYPE_X2APIC 9
|
|
|
|
typedef struct{
|
|
uint8_t type;
|
|
uint8_t length;
|
|
} __attribute__((packed)) entry_head;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint8_t acpi_id;
|
|
uint8_t apicid;
|
|
uint32_t flags;
|
|
} __attribute__((packed)) lapic_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint8_t id;
|
|
uint8_t reserved;
|
|
uint32_t address;
|
|
uint32_t gsibase;
|
|
} __attribute__((packed)) ioapic_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint8_t bus;
|
|
uint8_t irq;
|
|
uint32_t gsi;
|
|
uint16_t flags;
|
|
|
|
} __attribute__((packed)) override_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint8_t source;
|
|
uint8_t reserved;
|
|
uint16_t flags;
|
|
uint32_t gsi;
|
|
} __attribute__((packed)) ioapicnmi_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint8_t acpi_id;
|
|
uint16_t flags;
|
|
uint8_t lint;
|
|
} __attribute__((packed)) lapicnmi_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint16_t reserved;
|
|
uint64_t addr;
|
|
} __attribute__((packed)) overrideaddr_entry;
|
|
|
|
typedef struct{
|
|
entry_head header;
|
|
uint16_t reserved;
|
|
uint32_t x2apicid;
|
|
uint32_t flags;
|
|
uint32_t acpi_id;
|
|
} __attribute__((packed)) x2apic_entry;
|
|
|
|
typedef struct{
|
|
int id;
|
|
int gsi;
|
|
volatile void* address;
|
|
size_t mre;
|
|
} ioapic_descriptor;
|
|
|
|
static size_t lapic_count = 0;
|
|
static size_t ioapic_count = 0;
|
|
static size_t override_count = 0;
|
|
static size_t ionmi_count = 0;
|
|
static size_t localnmi_count = 0;
|
|
static size_t x2apic_count = 0;
|
|
|
|
void* lapicaddr;
|
|
bool done = false;
|
|
ioapic_descriptor* ioapics;
|
|
|
|
void* tablestart;
|
|
void* tableend;
|
|
|
|
static inline entry_head* next(entry_head* entry){
|
|
return (void*)entry + entry->length;
|
|
}
|
|
|
|
static void* findstructure(size_t type, size_t n){
|
|
|
|
entry_head* entry = tablestart;
|
|
|
|
|
|
while(entry < tableend){
|
|
if(entry->type == type){
|
|
if(n-- == 0)
|
|
return entry;
|
|
}
|
|
|
|
entry = next(entry);
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static inline void ioapic_writereg(ioapic_descriptor* ioapic, uint32_t offset, uint32_t data){
|
|
volatile uint32_t* reg = ioapic->address;
|
|
*reg = offset; // select
|
|
*(reg + 4) = data; // data
|
|
}
|
|
|
|
static inline uint32_t ioapic_readreg(ioapic_descriptor* ioapic, uint32_t offset){
|
|
volatile uint32_t* reg = ioapic->address;
|
|
*reg = offset; // select
|
|
return *(reg + 4); // data
|
|
|
|
|
|
}
|
|
|
|
static void ioapic_writeiored(ioapic_descriptor* ioapic, uint8_t irq, uint8_t vector, uint8_t delivery, uint8_t destmode, uint8_t polarity, uint8_t irr, uint8_t mode, uint8_t mask, uint8_t dest){
|
|
uint32_t val = vector;
|
|
val |= (delivery & 0b111) << 8;
|
|
val |= (destmode & 1) << 11;
|
|
val |= (polarity & 1) << 13;
|
|
val |= (irr & 1) << 14;
|
|
val |= (mode & 1) << 15;
|
|
val |= (mask & 1) << 16;
|
|
|
|
ioapic_writereg(ioapic, 0x10 + irq * 2, val & 0xFFFFFFFF);
|
|
ioapic_writereg(ioapic, 0x10 + irq * 2 + 1, dest << 24);
|
|
|
|
}
|
|
|
|
static inline uint32_t lapic_readreg(size_t reg){
|
|
return *(uint32_t volatile*)(lapicaddr + reg);
|
|
}
|
|
|
|
static inline void lapic_writereg(size_t reg, uint32_t data){
|
|
*(uint32_t volatile*)(lapicaddr + reg) = data;
|
|
}
|
|
|
|
void apic_sendipi(uint8_t cpu, uint8_t vec, uint8_t dest, uint8_t mode, uint8_t level){
|
|
|
|
if(!done) return; // this function will be called before the apic initialization code is run so we need this safeguard
|
|
|
|
lapic_writereg(APIC_REGISTER_ICR_HI, (uint32_t)cpu << 24);
|
|
lapic_writereg(APIC_REGISTER_ICR_LO, vec | (level << 14) | (mode << 15) | (dest << 18));
|
|
}
|
|
|
|
void apic_eoi(){
|
|
lapic_writereg(APIC_REGISTER_EOI, 0);
|
|
}
|
|
|
|
void apic_timerstart(size_t ticks){
|
|
lapic_writereg(APIC_TIMER_INITIALCOUNT, ticks);
|
|
}
|
|
|
|
void apic_timerinterruptset(uint8_t vector){
|
|
lapic_writereg(APIC_TIMER_LVT, vector);
|
|
}
|
|
|
|
void apic_timerstop(){
|
|
lapic_writereg(APIC_TIMER_INITIALCOUNT, 0);
|
|
}
|
|
|
|
size_t apic_timercalibrate(size_t ms){
|
|
|
|
lapic_writereg(APIC_TIMER_INITIALCOUNT, 0xFFFFFFFF);
|
|
lapic_writereg(APIC_TIMER_DIVIDE, 0b111); // no divider
|
|
|
|
hpet_wait_ms(ms);
|
|
|
|
size_t ticks = 0xFFFFFFFF - lapic_readreg(APIC_TIMER_COUNT);
|
|
|
|
apic_timerstop();
|
|
|
|
return ticks;
|
|
|
|
}
|
|
|
|
void apic_lapicinit(){
|
|
|
|
// mask everything off besides the timer
|
|
|
|
lapic_writereg(APIC_THERMAL_LVT, LVT_MASK);
|
|
lapic_writereg(APIC_PERFORMANCE_LVT, LVT_MASK);
|
|
lapic_writereg(APIC_LINT0_LVT, LVT_MASK);
|
|
lapic_writereg(APIC_LINT1_LVT, LVT_MASK);
|
|
lapic_writereg(APIC_ERROR_LVT, LVT_MASK);
|
|
|
|
|
|
arch_getcls()->lapicid = (lapic_readreg(APIC_REGISTER_ID));
|
|
arch_getcls()->lapicid >>= 24;
|
|
|
|
lapic_entry* current;
|
|
// get the acpi id for the local nmi sources
|
|
|
|
for(size_t i = 0; i < lapic_count; ++i){
|
|
current = findstructure(TYPE_LAPIC, i);
|
|
if(arch_getcls()->lapicid == current->apicid)
|
|
break;
|
|
}
|
|
|
|
arch_getcls()->acpi_id = current->acpi_id;
|
|
|
|
lapic_writereg(APIC_REGISTER_SPURIOUS, 0x1FF);
|
|
lapic_writereg(APIC_TIMER_LVT, 1 << 16); // mask timer interrupt off
|
|
|
|
for(size_t i = 0; i < localnmi_count; ++i){
|
|
lapicnmi_entry* e = findstructure(TYPE_LAPICNMI, i);
|
|
if(e->acpi_id == current->acpi_id || e->acpi_id == 0xFF){
|
|
lapic_writereg(APIC_LINT0_LVT + 0x10*e->lint, 0x30 | LVT_DELIVERY_NMI | (e->flags << 12));
|
|
}
|
|
}
|
|
|
|
asm("sti");
|
|
|
|
}
|
|
|
|
void apic_init(){
|
|
|
|
// first get some cool info about the apic in the system
|
|
|
|
madt_t* apic = acpi_gettable("APIC", 0);
|
|
|
|
if(!apic)
|
|
_panic("No MADT found", 0);
|
|
|
|
printf("MADT at %p\n", apic);
|
|
|
|
lapicaddr = apic->apicaddr;
|
|
|
|
// get the count of relevant entries
|
|
|
|
size_t len = apic->header.length - sizeof(madt_t);
|
|
void* start = (void*)apic + sizeof(madt_t);
|
|
void* end = start + len;
|
|
entry_head* entry = start;
|
|
tablestart = start;
|
|
tableend = end;
|
|
while(entry < end){
|
|
switch(entry->type){
|
|
case TYPE_LAPIC:
|
|
++lapic_count;
|
|
break;
|
|
case TYPE_IOAPIC:
|
|
++ioapic_count;
|
|
break;
|
|
case TYPE_OVERRIDE:
|
|
++override_count;
|
|
break;
|
|
case TYPE_IOAPICNMI:
|
|
++ionmi_count;
|
|
break;
|
|
case TYPE_LAPICNMI:
|
|
++localnmi_count;
|
|
break;
|
|
case TYPE_ADDROVERRIDE:
|
|
lapicaddr = ((overrideaddr_entry*)entry)->addr;
|
|
break;
|
|
case TYPE_X2APIC:
|
|
++x2apic_count;
|
|
break;
|
|
|
|
}
|
|
entry = next(entry);
|
|
}
|
|
|
|
printf("MADT info:\nLocal APIC count: %lu\nIO APIC count: %lu\noverride count: %lu\nIO NMI count: %lu\nLocal NMI count: %lu\nX2APIC count: %lu\nLocal apic address: %p\n", lapic_count, ioapic_count, override_count, ionmi_count, localnmi_count, x2apic_count, lapicaddr);
|
|
|
|
// map the local apic into memory (sea bios doesn't pass this to the limine map for some reason? I know my laptop UEFI firmware passes it)
|
|
|
|
void* tmp = vmm_alloc(1, 0);
|
|
if(!tmp) _panic("Out of memory", 0);
|
|
vmm_map(lapicaddr, tmp, 1, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE);
|
|
lapicaddr = tmp;
|
|
|
|
// now set up the IO apic
|
|
|
|
ioapics = alloc(sizeof(ioapic_descriptor)*ioapic_count);
|
|
|
|
for(size_t i = 0; i < ioapic_count; ++i){
|
|
|
|
ioapic_entry* entry = findstructure(TYPE_IOAPIC, i);
|
|
void* tmp = vmm_alloc(1, 0);
|
|
if(!tmp) _panic("Out of memory", 0);
|
|
vmm_map(entry->address, tmp, 1, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE);
|
|
|
|
ioapics[i].address = tmp;
|
|
ioapics[i].gsi = entry->gsibase;
|
|
ioapics[i].id = entry->id;
|
|
ioapics[i].mre = (ioapic_readreg(&ioapics[i], 1) >> 16) & 0xFF;
|
|
|
|
for(size_t j = 0; j <= ioapics[i].mre; ++j)
|
|
ioapic_writeiored(&ioapics[i], j, 0, 0, 0, 0, 0, 0, 1, 0);
|
|
}
|
|
|
|
done = true;
|
|
|
|
}
|