From f6ee1e572fc4a71591e7a66611248d923643bdb6 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Sat, 18 Feb 2023 22:10:27 +0100 Subject: [PATCH] feature (kernel): implemented a basic scheduler --- kernel/inc/proc/scheduler.h | 58 +++++++++++ kernel/inc/proc/thread.h | 32 ++++++ kernel/src/kmain.c | 45 ++++++++- kernel/src/platform/interrupts.c | 3 + kernel/src/platform/syscall.c | 2 +- kernel/src/proc/scheduler.c | 168 +++++++++++++++++++++++++++++++ kernel/src/utils/logger.c | 2 +- 7 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 kernel/inc/proc/scheduler.h create mode 100644 kernel/inc/proc/thread.h create mode 100644 kernel/src/proc/scheduler.c diff --git a/kernel/inc/proc/scheduler.h b/kernel/inc/proc/scheduler.h new file mode 100644 index 0000000..0557643 --- /dev/null +++ b/kernel/inc/proc/scheduler.h @@ -0,0 +1,58 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#ifndef NOX_SCHEDULER_H +#define NOX_SCHEDULER_H + +#include "proc/thread.h" +#include "utils/bitmap.h" + +#define SCHEDULER_MAX_PROCESSES 32 +#define SCHEDULER_MAX_THREADS_PER_PROCESS 16 + +typedef struct { + uint32_t max_threads; + uint32_t num_threads; + + thread_T* threads; + + bitmap_T threads_bitmap; + bitmap_T running_threads_bitmap; + + thread_T** queue; + uint32_t queue_index; + uint32_t queue_length; + uint32_t running_thread; + + bool blocked; + bool initialized; +} scheduler_T; + +void scheduler_init (); +cpu_state_T* scheduler_start (cpu_state_T* state); +bool scheduler_is_initialized (); + +uint32_t scheduler_register_thread (thread_T* thread); +void scheduler_pause_thread (uint32_t id); +void scheduler_start_thread (uint32_t id); +void scheduler_kill_thread (uint32_t id); +thread_T* scheduler_get_thread (uint32_t id); + +uint32_t scheduler_get_current_thread (); + +void scheduler_calculate_queue (); +cpu_state_T* scheduler_switch_context (cpu_state_T* state); + +#endif //NOX_SCHEDULER_H diff --git a/kernel/inc/proc/thread.h b/kernel/inc/proc/thread.h new file mode 100644 index 0000000..3090312 --- /dev/null +++ b/kernel/inc/proc/thread.h @@ -0,0 +1,32 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#ifndef NOX_THREAD_H +#define NOX_THREAD_H + +#include "utils/stdtypes.h" +#include "platform/cpu.h" + +typedef enum { + THREAD_NONE = -1 +} thread_standard_E; + +typedef struct { + uint32_t id; + cpu_state_T state; + uint64_t cpu_time; +} thread_T; + +#endif //NOX_THREAD_H diff --git a/kernel/src/kmain.c b/kernel/src/kmain.c index 8cfa1be..be3a401 100644 --- a/kernel/src/kmain.c +++ b/kernel/src/kmain.c @@ -21,6 +21,10 @@ #include "mm/page_frame.h" #include "mm/page_map.h" #include "drivers/time/pit.h" +#include "proc/scheduler.h" + +#include "utils/io.h" +#include "platform/gdt.h" void limine_terminal_print(boot_info_T* boot_info, string_t string) { boot_info->terminal->write(boot_info->terminal->terminals[0], string, string_length(string)); @@ -37,21 +41,54 @@ void kernel_init(boot_info_T* boot_info) { paging_init(); memory_allocator_init((void*)0x100000000000); -// scheduler_init(); + scheduler_init(); +} + +void test_a() { + while (true) { + io_out_byte(LOG_PORT, 'A'); + } +} + +void test_b() { + while (true) { + io_out_byte(LOG_PORT, 'B'); + } } void kmain(boot_info_T boot_info) { - limine_terminal_print(&boot_info, "Booting NoxOS..."); + limine_terminal_print(&boot_info, "Booting NoxOS...\n"); log(LOG_DEBUG, "Booting NoxOS"); kernel_init(&boot_info); + limine_terminal_print(&boot_info, "Kernel initialized\n"); log(LOG_INFO, "!=====[ Kernel Initialized ]=====!\n"); - asm("int $0x80"); + void* stack = memory_allocate(PFRAME_SIZE * 10); + thread_T test; + test.state = (cpu_state_T){ + .cr3 = (uint64_t)g_kernel_page_map, + .rax = 0, + .rbx = 0, + .rcx = 0, + .rdx = 0, + .rsi = 0, + .rdi = 0, + .rbp = (uint64_t)stack + (PFRAME_SIZE * 10), + .interrupt_id = 0, + .error_code = 0, + .rip = (uint64_t)test_b, + .cs = GDT_SELECTOR_KERNEL_CODE, + .flags = 1 << CPU_FLAG_INTERRUPT_ENABLE, + .rsp = (uint64_t)stack + (PFRAME_SIZE * 10), + .ss = GDT_SELECTOR_KERNEL_DATA + }; + scheduler_register_thread(&test); + scheduler_start_thread(test.id); -// void* debug = memory_allocate(1312); + test_a(); CORE_HALT_FOREVER } diff --git a/kernel/src/platform/interrupts.c b/kernel/src/platform/interrupts.c index 146e4f7..d76279a 100644 --- a/kernel/src/platform/interrupts.c +++ b/kernel/src/platform/interrupts.c @@ -21,6 +21,7 @@ #include "mm/page_frame.h" #include "utils/logger.h" #include "utils/io.h" +#include "proc/scheduler.h" idt_register_T g_idt_register; @@ -232,6 +233,7 @@ cpu_state_T* irq_handle(cpu_state_T* state, pic_irq_E irq) { switch (irq) { case IRQ_PIT: { + return_state = scheduler_switch_context(state); // log(LOG_DEBUG, "PIT -> Tick"); break; } @@ -246,6 +248,7 @@ cpu_state_T* irq_handle(cpu_state_T* state, pic_irq_E irq) { } cpu_state_T* interrupts_handle(cpu_state_T* state) { + log(LOG_DEBUG, "INT 0x%xb", state->interrupt_id); if (state->interrupt_id < EXCEPTIONS_ENUM_END) { return exception_handle(state); } diff --git a/kernel/src/platform/syscall.c b/kernel/src/platform/syscall.c index dd10fdb..dac7939 100644 --- a/kernel/src/platform/syscall.c +++ b/kernel/src/platform/syscall.c @@ -31,7 +31,7 @@ cpu_state_T* syscall_handle(cpu_state_T* state) { switch (state->rax) { case SYSCALL_KERNEL_SCHEDULER_START: { - scheduler_start(state); + return_state = scheduler_start(state); break; } diff --git a/kernel/src/proc/scheduler.c b/kernel/src/proc/scheduler.c new file mode 100644 index 0000000..f954d7e --- /dev/null +++ b/kernel/src/proc/scheduler.c @@ -0,0 +1,168 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "proc/scheduler.h" +#include "utils/memory.h" +#include "utils/logger.h" +#include "utils/core.h" +#include "platform/syscall.h" + +scheduler_T g_scheduler; + +void scheduler_init() { + g_scheduler.max_threads = SCHEDULER_MAX_THREADS_PER_PROCESS * SCHEDULER_MAX_PROCESSES; + g_scheduler.num_threads = 0; + g_scheduler.threads = memory_allocate(g_scheduler.max_threads * sizeof(thread_T)); + g_scheduler.threads_bitmap = bitmap_init(g_scheduler.max_threads); + g_scheduler.running_threads_bitmap = bitmap_init(g_scheduler.max_threads); + g_scheduler.queue = memory_allocate(g_scheduler.max_threads * sizeof(thread_T*)); + g_scheduler.queue_index = 0; + g_scheduler.queue_length = 0; + g_scheduler.running_thread = 0; + g_scheduler.blocked = false; + + syscall_perform(SYSCALL_KERNEL_SCHEDULER_START); +} + +cpu_state_T* scheduler_start(cpu_state_T* state) { + thread_T thread; + + memory_copy(state, &thread.state, sizeof(cpu_state_T)); + thread.id = 0; + thread.cpu_time = 0; + + scheduler_register_thread(&thread); + scheduler_start_thread(thread.id); + + g_scheduler.initialized = true; + + return scheduler_switch_context(state); +} + +bool scheduler_is_initialized() { + return g_scheduler.initialized; +} + +uint32_t scheduler_request_thread_id() { + for (int i = 0; i < g_scheduler.max_threads; i++) { + if (!bitmap_get(&g_scheduler.threads_bitmap, i)) { + return i; + } + } + return THREAD_NONE; +} + +uint32_t scheduler_register_thread(thread_T* thread) { + if (g_scheduler.num_threads >= g_scheduler.max_threads) { + log(LOG_WARNING, " Failed to register Thread (max thread num reached)"); + return THREAD_NONE; + } + + + thread->id = scheduler_request_thread_id(); + if (thread->id == THREAD_NONE) { + log(LOG_WARNING, " Failed to register Thread (generation of thread id failed)"); + return THREAD_NONE; + } + + bitmap_set(&g_scheduler.threads_bitmap, thread->id, true); + memory_copy(thread, &g_scheduler.threads[thread->id], sizeof(thread_T)); + + g_scheduler.num_threads++; + + log(LOG_INFO, " Registered thread %d", thread->id); + + return thread->id; +} + +void scheduler_pause_thread(uint32_t id) { + bitmap_set(&g_scheduler.running_threads_bitmap, id, false); + log(LOG_INFO, " Paused thread %d", id); + scheduler_calculate_queue(); +} + +void scheduler_start_thread(uint32_t id) { + bitmap_set(&g_scheduler.running_threads_bitmap, id, true); + log(LOG_INFO, " Started thread %d", id); + scheduler_calculate_queue(); +} + +void scheduler_kill_thread(uint32_t id) { + scheduler_pause_thread(id); + bitmap_set(&g_scheduler.threads_bitmap, id, false); + + g_scheduler.num_threads--; + + log(LOG_INFO, " Killed thread %d", id); +} + +thread_T* scheduler_get_thread(uint32_t id) { + if (!bitmap_get(&g_scheduler.threads_bitmap, id)) { + return NULL; + } + + return &g_scheduler.threads[id]; +} + +uint32_t scheduler_get_current_thread() { + return g_scheduler.running_thread; +} + +void scheduler_calculate_queue() { + int index = 0; + + log(LOG_INFO, " Calculating queue:"); + + for (int i = 0; i < g_scheduler.max_threads; i++) { + if (!bitmap_get(&g_scheduler.running_threads_bitmap, i)) { continue; } + + log(LOG_NONE, " > [%d] Added thread %d", index, i); + + g_scheduler.queue[index] = &g_scheduler.threads[i]; + index++; + } + + g_scheduler.queue_length = index; +} + +cpu_state_T* scheduler_switch_context(cpu_state_T* state) { + if (!g_scheduler.initialized) { + log(LOG_WARNING, "Failed to switch context (scheduler not initialized) INT[0x%xb]", state->interrupt_id); + return state; + } + + CORE_HALT_WHILE(g_scheduler.blocked) + g_scheduler.blocked = true; + + thread_T* old_thread = &g_scheduler.threads[g_scheduler.running_thread]; + thread_T* new_thread = g_scheduler.queue [g_scheduler.queue_index]; + + log(LOG_DEBUG, "Switching Thread OLD[%d] NEW[%d]", old_thread->id, new_thread->id); + + if (old_thread->cpu_time > 0) { + memory_copy(state, &old_thread->state, sizeof(cpu_state_T)); + } + + old_thread->cpu_time += 1; + g_scheduler.running_thread = new_thread->id; + + g_scheduler.queue_index++; + if (g_scheduler.queue_index == g_scheduler.queue_length) { + g_scheduler.queue_index = 0; + } + + g_scheduler.blocked = false; + return &new_thread->state; +} \ No newline at end of file diff --git a/kernel/src/utils/logger.c b/kernel/src/utils/logger.c index b37b2ae..8d3b7df 100644 --- a/kernel/src/utils/logger.c +++ b/kernel/src/utils/logger.c @@ -36,7 +36,7 @@ void log_send_string_to_port(string_t str) { } void log(log_level_E log_level, string_t str, ...) { - CORE_HALT_WHILE(g_logger_blocked) +// CORE_HALT_WHILE(g_logger_blocked) g_logger_blocked = true; va_list args;