From bbfaa4645a5f9e3afae29998d816e06390eeb3ef Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 12:15:30 +0100 Subject: [PATCH 01/19] Add a should_invoke_scheduler method --- kernel/src/arch/Timer.cpp | 5 +++++ kernel/src/arch/Timer.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/src/arch/Timer.cpp b/kernel/src/arch/Timer.cpp index 35599f2c..42667e1c 100644 --- a/kernel/src/arch/Timer.cpp +++ b/kernel/src/arch/Timer.cpp @@ -126,4 +126,9 @@ namespace Timer boot_timestamp = bootloader_time_to_unix(bootboot.datetime); arch_init(); } +} + +bool should_invoke_scheduler() +{ + return (timer_ticks % ARCH_TIMER_FREQ) == 0; } \ No newline at end of file diff --git a/kernel/src/arch/Timer.h b/kernel/src/arch/Timer.h index c540631b..2cd462d1 100644 --- a/kernel/src/arch/Timer.h +++ b/kernel/src/arch/Timer.h @@ -34,4 +34,6 @@ namespace Timer void arch_init(); void init(); -} \ No newline at end of file +} + +bool should_invoke_scheduler(); \ No newline at end of file From 287c4ab060578e2d33a45ae2365af9348181766d Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 12:25:42 +0100 Subject: [PATCH 02/19] Run clang-format --- kernel/src/arch/Serial.cpp | 2 +- kernel/src/arch/x86_64/MMU.cpp | 2 +- kernel/src/boot/Init.cpp | 2 +- kernel/src/main.cpp | 2 +- kernel/src/video/TextConsole.cpp | 2 +- luna/src/Format.cpp | 2 +- luna/src/Units.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/src/arch/Serial.cpp b/kernel/src/arch/Serial.cpp index 1f0d59ea..7e6f5f98 100644 --- a/kernel/src/arch/Serial.cpp +++ b/kernel/src/arch/Serial.cpp @@ -1,6 +1,6 @@ #include "arch/Serial.h" -#include #include +#include #include namespace Serial diff --git a/kernel/src/arch/x86_64/MMU.cpp b/kernel/src/arch/x86_64/MMU.cpp index 7d76da52..25edd6ff 100644 --- a/kernel/src/arch/x86_64/MMU.cpp +++ b/kernel/src/arch/x86_64/MMU.cpp @@ -1,8 +1,8 @@ #include "arch/MMU.h" #include "memory/MemoryManager.h" +#include #include #include -#include #pragma GCC push_options #pragma GCC diagnostic ignored "-Wconversion" diff --git a/kernel/src/boot/Init.cpp b/kernel/src/boot/Init.cpp index 870d385e..64605a5b 100644 --- a/kernel/src/boot/Init.cpp +++ b/kernel/src/boot/Init.cpp @@ -4,8 +4,8 @@ #include "boot/bootboot.h" #include "memory/MemoryManager.h" #include "video/Framebuffer.h" -#include #include +#include extern const BOOTBOOT bootboot; diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6bc1c5b6..33f7410e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,8 +4,8 @@ #include "boot/Init.h" #include "config.h" #include "memory/MemoryManager.h" -#include #include +#include Result init() { diff --git a/kernel/src/video/TextConsole.cpp b/kernel/src/video/TextConsole.cpp index 80565daf..dc5f96a0 100644 --- a/kernel/src/video/TextConsole.cpp +++ b/kernel/src/video/TextConsole.cpp @@ -2,8 +2,8 @@ #include "boot/bootboot.h" #include "video/Framebuffer.h" #include -#include #include +#include #include extern const BOOTBOOT bootboot; diff --git a/luna/src/Format.cpp b/luna/src/Format.cpp index ccdde3ab..a74b0a38 100644 --- a/luna/src/Format.cpp +++ b/luna/src/Format.cpp @@ -2,8 +2,8 @@ #include #include #include -#include #include +#include extern "C" usize strlen(const char*); diff --git a/luna/src/Units.cpp b/luna/src/Units.cpp index d0362a75..0b64dd9e 100644 --- a/luna/src/Units.cpp +++ b/luna/src/Units.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include Result to_dynamic_unit(usize value, char* buffer, usize max) { From fd6a74e61c2758c8a775bba207f9c4c7dc1f9e43 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 12:26:09 +0100 Subject: [PATCH 03/19] CPU: Add an idle_loop method --- kernel/src/arch/CPU.h | 2 ++ kernel/src/arch/x86_64/CPU.cpp | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/kernel/src/arch/CPU.h b/kernel/src/arch/CPU.h index abf826e3..9d973a3d 100644 --- a/kernel/src/arch/CPU.h +++ b/kernel/src/arch/CPU.h @@ -13,6 +13,8 @@ namespace CPU [[noreturn]] void efficient_halt(); + void idle_loop(); + void switch_kernel_stack(u64 top); void enable_interrupts(); diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index e3ef18a2..8a9346fb 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -1,14 +1,14 @@ #include "arch/CPU.h" -#include "arch/x86_64/CPU.h" #include "Log.h" #include "arch/Timer.h" +#include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" #include +#include +#include #include #include #include -#include -#include extern "C" void enable_sse(); extern "C" void enable_write_protect(); @@ -430,6 +430,14 @@ namespace CPU goto loop; // Safeguard: if we ever wake up, start our low-power rest again } + void idle_loop() + { + asm volatile("sti"); + loop: + asm volatile("hlt"); + goto loop; + } + void switch_kernel_stack(u64 top) { task_state_segment.rsp[0] = top; From c657b302c9688d2dfb9667021b426db451ea28c4 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:31:40 +0100 Subject: [PATCH 04/19] Timer: add raw_ticks --- kernel/src/arch/Timer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/arch/Timer.cpp b/kernel/src/arch/Timer.cpp index 42667e1c..4ed1b780 100644 --- a/kernel/src/arch/Timer.cpp +++ b/kernel/src/arch/Timer.cpp @@ -58,6 +58,11 @@ namespace Timer timer_ticks++; } + usize raw_ticks() + { + return timer_ticks; + } + usize ticks() { return ticks_ms() / 1000; @@ -130,5 +135,6 @@ namespace Timer bool should_invoke_scheduler() { + // FIXME: Modulo is SLOW. We're calling this every tick. return (timer_ticks % ARCH_TIMER_FREQ) == 0; } \ No newline at end of file From 4fdd1d57f5b0979ca97565cf906e0b49992be967 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:32:07 +0100 Subject: [PATCH 05/19] Make build-debug.sh also add debug symbols to luna --- luna/CMakeLists.txt | 7 +++++++ tools/build-debug.sh | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/luna/CMakeLists.txt b/luna/CMakeLists.txt index b8e43f9e..a8af5718 100644 --- a/luna/CMakeLists.txt +++ b/luna/CMakeLists.txt @@ -7,6 +7,7 @@ set(FREESTANDING_SOURCES src/Units.cpp src/SystemError.cpp src/Bitmap.cpp + src/Stack.cpp ) set(SOURCES @@ -41,4 +42,10 @@ target_compile_options(luna-freestanding PRIVATE -mno-red-zone) target_compile_options(luna-freestanding PRIVATE -mno-80387 -mno-mmx -mno-sse -mno-sse2) target_compile_definitions(luna-freestanding PUBLIC ARCH_X86_64) target_compile_definitions(luna PUBLIC ARCH_X86_64) +endif() + +if(LUNA_DEBUG_SYMBOLS) + message(STATUS "Building Luna with debug symbols") + target_compile_options(luna PRIVATE -ggdb) + target_compile_options(luna-freestanding PRIVATE -ggdb) endif() \ No newline at end of file diff --git a/tools/build-debug.sh b/tools/build-debug.sh index 85d83455..0fcb695e 100755 --- a/tools/build-debug.sh +++ b/tools/build-debug.sh @@ -9,7 +9,7 @@ tools/setup.sh tools/full-clean.sh -cmake -S . -B $BUILD_DIR -DMOON_DEBUG_SYMBOLS=ON -G "$CMAKE_GEN" +cmake -S . -B $BUILD_DIR -DMOON_DEBUG_SYMBOLS=ON -DLUNA_DEBUG_SYMBOLS=ON -G "$CMAKE_GEN" cmake --build $BUILD_DIR cmake --install $BUILD_DIR From d3458f2f0f4d49dbd623f33c07f56651e6956215 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:32:41 +0100 Subject: [PATCH 06/19] Bitmap: short circuit on 0-byte clears --- luna/src/Bitmap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/luna/src/Bitmap.cpp b/luna/src/Bitmap.cpp index 1928208e..a29fc947 100644 --- a/luna/src/Bitmap.cpp +++ b/luna/src/Bitmap.cpp @@ -62,6 +62,8 @@ void Bitmap::clear_region(usize start, usize bits, bool value) expect(initialized(), "Bitmap was never initialized"); expect((start + bits) <= size(), "Bitmap clear out of range"); + if (!bits) return; + // Set individual bits while not on a byte boundary. while ((start % 8) && bits--) { From f65ed465c486a3d5f9bb5f885de5c2691cd4b002 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:46:11 +0100 Subject: [PATCH 07/19] Bitmap: Fix crash by attempting to memset -1 bits (UINT64_MAX) --- luna/src/Bitmap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luna/src/Bitmap.cpp b/luna/src/Bitmap.cpp index a29fc947..7fb5302b 100644 --- a/luna/src/Bitmap.cpp +++ b/luna/src/Bitmap.cpp @@ -65,10 +65,11 @@ void Bitmap::clear_region(usize start, usize bits, bool value) if (!bits) return; // Set individual bits while not on a byte boundary. - while ((start % 8) && bits--) + while ((start % 8) && bits) { set(start, value); start++; + bits--; } // Clear out the rest in bytes. From 5d988c088f6c5cedd84659796fb6d856ad0c0b4f Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:46:56 +0100 Subject: [PATCH 08/19] Make idle_loop noreturn --- kernel/src/arch/CPU.h | 2 +- kernel/src/arch/x86_64/CPU.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/arch/CPU.h b/kernel/src/arch/CPU.h index 9d973a3d..0021fea8 100644 --- a/kernel/src/arch/CPU.h +++ b/kernel/src/arch/CPU.h @@ -13,7 +13,7 @@ namespace CPU [[noreturn]] void efficient_halt(); - void idle_loop(); + [[noreturn]] void idle_loop(); void switch_kernel_stack(u64 top); diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 8a9346fb..40e86dc6 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -430,7 +430,7 @@ namespace CPU goto loop; // Safeguard: if we ever wake up, start our low-power rest again } - void idle_loop() + [[noreturn]] void idle_loop() { asm volatile("sti"); loop: From 0bbd02666075435b316ffefdcd5e5b226d4ed8e7 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:48:08 +0100 Subject: [PATCH 09/19] CPU.h: pragma once --- kernel/src/arch/x86_64/CPU.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/src/arch/x86_64/CPU.h b/kernel/src/arch/x86_64/CPU.h index 9e81fd41..a06de7b9 100644 --- a/kernel/src/arch/x86_64/CPU.h +++ b/kernel/src/arch/x86_64/CPU.h @@ -1,3 +1,4 @@ +#pragma once #include struct Registers // Saved CPU registers for x86-64 From 8da55212737b5c7bfb252b76316f20d0578ed6c4 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:48:24 +0100 Subject: [PATCH 10/19] Add a Stack convenience class --- luna/include/luna/Stack.h | 23 +++++++++++++++++++++++ luna/src/Stack.cpp | 10 ++++++++++ 2 files changed, 33 insertions(+) create mode 100644 luna/include/luna/Stack.h create mode 100644 luna/src/Stack.cpp diff --git a/luna/include/luna/Stack.h b/luna/include/luna/Stack.h new file mode 100644 index 00000000..7870402c --- /dev/null +++ b/luna/include/luna/Stack.h @@ -0,0 +1,23 @@ +#pragma once +#include + +struct Stack +{ + Stack(u64 base, usize bytes); + + u64 bottom() + { + return m_base; + } + + u64 top(); + + usize bytes() + { + return m_bytes; + } + + private: + u64 m_base; + usize m_bytes; +}; \ No newline at end of file diff --git a/luna/src/Stack.cpp b/luna/src/Stack.cpp new file mode 100644 index 00000000..0c53e6f3 --- /dev/null +++ b/luna/src/Stack.cpp @@ -0,0 +1,10 @@ +#include + +Stack::Stack(u64 base, usize bytes) : m_base(base), m_bytes(bytes) +{ +} + +u64 Stack::top() +{ + return (m_base + m_bytes) - sizeof(void*); +} \ No newline at end of file From fbd290c01b1f84fc730659e01cdace04a3a95392 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 14:48:50 +0100 Subject: [PATCH 11/19] KernelVM: clean up a bit --- kernel/src/memory/KernelVM.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/kernel/src/memory/KernelVM.cpp b/kernel/src/memory/KernelVM.cpp index 0a4a445b..dbb4eb85 100644 --- a/kernel/src/memory/KernelVM.cpp +++ b/kernel/src/memory/KernelVM.cpp @@ -21,6 +21,7 @@ namespace KernelVM void init() { g_kernelvm_bitmap.initialize(bitmap_memory, sizeof(bitmap_memory)); + g_kernelvm_bitmap.clear(false); } Result alloc_one_page() @@ -36,7 +37,7 @@ namespace KernelVM return err(ENOMEM); } - Result alloc_several_pages(usize count) + bool find_several_pages_impl(usize count, u64& start_index) { u64 first_free_index = 0; u64 free_contiguous_pages = 0; @@ -56,12 +57,24 @@ namespace KernelVM // Found enough contiguous free pages!! if (free_contiguous_pages == count) { - g_used_vm += ARCH_PAGE_SIZE * count; - g_kernelvm_bitmap.clear_region(first_free_index, count, true); - return KERNEL_VM_RANGE_START + (first_free_index * ARCH_PAGE_SIZE); + start_index = first_free_index; + return true; } } + return false; + } + + Result 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); } From 12aa014a3dcb18ea6ef2d36816e9fbccbfb7d659 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:02:46 +0100 Subject: [PATCH 12/19] Add a Thread class which can be part of a DoublyLinkedList --- kernel/src/arch/x86_64/Thread.cpp | 49 +++++++++++++++++++++++++++++++ kernel/src/thread/Thread.cpp | 15 ++++++++++ kernel/src/thread/Thread.h | 46 +++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 kernel/src/arch/x86_64/Thread.cpp create mode 100644 kernel/src/thread/Thread.cpp create mode 100644 kernel/src/thread/Thread.h diff --git a/kernel/src/arch/x86_64/Thread.cpp b/kernel/src/arch/x86_64/Thread.cpp new file mode 100644 index 00000000..1abd34a0 --- /dev/null +++ b/kernel/src/arch/x86_64/Thread.cpp @@ -0,0 +1,49 @@ +#include "thread/Thread.h" +#include + +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)); +} \ No newline at end of file diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp new file mode 100644 index 00000000..5f834d6e --- /dev/null +++ b/kernel/src/thread/Thread.cpp @@ -0,0 +1,15 @@ +#include "thread/Thread.h" +#include + +static u64 g_next_id = 1; + +DoublyLinkedList g_threads; + +Result new_thread() +{ + Thread* thread = TRY(make()); + + thread->id = g_next_id++; + + return thread; +} \ No newline at end of file diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h new file mode 100644 index 00000000..3b3b7a39 --- /dev/null +++ b/kernel/src/thread/Thread.h @@ -0,0 +1,46 @@ +#pragma once + +#include "arch/CPU.h" +#include +#include + +#ifdef ARCH_X86_64 +#include "arch/x86_64/CPU.h" +#else +#error "Unknown architecture." +#endif + +struct Thread : public DoublyLinkedListNode +{ + 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 new_thread(); +Result create_idle_thread(); + +extern DoublyLinkedList g_threads; \ No newline at end of file From 13f5d09cfd5885f986cac25ba03774abed0c83b5 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:03:34 +0100 Subject: [PATCH 13/19] Add a Scheduler!! --- kernel/CMakeLists.txt | 3 + kernel/src/thread/Scheduler.cpp | 144 ++++++++++++++++++++++++++++++++ kernel/src/thread/Scheduler.h | 19 +++++ 3 files changed, 166 insertions(+) create mode 100644 kernel/src/thread/Scheduler.cpp create mode 100644 kernel/src/thread/Scheduler.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e20cca1c..5e8e03c6 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -12,6 +12,8 @@ set(SOURCES src/boot/Init.cpp src/arch/Serial.cpp src/arch/Timer.cpp + src/thread/Thread.cpp + src/thread/Scheduler.cpp ) if("${ARCH}" MATCHES "x86_64") @@ -22,6 +24,7 @@ if("${ARCH}" MATCHES "x86_64") src/arch/x86_64/MMU.cpp src/arch/x86_64/CPU.cpp src/arch/x86_64/Timer.cpp + src/arch/x86_64/Thread.cpp ) endif() diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp new file mode 100644 index 00000000..c4058e24 --- /dev/null +++ b/kernel/src/thread/Scheduler.cpp @@ -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 + +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 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 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 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 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); + } +} \ No newline at end of file diff --git a/kernel/src/thread/Scheduler.h b/kernel/src/thread/Scheduler.h new file mode 100644 index 00000000..95b12451 --- /dev/null +++ b/kernel/src/thread/Scheduler.h @@ -0,0 +1,19 @@ +#pragma once +#include "thread/Thread.h" + +namespace Scheduler +{ + void init(); + + Thread* current(); + + Result new_kernel_thread(u64 address); + Result new_kernel_thread(void (*func)(void)); + Result new_kernel_thread(void (*func)(void*), void* arg); + + Thread* pick_task(); + + void switch_task(Registers* regs); + + void invoke(Registers* regs); +} \ No newline at end of file From c907e163114735e9d18c24b0287e5a6d4deda598 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:04:02 +0100 Subject: [PATCH 14/19] x86_64: Invoke the scheduler every millisecond --- kernel/src/arch/x86_64/CPU.asm | 7 +------ kernel/src/arch/x86_64/CPU.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/kernel/src/arch/x86_64/CPU.asm b/kernel/src/arch/x86_64/CPU.asm index 59f4d68a..f3e2d2fc 100644 --- a/kernel/src/arch/x86_64/CPU.asm +++ b/kernel/src/arch/x86_64/CPU.asm @@ -48,12 +48,7 @@ load_tr: ltr ax ret -global switch_task -switch_task: - cli -.loop: - hlt - jmp .loop +extern switch_task global kernel_yield kernel_yield: diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 40e86dc6..6a9f8600 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -3,6 +3,7 @@ #include "arch/Timer.h" #include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" +#include "thread/Scheduler.h" #include #include #include @@ -336,6 +337,7 @@ extern "C" void arch_interrupt_entry(Registers* regs) else if (regs->isr == 32) { Timer::tick(); + if (should_invoke_scheduler()) Scheduler::invoke(regs); pic_eoi(regs); } else @@ -442,4 +444,10 @@ namespace CPU { task_state_segment.rsp[0] = top; } +} + +// called by kernel_yield +extern "C" void switch_task(Registers* regs) +{ + Scheduler::switch_task(regs); } \ No newline at end of file From f169718a4b550356a30a7d3a97b74238da55c96c Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:04:11 +0100 Subject: [PATCH 15/19] Initialize and demo the scheduler --- kernel/src/main.cpp | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 33f7410e..7016296d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,12 +1,35 @@ #include "Log.h" #include "arch/CPU.h" +#include "arch/Serial.h" #include "arch/Timer.h" #include "boot/Init.h" #include "config.h" #include "memory/MemoryManager.h" +#include "thread/Scheduler.h" #include #include +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 init() { kinfoln("Starting Moon %s", MOON_VERSION); @@ -17,10 +40,6 @@ Result init() Timer::init(); - CPU::platform_finish_init(); - - CPU::enable_interrupts(); - char buffer[64]; to_dynamic_unit(MemoryManager::total(), buffer, sizeof(buffer)); kinfoln("Total memory: %s", buffer); @@ -31,6 +50,20 @@ Result init() to_dynamic_unit(MemoryManager::reserved(), buffer, sizeof(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 {}; } @@ -40,5 +73,5 @@ extern "C" [[noreturn]] void _start() Init::early_init(); auto rc = init(); if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string()); - CPU::efficient_halt(); + CPU::idle_loop(); } \ No newline at end of file From 57517252d846c567e1daa036b4eb039a655ab132 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:14:58 +0100 Subject: [PATCH 16/19] Add a state to Thread --- kernel/src/arch/x86_64/Thread.cpp | 2 +- kernel/src/thread/Scheduler.cpp | 6 +++--- kernel/src/thread/Thread.h | 14 +++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/kernel/src/arch/x86_64/Thread.cpp b/kernel/src/arch/x86_64/Thread.cpp index 1abd34a0..7eb3160e 100644 --- a/kernel/src/arch/x86_64/Thread.cpp +++ b/kernel/src/arch/x86_64/Thread.cpp @@ -43,7 +43,7 @@ void Thread::set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4) void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs) { - if (!old_thread->is_idle) memcpy(&old_thread->regs, regs, sizeof(Registers)); + if (!old_thread->is_idle()) memcpy(&old_thread->regs, regs, sizeof(Registers)); memcpy(regs, &new_thread->regs, sizeof(Registers)); } \ No newline at end of file diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index c4058e24..04dd4e1f 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -18,7 +18,7 @@ namespace Scheduler g_idle.id = 0; g_idle.init_regs_kernel(); g_idle.set_ip((u64)CPU::idle_loop); - g_idle.is_idle = true; + g_idle.state = ThreadState::Idle; g_idle.ticks_left = 1; @@ -86,7 +86,7 @@ namespace Scheduler Thread* pick_task() { Thread* old = g_current; - if (old->is_idle) + if (old->is_idle()) { auto maybe_first = g_threads.last(); if (maybe_first.has_error()) // No threads!! @@ -111,7 +111,7 @@ namespace Scheduler { if (old_thread != new_thread) switch_context(old_thread, new_thread, regs); - if (new_thread->is_idle) + 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. diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 3b3b7a39..d63e58c4 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -10,6 +10,13 @@ #error "Unknown architecture." #endif +enum class ThreadState +{ + Idle, + Runnable, + Sleeping +}; + struct Thread : public DoublyLinkedListNode { Registers regs; @@ -22,7 +29,12 @@ struct Thread : public DoublyLinkedListNode u64 ticks_left; - bool is_idle = false; + ThreadState state = ThreadState::Runnable; + + bool is_idle() + { + return state == ThreadState::Idle; + } void init_regs_kernel(); void init_regs_user(); From 70497c37fb3adb2005de04af5b1d60dd58c4f628 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:17:20 +0100 Subject: [PATCH 17/19] Check for runnable threads --- kernel/src/thread/Scheduler.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 04dd4e1f..c925d356 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -88,22 +88,29 @@ namespace Scheduler Thread* old = g_current; if (old->is_idle()) { - auto maybe_first = g_threads.last(); - if (maybe_first.has_error()) // No threads!! + auto maybe_last = g_threads.last(); + if (maybe_last.has_error()) // No threads!! return &g_idle; - g_current = old = maybe_first.value(); + g_current = old = maybe_last.value(); } + bool has_found_thread = false; + 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. + if (g_current->state == ThreadState::Runnable) + { + has_found_thread = true; break; + } } while (g_current != old); + if (!has_found_thread) g_current = &g_idle; + return g_current; } From 757cee46934d5b50e769f0093e45bbcc25f3e4ce Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:21:50 +0100 Subject: [PATCH 18/19] Add accessors for when you're sure a linked list is not empty --- kernel/src/memory/Heap.cpp | 4 ++-- kernel/src/thread/Scheduler.cpp | 2 +- luna/include/luna/LinkedList.h | 10 ++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/kernel/src/memory/Heap.cpp b/kernel/src/memory/Heap.cpp index ad16eebe..e7dba4f2 100644 --- a/kernel/src/memory/Heap.cpp +++ b/kernel/src/memory/Heap.cpp @@ -178,7 +178,7 @@ Result kmalloc(usize size) heap.append(block); } - HeapBlock* block = heap.first().value(); + HeapBlock* block = heap.expect_first(); while (block) { // Trying to find a free block... @@ -330,7 +330,7 @@ void dump_heap_usage() } usize alloc_total = 0; usize alloc_used = 0; - HeapBlock* block = heap.first().value(); + HeapBlock* block = heap.expect_first(); while (block) { if (is_block_free(block)) diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index c925d356..9722afac 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -98,7 +98,7 @@ namespace Scheduler do { auto maybe_next = g_threads.next(g_current); - if (maybe_next.has_error()) g_current = g_threads.first().value(); + if (maybe_next.has_error()) g_current = g_threads.expect_first(); else g_current = maybe_next.value(); diff --git a/luna/include/luna/LinkedList.h b/luna/include/luna/LinkedList.h index 5e343b3d..698e5a9a 100644 --- a/luna/include/luna/LinkedList.h +++ b/luna/include/luna/LinkedList.h @@ -106,11 +106,21 @@ template class DoublyLinkedList return nonnull_or_error((T*)m_start_node); } + T* expect_first() + { + return first().value(); + } + Result last() { return nonnull_or_error((T*)m_end_node); } + T* expect_last() + { + return last().value(); + } + Result next(T* item) { return nonnull_or_error((T*)extract_node(item)->get_next()); From d93d95f23c8c65fa049e9f6b59f044dcfb46fd52 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 7 Dec 2022 15:55:58 +0100 Subject: [PATCH 19/19] Add sleeping mechanism --- kernel/src/arch/CPU.h | 4 +--- kernel/src/arch/x86_64/Thread.cpp | 1 + kernel/src/main.cpp | 29 ++++++++--------------------- kernel/src/thread/Scheduler.cpp | 19 +++++++++++++++++++ kernel/src/thread/Scheduler.h | 6 +++++- kernel/src/thread/Thread.h | 1 + 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/kernel/src/arch/CPU.h b/kernel/src/arch/CPU.h index 0021fea8..35159630 100644 --- a/kernel/src/arch/CPU.h +++ b/kernel/src/arch/CPU.h @@ -20,6 +20,4 @@ namespace CPU void enable_interrupts(); void disable_interrupts(); void wait_for_interrupt(); -} - -extern "C" void kernel_yield(); \ No newline at end of file +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/Thread.cpp b/kernel/src/arch/x86_64/Thread.cpp index 7eb3160e..4e1ffe86 100644 --- a/kernel/src/arch/x86_64/Thread.cpp +++ b/kernel/src/arch/x86_64/Thread.cpp @@ -31,6 +31,7 @@ void Thread::init_regs_kernel() memset(®s, 0, sizeof(Registers)); regs.cs = 0x08; regs.ss = 0x10; + regs.rflags = 1 << 9; // IF (Interrupt enable flag) } void Thread::set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 7016296d..80c7accc 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -9,24 +9,17 @@ #include #include -void print_in_loop() +void async_thread() { while (true) { - CPU::disable_interrupts(); - Serial::printf("%lu", Scheduler::current()->id); - CPU::enable_interrupts(); - } -} + Thread* current = Scheduler::current(); + kinfoln("Ticks: %lu, %lu user, %lu kernel, %lu idle", current->ticks, current->ticks_in_user, + current->ticks_in_kernel, Scheduler::idle()->ticks); -void print_one_and_then_yield() -{ - while (true) - { - CPU::disable_interrupts(); - Serial::printf("%lu", Scheduler::current()->id); - kernel_yield(); - CPU::enable_interrupts(); + CPU::wait_for_interrupt(); + + kernel_sleep(1000); } } @@ -52,13 +45,7 @@ Result init() 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)); + TRY(Scheduler::new_kernel_thread(async_thread)); CPU::platform_finish_init(); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 9722afac..c6b7fe90 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -39,6 +39,11 @@ namespace Scheduler return g_current; } + Thread* idle() + { + return &g_idle; + } + Result kernel_thread_alloc_stack_and_append_impl(Thread* thread) { // FIXME: We will leak the thread if VM allocation or alloc_at fail. @@ -146,6 +151,20 @@ namespace Scheduler g_current->ticks_left--; + g_threads.for_each([](Thread* thread) { + if (thread->state == ThreadState::Sleeping) + { + if (--thread->sleep_ticks_left == 0) thread->state = ThreadState::Runnable; + } + }); + if (!g_current->ticks_left) switch_task(regs); } +} + +void kernel_sleep(u64 ms) +{ + g_current->sleep_ticks_left = ms; + g_current->state = ThreadState::Sleeping; + kernel_yield(); } \ No newline at end of file diff --git a/kernel/src/thread/Scheduler.h b/kernel/src/thread/Scheduler.h index 95b12451..57bb85ad 100644 --- a/kernel/src/thread/Scheduler.h +++ b/kernel/src/thread/Scheduler.h @@ -6,6 +6,7 @@ namespace Scheduler void init(); Thread* current(); + Thread* idle(); Result new_kernel_thread(u64 address); Result new_kernel_thread(void (*func)(void)); @@ -16,4 +17,7 @@ namespace Scheduler void switch_task(Registers* regs); void invoke(Registers* regs); -} \ No newline at end of file +} + +extern "C" void kernel_yield(); +void kernel_sleep(u64 ms); \ No newline at end of file diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index d63e58c4..53e41a21 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -28,6 +28,7 @@ struct Thread : public DoublyLinkedListNode u64 ticks_in_kernel = 0; u64 ticks_left; + u64 sleep_ticks_left; ThreadState state = ThreadState::Runnable;