feature: apic is now enabled and the double fault interrupt at boot no longer occurs

This commit is contained in:
0x35c 2024-10-03 15:20:19 +02:00
parent 9479515685
commit d6b35a2786
9 changed files with 255 additions and 29 deletions

16
headers/apic.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
// MSR
bool cpu_has_msr();
void cpu_set_msr(uint32_t msr, uint32_t lo, uint32_t hi);
void cpu_get_msr(uint32_t msr, uint32_t *lo, uint32_t *hi);
// 8259 PIC
void pic_disable(void);
void pic_remap(int offset_master, int offset_slave);
// APIC
void enable_apic(void);

66
headers/cpuid.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
enum {
CPUID_FEAT_ECX_SSE3 = 1 << 0,
CPUID_FEAT_ECX_PCLMUL = 1 << 1,
CPUID_FEAT_ECX_DTES64 = 1 << 2,
CPUID_FEAT_ECX_MONITOR = 1 << 3,
CPUID_FEAT_ECX_DS_CPL = 1 << 4,
CPUID_FEAT_ECX_VMX = 1 << 5,
CPUID_FEAT_ECX_SMX = 1 << 6,
CPUID_FEAT_ECX_EST = 1 << 7,
CPUID_FEAT_ECX_TM2 = 1 << 8,
CPUID_FEAT_ECX_SSSE3 = 1 << 9,
CPUID_FEAT_ECX_CID = 1 << 10,
CPUID_FEAT_ECX_SDBG = 1 << 11,
CPUID_FEAT_ECX_FMA = 1 << 12,
CPUID_FEAT_ECX_CX16 = 1 << 13,
CPUID_FEAT_ECX_XTPR = 1 << 14,
CPUID_FEAT_ECX_PDCM = 1 << 15,
CPUID_FEAT_ECX_PCID = 1 << 17,
CPUID_FEAT_ECX_DCA = 1 << 18,
CPUID_FEAT_ECX_SSE4_1 = 1 << 19,
CPUID_FEAT_ECX_SSE4_2 = 1 << 20,
CPUID_FEAT_ECX_X2APIC = 1 << 21,
CPUID_FEAT_ECX_MOVBE = 1 << 22,
CPUID_FEAT_ECX_POPCNT = 1 << 23,
CPUID_FEAT_ECX_TSC = 1 << 24,
CPUID_FEAT_ECX_AES = 1 << 25,
CPUID_FEAT_ECX_XSAVE = 1 << 26,
CPUID_FEAT_ECX_OSXSAVE = 1 << 27,
CPUID_FEAT_ECX_AVX = 1 << 28,
CPUID_FEAT_ECX_F16C = 1 << 29,
CPUID_FEAT_ECX_RDRAND = 1 << 30,
CPUID_FEAT_ECX_HYPERVISOR = 1 << 31,
CPUID_FEAT_EDX_FPU = 1 << 0,
CPUID_FEAT_EDX_VME = 1 << 1,
CPUID_FEAT_EDX_DE = 1 << 2,
CPUID_FEAT_EDX_PSE = 1 << 3,
CPUID_FEAT_EDX_TSC = 1 << 4,
CPUID_FEAT_EDX_MSR = 1 << 5,
CPUID_FEAT_EDX_PAE = 1 << 6,
CPUID_FEAT_EDX_MCE = 1 << 7,
CPUID_FEAT_EDX_CX8 = 1 << 8,
CPUID_FEAT_EDX_APIC = 1 << 9,
CPUID_FEAT_EDX_SEP = 1 << 11,
CPUID_FEAT_EDX_MTRR = 1 << 12,
CPUID_FEAT_EDX_PGE = 1 << 13,
CPUID_FEAT_EDX_MCA = 1 << 14,
CPUID_FEAT_EDX_CMOV = 1 << 15,
CPUID_FEAT_EDX_PAT = 1 << 16,
CPUID_FEAT_EDX_PSE36 = 1 << 17,
CPUID_FEAT_EDX_PSN = 1 << 18,
CPUID_FEAT_EDX_CLFLUSH = 1 << 19,
CPUID_FEAT_EDX_DS = 1 << 21,
CPUID_FEAT_EDX_ACPI = 1 << 22,
CPUID_FEAT_EDX_MMX = 1 << 23,
CPUID_FEAT_EDX_FXSR = 1 << 24,
CPUID_FEAT_EDX_SSE = 1 << 25,
CPUID_FEAT_EDX_SSE2 = 1 << 26,
CPUID_FEAT_EDX_SS = 1 << 27,
CPUID_FEAT_EDX_HTT = 1 << 28,
CPUID_FEAT_EDX_TM = 1 << 29,
CPUID_FEAT_EDX_IA64 = 1 << 30,
CPUID_FEAT_EDX_PBE = 1 << 31
};

View File

@ -1,5 +1,6 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
static inline void outb(uint16_t port, uint8_t val)
@ -25,3 +26,18 @@ static inline uint16_t inw(uint16_t port)
__asm__ volatile("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory");
return ret;
}
static inline void io_wait(void)
{
outb(0x80, 0);
}
static inline void write_reg(size_t addr, uint32_t val)
{
*(volatile uint32_t *)addr = val;
}
static inline uint32_t read_reg(size_t addr)
{
return *(volatile uint32_t *)addr;
}

60
src/interrupt/apic.c Normal file
View File

@ -0,0 +1,60 @@
#include "apic.h"
#include "cpuid.h"
#include "sys/io.h"
#include <cpuid.h>
#include <stdbool.h>
#include <stdint.h>
#define IA32_APIC_BASE_MSR 0x1B
#define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP
#define IA32_APIC_BASE_MSR_ENABLE 0x800
/** returns a 'true' value if the CPU supports APIC
* and if the local APIC hasn't been disabled in MSRs
* note that this requires CPUID to be supported.
*/
bool check_apic()
{
static unsigned int eax, edx, unused;
__get_cpuid(1, &eax, &unused, &unused, &edx);
return edx & CPUID_FEAT_EDX_APIC;
}
/* Set the physical address for local APIC registers */
static void cpu_set_apic_base(uintptr_t apic)
{
uint32_t edx = 0;
uint32_t eax = (apic & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
#ifdef __PHYSICAL_MEMORY_EXTENSION__
edx = (apic >> 32) & 0x0f;
#endif
cpu_set_msr(IA32_APIC_BASE_MSR, eax, edx);
}
/**
* Get the physical address of the APIC registers page
* make sure you map it to virtual memory ;)
*/
static uintptr_t cpu_get_apic_base(void)
{
uint32_t eax, edx;
cpu_get_msr(IA32_APIC_BASE_MSR, &eax, &edx);
#ifdef __PHYSICAL_MEMORY_EXTENSION__
return (eax & 0xfffff000) | ((edx & 0x0f) << 32);
#else
return (eax & 0xfffff000);
#endif
}
void enable_apic(void)
{
/* Hardware enable the Local APIC if it wasn't enabled */
cpu_set_apic_base(cpu_get_apic_base());
/* Set the Spurious Interrupt Vector Register bit 8 to start receiving
* interrupts */
write_reg(0xF0, read_reg(0xF0) | 0x100);
}

View File

@ -6,7 +6,7 @@
void exception_handler(void)
{
int8_t index = -1;
__asm__ volatile("movb %%al, %0" ::"m"(index));
__asm__ volatile("movb %%bl, %0" ::"m"(index));
kprintf(KERN_CRIT "interrupt: ");
switch (index) {

View File

@ -1,39 +1,22 @@
#include "sys/io.h"
#include <stdbool.h>
#include <stdint.h>
#include "apic.h"
#include "gdt.h"
#include "idt.h"
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1 + 1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2 + 1)
extern void *isr_stub_table[];
__attribute__((aligned(0x10))) static struct idt_entry idt_entries[IDT_SIZE];
static struct idt_descriptor idtr;
/*
static void set_idt_entry_value(uint16_t *target, uint32_t offset,
uint16_t selector, uint8_t dpl,
uint8_t gate_type)
{
// Encode the offset
target[0] = offset & 0xFFFF; // Low 16 bits
target[3] = (offset >> 16) & 0xFFFF; // High 16 bits
// Encode the presence (bit 15)
target[1] |= 1 << 15;
// Encode the CPU Privilege Levels (DPL)
target[1] &= ~(0b11 << 13); // Clear previous DPL
target[1] |= (dpl & 0b11) << 13; // Set new DPL
// Clear bit 12 (always 0 for interrupts)
target[1] &= ~(1 << 12);
// Encode Gate Type
target[1] &= ~0x0F00; // Clear previous gate type
target[1] |= (gate_type & 0x0F) << 8; // Set new gate type
// Encode selector
target[2] = selector;
}*/
void idt_set_descriptor(uint8_t index, void *isr, uint8_t flags)
{
@ -53,7 +36,9 @@ void init_idt(void)
for (uint8_t i = 0; i < 32; i++)
idt_set_descriptor(i, isr_stub_table[i], 0x8E);
__asm__ volatile("lidt %0" : : "m"(idtr));
__asm__ volatile("sti");
pic_remap(32, 32);
pic_disable();
enable_apic();
}

22
src/interrupt/msr.c Normal file
View File

@ -0,0 +1,22 @@
#include <cpuid.h>
#include <stdbool.h>
#include <stdint.h>
const uint32_t CPUID_FLAG_MSR = 1 << 5;
bool cpu_has_msr()
{
static unsigned int eax, edx, unused;
__get_cpuid(1, &eax, &unused, &unused, &edx);
return edx & CPUID_FLAG_MSR;
}
void cpu_get_msr(uint32_t msr, uint32_t *lo, uint32_t *hi)
{
asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr));
}
void cpu_set_msr(uint32_t msr, uint32_t lo, uint32_t hi)
{
asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr));
}

61
src/interrupt/pic.c Normal file
View File

@ -0,0 +1,61 @@
#include "sys/io.h"
#include <stdint.h>
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1 + 1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2 + 1)
#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
#define ICW1_INIT 0x10 /* Initialization - required! */
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
void pic_remap(int offset_master, int offset_slave)
{
uint8_t a1, a2;
a1 = inb(PIC1_DATA); // save masks
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization
// sequence (in cascade mode)
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, offset_master); // ICW2: Master PIC vector offset
io_wait();
outb(PIC2_DATA, offset_slave); // ICW2: Slave PIC vector offset
io_wait();
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC
// at IRQ2 (0000 0100)
io_wait();
outb(PIC2_DATA,
2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
io_wait();
outb(
PIC1_DATA,
ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode)
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
outb(PIC1_DATA, a1); // restore saved masks.
outb(PIC2_DATA, a2);
}
void pic_disable(void)
{
outb(PIC1_DATA, 0xff);
outb(PIC2_DATA, 0xff);
}

View File

@ -27,7 +27,7 @@ void kernel_main(void)
{
terminal_initialize();
init_gdt();
init_memory();
init_idt();
init_memory();
shell_init();
}