Compare commits
5 Commits
8c04788793
...
48fe2d4b04
Author | SHA1 | Date | |
---|---|---|---|
48fe2d4b04 | |||
c561b0b310 | |||
f97e392f89 | |||
ad11aa719e | |||
b9b7d1e201 |
@ -12,6 +12,8 @@ set(SOURCES
|
|||||||
src/boot/Init.cpp
|
src/boot/Init.cpp
|
||||||
src/arch/Serial.cpp
|
src/arch/Serial.cpp
|
||||||
src/arch/Timer.cpp
|
src/arch/Timer.cpp
|
||||||
|
src/thread/Thread.cpp
|
||||||
|
src/thread/Scheduler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if("${ARCH}" MATCHES "x86_64")
|
if("${ARCH}" MATCHES "x86_64")
|
||||||
@ -22,6 +24,7 @@ if("${ARCH}" MATCHES "x86_64")
|
|||||||
src/arch/x86_64/MMU.cpp
|
src/arch/x86_64/MMU.cpp
|
||||||
src/arch/x86_64/CPU.cpp
|
src/arch/x86_64/CPU.cpp
|
||||||
src/arch/x86_64/Timer.cpp
|
src/arch/x86_64/Timer.cpp
|
||||||
|
src/arch/x86_64/Thread.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -48,12 +48,7 @@ load_tr:
|
|||||||
ltr ax
|
ltr ax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
global switch_task
|
extern switch_task
|
||||||
switch_task:
|
|
||||||
cli
|
|
||||||
.loop:
|
|
||||||
hlt
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
global kernel_yield
|
global kernel_yield
|
||||||
kernel_yield:
|
kernel_yield:
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/CPU.h"
|
#include "arch/x86_64/CPU.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#include <luna/Check.h>
|
#include <luna/Check.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
@ -336,6 +337,7 @@ extern "C" void arch_interrupt_entry(Registers* regs)
|
|||||||
else if (regs->isr == 32)
|
else if (regs->isr == 32)
|
||||||
{
|
{
|
||||||
Timer::tick();
|
Timer::tick();
|
||||||
|
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
||||||
pic_eoi(regs);
|
pic_eoi(regs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -442,4 +444,10 @@ namespace CPU
|
|||||||
{
|
{
|
||||||
task_state_segment.rsp[0] = top;
|
task_state_segment.rsp[0] = top;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by kernel_yield
|
||||||
|
extern "C" void switch_task(Registers* regs)
|
||||||
|
{
|
||||||
|
Scheduler::switch_task(regs);
|
||||||
}
|
}
|
49
kernel/src/arch/x86_64/Thread.cpp
Normal file
49
kernel/src/arch/x86_64/Thread.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "thread/Thread.h"
|
||||||
|
#include <luna/String.h>
|
||||||
|
|
||||||
|
bool is_in_kernel(Registers* regs)
|
||||||
|
{
|
||||||
|
return regs->cs == 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::set_ip(u64 ip)
|
||||||
|
{
|
||||||
|
regs.rip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Thread::ip()
|
||||||
|
{
|
||||||
|
return regs.rip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::set_sp(u64 sp)
|
||||||
|
{
|
||||||
|
regs.rsp = sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Thread::sp()
|
||||||
|
{
|
||||||
|
return regs.rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::init_regs_kernel()
|
||||||
|
{
|
||||||
|
memset(®s, 0, sizeof(Registers));
|
||||||
|
regs.cs = 0x08;
|
||||||
|
regs.ss = 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4)
|
||||||
|
{
|
||||||
|
regs.rdi = arg1;
|
||||||
|
regs.rsi = arg2;
|
||||||
|
regs.rdx = arg3;
|
||||||
|
regs.rcx = arg4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||||
|
{
|
||||||
|
if (!old_thread->is_idle) memcpy(&old_thread->regs, regs, sizeof(Registers));
|
||||||
|
|
||||||
|
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
||||||
|
}
|
@ -1,12 +1,35 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
|
#include "arch/Serial.h"
|
||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "boot/Init.h"
|
#include "boot/Init.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/Units.h>
|
#include <luna/Units.h>
|
||||||
|
|
||||||
|
void print_in_loop()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
Serial::printf("%lu", Scheduler::current()->id);
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_one_and_then_yield()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
Serial::printf("%lu", Scheduler::current()->id);
|
||||||
|
kernel_yield();
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> init()
|
Result<void> init()
|
||||||
{
|
{
|
||||||
kinfoln("Starting Moon %s", MOON_VERSION);
|
kinfoln("Starting Moon %s", MOON_VERSION);
|
||||||
@ -17,10 +40,6 @@ Result<void> init()
|
|||||||
|
|
||||||
Timer::init();
|
Timer::init();
|
||||||
|
|
||||||
CPU::platform_finish_init();
|
|
||||||
|
|
||||||
CPU::enable_interrupts();
|
|
||||||
|
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
to_dynamic_unit(MemoryManager::total(), buffer, sizeof(buffer));
|
to_dynamic_unit(MemoryManager::total(), buffer, sizeof(buffer));
|
||||||
kinfoln("Total memory: %s", buffer);
|
kinfoln("Total memory: %s", buffer);
|
||||||
@ -31,6 +50,20 @@ Result<void> init()
|
|||||||
to_dynamic_unit(MemoryManager::reserved(), buffer, sizeof(buffer));
|
to_dynamic_unit(MemoryManager::reserved(), buffer, sizeof(buffer));
|
||||||
kinfoln("Reserved memory: %s", buffer);
|
kinfoln("Reserved memory: %s", buffer);
|
||||||
|
|
||||||
|
Scheduler::init();
|
||||||
|
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_in_loop));
|
||||||
|
TRY(Scheduler::new_kernel_thread(print_one_and_then_yield));
|
||||||
|
|
||||||
|
CPU::platform_finish_init();
|
||||||
|
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,5 +73,5 @@ extern "C" [[noreturn]] void _start()
|
|||||||
Init::early_init();
|
Init::early_init();
|
||||||
auto rc = init();
|
auto rc = init();
|
||||||
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
|
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
|
||||||
CPU::efficient_halt();
|
CPU::idle_loop();
|
||||||
}
|
}
|
@ -21,6 +21,7 @@ namespace KernelVM
|
|||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
g_kernelvm_bitmap.initialize(bitmap_memory, sizeof(bitmap_memory));
|
g_kernelvm_bitmap.initialize(bitmap_memory, sizeof(bitmap_memory));
|
||||||
|
g_kernelvm_bitmap.clear(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> alloc_one_page()
|
Result<u64> alloc_one_page()
|
||||||
@ -36,7 +37,7 @@ namespace KernelVM
|
|||||||
return err(ENOMEM);
|
return err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> alloc_several_pages(usize count)
|
bool find_several_pages_impl(usize count, u64& start_index)
|
||||||
{
|
{
|
||||||
u64 first_free_index = 0;
|
u64 first_free_index = 0;
|
||||||
u64 free_contiguous_pages = 0;
|
u64 free_contiguous_pages = 0;
|
||||||
@ -56,12 +57,24 @@ namespace KernelVM
|
|||||||
// Found enough contiguous free pages!!
|
// Found enough contiguous free pages!!
|
||||||
if (free_contiguous_pages == count)
|
if (free_contiguous_pages == count)
|
||||||
{
|
{
|
||||||
g_used_vm += ARCH_PAGE_SIZE * count;
|
start_index = first_free_index;
|
||||||
g_kernelvm_bitmap.clear_region(first_free_index, count, true);
|
return true;
|
||||||
return KERNEL_VM_RANGE_START + (first_free_index * ARCH_PAGE_SIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> alloc_several_pages(const usize count)
|
||||||
|
{
|
||||||
|
u64 start_index;
|
||||||
|
if (find_several_pages_impl(count, start_index))
|
||||||
|
{
|
||||||
|
g_kernelvm_bitmap.clear_region(start_index, count, true);
|
||||||
|
g_used_vm += ARCH_PAGE_SIZE * count;
|
||||||
|
return KERNEL_VM_RANGE_START + (start_index * ARCH_PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
return err(ENOMEM);
|
return err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
144
kernel/src/thread/Scheduler.cpp
Normal file
144
kernel/src/thread/Scheduler.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#include "thread/Scheduler.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "arch/CPU.h"
|
||||||
|
#include "arch/MMU.h"
|
||||||
|
#include "memory/KernelVM.h"
|
||||||
|
#include "memory/MemoryManager.h"
|
||||||
|
#include <luna/Stack.h>
|
||||||
|
|
||||||
|
static Thread g_idle;
|
||||||
|
static Thread* g_current = nullptr;
|
||||||
|
|
||||||
|
static const usize TICKS_PER_TIMESLICE = 20;
|
||||||
|
|
||||||
|
namespace Scheduler
|
||||||
|
{
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
g_idle.id = 0;
|
||||||
|
g_idle.init_regs_kernel();
|
||||||
|
g_idle.set_ip((u64)CPU::idle_loop);
|
||||||
|
g_idle.is_idle = true;
|
||||||
|
|
||||||
|
g_idle.ticks_left = 1;
|
||||||
|
|
||||||
|
// Map some stack for the idle task
|
||||||
|
u64 idle_stack_vm = KernelVM::alloc_one_page().release_value();
|
||||||
|
MemoryManager::alloc_at(idle_stack_vm, 1, MMU::NoExecute | MMU::ReadWrite).release_value();
|
||||||
|
|
||||||
|
Stack idle_stack{idle_stack_vm, ARCH_PAGE_SIZE};
|
||||||
|
g_idle.set_sp(idle_stack.top());
|
||||||
|
|
||||||
|
kinfoln("CREATED IDLE THREAD: id %lu with ip %lx and sp %lx", g_idle.id, g_idle.ip(), g_idle.sp());
|
||||||
|
|
||||||
|
g_current = &g_idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* current()
|
||||||
|
{
|
||||||
|
return g_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> kernel_thread_alloc_stack_and_append_impl(Thread* thread)
|
||||||
|
{
|
||||||
|
// FIXME: We will leak the thread if VM allocation or alloc_at fail.
|
||||||
|
u64 thread_stack_vm = TRY(KernelVM::alloc_several_pages(4));
|
||||||
|
TRY(MemoryManager::alloc_at(thread_stack_vm, 4, MMU::NoExecute | MMU::ReadWrite));
|
||||||
|
|
||||||
|
Stack thread_stack{thread_stack_vm, ARCH_PAGE_SIZE * 4};
|
||||||
|
thread->set_sp(thread_stack.top());
|
||||||
|
|
||||||
|
g_threads.append(thread);
|
||||||
|
|
||||||
|
kinfoln("CREATED THREAD: id %lu with ip %lx and sp %lx", thread->id, thread->ip(), thread->sp());
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> new_kernel_thread(u64 address)
|
||||||
|
{
|
||||||
|
Thread* thread = TRY(new_thread());
|
||||||
|
thread->init_regs_kernel();
|
||||||
|
thread->set_ip(address);
|
||||||
|
|
||||||
|
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> new_kernel_thread(void (*func)(void))
|
||||||
|
{
|
||||||
|
Thread* thread = TRY(new_thread());
|
||||||
|
thread->init_regs_kernel();
|
||||||
|
thread->set_ip((u64)func);
|
||||||
|
|
||||||
|
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> new_kernel_thread(void (*func)(void*), void* arg)
|
||||||
|
{
|
||||||
|
Thread* thread = TRY(new_thread());
|
||||||
|
thread->init_regs_kernel();
|
||||||
|
thread->set_ip((u64)func);
|
||||||
|
thread->set_arguments((u64)arg, 0, 0, 0);
|
||||||
|
|
||||||
|
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* pick_task()
|
||||||
|
{
|
||||||
|
Thread* old = g_current;
|
||||||
|
if (old->is_idle)
|
||||||
|
{
|
||||||
|
auto maybe_first = g_threads.last();
|
||||||
|
if (maybe_first.has_error()) // No threads!!
|
||||||
|
return &g_idle;
|
||||||
|
g_current = old = maybe_first.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto maybe_next = g_threads.next(g_current);
|
||||||
|
if (maybe_next.has_error()) g_current = g_threads.first().value();
|
||||||
|
else
|
||||||
|
g_current = maybe_next.value();
|
||||||
|
|
||||||
|
if (true) // FIXME: Check if the current task is runnable.
|
||||||
|
break;
|
||||||
|
} while (g_current != old);
|
||||||
|
|
||||||
|
return g_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generic_switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||||
|
{
|
||||||
|
if (old_thread != new_thread) switch_context(old_thread, new_thread, regs);
|
||||||
|
|
||||||
|
if (new_thread->is_idle)
|
||||||
|
{
|
||||||
|
new_thread->ticks_left = 1; // The idle task only runs for 1 tick so we can check for new runnable tasks
|
||||||
|
// as fast as possible.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
new_thread->ticks_left = TICKS_PER_TIMESLICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_task(Registers* regs)
|
||||||
|
{
|
||||||
|
Thread* old_thread = g_current;
|
||||||
|
Thread* new_thread = pick_task();
|
||||||
|
generic_switch_context(old_thread, new_thread, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke(Registers* regs)
|
||||||
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
|
g_current->ticks++;
|
||||||
|
|
||||||
|
if (is_in_kernel(regs)) g_current->ticks_in_kernel++;
|
||||||
|
else
|
||||||
|
g_current->ticks_in_user++;
|
||||||
|
|
||||||
|
g_current->ticks_left--;
|
||||||
|
|
||||||
|
if (!g_current->ticks_left) switch_task(regs);
|
||||||
|
}
|
||||||
|
}
|
19
kernel/src/thread/Scheduler.h
Normal file
19
kernel/src/thread/Scheduler.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "thread/Thread.h"
|
||||||
|
|
||||||
|
namespace Scheduler
|
||||||
|
{
|
||||||
|
void init();
|
||||||
|
|
||||||
|
Thread* current();
|
||||||
|
|
||||||
|
Result<void> new_kernel_thread(u64 address);
|
||||||
|
Result<void> new_kernel_thread(void (*func)(void));
|
||||||
|
Result<void> new_kernel_thread(void (*func)(void*), void* arg);
|
||||||
|
|
||||||
|
Thread* pick_task();
|
||||||
|
|
||||||
|
void switch_task(Registers* regs);
|
||||||
|
|
||||||
|
void invoke(Registers* regs);
|
||||||
|
}
|
15
kernel/src/thread/Thread.cpp
Normal file
15
kernel/src/thread/Thread.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "thread/Thread.h"
|
||||||
|
#include <luna/Alloc.h>
|
||||||
|
|
||||||
|
static u64 g_next_id = 1;
|
||||||
|
|
||||||
|
DoublyLinkedList<Thread> g_threads;
|
||||||
|
|
||||||
|
Result<Thread*> new_thread()
|
||||||
|
{
|
||||||
|
Thread* thread = TRY(make<Thread>());
|
||||||
|
|
||||||
|
thread->id = g_next_id++;
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
46
kernel/src/thread/Thread.h
Normal file
46
kernel/src/thread/Thread.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arch/CPU.h"
|
||||||
|
#include <luna/LinkedList.h>
|
||||||
|
#include <luna/Result.h>
|
||||||
|
|
||||||
|
#ifdef ARCH_X86_64
|
||||||
|
#include "arch/x86_64/CPU.h"
|
||||||
|
#else
|
||||||
|
#error "Unknown architecture."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Thread : public DoublyLinkedListNode<Thread>
|
||||||
|
{
|
||||||
|
Registers regs;
|
||||||
|
|
||||||
|
u64 id;
|
||||||
|
|
||||||
|
u64 ticks = 0;
|
||||||
|
u64 ticks_in_user = 0;
|
||||||
|
u64 ticks_in_kernel = 0;
|
||||||
|
|
||||||
|
u64 ticks_left;
|
||||||
|
|
||||||
|
bool is_idle = false;
|
||||||
|
|
||||||
|
void init_regs_kernel();
|
||||||
|
void init_regs_user();
|
||||||
|
|
||||||
|
void set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4);
|
||||||
|
|
||||||
|
void set_ip(u64 ip);
|
||||||
|
u64 ip();
|
||||||
|
|
||||||
|
void set_sp(u64 sp);
|
||||||
|
u64 sp();
|
||||||
|
};
|
||||||
|
|
||||||
|
void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs);
|
||||||
|
|
||||||
|
bool is_in_kernel(Registers* regs);
|
||||||
|
|
||||||
|
Result<Thread*> new_thread();
|
||||||
|
Result<Thread*> create_idle_thread();
|
||||||
|
|
||||||
|
extern DoublyLinkedList<Thread> g_threads;
|
Loading…
Reference in New Issue
Block a user