feat: added ACPI, APIC and HPET

This commit is contained in:
Jordan ⌨️ 2023-12-15 22:31:51 +01:00
parent 03d28e62ef
commit 5fc4c7b546
8 changed files with 448 additions and 3 deletions

View file

@ -1,5 +1,5 @@
#include <dbg/log.h>
#include <hal.h> #include <hal.h>
#include <dbg/log.h>
#include <string.h> #include <string.h>
#include "acpi.h" #include "acpi.h"

View file

@ -0,0 +1,197 @@
#include <hal.h>
#include <dbg/log.h>
#include "apic.h"
#include "asm.h"
#include "hpet.h"
static Madt *madt = NULL;
/* --- Lapic --------------------------------------------------------------- */
static uint32_t lapic_read(uint32_t reg)
{
return *((volatile uint32_t *)(hal_mmap_l2h(madt->local_controller_address) + reg));
}
static void lapic_write(uint32_t reg, uint32_t value)
{
*((volatile uint32_t *)(hal_mmap_l2h(madt->local_controller_address) + reg)) = value;
}
void lapic_timer_start(void)
{
lapic_write(LAPIC_REG_TIMER_DIV, APIC_TIMER_DIVIDE_BY_16);
lapic_write(LAPIC_REG_TIMER_INITCNT, 0xFFFFFFFF);
hpet_sleep(10);
lapic_write(LAPIC_REG_LVT_TIMER, LAPIC_TIMER_MASKED);
uint32_t tick_in_10ms = 0xFFFFFFFF - lapic_read(LAPIC_REG_TIMER_CURRCNT);
lapic_write(LAPIC_REG_LVT_TIMER, LAPIC_TIMER_IRQ | LAPIC_TIMER_PERIODIC);
lapic_write(LAPIC_REG_TIMER_DIV, APIC_TIMER_DIVIDE_BY_16);
lapic_write(LAPIC_REG_TIMER_INITCNT, tick_in_10ms / 10);
}
static void lapic_enable(void)
{
asm_write_msr(MSR_APIC, (asm_read_msr(MSR_APIC) | LAPIC_ENABLE) & ~((1 << 10)));
lapic_write(LAPIC_REG_SPURIOUS, lapic_read(LAPIC_REG_SPURIOUS) | (LAPIC_SPURIOUS_ALL | LAPIC_SPURIOUS_ENABLE_APIC));
lapic_timer_start();
}
void lapic_eoi(void)
{
if (madt == NULL)
{
return;
}
lapic_write(LAPIC_REG_EOI, 0);
}
int lapic_id(void)
{
if (madt == NULL)
{
return -1;
}
return lapic_read(LAPIC_CPU_ID) >> 24;
}
/* --- Ioapic --------------------------------------------------------------- */
static void ioapic_write(MadtIoapic *io_apic, uint32_t reg, uint32_t value)
{
uintptr_t base = (uintptr_t)hal_mmap_l2h(io_apic->ioapic_addr);
*(volatile uint32_t *)base = reg;
*(volatile uint32_t *)(base + 16) = value;
}
static uint32_t ioapic_read(MadtIoapic *ioapic, uint32_t reg)
{
uintptr_t base = (uintptr_t)hal_mmap_l2h(ioapic->ioapic_addr);
*(volatile uint32_t *)(base) = reg;
return *(volatile uint32_t *)(base + 0x10);
}
static void ioapic_redirect_legacy(void)
{
for (size_t i = 0; i < 16; i++)
{
ioapic_redirect_irq(0, i + 32, i);
}
}
static MadtIso *madt_get_iso_irq(uint8_t irq)
{
size_t i = 0;
while (i < madt->header.length - sizeof(Madt))
{
MadtEntry *entry = (MadtEntry *)madt->entries + i;
if (entry->type == 2)
{
MadtIso *iso = (MadtIso *)entry;
if (iso->irq_src == irq)
{
return iso;
}
}
i += max$(2, entry->length);
}
return NULL;
}
static size_t ioapic_gsi_count(MadtIoapic *ioapic)
{
uint32_t val = ioapic_read(ioapic, 1);
IoapicVer *ver = (IoapicVer *)&val;
return ver->max_redirect;
}
MadtIoapic *madt_get_ioapic_from_gsi(uint32_t gsi)
{
size_t i = 0;
MadtEntry *entry;
while (i < madt->header.length - sizeof(Madt))
{
entry = (MadtEntry *)(madt->entries + i);
if (entry->type == 1)
{
MadtIoapic *ioapic = (MadtIoapic *)entry;
if (gsi >= ioapic->gsib && gsi < ioapic->gsib + ioapic_gsi_count(ioapic))
{
return ioapic;
}
}
i += max$(2, entry->length);
}
return NULL;
}
static void ioapic_set_gsi_redirect(uint32_t lapic_id, uint8_t intno, uint8_t gsi, uint16_t flags)
{
uint32_t io_redirect_table;
IoapicRedirect redirect = {0};
MadtIoapic *ioapic = madt_get_ioapic_from_gsi(gsi);
if (ioapic == NULL)
{
return;
}
redirect.vector = intno;
if (flags & IOAPIC_ACTIVE_HIGH_LOW)
{
redirect.polarity = 1;
}
if (flags & IOAPIC_TRIGGER_EDGE_LOW)
{
redirect.trigger = 1;
}
redirect.dest_id = lapic_id;
io_redirect_table = (gsi - ioapic->gsib) * 2 + 16;
ioapic_write(ioapic, io_redirect_table, (uint32_t)redirect._raw.low_byte);
ioapic_write(ioapic, io_redirect_table + 1, (uint32_t)redirect._raw.high_byte);
}
void ioapic_redirect_irq(uint32_t lapic_id, uint8_t intno, uint8_t irq)
{
MadtIso *iso = madt_get_iso_irq(irq);
if (iso != NULL)
{
ioapic_set_gsi_redirect(lapic_id, intno, iso->gsi, iso->flags);
}
else
{
ioapic_set_gsi_redirect(lapic_id, intno, irq, 0);
}
}
Res apic_init(void)
{
madt = (Madt *)try$(acpi_parse_sdt("APIC"));
lapic_enable();
ioapic_redirect_legacy();
hal_enable_interrupts();
log$("APIC initialised");
return ok$();
}

View file

@ -0,0 +1,115 @@
#pragma once
#include "acpi.h"
#define IPI_RESCHED (100)
#define IPI_STOP (101)
#define LAPIC_ENABLE (0x800)
#define LAPIC_SPURIOUS_ALL 0xff
#define LAPIC_SPURIOUS_ENABLE_APIC 0x100
#define LAPIC_ICR_CPUID_OFFSET 24
#define LAPIC_ICR_CLEAR_INIT_LEVEL (1 << 14)
#define LAPIC_ICR_DEST_INIT (5 << 8)
#define LAPIC_ICR_DEST_SEND_IPI (6 << 8)
#define IOAPIC_REG_OFFSET (0)
#define IOAPIC_VALUE_OFFSET (16)
#define LAPIC_TIMER_IRQ (32)
#define LAPIC_TIMER_PERIODIC (0x20000)
#define LAPIC_TIMER_MASKED (0x10000)
#define IOAPIC_ACTIVE_HIGH_LOW (1 << 1)
#define IOAPIC_TRIGGER_EDGE_LOW (1 << 3)
enum lapic_reg
{
LAPIC_CPU_ID = 0x20,
LAPIC_REG_EOI = 0x0b0,
LAPIC_REG_SPURIOUS = 0x0f0,
LAPIC_REG_ICR0 = 0x300,
LAPIC_REG_ICR1 = 0x310,
LAPIC_REG_LVT_TIMER = 0x320,
LAPIC_REG_TIMER_INITCNT = 0x380,
LAPIC_REG_TIMER_CURRCNT = 0x390,
LAPIC_REG_TIMER_DIV = 0x3e0,
};
enum apic_timer_division
{
APIC_TIMER_DIVIDE_BY_2 = 0,
APIC_TIMER_DIVIDE_BY_4 = 1,
APIC_TIMER_DIVIDE_BY_8 = 2,
APIC_TIMER_DIVIDE_BY_16 = 3,
APIC_TIMER_DIVIDE_BY_32 = 4,
APIC_TIMER_DIVIDE_BY_64 = 5,
APIC_TIMER_DIVIDE_BY_128 = 6,
APIC_TIMER_DIVIDE_BY_1 = 7
};
typedef struct [[gnu::packed]]
{
SdtHeader header;
uint32_t local_controller_address;
uint32_t flags;
uint8_t entries[];
} Madt;
typedef struct [[gnu::packed]]
{
uint8_t type;
uint8_t length;
} MadtEntry;
typedef struct [[gnu::packed]]
{
MadtEntry header;
uint8_t ioapic_id;
uint8_t _reserved;
uint32_t ioapic_addr;
uint32_t gsib;
} MadtIoapic;
typedef struct [[gnu::packed]]
{
uint8_t version;
uint8_t reserved;
uint8_t max_redirect;
uint8_t reserved2;
} IoapicVer;
typedef union [[gnu::packed]]
{
struct [[gnu::packed]]
{
uint8_t vector;
uint8_t delivery_mode : 3;
uint8_t dest_mode : 1;
uint8_t delivery_status : 1;
uint8_t polarity : 1;
uint8_t remote_irr : 1;
uint8_t trigger : 1;
uint8_t mask : 1;
uint8_t reserved : 7;
uint8_t dest_id;
};
struct [[gnu::packed]]
{
uint32_t low_byte;
uint32_t high_byte;
} _raw;
} IoapicRedirect;
typedef struct [[gnu::packed]]
{
MadtEntry header;
uint8_t bus_src;
uint8_t irq_src;
uint32_t gsi;
uint16_t flags;
} MadtIso;
Res apic_init(void);
void lapic_eoi(void);
int lapic_id(void);
void ioapic_redirect_irq(uint32_t lapic_id, uint8_t intno, uint8_t irq);
void lapic_timer_start(void);
void lapic_timer_stop(void);

View file

@ -1,3 +1,5 @@
#include "asm.h"
void hal_disable_interrupts(void) void hal_disable_interrupts(void)
{ {
asm volatile("cli"); asm volatile("cli");
@ -17,3 +19,21 @@ void hal_panic(void)
{ {
asm volatile("int $1"); asm volatile("int $1");
} }
void asm_write_msr(uint64_t msr, uint64_t value)
{
uint32_t low = value & 0xFFFFFFFF;
uint32_t high = value >> 32;
__asm__ volatile("wrmsr" ::"c"((uint64_t)msr), "a"(low), "d"(high));
}
uint64_t asm_read_msr(uint64_t msr)
{
uint32_t low, high;
__asm__ volatile("rdmsr"
: "=a"(low), "=d"(high)
: "c"((uint64_t)msr));
return ((uint64_t)high << 32) | low;
}

View file

@ -1,8 +1,33 @@
#pragma once #pragma once
#include <stdint.h>
#define asm_read_cr(n, reg) asm volatile("mov %%cr" #n ", %0" \ #define asm_read_cr(n, reg) asm volatile("mov %%cr" #n ", %0" \
: "=r"(reg)) : "=r"(reg))
#define asm_write_cr(n, reg) asm volatile("mov %0, %%cr" #n \ #define asm_write_cr(n, reg) asm volatile("mov %0, %%cr" #n \
: \ : \
: "r"(reg)) : "r"(reg))
enum msr_registers
{
MSR_APIC = 0x1B,
MSR_EFER = 0xC0000080,
MSR_STAR = 0xC0000081,
MSR_LSTAR = 0xC0000082,
MSR_COMPAT_STAR = 0xC0000083,
MSR_SYSCALL_FLAG_MASK = 0xC0000084,
MSR_FS_BASE = 0xC0000100,
MSR_GS_BASE = 0xC0000101,
MSR_KERN_GS_BASE = 0xc0000102,
};
enum msr_star_reg
{
STAR_KCODE_OFFSET = 32,
STAR_UCODE_OFFSET = 48,
};
void asm_write_msr(uint64_t msr, uint64_t value);
uint64_t asm_read_msr(uint64_t msr);

View file

@ -0,0 +1,45 @@
#include <hal.h>
#include <dbg/log.h>
#include "hpet.h"
static uintptr_t hpet_base = 0;
static size_t hpet_tick = 0;
static void hpet_write(uint32_t reg, uint64_t value)
{
*(volatile uint64_t *)(hpet_base + reg) = value;
}
static uint64_t hpet_read(uint32_t reg)
{
return *(volatile uint64_t *)(hpet_base + reg);
}
Res hpet_init(void)
{
AcpiHpet *hpet = (AcpiHpet *)try$(acpi_parse_sdt("HPET"));
hpet_base = hal_mmap_l2h(hpet->address);
if (hpet->address_space_id == HPET_ADDRESS_SPACE_IO)
{
err$(RES_NOENT);
}
hpet_tick = hpet_read(HPET_GENERAL_CAPABILITIES) >> HPET_CAP_COUNTER_CLOCK_OFFSET;
hpet_write(HPET_GENERAL_CONFIGUATION, HPET_CONF_TURN_OFF);
hpet_write(HPET_MAIN_COUNTER_VALUE, 0);
hpet_write(HPET_GENERAL_CONFIGUATION, HPET_CONF_TURN_ON);
log$("Hpet initialised");
return ok$();
}
void hpet_sleep(int ms)
{
uint64_t target = hpet_read(HPET_MAIN_COUNTER_VALUE) + (ms * 1000000000000) / hpet_tick;
while (hpet_read(HPET_MAIN_COUNTER_VALUE) <= target)
;
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "acpi.h"
#define HPET_ADDRESS_SPACE_MEMORY 0
#define HPET_ADDRESS_SPACE_IO 1
#define HPET_CAP_COUNTER_CLOCK_OFFSET (32)
#define HPET_CONF_TURN_ON (1)
#define HPET_CONF_TURN_OFF (0)
enum hpet_registers
{
HPET_GENERAL_CAPABILITIES = 0,
HPET_GENERAL_CONFIGUATION = 16,
HPET_MAIN_COUNTER_VALUE = 240,
};
typedef struct [[gnu::packed]]
{
SdtHeader header;
uint8_t hardware_rev_id;
uint8_t info;
uint16_t pci_vendor_id;
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t reserved1;
uint64_t address;
uint8_t hpet_number;
uint16_t minimum_tick;
uint8_t page_protection;
} AcpiHpet;
Res hpet_init(void);
void hpet_sleep(int ms);

View file

@ -6,6 +6,8 @@
#include "gdt.h" #include "gdt.h"
#include "idt.h" #include "idt.h"
#include "paging.h" #include "paging.h"
#include "hpet.h"
#include "apic.h"
Stream hal_dbg_stream(void) Stream hal_dbg_stream(void)
{ {
@ -18,7 +20,10 @@ Res hal_setup(void)
{ {
gdt_init(); gdt_init();
idt_init(); idt_init();
paging_init(); try$(paging_init());
acpi_init(); acpi_init();
try$(hpet_init());
try$(apic_init());
return ok$(); return ok$();
} }