2022-09-20 17:58:04 +00:00
|
|
|
#define MODULE "sched"
|
|
|
|
|
|
|
|
#include "thread/Scheduler.h"
|
2022-09-20 18:02:08 +00:00
|
|
|
#include "assert.h"
|
2022-09-20 17:58:04 +00:00
|
|
|
#include "interrupts/Interrupts.h"
|
|
|
|
#include "log/Log.h"
|
|
|
|
#include "memory/KernelMemoryManager.h"
|
|
|
|
#include "panic/hang.h"
|
|
|
|
#include "scheduling/PIT.h"
|
|
|
|
#include "std/string.h"
|
|
|
|
#include "thread/Task.h"
|
|
|
|
|
|
|
|
static Task tasks[32];
|
|
|
|
static uint64_t task_num = 0;
|
|
|
|
|
|
|
|
static Task idle_task;
|
|
|
|
|
|
|
|
static uint64_t free_tid = 0;
|
|
|
|
|
|
|
|
static Task* sched_current_task;
|
|
|
|
static Task* base_task;
|
|
|
|
static Task* end_task;
|
|
|
|
|
|
|
|
static void idle_task_function()
|
|
|
|
{
|
|
|
|
Interrupts::enable();
|
|
|
|
while (1) halt();
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t frequency;
|
|
|
|
|
|
|
|
void Scheduler::init()
|
|
|
|
{
|
|
|
|
memset(&idle_task, 0, sizeof(Task));
|
|
|
|
idle_task.id = free_tid++;
|
|
|
|
idle_task.regs.rip = (uint64_t)&idle_task_function;
|
|
|
|
idle_task.regs.rsp = (uint64_t)KernelMemoryManager::get_page();
|
|
|
|
idle_task.regs.cs = 0x08;
|
|
|
|
idle_task.regs.ds = 0x10;
|
|
|
|
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(idle_task.regs.rflags)::"%rax");
|
|
|
|
idle_task.regs.rflags |= 0x200;
|
|
|
|
idle_task.task_sleep = 1000;
|
|
|
|
|
|
|
|
base_task = &tasks[task_num++];
|
|
|
|
memset(base_task, 0, sizeof(Task));
|
|
|
|
end_task = base_task;
|
|
|
|
sched_current_task = base_task;
|
|
|
|
sched_current_task->id = free_tid++;
|
2022-09-20 18:02:08 +00:00
|
|
|
sched_current_task->task_time = 20; // gets 20 ms of cpu time before next switch
|
2022-09-20 17:58:04 +00:00
|
|
|
sched_current_task->next_task = nullptr;
|
|
|
|
// the other registers will be saved next task switch
|
|
|
|
|
|
|
|
frequency = 1000 / PIT::frequency();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scheduler::add_kernel_task(void (*task)(void))
|
|
|
|
{
|
|
|
|
if (task_num == 32) return; // FIXME: allow for dynamically allocated linked list instead of a fixed array of Tasks
|
|
|
|
Task* new_task = &tasks[task_num++];
|
|
|
|
memset(new_task, 0, sizeof(Task));
|
|
|
|
new_task->id = free_tid++;
|
|
|
|
new_task->regs.rip = (uint64_t)task;
|
|
|
|
new_task->regs.rsp = (uint64_t)KernelMemoryManager::get_pages(2); // 8 KB is enough for everyone, right?
|
|
|
|
new_task->regs.cs = 0x08;
|
|
|
|
new_task->regs.ds = 0x10;
|
|
|
|
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(new_task->regs.rflags)::"%rax");
|
2022-09-20 18:02:08 +00:00
|
|
|
new_task->regs.rflags |= 0x200; // enable interrupts
|
2022-09-20 17:58:04 +00:00
|
|
|
new_task->task_sleep = 0;
|
|
|
|
new_task->task_time = 0;
|
|
|
|
end_task->next_task = new_task;
|
|
|
|
end_task = new_task;
|
|
|
|
kinfoln("Adding task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, new_task->id,
|
|
|
|
new_task->regs.rsp, task_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sched_decrement_sleep_times()
|
|
|
|
{
|
|
|
|
Task* task = base_task;
|
|
|
|
while (task)
|
|
|
|
{
|
|
|
|
if (task->task_sleep > 0)
|
|
|
|
{
|
|
|
|
task->task_sleep -= frequency;
|
|
|
|
if (task->task_sleep < 0) task->task_sleep = 0;
|
|
|
|
}
|
|
|
|
task = task->next_task;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scheduler::task_tick(Context* context)
|
|
|
|
{
|
2022-09-20 18:02:08 +00:00
|
|
|
ASSERT(Interrupts::is_in_handler());
|
2022-09-20 17:58:04 +00:00
|
|
|
sched_decrement_sleep_times();
|
|
|
|
sched_current_task->task_time -= frequency;
|
|
|
|
if (sched_current_task->task_time < 0)
|
|
|
|
{
|
|
|
|
sched_current_task->task_time = 0;
|
|
|
|
task_yield(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scheduler::task_yield(Context* context)
|
|
|
|
{
|
2022-09-20 18:02:08 +00:00
|
|
|
ASSERT(Interrupts::is_in_handler());
|
2022-09-20 17:58:04 +00:00
|
|
|
get_context_to_task(*sched_current_task, context);
|
|
|
|
bool was_idle = false;
|
|
|
|
if (sched_current_task->id == 0) // idle task
|
|
|
|
{
|
|
|
|
sched_current_task = base_task;
|
|
|
|
was_idle = true;
|
|
|
|
}
|
|
|
|
Task* original_task = sched_current_task;
|
|
|
|
do {
|
|
|
|
sched_current_task = sched_current_task->next_task;
|
|
|
|
if (!sched_current_task) { sched_current_task = base_task; }
|
|
|
|
if (sched_current_task->task_sleep == 0)
|
|
|
|
{
|
2022-09-20 18:02:08 +00:00
|
|
|
sched_current_task->task_time = 20;
|
2022-09-20 17:58:04 +00:00
|
|
|
set_context_from_task(*sched_current_task, context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} while (sched_current_task != original_task);
|
|
|
|
if (original_task->task_sleep > 0)
|
|
|
|
{
|
|
|
|
sched_current_task = &idle_task;
|
2022-09-20 18:48:49 +00:00
|
|
|
sched_current_task->task_time = frequency;
|
2022-09-20 17:58:04 +00:00
|
|
|
if (!was_idle) { set_context_from_task(*sched_current_task, context); }
|
|
|
|
return;
|
|
|
|
}
|
2022-09-20 18:02:08 +00:00
|
|
|
original_task->task_time = 20; // grant 30 more ms, there is no other task available
|
2022-09-20 17:58:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scheduler::yield()
|
|
|
|
{
|
|
|
|
asm volatile("int $48");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scheduler::sleep(unsigned long ms)
|
|
|
|
{
|
|
|
|
current_task()->task_sleep = ms;
|
|
|
|
yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
Task* Scheduler::current_task()
|
|
|
|
{
|
|
|
|
return sched_current_task;
|
|
|
|
}
|