Skip to content

Commit

Permalink
kernel: implement a basic cooperative scheduler for now
Browse files Browse the repository at this point in the history
  • Loading branch information
matcool committed Aug 2, 2023
1 parent 26ece3c commit 593389a
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 6 deletions.
1 change: 1 addition & 0 deletions kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_executable(kernel
window/window.cpp
window/button.cpp
terminal/terminal.cpp
tasks/scheduler.cpp
)

target_link_libraries(kernel limine stl)
Expand Down
12 changes: 9 additions & 3 deletions kernel/idt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <kernel/idt.hpp>
#include <kernel/intrinsics.hpp>
#include <kernel/log.hpp>
#include <kernel/tasks/scheduler.hpp>
#include <stl/types.hpp>

using namespace kernel::interrupt;
Expand Down Expand Up @@ -131,7 +132,9 @@ static StringView get_interrupt_name(u64 idx) {
// regs - rdx
static void kernel_interrupt_handler(u64 which, u64 error_code, Registers* regs) {
if (which < kernel::PIC_IRQ_OFFSET) {
kdbgln("[INT] ({:#x}) {}, with error code {:#x}", which, get_interrupt_name(which), error_code);
kdbgln(
"\n[INT] ({:#x}) {}, with error code {:#x}", which, get_interrupt_name(which), error_code
);
const auto id = static_cast<InterruptId>(which);
if (id == InterruptId::PageFault) {
kdbgln(
Expand All @@ -152,6 +155,9 @@ static void kernel_interrupt_handler(u64 which, u64 error_code, Registers* regs)
kdbgln("rip - {:#x}", regs->rip);
kdbgln("rsp - {:#x}", regs->rsp);
halt();
} else if (which == kernel::tasks::SYSCALL_INTERRUPT_N) {
if (kernel::tasks::Scheduler::initialized())
kernel::tasks::Scheduler::get().handle_interrupt(regs);
} else {
if (which == kernel::PIC_IRQ_OFFSET + 0) {
kernel::pit::handle_interrupt();
Expand All @@ -161,7 +167,7 @@ static void kernel_interrupt_handler(u64 which, u64 error_code, Registers* regs)
kernel::ps2::handle_mouse();
} else {
kdbgln(
"[INT] ({:#x}) Unknown IRQ {}, error code {:#x}",
"\n[INT] ({:#x}) Unknown IRQ {}, error code {:#x}",
which,
which - kernel::PIC_IRQ_OFFSET,
error_code
Expand Down Expand Up @@ -217,7 +223,7 @@ void kernel::interrupt::init() {
CONST_FOR_EACH(
number,
idt_table[number] = IDTEntry(&raw_interrupt_handler<number>),
(0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16)
(0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, tasks::SYSCALL_INTERRUPT_N)
);

CONST_FOR_EACH(
Expand Down
39 changes: 37 additions & 2 deletions kernel/intrinsics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ inline void outb(u16 port, u8 value) {
asm volatile("outb %0, %1" : : "a"(value), "Nd"(port) : "memory");
}

[[gnu::noreturn]] inline void halt() {
asm("cli");
[[gnu::noreturn]] inline void halt(bool disable_interrupts = true) {
if (disable_interrupts) asm("cli");
while (true) {
asm("hlt");
}
Expand Down Expand Up @@ -41,4 +41,39 @@ inline u64 get_cr4() {
u64 value;
asm("movq %%cr4, %0" : "=r"(value));
return value;
}

// Disables interrupts
inline void cli() {
asm volatile("cli");
}

// Enables interrupts
inline void sti() {
asm volatile("sti");
}

inline uptr cpu_flags() {
uptr value;
asm volatile("pushf; pop %0" : "=rm"(value) : : "memory");
return value;
}

namespace kernel {

// A simple guard that will disable interrupts.
struct DisableInterruptsGuard {
bool ignore = false;

DisableInterruptsGuard() {
// if interrupts are already disabled, dont do anything
ignore = !(cpu_flags() & 0b1000000000);
cli();
}

~DisableInterruptsGuard() {
if (!ignore) sti();
}
};

}
5 changes: 4 additions & 1 deletion kernel/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <kernel/memory/paging.hpp>
#include <kernel/screen/framebuffer.hpp>
#include <kernel/serial.hpp>
#include <kernel/tasks/scheduler.hpp>

using namespace kernel;

Expand All @@ -30,5 +31,7 @@ extern "C" void kernel_init() {

kdbgln("Finished initialization");

framebuffer::loop();
tasks::Scheduler::get().init();

halt(false);
}
2 changes: 2 additions & 0 deletions kernel/screen/framebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <kernel/intrinsics.hpp>
#include <kernel/log.hpp>
#include <kernel/screen/framebuffer.hpp>
#include <kernel/tasks/scheduler.hpp>
#include <kernel/window/manager.hpp>
#include <limine/limine.h>
#include <stl/memory.hpp>
Expand Down Expand Up @@ -60,5 +61,6 @@ void kernel::framebuffer::loop() {
get_screen_framebuffer().paste(get_framebuffer(), 0, 0);
#endif
sleep(8);
kernel::tasks::yield_thread();
}
}
87 changes: 87 additions & 0 deletions kernel/tasks/scheduler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <kernel/intrinsics.hpp>
#include <kernel/log.hpp>
#include <kernel/memory/allocator.hpp>
#include <kernel/screen/framebuffer.hpp>
#include <kernel/tasks/scheduler.hpp>

using namespace kernel::tasks;

bool initialized = false;

bool Scheduler::initialized() {
return ::initialized;
}

Scheduler& Scheduler::get() {
DisableInterruptsGuard guard;
static Scheduler instance;
return instance;
}

void kernel::tasks::yield_thread() {
asm volatile("int %0;" : : "i"(SYSCALL_INTERRUPT_N));
}

void thread1_func() {
while (true) {
// kdbgln("hello from thread 1");
yield_thread();
}
halt(false);
}

void thread2_func() {
while (true) {
// kdbgln("hello from thread 2");
yield_thread();
}
halt(false);
}

void thread3_func() {
kernel::framebuffer::loop();
}

Thread create_thread(usize stack_pages, void (*function)()) {
Thread thread;
thread.stack = kernel::alloc::allocate_pages(stack_pages);
thread.state.rsp =
reinterpret_cast<uptr>(thread.stack) + kernel::PAGE_SIZE * stack_pages - sizeof(uptr);
*reinterpret_cast<uptr*>(thread.state.rsp) = 0;
thread.state.rip = reinterpret_cast<uptr>(function);
thread.state.cs = 0;
kdbgln("thread rsp={:#x}", thread.state.rsp);
return thread;
}

void Scheduler::init() {
m_threads.push(create_thread(1, &thread1_func));
m_threads.push(create_thread(1, &thread2_func));
m_threads.push(create_thread(3, &thread3_func));
::initialized = true;
yield_thread(); // jump into the scheduler interrupt
}

kernel::interrupt::Registers kernel_init_state;

void Scheduler::handle_interrupt(interrupt::Registers* regs) {
if (kernel_init_state.rip == 0) {
kernel_init_state = *regs;
}

if (m_threads.empty()) return;

if (!m_threads[m_active_idx].first_time) {
m_threads[m_active_idx].state = *regs;
}
const auto next_index = (m_active_idx + 1) % m_threads.size();
auto& next_thread = m_threads[next_index];
// silly
if (next_thread.first_time) {
next_thread.state.cs = kernel_init_state.cs;
next_thread.state.rflags = 0b1000000000; // interrupt enable flag
next_thread.first_time = false;
}
*regs = next_thread.state;
m_active_idx = next_index;
}
32 changes: 32 additions & 0 deletions kernel/tasks/scheduler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <kernel/idt.hpp>
#include <stl/types.hpp>
#include <stl/vector.hpp>

namespace kernel::tasks {

static constexpr u8 SYSCALL_INTERRUPT_N = 0x80;

void yield_thread();

struct Thread {
interrupt::Registers state;
void* stack = nullptr;
bool first_time = true;
};

class Scheduler {
Vector<Thread> m_threads;
usize m_active_idx = 0;

public:
static Scheduler& get();
static bool initialized();

void init();

void handle_interrupt(interrupt::Registers* regs);
};

}

0 comments on commit 593389a

Please sign in to comment.