Initial commit

This commit is contained in:
bdbrd 2025-10-22 15:51:24 +02:00
commit cbc51f523e
125 changed files with 34817 additions and 0 deletions

137
src/hal/apic.c Normal file
View file

@ -0,0 +1,137 @@
#include "../sys/acpi.h"
#include "../drivers/pmt.h"
#include "smp.h"
#include "timer.h"
#include "ioapic.h"
#include <lock.h>
#include <stdio.h>
#include <SFB25.h>
#include <cpuid.h> // GCC specific
#define LAPIC_ID_REG 0x020
#define LAPIC_EOI_REG 0x0B0
#define LAPIC_SPURIOUS_REG 0x0F0
#define LAPIC_ERR_REG 0x280
#define LAPIC_LINT0_REG 0x350
#define LAPIC_LINT1_REG 0x360
#define LAPIC_ICR_REG 0x300
#define LAPIC_LVT_TIMER_REG 0x320
#define LAPIC_TIMER_INITIAL_CNT_REG 0x380
#define LAPIC_TIMER_CURRENT_CNT_REG 0x390
#define LAPIC_TIMER_DIVIDER_REG 0x3E0
#define LAPIC_TIMER_MASK (1 << 16)
#define LAPIC_TIMER_UNMASK 0xFFFEFFFF
#define LAPIC_TIMER_PERIODIC (1 << 17)
#define LAPIC_TIMER_VECTOR 69
extern madt_t *madt;
extern uint64_t hhdmoffset;
uint64_t lapic_address = 0;
uint64_t timer_speed_us = 0;
void lapic_write_reg(uint32_t reg, uint32_t data){
*((uint32_t*)(lapic_address+reg)) = data;
}
uint32_t lapic_read_reg(uint32_t reg){
return(*((uint32_t*)(lapic_address+reg)));
}
/* Assumes single-threaded*/
void apic_sleep(uint64_t ms){
uint64_t lapic_timer_ticks = get_cpu_struct()->lapic_timer_ticks;
uint64_t curcnt = get_cpu_struct()->lapic_timer_ticks;
while (lapic_timer_ticks - curcnt < ms) {
lapic_timer_ticks = get_cpu_struct()->lapic_timer_ticks;
}
}
atomic_flag lapic_timer_flag = ATOMIC_FLAG_INIT;
void lapic_timer_init(int us){
acquire_lock(&lapic_timer_flag);
/* Stop the APIC timer */
lapic_write_reg(LAPIC_TIMER_INITIAL_CNT_REG, 0);
/* Set the divisor to 16 */
lapic_write_reg(LAPIC_TIMER_DIVIDER_REG, 0b11);
/* Set the intial count to max */
lapic_write_reg(LAPIC_TIMER_INITIAL_CNT_REG, 0xffffffff);
/* Call a delay function based on the available timer */
pmt_delay(us);
/* Mask the timer (prevents interrupts) */
lapic_write_reg(LAPIC_LVT_TIMER_REG, LAPIC_TIMER_MASK);
/* Determine the inital count to be used for a delay set by `timer_speed_us` */
uint32_t calibration = 0xffffffff - lapic_read_reg(LAPIC_TIMER_CURRENT_CNT_REG);
/* Set the timer interrupt vector and put the timer into periodic mode */
lapic_write_reg(LAPIC_LVT_TIMER_REG, LAPIC_TIMER_VECTOR | LAPIC_TIMER_PERIODIC);
/* Set the inital count to the calibration */
lapic_write_reg(LAPIC_TIMER_INITIAL_CNT_REG, calibration);
free_lock(&lapic_timer_flag);
}
void apic_init(void){
asm("cli");
lapic_address = madt->lic_address + hhdmoffset;
lapic_ao_t *lapic_ao = (lapic_ao_t*) find_ics(0x5); // Local APIC Address Override
/* If there is a lapic address override present then use that instead */
if(lapic_ao){
/* Check that the field isnt 0 */
if(lapic_ao->lapic_address != 0){
lapic_address = lapic_ao->lapic_address + hhdmoffset;
}
}
/* Enable the lapic and set the spurious interrupt vector to 0xFF */
lapic_write_reg(LAPIC_SPURIOUS_REG, 0x1FF);
/* Initialize the IOAPIC */
ioapic_init();
/* Start the timers for calibration of the APIC timer */
timer_init();
/* Start the APIC timer with 10ms timer */
lapic_timer_init(10000);
asm("sti");
}
void ap_apic_init(){
asm("cli");
/* Enable the lapic and set the spurious interrupt vector to 0xFF */
lapic_write_reg(LAPIC_SPURIOUS_REG, 0x1FF);
/* Start the APIC timer */
lapic_timer_init(10000);
asm("sti");
}
void apic_timer_handler(){
lapic_write_reg(LAPIC_EOI_REG, 0);
if(get_cpu_struct_initialized()){
get_cpu_struct()->lapic_timer_ticks++;
}
}
void apic_send_ipi(uint8_t dest_field, uint8_t dest_shorthand, uint8_t trigger, uint8_t level, uint8_t status, uint8_t destination, uint8_t delivery_mode, uint8_t vector){
}

4
src/hal/apic.h Normal file
View file

@ -0,0 +1,4 @@
void apic_init(void);
void ap_apic_init();
void apic_sleep(int ms);

34
src/hal/gdt.asm Normal file
View file

@ -0,0 +1,34 @@
[bits 64]
default rel
extern gdtr
global s_load_gdt
s_load_gdt:
lgdt [gdtr]
; move kernel data offset into data registers
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
; zero the optional data registers
xor ax, ax
mov fs, ax
mov gs, ax
; pop the return instruction pointer from the stack
pop rax
; first push the segment selector we will far return to (0x08 is the code segment)
push 0x08
; then push the return instruction pointer
push rax
; and finally far return
retfq

33
src/hal/gdt.c Normal file
View file

@ -0,0 +1,33 @@
#include "gdt.h"
#include <stdio.h>
gdt_descriptor gdt[5] = {0};
gdt_register gdtr = {sizeof(gdt)-1, (uint64_t)(&gdt)};
extern void s_load_gdt();
void gdt_set_entry(int num, unsigned long long base, unsigned long long limit, unsigned char access, unsigned char granularity){
// descriptor base access
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
// descriptor limits
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
// granularity and access flag
gdt[num].granularity |= (granularity & 0xF) << 4;
gdt[num].access = access;
}
void set_gdt(void){
gdt_set_entry(0, 0, 0, 0, 0); // null segment offset 0x00
gdt_set_entry(1, 0, 0xFFFFF, 0x9A, 0xA); // kernel code offset 0x08
gdt_set_entry(2, 0, 0xFFFFF, 0x92, 0xA); // kernel data offset 0x10
gdt_set_entry(3, 0, 0xFFFFF, 0xFA, 0xA); // userspace code offset 0x18
gdt_set_entry(4, 0, 0xFFFFF, 0xF2, 0xA); // userspace data offset 0x20
s_load_gdt();
}

17
src/hal/gdt.h Normal file
View file

@ -0,0 +1,17 @@
#include <stdint.h>
typedef struct gdt_descriptor {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute((packed)) gdt_descriptor;
typedef struct gdt_register {
uint16_t limit;
uint64_t base_address;
} __attribute((packed)) gdt_register;
void set_gdt(void);

330
src/hal/idt.asm Normal file
View file

@ -0,0 +1,330 @@
default rel
extern interrupt_handler
extern idtr
global s_isr0
global s_isr1
global s_isr2
global s_isr3
global s_isr4
global s_isr5
global s_isr6
global s_isr7
global s_isr8
global s_isr9
global s_isr10
global s_isr11
global s_isr12
global s_isr13
global s_isr14
global s_isr15
global s_isr16
global s_isr17
global s_isr18
global s_isr19
global s_isr20
global s_isr21
global s_isr22
global s_isr23
global s_isr24
global s_isr25
global s_isr26
global s_isr27
global s_isr28
global s_isr29
global s_isr30
global s_isr31
global s_isr44
global s_isr69
global s_isr70
global s_isr255
global s_load_idt
s_isr0:
;
push qword 0 ; dummy
push qword 0 ; isr num
jmp isr_handler
s_isr1:
push qword 0 ; dummy
push qword 1 ; isr num
jmp isr_handler
s_isr2:
push qword 0 ; dummy
push qword 2 ; isr num
jmp isr_handler
s_isr3:
push qword 0 ; dummy
push qword 3 ; isr num
jmp isr_handler
s_isr4:
push qword 0 ; dummy
push qword 4 ; isr num
jmp isr_handler
s_isr5:
push qword 0 ; dummy
push qword 5 ; isr num
jmp isr_handler
s_isr6:
push qword 0 ; dummy
push qword 6 ; isr num
jmp isr_handler
s_isr7:
push qword 0 ; dummy
push qword 7 ; isr num
jmp isr_handler
s_isr8:
; dont push dummy as it already pushes one
push qword 8 ; isr num
jmp isr_handler
s_isr9:
push qword 0 ; dummy
push qword 9 ; isr num
jmp isr_handler
s_isr10:
; dont push dummy as it already pushes one
push qword 10 ; isr num
jmp isr_handler
s_isr11:
; dont push dummy as it already pushes one
push qword 11 ; isr num
jmp isr_handler
s_isr12:
; dont push dummy as it already pushes one
push qword 12 ; isr num
jmp isr_handler
s_isr13:
; dont push dummy as it already pushes one
push qword 13 ; isr num
jmp isr_handler
s_isr14:
; dont push dummy as it already pushes one
push qword 14 ; isr num
jmp isr_handler
s_isr15:
push qword 0 ; dummy
push qword 15 ; isr num
jmp isr_handler
s_isr16:
push qword 0 ; dummy
push qword 16 ; isr num
jmp isr_handler
s_isr17:
push qword 0 ; dummy
push qword 17 ; isr num
jmp isr_handler
s_isr18:
push qword 0 ; dummy
push qword 18 ; isr num
jmp isr_handler
; 19: Reserved
s_isr19:
push qword 0
push qword 19
jmp isr_handler
; 20: Reserved
s_isr20:
push qword 0
push qword 20
jmp isr_handler
; 21: Reserved
s_isr21:
push qword 0
push qword 21
jmp isr_handler
; 22: Reserved
s_isr22:
push qword 0
push qword 22
jmp isr_handler
; 23: Reserved
s_isr23:
push qword 0
push qword 23
jmp isr_handler
; 24: Reserved
s_isr24:
push qword 0
push qword 24
jmp isr_handler
; 25: Reserved
s_isr25:
push qword 0
push qword 25
jmp isr_handler
; 26: Reserved
s_isr26:
push qword 0
push qword 26
jmp isr_handler
; 27: Reserved
s_isr27:
push qword 0
push qword 27
jmp isr_handler
; 28: Reserved
s_isr28:
push qword 0
push qword 28
jmp isr_handler
; 29: Reserved
s_isr29:
push qword 0
push qword 29
jmp isr_handler
; 30: Reserved
s_isr30:
push qword 0
push qword 30
jmp isr_handler
; 31: Reserved
s_isr31:
push qword 0
push qword 31
jmp isr_handler
s_isr44:
push qword 0
push qword 44
jmp isr_handler
; 69 - APIC timer
s_isr69:
push qword 0
push qword 69
jmp isr_handler
; 70 - Kernel panic
s_isr70:
push qword 0
push qword 70
jmp isr_handler
s_isr255:
push qword 0
push qword 255
jmp isr_handler
%macro pushaq 0
push rax
push rbx
push rcx
push rdx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
%endmacro
%macro popaq 0
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rdx
pop rcx
pop rbx
pop rax
%endmacro
isr_handler:
pushaq
mov rdi, rsp ; put stack frame as parameter for interrupt_handler
call interrupt_handler
popaq
add rsp, 16 ; remove vector and error code from the stack
iretq
s_load_idt:
lidt [idtr]
sti
ret

230
src/hal/idt.c Normal file
View file

@ -0,0 +1,230 @@
#include "idt.h"
#include "error.h"
#include "timer.h"
#include <stdio.h>
#include <lock.h>
#include <SFB25.h>
idt_descriptor idt[256] = {0};
idt_register idtr = {sizeof(idt)-1, (uint64_t)(&idt)};
/* Expand if needed */
#define MAX_IRQ 256
/* IRQ structure list, eventually restructure to support IRQs on multiple cores */
irq_t irq_list[MAX_IRQ] = {0};
extern void s_isr0();
extern void s_isr1();
extern void s_isr2();
extern void s_isr3();
extern void s_isr4();
extern void s_isr5();
extern void s_isr6();
extern void s_isr7();
extern void s_isr8();
extern void s_isr9();
extern void s_isr10();
extern void s_isr11();
extern void s_isr12();
extern void s_isr13();
extern void s_isr14();
extern void s_isr15();
extern void s_isr16();
extern void s_isr17();
extern void s_isr18();
extern void s_isr19();
extern void s_isr20();
extern void s_isr21();
extern void s_isr22();
extern void s_isr23();
extern void s_isr24();
extern void s_isr25();
extern void s_isr26();
extern void s_isr27();
extern void s_isr28();
extern void s_isr29();
extern void s_isr30();
extern void s_isr31();
extern void s_isr44();
extern void s_isr69();
extern void s_isr70();
extern void s_isr255();
extern void s_load_idt();
atomic_flag irq_register_lock = ATOMIC_FLAG_INIT;
/* Registers an IRQ with the specified vector. */
kstatus register_irq_vector(uint8_t vector, void *base, uint8_t flags){
acquire_lock(&irq_register_lock);
if(!irq_list[vector].in_use){
free_lock(&irq_register_lock);
return KERNEL_STATUS_ERROR;
}
set_idt_descriptor(vector, base, flags);
irq_list[vector].base = base;
irq_list[vector].in_use = true;
s_load_idt();
free_lock(&irq_register_lock);
return KERNEL_STATUS_SUCCESS;
}
/* Registers an IRQ and returns the vector */
int register_irq(void *base, uint8_t flags){
acquire_lock(&irq_register_lock);
for(size_t i = 0; i < MAX_IRQ; i++){
if(!irq_list[i].in_use) {
set_idt_descriptor(i, base, flags);
irq_list[i].base = base;
irq_list[i].in_use = true;
free_lock(&irq_register_lock);
s_load_idt();
return i;
}
}
free_lock(&irq_register_lock);
return -1;
}
void set_idt_descriptor(uint8_t vector, void *base, uint8_t flags){
idt[vector].offset_low = ((uint64_t)base & 0xffff);
idt[vector].segment_sel = 0x08; // kernel code segment
idt[vector].ist = 0;
idt[vector].attributes = flags;
idt[vector].offset_high = ((uint64_t)base >> 16) & 0xffff;
idt[vector].offset_higher = ((uint64_t)base >> 32) & 0xffffffff;
idt[vector].reserved = 0;
}
void set_idt(void){
/* Set all the reserved vectors as used */
for(size_t i = 0; i < 32; i++){
irq_list[i].in_use = true;
irq_list[i].base = NULL;
}
set_idt_descriptor(0, s_isr0, 0x8E);
set_idt_descriptor(1, s_isr1, 0x8E);
set_idt_descriptor(2, s_isr2, 0x8E);
set_idt_descriptor(3, s_isr3, 0x8E);
set_idt_descriptor(4, s_isr4, 0x8E);
set_idt_descriptor(5, s_isr5, 0x8E);
set_idt_descriptor(6, s_isr6, 0x8E);
set_idt_descriptor(7, s_isr7, 0x8E);
set_idt_descriptor(8, s_isr8, 0x8E);
set_idt_descriptor(9, s_isr9, 0x8E);
set_idt_descriptor(10, s_isr10, 0x8E);
set_idt_descriptor(11, s_isr11, 0x8E);
set_idt_descriptor(12, s_isr12, 0x8E);
set_idt_descriptor(13, s_isr13, 0x8E);
set_idt_descriptor(14, s_isr14, 0x8E);
set_idt_descriptor(15, s_isr15, 0x8E);
set_idt_descriptor(16, s_isr16, 0x8E);
set_idt_descriptor(17, s_isr17, 0x8E);
set_idt_descriptor(18, s_isr18, 0x8E);
set_idt_descriptor(19, s_isr19, 0x8E);
set_idt_descriptor(20, s_isr20, 0x8E);
set_idt_descriptor(21, s_isr21, 0x8E);
set_idt_descriptor(22, s_isr22, 0x8E);
set_idt_descriptor(23, s_isr23, 0x8E);
set_idt_descriptor(24, s_isr24, 0x8E);
set_idt_descriptor(25, s_isr25, 0x8E);
set_idt_descriptor(26, s_isr26, 0x8E);
set_idt_descriptor(27, s_isr27, 0x8E);
set_idt_descriptor(28, s_isr28, 0x8E);
set_idt_descriptor(29, s_isr29, 0x8E);
set_idt_descriptor(30, s_isr30, 0x8E);
set_idt_descriptor(31, s_isr31, 0x8E);
set_idt_descriptor(44, 0, 0x8E);
set_idt_descriptor(69, s_isr69, 0x8E);
set_idt_descriptor(70, s_isr70, 0x8E);
set_idt_descriptor(255, s_isr255, 0x8E);
s_load_idt();
}
char *exception_messages[] =
{
"Division Error",
"Debug",
"Non Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"Device not available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"x87 FPU Floating-point error",
"Alignment Check",
"Machine Check",
"SIMD Floating-point exception",
"Virtualization exception",
"Control Protection",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved"
};
void interrupt_handler(interrupt_frame *r){
if(r->int_no < 32){
kprintf("\nOh no! Received interrupt {d}, '{s}'. Below is the provided stack frame{n}{n}", r->int_no, exception_messages[r->int_no]);
kprintf("error code 0x{xn}", r->err);
kprintf("rax 0x{x} | rbx 0x{x} | rcx 0x{x} | rdx 0x{xn}", r->rax, r->rbx, r->rcx, r->rdx);
kprintf("rdi 0x{x} | rsi 0x{x} | rbp 0x{xn}", r->rdi, r->rsi, r->rbp);
kprintf("r8 0x{x} | r9 0x{x} | r10 0x{x} | r11 0x{x} | r12 0x{x} | r13 0x{x} | r14 0x{x} | r15 0x{xn}", r->r8, r->r9, r->r10, r->r11, r->r12, r->r13, r->r14, r->r15);
kprintf("rip 0x{x} | cs 0x{x} | ss 0x{x} | rsp 0x{x} | rflags 0x{xn}", r->rip, r->cs, r->ss, r->rsp, r->rflags);
kkill();
for(;;);
}
if(r->int_no == 255){
kprintf("hey");
}
if(r->int_no == 69){
apic_timer_handler();
}
if(r->int_no == 70){
for(;;){
asm("cli;hlt");
}
}
return;
}

37
src/hal/idt.h Normal file
View file

@ -0,0 +1,37 @@
#include "error.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct idt_descriptor {
uint16_t offset_low;
uint16_t segment_sel;
uint8_t ist;
uint8_t attributes;
uint16_t offset_high;
uint32_t offset_higher;
uint32_t reserved;
} __attribute((packed))idt_descriptor;
typedef struct idt_register {
uint16_t limit;
uint64_t base_address;
} __attribute((packed)) idt_register;
typedef struct interrupt_frame {
uint64_t r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbp, rdx, rcx, rbx, rax;
uint64_t int_no, err;
uint64_t rip, cs, rflags, rsp, ss;
} __attribute((packed)) interrupt_frame;
typedef struct irq_t {
void *base;
bool in_use;
}irq_t;
void set_idt_descriptor(uint8_t vector, void *base, uint8_t flags);
kstatus register_irq_vector(uint8_t vector, void *base, uint8_t flags);
int register_irq(void *base, uint8_t flags);
void set_idt(void);

62
src/hal/ioapic.c Normal file
View file

@ -0,0 +1,62 @@
#include <stdint.h>
#include <stdio.h>
#include <SFB25.h>
#include "../sys/acpi.h"
#include "error.h"
#define IOREGSEL 0x0
#define IOWIN 0x10
#define IOAPICID 0x0
#define IOAPICVER 0x1
#define IOAPICARB 0x2
#define IOREDTBL(x) (0x10 + (x * 2)) // 0-23 registers
extern uint64_t hhdmoffset;
extern madt_t *madt;
uint64_t ioapic_address;
void ioapic_write_reg(uint8_t reg, uint32_t data){
/* First we load IOREGSEL with the register we want to access */
*(uint32_t*)(ioapic_address + IOREGSEL) = (uint8_t) reg;
/* Then we write the data to the IOWIN register */
*(uint32_t*)(ioapic_address + IOWIN) = (uint32_t) data;
}
uint32_t ioapic_read_reg(uint8_t reg){
*(uint32_t*)(ioapic_address + IOREGSEL) = reg;
return *(uint32_t*)(ioapic_address + IOWIN);
}
void write_redir_entry(uint8_t reg, uint64_t data){
/* First write lower 32-bits of the data to the specified IOREDTBL register */
ioapic_write_reg(IOREDTBL(reg), (uint32_t)(data & 0xFFFFFFFF));
/* Then write the upper 32-bits */
ioapic_write_reg(IOREDTBL(reg)+1, (uint32_t)(data >> 32));
}
kstatus set_redir_entry(uint8_t pin, uint8_t vector, uint8_t delivery, uint8_t trigger, uint8_t destination_field, uint8_t destination_mode){
uint64_t data = ((uint64_t)destination_field << 56) | (uint64_t)trigger << 15 | (uint64_t)destination_mode << 11 | (uint64_t)delivery << 8 | vector;
write_redir_entry(pin, data);
return KERNEL_STATUS_SUCCESS;
}
void ioapic_init(void){
ioapic_t *ioapic = (ioapic_t*) find_ics(0x1);
if(!ioapic){
klog(LOG_ERROR, __func__, "IOAPIC ICS not found\n");
kkill();
}
ioapic_address = ioapic->ioapic_address + hhdmoffset;
}

13
src/hal/ioapic.h Normal file
View file

@ -0,0 +1,13 @@
#include "error.h"
#include <stdint.h>
void ioapic_init(void);
void write_redir_entry(uint8_t reg, uint64_t data);
kstatus set_redir_entry(uint8_t pin, uint8_t vector, uint8_t delivery, uint8_t trigger, uint8_t destination_field, uint8_t destination_mode);
#define IOREGSEL 0x0
#define IOWIN 0x10
#define IOAPICID 0x0
#define IOAPICVER 0x1
#define IOAPICARB 0x2
#define IOREDTBL(x) (0x10 + (x * 2)) // 0-23 registers

112
src/hal/smp.c Normal file
View file

@ -0,0 +1,112 @@
#include <limine.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <SFB25.h>
#include "gdt.h"
#include "smp.h"
#include "apic.h"
#include "idt.h"
#include "../mm/vmm.h"
#include <lock.h>
#include <io.h>
#include <string.h>
static volatile struct limine_smp_request smp_request = {
.id = LIMINE_SMP_REQUEST,
.revision = 0,
};
extern void s_load_idt();
extern void s_load_gdt();
extern uint64_t hhdmoffset;
/* Returns the CPU structure for this particular CPU */
cpu_state *get_cpu_struct(){
return (cpu_state*)rdmsr(GSBASE);
}
uint64_t get_cpu_count(){
return smp_request.response->cpu_count;
}
bool get_cpu_struct_initialized(){
if(rdmsr(GSBASE) < hhdmoffset){
return false;
}
return true;
}
atomic_flag ap_init_lock = ATOMIC_FLAG_INIT;
void ap_init(struct limine_smp_info *smp_info){
acquire_lock(&ap_init_lock);
/* Load the GDT */
s_load_gdt();
/* Load the IDT */
s_load_idt();
/* Set the CR3 context */
extern uint64_t *kernel_page_map;
vmm_set_ctx(kernel_page_map);
asm volatile(
"movq %%cr3, %%rax\n\
movq %%rax, %%cr3\n"
: : : "rax"
);
cpu_state *cpu_struct = (cpu_state*)kmalloc(sizeof(cpu_state));
memset(cpu_struct, 0, sizeof(cpu_state));
cpu_struct->lapic_id = smp_info->lapic_id;
wrmsr(KERNELGSBASE, (uint64_t)cpu_struct);
wrmsr(GSBASE, (uint64_t)cpu_struct);
/* Initialize APIC & APIC timer */
ap_apic_init();
free_lock(&ap_init_lock);
for(;;);
scheduler_init();
}
void smp_init(){
if(!smp_request.response){
klog(LOG_ERROR, __func__, "Failed to get SMP request");
kkill();
}
struct limine_smp_response *smp_response = smp_request.response;
kprintf("smp: {d} CPUs\n", smp_response->cpu_count);
for(uint64_t i = 0; i < smp_response->cpu_count; i++){
/* Pointer to smp_info is passed in RDI by Limine, so no need to pass any arguments here */
smp_response->cpus[i]->goto_address = &ap_init;
}
/* -- Setup CPU structure for BSP -- */
/* Allocate CPU structure */
cpu_state *cpu_struct = (cpu_state*)kmalloc(sizeof(cpu_state));
cpu_struct->lapic_id = smp_response->cpus[0]->lapic_id;
wrmsr(KERNELGSBASE, (uint64_t)cpu_struct);
wrmsr(GSBASE, (uint64_t)cpu_struct);
/* If one of the APs has halted, then halt the BSP */
extern bool kernel_killed;
if(kernel_killed == true){
kkill();
}
}

23
src/hal/smp.h Normal file
View file

@ -0,0 +1,23 @@
#include <stdbool.h>
#include <stdint.h>
#include "../scheduler/sched.h"
#pragma once
#define GSBASE 0xC0000101
#define KERNELGSBASE 0xC0000102
typedef struct cpu_state {
uint32_t lapic_id;
uint64_t lapic_timer_ticks;
proc process_list[PROC_MAX];
proc *current_process;
uint16_t process_count;
context scheduler_context;
}__attribute((packed))cpu_state;
void smp_init();
cpu_state *get_cpu_struct();
uint64_t get_cpu_count();
bool get_cpu_struct_initialized();

24
src/hal/timer.c Normal file
View file

@ -0,0 +1,24 @@
#include "../sys/acpi.h"
#include "../hal/ioapic.h"
#include "../hal/apic.h"
#include "../drivers/pmt.h"
#include "timer.h"
#include <stdio.h>
#include <SFB25.h>
/* Determines which timer will be used for calibration */
int calibration_timer = -1;
void timer_init(void){
if(pmt_init() == -1){
klog(LOG_INFO, __func__, "PMT Timer not found, falling back");
/* Fall back to PIT */
}else{
calibration_timer = PMT;
}
}
void sleep(int ms){
/* Eventually fix this */
apic_sleep(ms);
}

11
src/hal/timer.h Normal file
View file

@ -0,0 +1,11 @@
#include <stdint.h>
enum USABLE_TIMERS {
HPET = 0,
PMT,
PIT,
};
void timer_init(void);
void apic_timer_handler(void);
void sleep(int ms);

77
src/hal/tsc.c Normal file
View file

@ -0,0 +1,77 @@
#include <cpuid.h>
#include <stdio.h>
#include <stdint.h>
#include "error.h"
#include "../drivers/pmt.h"
uint32_t core_crystal_clock = 0;
void enable_tsc(){
asm(".intel_syntax noprefix\n\
mov rax, cr4\n\
or rax, 0b10\n\
mov cr4, rax\n\
.att_syntax prefix");
}
void disable_tsc(){
asm(".intel_syntax noprefix\n\
mov rax, cr4\n\
and rax, 0xFFFFFFFFFFFFFFFD\n\
mov cr4, rax\n\
.att_syntax prefix");
}
uint64_t read_tsc(){
uint32_t eax, edx;
asm("rdtsc" :"=a"(eax),"=d"(edx));
return ((uint64_t)edx << 32 | eax);
}
kstatus tsc_init(){
uint32_t edx, unused;
/* Check if there is an invariant TSC */
__get_cpuid(0x80000007, &unused, &unused, &unused, &edx);
if((edx & (1 << 8)) == 0){
return KERNEL_STATUS_ERROR;
}
kprintf("tsc: Invariant TSC found\n");
/* Get the core crystal clock so we can determine TSC speed */
__get_cpuid(0x15, &unused, &unused, &core_crystal_clock, &unused);
if(core_crystal_clock != 0){
kprintf("cpuid 15h supported!\n");
/* Make it so that it ticks every millisecond */
core_crystal_clock *= 1000;
}else{
/* Calibrate using the PMT */
enable_tsc();
uint32_t read1 = read_tsc();
pmt_delay(1000);
uint32_t read2 = read_tsc();
disable_tsc();
core_crystal_clock = read2 - read1;
}
kprintf("Core crystal clock: {d}\n", core_crystal_clock);
enable_tsc();
return KERNEL_STATUS_SUCCESS;
}
uint64_t tsc_get_timestamp(){
if(core_crystal_clock == 0){
return 0;
}
uint64_t read = read_tsc();
return read / core_crystal_clock;
}

6
src/hal/tsc.h Normal file
View file

@ -0,0 +1,6 @@
#include "error.h"
#include <stdint.h>
kstatus tsc_init();
uint64_t tsc_get_timestamp();