diff --git a/headers/apic.h b/headers/apic.h new file mode 100644 index 0000000..d77bc04 --- /dev/null +++ b/headers/apic.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +// 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); diff --git a/headers/cpuid.h b/headers/cpuid.h new file mode 100644 index 0000000..02d9faf --- /dev/null +++ b/headers/cpuid.h @@ -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 +}; diff --git a/libbozo/headers/sys/io.h b/libbozo/headers/sys/io.h index 01fc098..0ac1700 100644 --- a/libbozo/headers/sys/io.h +++ b/libbozo/headers/sys/io.h @@ -1,5 +1,6 @@ #pragma once +#include #include 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; +} diff --git a/src/interrupt/apic.c b/src/interrupt/apic.c new file mode 100644 index 0000000..3ceccf5 --- /dev/null +++ b/src/interrupt/apic.c @@ -0,0 +1,60 @@ +#include "apic.h" +#include "cpuid.h" +#include "sys/io.h" +#include +#include +#include + +#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); +} diff --git a/src/interrupt/handler.c b/src/interrupt/handler.c index aeb95d7..038cdf6 100644 --- a/src/interrupt/handler.c +++ b/src/interrupt/handler.c @@ -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) { diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index 4c04959..effa040 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -1,39 +1,22 @@ +#include "sys/io.h" #include #include +#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(); } diff --git a/src/interrupt/msr.c b/src/interrupt/msr.c new file mode 100644 index 0000000..15df27b --- /dev/null +++ b/src/interrupt/msr.c @@ -0,0 +1,22 @@ +#include +#include +#include + +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)); +} diff --git a/src/interrupt/pic.c b/src/interrupt/pic.c new file mode 100644 index 0000000..3ef7254 --- /dev/null +++ b/src/interrupt/pic.c @@ -0,0 +1,61 @@ +#include "sys/io.h" +#include + +#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); +} diff --git a/src/kernel.c b/src/kernel.c index cb3a31b..cf59a47 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -27,7 +27,7 @@ void kernel_main(void) { terminal_initialize(); init_gdt(); - init_memory(); init_idt(); + init_memory(); shell_init(); }