diff --git a/kernel/include/scheduler.hpp b/kernel/include/scheduler.hpp index a6fa2073..34da4ab8 100644 --- a/kernel/include/scheduler.hpp +++ b/kernel/include/scheduler.hpp @@ -8,6 +8,8 @@ #ifndef SCHEDULER_H #define SCHEDULER_H +#include "stl/string.hpp" + #include "process.hpp" namespace scheduler { @@ -21,16 +23,14 @@ void block_process(pid_t pid); void unblock_process(pid_t pid); void init(); -void start(); + +int64_t exec(const std::string& path); void kill_current_process(); void tick(); void reschedule(); -process_t& new_process(); -void queue_process(pid_t p); - void sleep_ms(pid_t pid, size_t time); } //end of namespace scheduler diff --git a/kernel/src/kernel.cpp b/kernel/src/kernel.cpp index 9e8280ad..07c36cfe 100644 --- a/kernel/src/kernel.cpp +++ b/kernel/src/kernel.cpp @@ -22,6 +22,7 @@ #include "console.hpp" #include "gdt.hpp" #include "terminal.hpp" +#include "scheduler.hpp" extern "C" { @@ -67,6 +68,8 @@ void kernel_main(){ init_console(); + scheduler::init(); + //Launch the shell init_shell(); diff --git a/kernel/src/scheduler.cpp b/kernel/src/scheduler.cpp index 53d9dc64..c6009c85 100644 --- a/kernel/src/scheduler.cpp +++ b/kernel/src/scheduler.cpp @@ -7,21 +7,26 @@ #include "stl/array.hpp" #include "stl/vector.hpp" +#include "stl/optional.hpp" +#include "stl/string.hpp" #include "scheduler.hpp" #include "paging.hpp" #include "assert.hpp" #include "gdt.hpp" #include "terminal.hpp" - +#include "disks.hpp" +#include "elf.hpp" #include "console.hpp" +#include "physical_allocator.hpp" +#include "virtual_allocator.hpp" -constexpr const bool DEBUG_SCHEDULER = true; +constexpr const bool DEBUG_SCHEDULER = false; //Provided by task_switch.s extern "C" { extern void task_switch(size_t current, size_t next); -extern void init_task_switch(size_t current); +extern void init_task_switch(size_t current) __attribute__((noreturn)); } namespace { @@ -58,8 +63,36 @@ void idle_task(){ char idle_stack[scheduler::user_stack_size]; char idle_kernel_stack[scheduler::kernel_stack_size]; +scheduler::process_t& new_process(){ + //TODO use get_free_pid() that searchs through the PCB + auto pid = next_pid++; + + auto& process = pcb[pid]; + + process.process.system = false; + process.process.pid = pid; + process.process.priority = scheduler::DEFAULT_PRIORITY; + process.state = scheduler::process_state::NEW; + process.process.tty = stdio::get_active_terminal().id; + + return process.process; +} + +void queue_process(scheduler::pid_t pid){ + thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); + + auto& process = pcb[pid]; + + thor_assert(process.process.priority <= scheduler::MAX_PRIORITY, "Invalid priority"); + thor_assert(process.process.priority >= scheduler::MIN_PRIORITY, "Invalid priority"); + + process.state = scheduler::process_state::READY; + + run_queues[process.process.priority].push_back(pid); +} + void create_idle_task(){ - auto& idle_process = scheduler::new_process(); + auto& idle_process = new_process(); idle_process.priority = scheduler::MIN_PRIORITY; @@ -83,7 +116,7 @@ void create_idle_task(){ idle_process.kernel_rsp = reinterpret_cast(&idle_kernel_stack[scheduler::kernel_stack_size - 1]); - scheduler::queue_process(idle_process.pid); + queue_process(idle_process.pid); } void switch_to_process(size_t pid){ @@ -152,23 +185,209 @@ size_t select_next_process(){ thor_unreachable("No process is READY"); } -} //end of anonymous namespace -void scheduler::init(){ //Create the idle task - create_idle_task(); +std::optional read_elf_file(const std::string& file){ + auto content = disks::read_file(file); + + if(content.empty()){ + k_print_line("The file does not exist or is empty"); + + return {}; + } + + if(!elf::is_valid(content)){ + k_print_line("This file is not an ELF file or not in ELF64 format"); + + return {}; + } + + return {std::move(content)}; } -void scheduler::start(){ +bool allocate_user_memory(scheduler::process_t& process, size_t address, size_t size, size_t& ref){ + //1. Calculate some stuff + auto first_page = paging::page_align(address); + auto left_padding = address - first_page; + auto bytes = left_padding + paging::PAGE_SIZE + size; + auto pages = (bytes / paging::PAGE_SIZE) + 1; + + //2. Get enough physical memory + auto physical_memory = physical_allocator::allocate(pages); + + if(!physical_memory){ + k_print_line("Cannot allocate physical memory, probably out of memory"); + return false; + } + + //3. Find a start of a page inside the physical memory + + auto aligned_physical_memory = paging::page_aligned(physical_memory) ? physical_memory : + (physical_memory / paging::PAGE_SIZE + 1) * paging::PAGE_SIZE; + + //4. Map physical allocated memory to the necessary virtual memory + + paging::user_map_pages(process, first_page, aligned_physical_memory, pages); + + ref = physical_memory; + + return true; +} + +void clear_physical_memory(size_t memory, size_t pages){ + auto virt = virtual_allocator::allocate(pages); + + paging::map_pages(virt, memory, pages); + + auto it = reinterpret_cast(virt); + std::fill_n(it, (pages * paging::PAGE_SIZE) / sizeof(uint64_t), 0); + + paging::unmap_pages(virt, pages); + + //TODO virt should be deallocated +} + +bool create_paging(char* buffer, scheduler::process_t& process){ + //1. Prepare PML4T + + //Get memory for cr3 + process.physical_cr3 = physical_allocator::allocate(1); + process.paging_size = paging::PAGE_SIZE; + + clear_physical_memory(process.physical_cr3, 1); + + //Map the kernel pages inside the user memory space + paging::map_kernel_inside_user(process); + + //2. Create all the other necessary structures + + //2.1 Allocate user stack + allocate_user_memory(process, scheduler::user_stack_start, scheduler::user_stack_size, process.physical_user_stack); + + //2.2 Allocate all user segments + + auto header = reinterpret_cast(buffer); + auto program_header_table = reinterpret_cast(buffer + header->e_phoff); + + for(size_t p = 0; p < header->e_phnum; ++p){ + auto& p_header = program_header_table[p]; + + if(p_header.p_type == 1){ + auto address = p_header.p_vaddr; + auto first_page = paging::page_align(address); + auto left_padding = address - first_page; + auto bytes = left_padding + paging::PAGE_SIZE + p_header.p_memsz; + auto pages = (bytes / paging::PAGE_SIZE) + 1; + + scheduler::segment_t segment; + segment.size = pages * paging::PAGE_SIZE; + + allocate_user_memory(process, first_page, pages, segment.physical); + + process.segments.push_back(segment); + + //Copy the code into memory + + auto virtual_memory = virtual_allocator::allocate(pages); + + paging::map_pages(virtual_memory, segment.physical, pages); + + auto memory_start = virtual_memory + left_padding; + + std::copy_n(reinterpret_cast(memory_start), buffer + p_header.p_offset, p_header.p_memsz); + + paging::unmap_pages(virtual_memory, pages); + } + } + + //2.3 Allocate kernel stack + auto virtual_kernel_stack = virtual_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE); + auto physical_kernel_stack = physical_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE); + + if(!paging::map_pages(virtual_kernel_stack, physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE)){ + return false; + } + + process.physical_kernel_stack = physical_kernel_stack; + process.kernel_rsp = virtual_kernel_stack + (scheduler::user_stack_size - 8); + + //3. Clear stacks + clear_physical_memory(process.physical_user_stack, scheduler::user_stack_size / paging::PAGE_SIZE); + clear_physical_memory(process.physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE); + + return true; +} + +void init_context(scheduler::process_t& process, const char* buffer){ + auto header = reinterpret_cast(buffer); + + auto pages = scheduler::user_stack_size / paging::PAGE_SIZE; + auto virt = virtual_allocator::allocate(pages); + + paging::map_pages(virt, process.physical_user_stack, pages); + + auto rsp = virt + scheduler::user_stack_size - 8; + rsp -= sizeof(interrupt::syscall_regs) * 8; + + auto regs = reinterpret_cast(rsp); + + regs->rsp = scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8; //Not sure about that + regs->rip = header->e_entry; + regs->cs = gdt::USER_CODE_SELECTOR + 3; + regs->ds = gdt::USER_DATA_SELECTOR + 3; + regs->rflags = 0x200; + + process.context = reinterpret_cast(scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8); + + paging::unmap_pages(virt, pages); + + //TODO virt should be deallocated +} + +void start() __attribute__((noreturn)); +void start(){ started = true; current_ticks = 0; current_pid = 0; pcb[current_pid].rounds = TURNOVER; - pcb[current_pid].state = process_state::RUNNING; + pcb[current_pid].state = scheduler::process_state::RUNNING; init_task_switch(current_pid); } +} //end of anonymous namespace + +void scheduler::init(){ //Create the idle task + create_idle_task(); +} + +int64_t scheduler::exec(const std::string& file){ + auto content = read_elf_file(file); + + if(!content){ + return -1; + } + + auto buffer = content->c_str(); + + auto& process = new_process(); + + if(!create_paging(buffer, process)){ + k_print_line("Impossible to initialize paging"); + return -1; + } + + init_context(process, buffer); + + queue_process(process.pid); + + if(!started){ + start(); + } else { + return process.pid; + } +} + void scheduler::kill_current_process(){ if(DEBUG_SCHEDULER){ k_printf("Kill %u\n", current_pid); @@ -242,34 +461,6 @@ void scheduler::reschedule(){ //At this point we just have to return to the current process } -scheduler::process_t& scheduler::new_process(){ - //TODO use get_free_pid() that searchs through the PCB - auto pid = next_pid++; - - auto& process = pcb[pid]; - - process.process.system = false; - process.process.pid = pid; - process.process.priority = scheduler::DEFAULT_PRIORITY; - process.state = process_state::NEW; - process.process.tty = stdio::get_active_terminal().id; - - return process.process; -} - -void scheduler::queue_process(scheduler::pid_t pid){ - thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); - - auto& process = pcb[pid]; - - thor_assert(process.process.priority <= scheduler::MAX_PRIORITY, "Invalid priority"); - thor_assert(process.process.priority >= scheduler::MIN_PRIORITY, "Invalid priority"); - - process.state = process_state::READY; - - run_queues[process.process.priority].push_back(pid); -} - scheduler::pid_t scheduler::get_pid(){ return current_pid; } diff --git a/kernel/src/shell.cpp b/kernel/src/shell.cpp index 88d6622c..30f869bc 100644 --- a/kernel/src/shell.cpp +++ b/kernel/src/shell.cpp @@ -720,186 +720,6 @@ void readelf_command(const std::vector& params){ } } -std::optional read_elf_file(const std::string& file, const std::string& name){ - auto content = disks::read_file(file); - - if(content.empty()){ - k_print(name); - k_print_line(": The file does not exist or is empty"); - - return {}; - } - - if(!elf::is_valid(content)){ - k_print(name); - k_print_line(": This file is not an ELF file or not in ELF64 format"); - - return {}; - } - - return {std::move(content)}; -} - -bool allocate_user_memory(scheduler::process_t& process, size_t address, size_t size, size_t& ref){ - //1. Calculate some stuff - auto first_page = paging::page_align(address); - auto left_padding = address - first_page; - auto bytes = left_padding + paging::PAGE_SIZE + size; - auto pages = (bytes / paging::PAGE_SIZE) + 1; - - //2. Get enough physical memory - auto physical_memory = physical_allocator::allocate(pages); - - if(!physical_memory){ - k_print_line("Cannot allocate physical memory, probably out of memory"); - return false; - } - - //3. Find a start of a page inside the physical memory - - auto aligned_physical_memory = paging::page_aligned(physical_memory) ? physical_memory : - (physical_memory / paging::PAGE_SIZE + 1) * paging::PAGE_SIZE; - - //4. Map physical allocated memory to the necessary virtual memory - - paging::user_map_pages(process, first_page, aligned_physical_memory, pages); - - ref = physical_memory; - - return true; -} - -void clear_physical_memory(size_t memory, size_t pages){ - auto virt = virtual_allocator::allocate(pages); - - paging::map_pages(virt, memory, pages); - - auto it = reinterpret_cast(virt); - std::fill_n(it, (pages * paging::PAGE_SIZE) / sizeof(uint64_t), 0); - - paging::unmap_pages(virt, pages); - - //TODO virt should be deallocated -} - -bool create_paging(char* buffer, scheduler::process_t& process){ - //1. Prepare PML4T - - //Get memory for cr3 - process.physical_cr3 = physical_allocator::allocate(1); - process.paging_size = paging::PAGE_SIZE; - - clear_physical_memory(process.physical_cr3, 1); - - //Map the kernel pages inside the user memory space - paging::map_kernel_inside_user(process); - - //2. Create all the other necessary structures - - //2.1 Allocate user stack - allocate_user_memory(process, scheduler::user_stack_start, scheduler::user_stack_size, process.physical_user_stack); - - //2.2 Allocate all user segments - - auto header = reinterpret_cast(buffer); - auto program_header_table = reinterpret_cast(buffer + header->e_phoff); - - for(size_t p = 0; p < header->e_phnum; ++p){ - auto& p_header = program_header_table[p]; - - if(p_header.p_type == 1){ - auto address = p_header.p_vaddr; - auto first_page = paging::page_align(address); - auto left_padding = address - first_page; - auto bytes = left_padding + paging::PAGE_SIZE + p_header.p_memsz; - auto pages = (bytes / paging::PAGE_SIZE) + 1; - - scheduler::segment_t segment; - segment.size = pages * paging::PAGE_SIZE; - - allocate_user_memory(process, first_page, pages, segment.physical); - - process.segments.push_back(segment); - - //Copy the code into memory - - auto virtual_memory = virtual_allocator::allocate(pages); - - paging::map_pages(virtual_memory, segment.physical, pages); - - auto memory_start = virtual_memory + left_padding; - - std::copy_n(reinterpret_cast(memory_start), buffer + p_header.p_offset, p_header.p_memsz); - - paging::unmap_pages(virtual_memory, pages); - } - } - - //2.3 Allocate kernel stack - auto virtual_kernel_stack = virtual_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE); - auto physical_kernel_stack = physical_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE); - - if(!paging::map_pages(virtual_kernel_stack, physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE)){ - return false; - } - - process.physical_kernel_stack = physical_kernel_stack; - process.kernel_rsp = virtual_kernel_stack + (scheduler::user_stack_size - 8); - - //3. Clear stacks - clear_physical_memory(process.physical_user_stack, scheduler::user_stack_size / paging::PAGE_SIZE); - clear_physical_memory(process.physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE); - - return true; -} - -void init_context(scheduler::process_t& process, const char* buffer){ - auto header = reinterpret_cast(buffer); - - auto pages = scheduler::user_stack_size / paging::PAGE_SIZE; - auto virt = virtual_allocator::allocate(pages); - - paging::map_pages(virt, process.physical_user_stack, pages); - - auto rsp = virt + scheduler::user_stack_size - 8; - rsp -= sizeof(interrupt::syscall_regs) * 8; - - auto regs = reinterpret_cast(rsp); - - regs->rsp = scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8; //Not sure about that - regs->rip = header->e_entry; - regs->cs = gdt::USER_CODE_SELECTOR + 3; - regs->ds = gdt::USER_DATA_SELECTOR + 3; - regs->rflags = 0x200; - - process.context = reinterpret_cast(scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8); - - paging::unmap_pages(virt, pages); - - //TODO virt should be deallocated -} - -void queue_process(const std::string& file){ - auto content = read_elf_file(file, "exec"); - - if(!content){ - return; - } - - auto buffer = content->c_str(); - - auto& process = scheduler::new_process(); - - if(!create_paging(buffer, process)){ - k_print_line("Impossible to initialize paging"); - return; - } - - init_context(process, buffer); - - scheduler::queue_process(process.pid); -} - void exec_command(const std::vector& params){ if(params.size() < 2){ k_print_line("exec: Need the name of the executable to read"); @@ -913,13 +733,7 @@ void exec_command(const std::vector& params){ return; } - scheduler::init(); - - for(size_t i = 1; i < params.size(); ++i){ - queue_process(params[i]); - } - - scheduler::start(); + scheduler::exec(params[1]); } void vesainfo_command(const std::vector&){