Espresso 0.0.2c
This commit is contained in:
177
files/idt.c
Normal file
177
files/idt.c
Normal file
@ -0,0 +1,177 @@
|
||||
#include <stdio.h>
|
||||
#include <port_io.h>
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <drivers/idt.h>
|
||||
|
||||
|
||||
#define IDT_MAX_DESCRIPTORS 256
|
||||
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
/*
|
||||
Most of the code is this file (and idt.h) are taken from the osdev wiki: https://wiki.osdev.org/Interrupts_Tutorial, though not all of it.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint16_t isr_low; // The lower 16 bits of the ISR's address
|
||||
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
|
||||
uint8_t reserved; // Set to zero
|
||||
uint8_t attributes; // Type and attributes; see the IDT page
|
||||
uint16_t isr_high; // The higher 16 bits of the ISR's address
|
||||
} __attribute__((packed)) idt_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed)) idtr_t;
|
||||
|
||||
__attribute__((aligned(0x10)))
|
||||
|
||||
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
|
||||
|
||||
static idtr_t idtr;
|
||||
|
||||
static bool vectors[IDT_MAX_DESCRIPTORS];
|
||||
|
||||
extern void* isr_stub_table[];
|
||||
|
||||
void idt_init(void)
|
||||
{
|
||||
idtr.base = (uintptr_t)&idt[0];
|
||||
idtr.limit = (uint16_t)sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
|
||||
|
||||
for (uint8_t vector = 0; vector < 32; vector++)
|
||||
{
|
||||
idt_set_descriptor(vector, isr_stub_table[vector], 0x8E);
|
||||
vectors[vector] = true;
|
||||
}
|
||||
|
||||
extern void* irq_stub_table[];
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
idt_set_descriptor(32 + i, irq_stub_table[i], 0x8E);
|
||||
}
|
||||
|
||||
|
||||
asm volatile ("lidt %0" : : "m"(idtr)); /* load the new IDT */
|
||||
asm volatile ("sti"); /* set the interrupt flag */
|
||||
}
|
||||
|
||||
void interrupt_dispatcher(registers_t* regs)
|
||||
{
|
||||
if (regs->int_no < 32)
|
||||
{
|
||||
exception_handler(regs);
|
||||
}
|
||||
else if (regs->int_no < 48)
|
||||
{
|
||||
uint32_t irq = regs->int_no - 32;
|
||||
irq_handler(irq, regs);
|
||||
|
||||
if (irq >= 8)
|
||||
{
|
||||
outb(0xA0, 0x20); /* acknowledge the IRQ to slave PIC */
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); /* acknowledge the IRQ to master PIC */
|
||||
}
|
||||
}
|
||||
|
||||
__noreturn
|
||||
void exception_handler(registers_t* regs)
|
||||
{
|
||||
uint32_t int_no = regs->int_no;
|
||||
uint32_t err_code = regs->err_code;
|
||||
|
||||
switch (int_no)
|
||||
{
|
||||
case 0:
|
||||
printf("Divide by zero exception (or other division error)\n");
|
||||
break;
|
||||
case 2:
|
||||
printf("NMI encountered\n");
|
||||
break;
|
||||
case 6: /* XXX: NOTE: this can be used to emulate instructions that do not exist on the current CPU :NOTE :XXX */
|
||||
printf("Invalid opcode encountered\n");
|
||||
break;
|
||||
case 7: /* XXX: NOTE: use this for FPU emulation and for saving/restoring FPU registers in a multiprocessing enviroment :NOTE :XXX */
|
||||
printf("FPU instructions used, but FPU is nonexistant/disabled\n");
|
||||
break;
|
||||
case 13:
|
||||
printf("General Protection Fault: err=0x%x at %p\n", err_code, regs->eip);
|
||||
break;
|
||||
case 14:
|
||||
{
|
||||
uint32_t cr2;
|
||||
asm volatile ("mov %%cr2, %0" : "=r"(cr2));
|
||||
printf("Page Fault at address: 0x%x, err=0x%x\n", cr2, err_code);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("Unhandled exception #%u, err=0x%x at %p\n", int_no, err_code, regs->eip);
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t cs, ds, es, ss;
|
||||
asm volatile ("mov %%cs, %0" : "=r"(cs));
|
||||
asm volatile ("mov %%ds, %0" : "=r"(ds));
|
||||
asm volatile ("mov %%es, %0" : "=r"(es));
|
||||
asm volatile ("mov %%ss, %0" : "=r"(ss));
|
||||
|
||||
printf("CS=0x%04x DS=0x%04x ES=0x%04x SS=0x%04x\n", cs, ds, es, ss);
|
||||
|
||||
asm volatile ("cli; hlt");
|
||||
|
||||
/* Will never be reached */
|
||||
while (true)
|
||||
{
|
||||
asm volatile ("hlt" ::: "memory");
|
||||
}
|
||||
}
|
||||
|
||||
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags)
|
||||
{
|
||||
idt_entry_t* descriptor = &idt[vector];
|
||||
|
||||
descriptor->isr_low = (uint32_t) isr & 0xFFFF;
|
||||
descriptor->kernel_cs = 0x08;
|
||||
descriptor->attributes = flags;
|
||||
descriptor->isr_high = (uint32_t) isr >> 16;
|
||||
descriptor->reserved = 0;
|
||||
}
|
||||
|
||||
void pic_remap(void)
|
||||
{
|
||||
uint8_t a1, a2;
|
||||
|
||||
/* save masks */
|
||||
a1 = inb(PIC1_DATA);
|
||||
a2 = inb(PIC2_DATA);
|
||||
|
||||
/* start initialization sequence (in cascade mode) */
|
||||
outb(PIC1_COMMAND, 0x11);
|
||||
outb(PIC2_COMMAND, 0x11);
|
||||
|
||||
/* set vector offset */
|
||||
outb(PIC1_DATA, 0x20); /* IRQs 0-7 mapped to IDT entries 0x20-0x27 (32–39) */
|
||||
outb(PIC2_DATA, 0x28); /* IRQs 8-15 mapped to IDT entries 0x28-0x2F (40–47) */
|
||||
|
||||
/* tell the master PIC about Slave PIC at IRQ2 (0000 0100) */
|
||||
outb(PIC1_DATA, 0x04);
|
||||
|
||||
/* tell the slave PIC its cascade identity (0000 0010) */
|
||||
outb(PIC2_DATA, 0x02);
|
||||
|
||||
/* set 8086/88 mode */
|
||||
outb(PIC1_DATA, 0x01);
|
||||
outb(PIC2_DATA, 0x01);
|
||||
|
||||
/* restore saved masks */
|
||||
outb(PIC1_DATA, a1);
|
||||
outb(PIC2_DATA, a2);
|
||||
}
|
||||
18
files/idt.h
Normal file
18
files/idt.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef _IDT_H
|
||||
#define _IDT_H
|
||||
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <types.h>
|
||||
|
||||
|
||||
void idt_init(void);
|
||||
|
||||
void pic_remap(void);
|
||||
|
||||
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);
|
||||
|
||||
void exception_handler(registers_t* regs);
|
||||
|
||||
|
||||
#endif
|
||||
69
files/irq.c
Normal file
69
files/irq.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include <stdio.h>
|
||||
#include <drivers/ps2_keyboard.h>
|
||||
#include <drivers/pit.h>
|
||||
#include <port_io.h>
|
||||
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#define MAX_IRQ_HANDLERS 128 /* the maximum number of irq handlers */
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t irq_no;
|
||||
irq_func_t func;
|
||||
} irq_handler_t;
|
||||
|
||||
uint32_t num_irq_handlers = 0;
|
||||
irq_handler_t handler_list[MAX_IRQ_HANDLERS];
|
||||
|
||||
static volatile uint32_t num_irqs_missed = 0;
|
||||
|
||||
void irq_init(void)
|
||||
{
|
||||
for (int i = 0; i < MAX_IRQ_HANDLERS; i++)
|
||||
{
|
||||
handler_list[i].irq_no = 0;
|
||||
handler_list[i].func = NULL;
|
||||
}
|
||||
set_irq_handler(0, (irq_func_t) pit_handler);
|
||||
set_irq_handler(1, (irq_func_t) ps2_keyboard_handler);
|
||||
}
|
||||
|
||||
void irq_handler(uint32_t irq, registers_t* regs)
|
||||
{
|
||||
uint8_t funcs_called = 0;
|
||||
|
||||
if (num_irq_handlers > 0)
|
||||
{
|
||||
for (unsigned int i = 0; i < num_irq_handlers; i++)
|
||||
{
|
||||
if (handler_list[i].irq_no == irq && handler_list[i].func != NULL)
|
||||
{
|
||||
handler_list[i].func(regs);
|
||||
funcs_called++;
|
||||
/* PIC IRQ acknowledgement happens in idt.c */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (funcs_called == 0)
|
||||
{
|
||||
num_irqs_missed++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_interrupts_missed(void)
|
||||
{
|
||||
return num_irqs_missed;
|
||||
}
|
||||
|
||||
void set_irq_handler(uint32_t num, irq_func_t handler)
|
||||
{
|
||||
if (num < MAX_IRQ_HANDLERS)
|
||||
{
|
||||
handler_list[num].irq_no = num;
|
||||
handler_list[num].func = handler;
|
||||
|
||||
num_irq_handlers++;
|
||||
}
|
||||
}
|
||||
43
files/irq.h
Normal file
43
files/irq.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef _IRQ_H
|
||||
#define _IRQ_H
|
||||
|
||||
#include <types.h>
|
||||
/*
|
||||
typedef struct registers {
|
||||
uint32_t ds, es, fs, gs;
|
||||
|
||||
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
|
||||
|
||||
uint32_t int_no;
|
||||
uint32_t err_code;
|
||||
|
||||
uint32_t eip, cs, eflags;
|
||||
} registers_t;
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint32_t ds, es, fs, gs;
|
||||
|
||||
uint32_t eax, ecx, edx, ebx;
|
||||
uint32_t esp, ebp, esi, edi;
|
||||
|
||||
uint32_t int_no;
|
||||
uint32_t err_code;
|
||||
|
||||
uint32_t eip, cs, eflags;
|
||||
} registers_t;
|
||||
|
||||
|
||||
typedef void (*irq_func_t)(registers_t*);
|
||||
|
||||
|
||||
void irq_init(void);
|
||||
|
||||
void irq_handler(uint32_t irq, registers_t* regs);
|
||||
|
||||
void set_irq_handler(uint32_t num, irq_func_t handler);
|
||||
/*void add_irq_handler(uint32_t num, irq_func_t* handler);*/
|
||||
|
||||
uint32_t get_interrupts_missed(void);
|
||||
|
||||
#endif
|
||||
154
files/isr.asm
Normal file
154
files/isr.asm
Normal file
@ -0,0 +1,154 @@
|
||||
[BITS 32]
|
||||
|
||||
[GLOBAL isr_stub_table]
|
||||
[GLOBAL irq_stub_table]
|
||||
[GLOBAL interrupt_entry]
|
||||
|
||||
extern interrupt_dispatcher
|
||||
|
||||
section .text
|
||||
|
||||
; ============================================================
|
||||
; COMMON INTERRUPT ENTRY
|
||||
; ============================================================
|
||||
|
||||
interrupt_entry:
|
||||
|
||||
; Stack already contains:
|
||||
; err_code
|
||||
; int_no
|
||||
; eip
|
||||
; cs
|
||||
; eflags
|
||||
|
||||
pusha ; eax, ecx, edx, ebx, esp, ebp, esi, edi
|
||||
|
||||
;push ds
|
||||
;push es
|
||||
;push fs
|
||||
;push gs
|
||||
|
||||
push gs
|
||||
push fs
|
||||
push es
|
||||
push ds
|
||||
|
||||
|
||||
mov ax, 0x10 ; load kernel data selector
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
push esp ; pass pointer to registers_t
|
||||
call interrupt_dispatcher
|
||||
add esp, 4
|
||||
|
||||
pop gs
|
||||
pop fs
|
||||
pop es
|
||||
pop ds
|
||||
|
||||
popa
|
||||
|
||||
add esp, 8 ; remove int_no + err_code
|
||||
|
||||
iret
|
||||
|
||||
|
||||
; ============================================================
|
||||
; IRQ STUBS
|
||||
; ============================================================
|
||||
|
||||
%macro IRQ 1
|
||||
irq_stub_%+%1:
|
||||
push dword 0 ; fake error code
|
||||
push dword (32 + %1) ; vector number
|
||||
jmp interrupt_entry
|
||||
%endmacro
|
||||
|
||||
IRQ 0
|
||||
IRQ 1
|
||||
IRQ 2
|
||||
IRQ 3
|
||||
IRQ 4
|
||||
IRQ 5
|
||||
IRQ 6
|
||||
IRQ 7
|
||||
IRQ 8
|
||||
IRQ 9
|
||||
IRQ 10
|
||||
IRQ 11
|
||||
IRQ 12
|
||||
IRQ 13
|
||||
IRQ 14
|
||||
IRQ 15
|
||||
|
||||
irq_stub_table:
|
||||
%assign i 0
|
||||
%rep 16
|
||||
dd irq_stub_%+i
|
||||
%assign i i+1
|
||||
%endrep
|
||||
|
||||
|
||||
; ============================================================
|
||||
; EXCEPTION STUBS
|
||||
; ============================================================
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
isr_stub_%+%1:
|
||||
push dword 0 ; fake error code
|
||||
push dword %1 ; interrupt number
|
||||
jmp interrupt_entry
|
||||
%endmacro
|
||||
|
||||
%macro ISR_ERR 1
|
||||
isr_stub_%+%1:
|
||||
push dword %1 ; interrupt number
|
||||
jmp interrupt_entry
|
||||
%endmacro
|
||||
|
||||
|
||||
; Exceptions 0–31
|
||||
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
ISR_NOERR 15
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
ISR_NOERR 20
|
||||
ISR_NOERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_ERR 30
|
||||
ISR_NOERR 31
|
||||
|
||||
isr_stub_table:
|
||||
%assign i 0
|
||||
%rep 32
|
||||
dd isr_stub_%+i
|
||||
%assign i i+1
|
||||
%endrep
|
||||
|
||||
164
files/kernel.c
Normal file
164
files/kernel.c
Normal file
@ -0,0 +1,164 @@
|
||||
/* Check if the compiler thinks you are targeting the wrong operating system. */
|
||||
#if defined(__linux__)
|
||||
#error "You are not using a cross-compiler, you will most certainly run into trouble"
|
||||
#endif
|
||||
|
||||
/* This tutorial will only work for the 32-bit ix86 targets. */
|
||||
#if !defined(__i386__)
|
||||
#error "This kernel needs to be compiled with a ix86-elf compiler"
|
||||
#endif
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <kernel/boot.h>
|
||||
#include <kernel/kshell.h>
|
||||
|
||||
#include <kdebug.h>
|
||||
|
||||
#include <gdt.h>
|
||||
#include <drivers/idt.h>
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <scheduler.h>
|
||||
|
||||
#include <multiboot.h>
|
||||
|
||||
#include <drivers/pci.h>
|
||||
#include <drivers/ps2_keyboard.h>
|
||||
#include <drivers/pit.h>
|
||||
/*#include <drivers/ahci.h>*/
|
||||
#include <drivers/ide.h>
|
||||
#include <mm/mm.h>
|
||||
#include <fs/fat32.h>
|
||||
/*#include <fs/duckfs.h>*/
|
||||
#include <fs/vfs.h>
|
||||
#include <fs/sfs.h>
|
||||
|
||||
#include <vector_extensions/sse.h>
|
||||
|
||||
#include <kernel/intro.h>
|
||||
|
||||
#include <builtin_games/miner.h>
|
||||
|
||||
#include <fs/ssfs.h>
|
||||
|
||||
|
||||
extern void _hang_asm(void);
|
||||
extern void _sti_asm(void);
|
||||
|
||||
void idle_task(void)
|
||||
{
|
||||
while (1)
|
||||
asm volatile("hlt");
|
||||
}
|
||||
|
||||
|
||||
void kernel_main(multiboot_info_t* mbd, uint32_t magic)
|
||||
{
|
||||
|
||||
/* --- BEGIN INITIALIZATION SECTION --- */
|
||||
|
||||
/* We need to initialize the terminal so that any error/debugging messages show. */
|
||||
terminal_initialize();
|
||||
|
||||
printf("Loading Espresso %s... ", KERNEL_VERSION);
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("[ DEBUG BUILD ]");
|
||||
#endif
|
||||
|
||||
printf("\n");
|
||||
|
||||
terminal_setcolor(VGA_COLOR_RED);
|
||||
|
||||
/* Make sure the magic number matches for memory mapping */
|
||||
if(magic != MULTIBOOT_BOOTLOADER_MAGIC)
|
||||
{
|
||||
printf("[ ERROR ] invalid magic number!\n");
|
||||
_hang_asm();
|
||||
}
|
||||
|
||||
/* Check bit 6 to see if we have a valid memory map */
|
||||
if(!(mbd->flags >> 6 & 0x1))
|
||||
{
|
||||
printf("[ ERROR ] invalid memory map given by GRUB bootloader\n");
|
||||
_hang_asm();
|
||||
}
|
||||
|
||||
gdt_install(false);
|
||||
|
||||
pic_remap(); /* must be done before idt_init() */
|
||||
|
||||
idt_init();
|
||||
|
||||
_sti_asm();
|
||||
|
||||
irq_init(); /* MUST be done after pic_remap() and idt_init() */
|
||||
|
||||
terminal_setcolor(VGA_COLOR_GREEN);
|
||||
|
||||
pmm_init(mbd);
|
||||
|
||||
paging_init();
|
||||
|
||||
heap_init(0xC2000000, 0x80000);
|
||||
|
||||
#ifdef _DEBUG
|
||||
printd("Testing SSE...\n");
|
||||
#endif
|
||||
int32_t sse_test_result = test_sse();
|
||||
if (sse_test_result != 0)
|
||||
{
|
||||
printf("[ SSE ] SSE test failed with RV %d\n", sse_test_result);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
printd("SSE test passed\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
pit_init();
|
||||
|
||||
keyboard_init();
|
||||
|
||||
ide_initialize();
|
||||
|
||||
pci_init();
|
||||
|
||||
scheduler_init();
|
||||
|
||||
/* --- END INITIALIZATION SECTION --- */
|
||||
|
||||
|
||||
terminal_setcolor(VGA_COLOR_LIGHT_GREEN);
|
||||
|
||||
printf("Guten tag and welcome to Espresso %s\n", KERNEL_VERSION);
|
||||
|
||||
//terminal_clear();
|
||||
|
||||
create_task(kshell_start);
|
||||
create_task(idle_task);
|
||||
|
||||
extern task_t* task_list;
|
||||
extern task_t* current_task;
|
||||
|
||||
current_task = task_list;
|
||||
|
||||
printf("in kernel_main\n");
|
||||
|
||||
/*extern void terminal_clear(void);
|
||||
|
||||
terminal_clear();
|
||||
|
||||
kshell_start(1, NULL);*/
|
||||
|
||||
for(;;) /* Loop infinitely. We only do something when an interrupt/syscall happens now. */
|
||||
{
|
||||
asm volatile("hlt" ::: "memory");
|
||||
}
|
||||
}
|
||||
62
files/pit.c
Normal file
62
files/pit.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <types.h>
|
||||
#include <port_io.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <scheduler.h>
|
||||
|
||||
#include <drivers/pit.h>
|
||||
|
||||
|
||||
#define PIT_CHANNEL0 0x40
|
||||
#define PIT_COMMAND 0x43
|
||||
#define PIT_IRQ_LINE 0
|
||||
#define PIT_FREQUENCY_BASE 1193182
|
||||
|
||||
volatile uint64_t pit_ticks = 0;
|
||||
bool pit_initialized = false;
|
||||
|
||||
void pit_init(void)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
printf("[ PIT ] Initializing the PIT...\n");
|
||||
#endif
|
||||
|
||||
uint16_t divisor = (uint16_t) 1193;
|
||||
|
||||
/* Send command byte */
|
||||
outb(PIT_COMMAND, 0x36); /* Channel 0, low/high byte, mode 3 (square wave), binary */
|
||||
|
||||
/* Send divisor low and high byte */
|
||||
outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xFF)); /* Low byte */
|
||||
outb(PIT_CHANNEL0, (uint8_t)((divisor >> 8) & 0xFF)); /* High byte */
|
||||
|
||||
pit_initialized = true;
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("[ PIT ] PIT Initialized\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void pit_handler(void)
|
||||
{
|
||||
pit_ticks++;
|
||||
}
|
||||
|
||||
/*void pit_sleep(uint64_t ms)
|
||||
{
|
||||
uint64_t target = pit_ticks + ms;
|
||||
while (pit_ticks < target)
|
||||
{
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
}*/
|
||||
|
||||
void pit_sleep(uint64_t ms)
|
||||
{
|
||||
extern task_t* current_task;
|
||||
|
||||
current_task->wakeup_tick = pit_ticks + ms;
|
||||
current_task->state = TASK_SLEEPING;
|
||||
|
||||
asm volatile("int $32"); /* force reschedule immediately */
|
||||
}
|
||||
158
files/scheduler.c
Normal file
158
files/scheduler.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <scheduler.h>
|
||||
|
||||
#define STACK_SIZE 4096
|
||||
|
||||
task_t* task_list = NULL;
|
||||
task_t* current_task = NULL;
|
||||
uint32_t next_task_id = 1;
|
||||
|
||||
static void schedule(void);
|
||||
|
||||
/* scheduler init */
|
||||
void scheduler_init(void)
|
||||
{
|
||||
set_irq_handler(0, (irq_func_t) scheduler_tick);
|
||||
}
|
||||
|
||||
static void schedule(void)
|
||||
{
|
||||
if (!task_list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* wake sleeping tasks */
|
||||
task_t* t = task_list;
|
||||
|
||||
extern uint64_t pit_ticks;
|
||||
|
||||
do
|
||||
{
|
||||
if (t->state == TASK_SLEEPING && pit_ticks >= t->wakeup_tick)
|
||||
{
|
||||
t->state = TASK_READY;
|
||||
}
|
||||
|
||||
t = t->next;
|
||||
}
|
||||
while (t != task_list);
|
||||
|
||||
/* find next ready task */
|
||||
task_t* start = current_task;
|
||||
|
||||
do
|
||||
{
|
||||
current_task = current_task->next;
|
||||
|
||||
if (current_task->state == TASK_READY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (current_task != start);
|
||||
}
|
||||
|
||||
|
||||
/* tick handler */
|
||||
void scheduler_tick(registers_t* regs)
|
||||
{
|
||||
if (!task_list)
|
||||
{
|
||||
printf("!task_list\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current_task)
|
||||
{
|
||||
printf("!current_task\n");
|
||||
current_task = task_list;
|
||||
}
|
||||
|
||||
/* save current task state */
|
||||
*(current_task->regs) = *regs;
|
||||
|
||||
/* pick next */
|
||||
schedule();
|
||||
|
||||
/* load next task state */
|
||||
*regs = *(current_task->regs);
|
||||
}
|
||||
|
||||
/* task creation */
|
||||
task_t* create_task(void (*entry)())
|
||||
{
|
||||
task_t* task = malloc(sizeof(task_t));
|
||||
|
||||
if (!task)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(task, 0, sizeof(task_t));
|
||||
|
||||
/* allocate stack */
|
||||
uint8_t* stack = malloc(STACK_SIZE);
|
||||
|
||||
if (!stack)
|
||||
{
|
||||
free(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("Creating task %i @ %p\n", next_task_id, entry);
|
||||
|
||||
/*
|
||||
align stack to 16 bytes for safety (SSE friendly).
|
||||
stack grows downward.
|
||||
*/
|
||||
uintptr_t stack_top = (uintptr_t)(stack + STACK_SIZE);
|
||||
stack_top &= ~0xF;
|
||||
|
||||
registers_t* regs = (registers_t*) (stack_top - sizeof(registers_t));
|
||||
memset(regs, 0, sizeof(registers_t));
|
||||
|
||||
/* initialize register frame */
|
||||
regs->eip = (uint32_t) entry;
|
||||
regs->cs = 0x08; /* Kernel code segment */
|
||||
regs->eflags = 0x202; /* IF=1 */
|
||||
|
||||
task->id = next_task_id++;
|
||||
task->regs = regs;
|
||||
task->stack_base = (uint32_t*) stack;
|
||||
task->state = TASK_READY;
|
||||
|
||||
/* insert into circular list */
|
||||
if (!task_list)
|
||||
{
|
||||
task_list = task;
|
||||
task->next = task;
|
||||
}
|
||||
else
|
||||
{
|
||||
task_t* last = task_list;
|
||||
while (last->next != task_list)
|
||||
{
|
||||
last = last->next;
|
||||
}
|
||||
|
||||
last->next = task;
|
||||
task->next = task_list;
|
||||
}
|
||||
|
||||
printf("Task %i @ %p created successfully\n", next_task_id - 1, entry);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/* task destruction */
|
||||
void destroy_task(task_t* task)
|
||||
{
|
||||
free(task->stack_base);
|
||||
free(task);
|
||||
}
|
||||
32
files/scheduler.h
Normal file
32
files/scheduler.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef _SCHEDULER_H
|
||||
#define _SCHEDULER_H
|
||||
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <types.h>
|
||||
|
||||
typedef enum {
|
||||
TASK_READY,
|
||||
TASK_RUNNING,
|
||||
TASK_SLEEPING,
|
||||
TASK_BLOCKED,
|
||||
TASK_TERMINATED,
|
||||
} task_state_t;
|
||||
|
||||
typedef struct task {
|
||||
uint32_t id;
|
||||
|
||||
registers_t* regs; /* pointer to saved register frame */
|
||||
uint32_t* stack_base; /* original malloc() pointer */
|
||||
|
||||
task_state_t state;
|
||||
struct task* next;
|
||||
|
||||
uint64_t wakeup_tick;
|
||||
} task_t;
|
||||
|
||||
void scheduler_init(void);
|
||||
task_t* create_task(void (*entry)());
|
||||
void scheduler_tick(registers_t* regs);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user