From 6a9d60a8fb1370326c9ae442c28c4a2ddfea2848 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 1 Feb 2023 01:53:35 +0200 Subject: [PATCH] Kernel: Implement somewhat functioning multithread support This still uses only a single cpu, but we can now have 'parallelization' This seems to work fine in qemu and bochs, but my own computer did not like this when I last tried. I have absolutely no idea how multithreading should actually be implmemented and I just thought and implemented the most simple one I could think of. This might not be in any way correct :D --- kernel/Makefile | 2 + kernel/include/kernel/PIT.h | 2 + kernel/include/kernel/Scheduler.h | 34 +++++++ kernel/include/kernel/TTY.h | 2 + kernel/include/kernel/Thread.h | 47 ++++++++++ kernel/kernel/PIT.cpp | 10 +- kernel/kernel/Scheduler.cpp | 150 ++++++++++++++++++++++++++++++ kernel/kernel/TTY.cpp | 4 + kernel/kernel/Thread.cpp | 53 +++++++++++ kernel/kernel/kernel.cpp | 34 ++++--- 10 files changed, 320 insertions(+), 18 deletions(-) create mode 100644 kernel/include/kernel/Scheduler.h create mode 100644 kernel/include/kernel/Thread.h create mode 100644 kernel/kernel/Scheduler.cpp create mode 100644 kernel/kernel/Thread.cpp diff --git a/kernel/Makefile b/kernel/Makefile index 35aaed4d24..c483f414e5 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -43,10 +43,12 @@ kernel/kmalloc.o \ kernel/PIC.o \ kernel/PIT.o \ kernel/RTC.o \ +kernel/Scheduler.o \ kernel/Serial.o \ kernel/Shell.o \ kernel/SpinLock.o \ kernel/SSP.o \ +kernel/Thread.o \ kernel/TTY.o \ kernel/VesaTerminalDriver.o \ icxxabi.o \ diff --git a/kernel/include/kernel/PIT.h b/kernel/include/kernel/PIT.h index dc7c879ddf..8999099fc7 100644 --- a/kernel/include/kernel/PIT.h +++ b/kernel/include/kernel/PIT.h @@ -2,6 +2,8 @@ #include +#define PIT_IRQ 0 + namespace PIT { diff --git a/kernel/include/kernel/Scheduler.h b/kernel/include/kernel/Scheduler.h new file mode 100644 index 0000000000..21fb3a4e7f --- /dev/null +++ b/kernel/include/kernel/Scheduler.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace Kernel +{ + + class Scheduler + { + BAN_NON_COPYABLE(Scheduler); + BAN_NON_MOVABLE(Scheduler); + + public: + static void Initialize(); + static Scheduler& Get(); + + const Thread& CurrentThread() const; + + void AddThread(void(*)()); + void Switch(); + void Start(); + + static constexpr size_t ms_between_switch = 4; + + private: + Scheduler() {} + + private: + BAN::LinkedList m_threads; + BAN::LinkedList::iterator m_current_iterator; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/TTY.h b/kernel/include/kernel/TTY.h index 932d0089f1..b1c27fd9e1 100644 --- a/kernel/include/kernel/TTY.h +++ b/kernel/include/kernel/TTY.h @@ -1,6 +1,7 @@ #pragma once #include +#include class TTY { @@ -51,4 +52,5 @@ private: Cell* m_buffer { nullptr }; AnsiState m_ansi_state; TerminalDriver* m_terminal_driver { nullptr }; + Kernel::SpinLock m_lock; }; diff --git a/kernel/include/kernel/Thread.h b/kernel/include/kernel/Thread.h new file mode 100644 index 0000000000..0fd57e517a --- /dev/null +++ b/kernel/include/kernel/Thread.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace Kernel +{ + + class Thread + { + BAN_NON_COPYABLE(Thread); + BAN_NON_MOVABLE(Thread); + + public: + enum class State + { + NotStarted, + Running, + Paused, + Done, + }; + + public: + Thread(void(*)()); + ~Thread(); + + uint32_t id() const { return m_id; } + + void set_rip(uintptr_t rip) { m_rip = rip; } + void set_rsp(uintptr_t rsp) { m_rsp = rsp; } + void set_state(State state) { m_state = state; } + uintptr_t rip() const { return m_rip; } + uintptr_t rsp() const { return m_rsp; } + State state() const { return m_state; } + + private: + static void on_exit(); + + private: + void* m_stack_base = nullptr; + State m_state = State::NotStarted; + uintptr_t m_rip = 0; + uintptr_t m_rsp = 0; + const uint32_t m_id = 0; + }; + + +} \ No newline at end of file diff --git a/kernel/kernel/PIT.cpp b/kernel/kernel/PIT.cpp index 35d8f42344..f80eeddfde 100644 --- a/kernel/kernel/PIT.cpp +++ b/kernel/kernel/PIT.cpp @@ -2,8 +2,7 @@ #include #include #include - -#define IRQ_TIMER 0 +#include #define TIMER0_CTL 0x40 #define TIMER1_CTL 0x41 @@ -26,9 +25,10 @@ namespace PIT { static uint64_t s_system_time = 0; - void clock_handle() + void irq_handler() { s_system_time++; + Kernel::Scheduler::Get().Switch(); } uint64_t ms_since_boot() @@ -45,9 +45,9 @@ namespace PIT IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff); IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff); - IDT::register_irq_handler(IRQ_TIMER, clock_handle); + IDT::register_irq_handler(PIT_IRQ, irq_handler); - InterruptController::Get().EnableIrq(IRQ_TIMER); + InterruptController::Get().EnableIrq(PIT_IRQ); } } diff --git a/kernel/kernel/Scheduler.cpp b/kernel/kernel/Scheduler.cpp new file mode 100644 index 0000000000..61babf4e5c --- /dev/null +++ b/kernel/kernel/Scheduler.cpp @@ -0,0 +1,150 @@ +#include +#include +#include + +namespace Kernel +{ + + static Scheduler* s_instance = nullptr; + + extern "C" uintptr_t read_rip(); + asm( + ".global read_rip;" + "read_rip:" +#if ARCH(x86_64) + "popq %rax;" + "jmp *%rax" +#else + "popl %eax;" + "jmp *%eax" +#endif + ); + + void Scheduler::Initialize() + { + ASSERT(!s_instance); + s_instance = new Scheduler(); + } + + Scheduler& Scheduler::Get() + { + ASSERT(s_instance); + return *s_instance; + } + + const Thread& Scheduler::CurrentThread() const + { + return *m_current_iterator; + } + + void Scheduler::AddThread(void(*thread)()) + { + MUST(m_threads.EmplaceBack(thread)); + if (!m_current_iterator) + m_current_iterator = m_threads.begin(); + } + + void Scheduler::Switch() + { + static uint8_t cnt = 0; + if (cnt++ % ms_between_switch) + return; + + ASSERT(InterruptController::Get().IsInService(PIT_IRQ)); + + ASSERT(m_threads.Size() > 0); + if (m_threads.Size() == 1) + return; + + ASSERT(m_current_iterator); + + auto next_iterator = m_current_iterator; + if (++next_iterator == m_threads.end()) + next_iterator = m_threads.begin(); + + Thread& current = *m_current_iterator; + Thread& next = *next_iterator; + + if (current.state() == Thread::State::Done) + { + // NOTE: this does not invalidate the next/next_iterator + // since we are working with linked list + m_threads.Remove(m_current_iterator); + m_current_iterator = decltype(m_threads)::iterator(); + } + + uintptr_t rip = read_rip(); + if (rip == 0) + return; + + uintptr_t rsp; +#if ARCH(x86_64) + asm volatile("movq %%rsp, %0" : "=r"(rsp)); +#else + asm volatile("movl %%esp, %0" : "=r"(rsp)); +#endif + + if (m_current_iterator) + { + current.set_rip(rip); + current.set_rsp(rsp); + current.set_state(Thread::State::Paused); + } + + m_current_iterator = next_iterator; + + if (next.state() == Thread::State::NotStarted) + { + InterruptController::Get().EOI(PIT_IRQ); + next.set_state(Thread::State::Running); + asm volatile( +#if ARCH(x86_64) + "movq %0, %%rsp;" +#else + "movl %0, %%esp;" +#endif + "sti;" + "jmp *%1;" + :: "r"(next.rsp()), "r"(next.rip()) + ); + } + else if (next.state() == Thread::State::Paused) + { + next.set_state(Thread::State::Running); + asm volatile( +#if ARCH(x86_64) + "movq %0, %%rsp;" + "movq $0, %%rax;" +#else + "movl %0, %%esp;" + "movl $0, %%eax;" +#endif + "jmp *%1;" + :: "r"(next.rsp()), "r"(next.rip()) + ); + } + + ASSERT(false); + } + + void Scheduler::Start() + { + ASSERT(m_threads.Size() > 0); + ASSERT(m_current_iterator); + + Thread& current = *m_current_iterator; + ASSERT(current.state() == Thread::State::NotStarted); + current.set_state(Thread::State::Running); + asm volatile( +#if ARCH(x86_64) + "movq %0, %%rsp;" +#else + "movl %0, %%esp;" +#endif + "sti;" + "jmp *%1;" + :: "r"(current.rsp()), "r"(current.rip()) + ); + } + +} \ No newline at end of file diff --git a/kernel/kernel/TTY.cpp b/kernel/kernel/TTY.cpp index 0d29b45645..1538931b69 100644 --- a/kernel/kernel/TTY.cpp +++ b/kernel/kernel/TTY.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include @@ -261,6 +263,8 @@ void TTY::PutCharAt(uint16_t ch, uint32_t x, uint32_t y) void TTY::PutChar(char ch) { + Kernel::LockGuard guard(m_lock); + uint16_t cp = handle_unicode(ch); if (cp == 0xFFFF) return; diff --git a/kernel/kernel/Thread.cpp b/kernel/kernel/Thread.cpp new file mode 100644 index 0000000000..5d99d6244f --- /dev/null +++ b/kernel/kernel/Thread.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +#define PAGE_SIZE 4096 + +namespace Kernel +{ + + static uint32_t s_next_id = 1; + + static constexpr size_t thread_stack_size = PAGE_SIZE; + + template + static void write_to_stack(uintptr_t& rsp, const T& value) + { + rsp -= sizeof(T); + *(T*)rsp = value; + } + + Thread::Thread(void(*function)()) + : m_id(s_next_id++) + { + m_stack_base = kmalloc(thread_stack_size, PAGE_SIZE); + ASSERT(m_stack_base); + + m_rip = (uintptr_t)function; + m_rsp = (uintptr_t)m_stack_base + thread_stack_size; + write_to_stack(m_rsp, this); + write_to_stack(m_rsp, &Thread::on_exit); + } + + Thread::~Thread() + { + kfree(m_stack_base); + } + + void Thread::on_exit() + { + Thread* thread = nullptr; +#if ARCH(x86_64) + asm volatile("movq (%%rsp), %0" : "=r"(thread)); +#else + asm volatile("movl (%%esp), %0" : "=r"(thread)); +#endif + thread->m_state = State::Done; + for (;;) asm volatile("hlt"); + } + +} \ No newline at end of file diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 467864bf8b..71579aecbb 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -55,8 +56,12 @@ ParsedCommandLine ParseCommandLine() return result; } +static TTY* tty1 = nullptr; + extern "C" void kernel_main() { + using namespace Kernel; + DISABLE_INTERRUPTS(); auto cmdline = ParseCommandLine(); @@ -82,26 +87,29 @@ extern "C" void kernel_main() TerminalDriver* terminal_driver = VesaTerminalDriver::Create(); ASSERT(terminal_driver); dprintln("VESA initialized"); - TTY* tty1 = new TTY(terminal_driver); + tty1 = new TTY(terminal_driver); InterruptController::Initialize(cmdline.force_pic); dprintln("Interrupt controller initialized"); - + PIT::initialize(); dprintln("PIT initialized"); if (!Input::initialize()) return; dprintln("8042 initialized"); - ENABLE_INTERRUPTS(); - - kprintln("Hello from the kernel!"); - - Kernel::Shell shell(tty1); - shell.Run(); - - for (;;) - { - asm("hlt"); - } + Scheduler::Initialize(); + Scheduler& scheduler = Scheduler::Get(); + scheduler.AddThread([](){ Shell(tty1).Run(); }); + scheduler.AddThread( + []() + { + uint64_t start = PIT::ms_since_boot(); + while (PIT::ms_since_boot() < start + 3000) + continue; + kprintln("\nHello!"); + } + ); + scheduler.Start(); + ASSERT(false); } \ No newline at end of file