diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 7a828f2..4ea973a 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -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) diff --git a/kernel/idt.cpp b/kernel/idt.cpp index d825eb0..78c405b 100644 --- a/kernel/idt.cpp +++ b/kernel/idt.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace kernel::interrupt; @@ -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(which); if (id == InterruptId::PageFault) { kdbgln( @@ -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(); @@ -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 @@ -217,7 +223,7 @@ void kernel::interrupt::init() { CONST_FOR_EACH( number, idt_table[number] = IDTEntry(&raw_interrupt_handler), - (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( diff --git a/kernel/intrinsics.hpp b/kernel/intrinsics.hpp index 4a63325..f55dcfd 100644 --- a/kernel/intrinsics.hpp +++ b/kernel/intrinsics.hpp @@ -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"); } @@ -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(); + } +}; + } \ No newline at end of file diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 0276929..ffd2cd1 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using namespace kernel; @@ -30,5 +31,7 @@ extern "C" void kernel_init() { kdbgln("Finished initialization"); - framebuffer::loop(); + tasks::Scheduler::get().init(); + + halt(false); } diff --git a/kernel/screen/framebuffer.cpp b/kernel/screen/framebuffer.cpp index 0e947b1..77fe657 100644 --- a/kernel/screen/framebuffer.cpp +++ b/kernel/screen/framebuffer.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -60,5 +61,6 @@ void kernel::framebuffer::loop() { get_screen_framebuffer().paste(get_framebuffer(), 0, 0); #endif sleep(8); + kernel::tasks::yield_thread(); } } \ No newline at end of file diff --git a/kernel/tasks/scheduler.cpp b/kernel/tasks/scheduler.cpp new file mode 100644 index 0000000..886a068 --- /dev/null +++ b/kernel/tasks/scheduler.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +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(thread.stack) + kernel::PAGE_SIZE * stack_pages - sizeof(uptr); + *reinterpret_cast(thread.state.rsp) = 0; + thread.state.rip = reinterpret_cast(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; +} \ No newline at end of file diff --git a/kernel/tasks/scheduler.hpp b/kernel/tasks/scheduler.hpp new file mode 100644 index 0000000..52b38c2 --- /dev/null +++ b/kernel/tasks/scheduler.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +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 m_threads; + usize m_active_idx = 0; + +public: + static Scheduler& get(); + static bool initialized(); + + void init(); + + void handle_interrupt(interrupt::Registers* regs); +}; + +} \ No newline at end of file