Files
RockOS/kernel/scheduler.c

156 lines
3.7 KiB
C

#include <lib/tasks.h>
#include <lib/memory.h>
#include <memory/mm.h>
#include <stddef.h>
#include "common.h"
#include "process.h"
#include "scheduler.h"
static process_t* process_table;
static int current_process = 0;
static int total_processes = 0;
#define IDLE_TASK_PID 1
static void idle_task() {
while (1) asm volatile("hlt");
}
static void lock_scheduler() {
disable_interrupts();
}
static void unlock_scheduler() {
enable_interrupts();
}
// called from isr and yield, interrupts are already disabled and re enabled in the isr/yield func
uint32_t switch_context(uint32_t current_esp) {
process_table[current_process].esp = current_esp;
for (int i = 0; i < total_processes; i++) {
if (process_table[i].state == STATE_SLEEPING) {
if (process_table[i].sleep > 0) {
process_table[i].sleep--;
}
if (process_table[i].sleep == 0) {
process_table[i].state = STATE_READY;
}
}
}
int next_process = current_process;
int found_ready = 0;
while (1) {
next_process = (next_process + 1) % total_processes;
if (next_process != IDLE_TASK_PID && process_table[next_process].state == STATE_READY) {
found_ready = 1;
break;
}
if (next_process == current_process) {
break;
}
}
if (!found_ready) {
if (process_table[current_process].state == STATE_READY) {
next_process = current_process;
} else {
next_process = IDLE_TASK_PID;
}
}
current_process = next_process;
if (fetch_cr3() != process_table[current_process].cr3) {
extern void load_pd(uint32_t table);
load_pd(process_table[current_process].cr3);
}
return process_table[current_process].esp;
}
void init_scheduler() {
process_table = kalloc(16 * sizeof(process_t));
total_processes = 0;
process_t* process = &process_table[total_processes];
process->pid = total_processes;
process->esp = 0;
process->cr3 = fetch_cr3();
process->state = STATE_READY;
process->sleep = 0;
total_processes++;
create_task((uint32_t)idle_task, fetch_cr3());
current_process = 0;
}
int create_task(uint32_t entry_point, uint32_t cr3) {
if (total_processes >= 16) {
return 0;
}
lock_scheduler();
// default to a new task, but if we find a dead one, re use it
process_t* process = &process_table[total_processes];
uint32_t pid = total_processes;
for (uint32_t i = 0; i < total_processes; i++) {
if (process_table[i].state == STATE_DEAD) {
process = &process_table[i];
pid = i;
}
}
process->pid = pid;
process->cr3 = cr3;
process->state = STATE_READY;
process->sleep = 0;
void* stack_bottom = kalloc(4096);
uint32_t* esp = (uint32_t*)((uint32_t)stack_bottom + 4096);
*(--esp) = 0x0202; // EFLAGS
*(--esp) = 0x08; // CS
*(--esp) = entry_point; // EIP
*(--esp) = 0; // EAX
*(--esp) = 0; // ECX
*(--esp) = 0; // EDX
*(--esp) = 0; // EBX
*(--esp) = 0; // ESP
*(--esp) = 0; // EBP
*(--esp) = 0; // ESI
*(--esp) = 0; // EDI
process->esp = (uint32_t)esp;
total_processes++;
unlock_scheduler();
return 1;
}
void sleep(uint32_t ms) {
if (ms == 0) return;
lock_scheduler();
process_table[current_process].sleep = ms;
process_table[current_process].state = STATE_SLEEPING;
yield();
unlock_scheduler();
}
void exit() {
lock_scheduler();
process_table[current_process].state = STATE_DEAD;
yield(); // yield forever
unlock_scheduler();
}