diff --git a/Makefile b/Makefile index 804389c0..432d16dd 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,9 @@ bochs: default bochs -qf tools/bochsrc.txt -rc commands rm commands +bochs_simple: default + bochs -qf tools/bochsrc.txt + debug: default echo "c" > commands bochs -qf tools/debug_bochsrc.txt -rc commands diff --git a/cpp.mk b/cpp.mk index 539864ca..366709f0 100644 --- a/cpp.mk +++ b/cpp.mk @@ -4,7 +4,7 @@ OC=x86_64-elf-objcopy AR=x86_64-elf-ar WARNING_FLAGS=-Wall -Wextra -pedantic -Wold-style-cast -COMMON_CPP_FLAGS=-masm=intel -I../../tstl/include/ -I../printf/include/ -I../tstl/include/ -I../tlib/include/ -Iinclude/ -nostdlib -g -Os -std=c++11 -fno-stack-protector -fno-exceptions -funsigned-char -fno-rtti -ffreestanding -fomit-frame-pointer -mno-red-zone -mno-3dnow -mno-mmx -fno-asynchronous-unwind-tables +COMMON_CPP_FLAGS=-masm=intel -I../../tstl/include/ -I../printf/include/ -I../tstl/include/ -I../tlib/include/ -Iinclude/ -nostdlib -g -std=c++11 -fno-stack-protector -fno-exceptions -funsigned-char -fno-rtti -ffreestanding -mno-red-zone -mno-3dnow -mno-mmx -fno-asynchronous-unwind-tables DISABLE_SSE_FLAGS=-mno-sse -mno-sse2 -mno-sse3 -mno-sse4 -mno-sse4.1 -mno-sse4.2 ENABLE_SSE_FLAGS=-msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 diff --git a/kernel/include/irq_wait_queue.hpp b/kernel/include/irq_wait_queue.hpp new file mode 100644 index 00000000..7a42c449 --- /dev/null +++ b/kernel/include/irq_wait_queue.hpp @@ -0,0 +1,54 @@ +//======================================================================= +// Copyright Baptiste Wicht 2013-2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +//======================================================================= + +#ifndef IRQ_WAIT_QUEUE_HPP +#define IRQ_WAIT_QUEUE_HPP + +#include "scheduler.hpp" +#include "lock_guard.hpp" +#include "spinlock.hpp" + +template +struct irq_wait_queue { +private: + volatile size_t head = 0; + volatile size_t tail = 0; + + scheduler::pid_t pids[S]; + + spinlock lock; + +public: + bool empty() const { + return tail - head == 0; + } + + scheduler::pid_t signal(){ + auto pid = pids[head % S]; + ++head; + + scheduler::soft_unblock(pid); + + return pid; + } + + void wait(){ + auto pid = scheduler::get_pid(); + + scheduler::soft_block(pid); + + lock.acquire(); + + pids[tail % S] = pid; + ++tail; + + lock.release(); + scheduler::soft_reschedule(pid); + } +}; + +#endif diff --git a/kernel/include/scheduler.hpp b/kernel/include/scheduler.hpp index 03a1f196..2bff7a1e 100644 --- a/kernel/include/scheduler.hpp +++ b/kernel/include/scheduler.hpp @@ -17,12 +17,40 @@ namespace scheduler { constexpr const size_t MAX_PROCESS = 128; +struct sleep_queue_ptr { + sleep_queue_ptr* next; + sleep_queue_ptr* prev; + pid_t pid; +}; + +struct tasklet { + void (*fun)(size_t,size_t); + size_t d1; + size_t d2; +}; + +bool is_started(); + pid_t get_pid(); scheduler::process_t& get_process(pid_t pid); void block_process(pid_t pid); void unblock_process(pid_t pid); +scheduler::sleep_queue_ptr* queue_ptr(scheduler::pid_t pid); + + + +//TODO Probably should be pruned +void soft_block(pid_t pid); +void soft_unblock(pid_t pid); +void soft_reschedule(pid_t pid); + + +void irq_register_tasklet(const tasklet& task, size_t priority); + + + void init(); void start() __attribute__((noreturn)); diff --git a/kernel/include/semaphore.hpp b/kernel/include/semaphore.hpp index e171eaf8..b6267429 100644 --- a/kernel/include/semaphore.hpp +++ b/kernel/include/semaphore.hpp @@ -8,31 +8,31 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H -#include #include #include "spinlock.hpp" #include "scheduler.hpp" +#include "sleep_queue.hpp" struct semaphore { private: spinlock lock; volatile size_t value; - std::queue queue; + //std::queue queue; + sleep_queue queue; public: void init(size_t v){ value = v; } + //TODO Make sure it doesn't have the lost wake up problem void wait(){ lock.acquire(); if(!value){ - queue.push(scheduler::get_pid()); - scheduler::set_current_state(scheduler::process_state::BLOCKED); lock.release(); - scheduler::reschedule(); + queue.sleep(); lock.acquire(); } @@ -47,10 +47,7 @@ public: ++value; if(!queue.empty()){ - auto pid = queue.top(); - queue.pop(); - - scheduler::unblock_process(pid); + queue.wake_up(); } } }; diff --git a/kernel/include/sleep_queue.hpp b/kernel/include/sleep_queue.hpp index a35e367b..9ddbac9b 100644 --- a/kernel/include/sleep_queue.hpp +++ b/kernel/include/sleep_queue.hpp @@ -8,41 +8,42 @@ #ifndef SLEEP_QUEUE_H #define SLEEP_QUEUE_H -#include #include #include "spinlock.hpp" #include "scheduler.hpp" -#include "console.hpp" - struct sleep_queue { private: - mutable spinlock lock; + typedef spinlock lock_type; - std::queue queue; + mutable lock_type lock; + + scheduler::sleep_queue_ptr* head = nullptr; + scheduler::sleep_queue_ptr* tail = nullptr; public: bool empty() const { - std::lock_guard l(lock); + std::lock_guard l(lock); - return queue.empty(); + return head == nullptr; } scheduler::pid_t top_process() const { - std::lock_guard l(lock); + std::lock_guard l(lock); - return queue.top(); + return head->pid; } scheduler::pid_t wake_up(){ - std::lock_guard l(lock); - - //Get the first process - auto pid = queue.top(); + std::lock_guard l(lock); //Remove the process from the queue - queue.pop(); + auto queue_ptr = head; + head = head->next; + + //Get the first process + auto pid = queue_ptr->pid; //Indicate to the scheduler that this process will be able //to run @@ -57,8 +58,18 @@ public: //Get the current process information auto pid = scheduler::get_pid(); + auto queue_ptr = scheduler::queue_ptr(pid); + queue_ptr->pid = pid; + queue_ptr->next = nullptr; + queue_ptr->prev = nullptr; + //Enqueue the process in the sleep queue - queue.push(pid); + if(!head){ + head = queue_ptr; + } else { + tail->next = queue_ptr; + tail = queue_ptr; + } lock.release(); diff --git a/kernel/include/spinlock.hpp b/kernel/include/spinlock.hpp index f607fb44..5050f610 100644 --- a/kernel/include/spinlock.hpp +++ b/kernel/include/spinlock.hpp @@ -14,13 +14,15 @@ private: public: void acquire(){ - //while(!__sync_bool_compare_and_swap(&lock, 0, 1)); - while(!__sync_lock_test_and_set(&lock, 1)){} + while(__sync_lock_test_and_set(&lock, 1)){ + while(lock){ + //Wait + } + } } void release(){ - __sync_synchronize(); - lock = 0; + __sync_lock_release(&lock); } }; diff --git a/kernel/include/terminal.hpp b/kernel/include/terminal.hpp index 65840697..f28a613a 100644 --- a/kernel/include/terminal.hpp +++ b/kernel/include/terminal.hpp @@ -12,6 +12,7 @@ #include #include "sleep_queue.hpp" +#include "spinlock.hpp" namespace stdio { @@ -22,14 +23,19 @@ struct virtual_terminal { bool active; bool canonical; + spinlock terminal_lock; + circular_buffer input_buffer; circular_buffer canonical_buffer; sleep_queue input_queue; void print(char c); - void send_input(char c); + void handle_input(char c); size_t read_input(char* buffer, size_t max); + + //Perhaps remove that + void send_input(char c); virtual_terminal(){} diff --git a/kernel/src/ata.cpp b/kernel/src/ata.cpp index d6737f26..f5f43877 100644 --- a/kernel/src/ata.cpp +++ b/kernel/src/ata.cpp @@ -16,7 +16,6 @@ #include "disks.hpp" #include "mutex.hpp" -#include "semaphore.hpp" #include "lock_guard.hpp" namespace { @@ -60,58 +59,65 @@ static constexpr const size_t BLOCK_SIZE = 512; ata::drive_descriptor* drives; +//TODO Use one lock per controller +//TODO Separate all the data in structure controller + mutex controller_lock; -semaphore primary_sem; -semaphore secondary_sem; +volatile size_t primary_wait_pid = 0; +volatile bool primary_release = false; +volatile bool primary_wait = false; -volatile bool primary_invoked = false; -volatile bool secondary_invoked = false; +volatile size_t secondary_wait_pid = 0; +volatile bool secondary_release = false; +volatile bool secondary_wait = false; -//TODO In the future, the wait for IRQs, could -//be done with a semaphore - -void primary_controller_handler(interrupt::syscall_regs*){ - //primary_invoked = true; - primary_sem.signal(); +//TODO Perhaps not correct +//TODO Should choose the controller more safely +static void ata_delay(){ + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); + in_byte(0x1F0 + ATA_STATUS); } -void secondary_controller_handler(interrupt::syscall_regs*){ - //secondary_invoked = true; - secondary_sem.signal(); +void primary_controller_handler(interrupt::syscall_regs*){ + scheduler::soft_unblock(primary_wait_pid); +// primary_release = true; +// if(!primary_wait){ +// scheduler::irq_sync_release(primary_wait_pid); +// } } void ata_wait_irq_primary(){ - primary_sem.wait(); - /*while(!primary_invoked){ - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - } - - primary_invoked = false;*/ + scheduler::soft_reschedule(scheduler::get_pid()); +// primary_wait = true; +// if(!primary_release){ +// scheduler::irq_sync_wait(); +// } + + //Cleanup + //primary_wait = false; + //primary_release = false; } +void secondary_controller_handler(interrupt::syscall_regs*){ + //TODO +} void ata_wait_irq_secondary(){ - secondary_sem.wait(); - /*while(!secondary_invoked){ - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - asm volatile ("nop"); - } - - secondary_invoked = false;*/ + //TODO } static uint8_t wait_for_controller(uint16_t controller, uint8_t mask, uint8_t value, uint16_t timeout){ uint8_t status; do { status = in_byte(controller + ATA_STATUS); - timer::sleep_ms(1); + ata_delay(); + //timer::sleep_ms(1); } while ((status & mask) != value && --timeout); return timeout; @@ -130,7 +136,7 @@ bool select_device(ata::drive_descriptor& drive){ out_byte(controller + ATA_DRV_HEAD, 0xA0 | (drive.slave << 4)); //Sleep at least 400ns before reading the status register - timer::sleep_ms(1); + ata_delay(); if(!wait_for_controller(controller, wait_mask, 0, 10000)){ return false; @@ -152,6 +158,15 @@ bool read_write_sector(ata::drive_descriptor& drive, uint64_t start, void* data, uint8_t ch = (start >> 16) & 0xFF; uint8_t hd = (start >> 24) & 0x0F; + //Prepare for waiting + if(controller == ATA_PRIMARY){ + primary_wait_pid = scheduler::get_pid(); + } else { + secondary_wait_pid = scheduler::get_pid(); + } + + scheduler::soft_block(scheduler::get_pid()); + auto command = read ? ATA_READ_BLOCK : ATA_WRITE_BLOCK; //Process the command @@ -163,7 +178,7 @@ bool read_write_sector(ata::drive_descriptor& drive, uint64_t start, void* data, out_byte(controller + ATA_COMMAND, command); //Wait at least 400ns before reading status register - timer::sleep_ms(1); + ata_delay(); //Wait at most 30 seconds for BSY flag to be cleared if(!wait_for_controller(controller, ATA_STATUS_BSY, 0, 30000)){ @@ -339,9 +354,6 @@ void identify(ata::drive_descriptor& drive){ void ata::detect_disks(){ controller_lock.init(); - primary_sem.init(0); - secondary_sem.init(0); - drives = new drive_descriptor[4]; drives[0] = {ATA_PRIMARY, 0xE0, false, MASTER_BIT, false, "", "", ""}; diff --git a/kernel/src/kernel.cpp b/kernel/src/kernel.cpp index d98de672..80c436c4 100644 --- a/kernel/src/kernel.cpp +++ b/kernel/src/kernel.cpp @@ -88,10 +88,6 @@ void kernel_main(){ timer::install(); //acpi::init(); keyboard::install_driver(); - disks::detect_disks(); - - //Init the virtual file system - vfs::init(); //Only install system calls when everything else is ready install_system_calls(); diff --git a/kernel/src/keyboard.cpp b/kernel/src/keyboard.cpp index c7c8043a..3bc2c043 100644 --- a/kernel/src/keyboard.cpp +++ b/kernel/src/keyboard.cpp @@ -10,6 +10,9 @@ #include "kernel_utils.hpp" #include "terminal.hpp" +//TODO REMOVE +#include "console.hpp" + namespace { char qwertz[128] = { @@ -93,7 +96,9 @@ char shifted_qwertz[128] = { void keyboard_handler(interrupt::syscall_regs*){ auto key = static_cast(in_byte(0x60)); - stdio::get_active_terminal().send_input(key); + if(scheduler::is_started()){ + stdio::get_active_terminal().send_input(key); + } } } //end of anonymous namespace diff --git a/kernel/src/scheduler.cpp b/kernel/src/scheduler.cpp index 989bedd3..efac8951 100644 --- a/kernel/src/scheduler.cpp +++ b/kernel/src/scheduler.cpp @@ -26,6 +26,7 @@ #include "mutex.hpp" #include "kernel_utils.hpp" #include "logging.hpp" +#include "circular_buffer.hpp" constexpr const bool DEBUG_SCHEDULER = false; @@ -39,11 +40,12 @@ namespace { struct process_control_t { scheduler::process_t process; - scheduler::process_state state; + volatile scheduler::process_state state; size_t rounds; size_t sleep_timeout; std::vector> handles; std::vector working_directory; + scheduler::sleep_queue_ptr queue_ptr; }; //The Process Control Block @@ -53,27 +55,70 @@ std::array pcb; std::array, scheduler::PRIORITY_LEVELS> run_queues; std::array run_queue_locks; +std::array, scheduler::PRIORITY_LEVELS> tasklets; + +constexpr size_t priority_to_index(size_t priority){ + return priority - scheduler::MIN_PRIORITY; +} + std::vector& run_queue(size_t priority){ - return run_queues[priority - scheduler::MIN_PRIORITY]; + return run_queues[priority_to_index(priority)]; } mutex& run_queue_lock(size_t priority){ - return run_queue_locks[priority - scheduler::MIN_PRIORITY]; + return run_queue_locks[priority_to_index(priority)]; } -bool started = false; - constexpr const size_t STACK_ALIGNMENT = 16; constexpr const size_t TURNOVER = 10; -constexpr const size_t QUANTUM_SIZE = 1000; +constexpr const size_t QUANTUM_SIZE = 100; -size_t current_ticks = 0; +volatile bool started = false; -size_t current_pid; -size_t next_pid = 0; +volatile size_t current_ticks = 0; -size_t gc_pid = 0; +volatile size_t current_pid = 0; +volatile size_t next_pid = 0; + +scheduler::pid_t init_pid = 0; +scheduler::pid_t idle_pid = 0; +scheduler::pid_t gc_pid = 0; + +std::array tasklets_executors_pids; + +size_t select_next_process(); +void switch_to_process(size_t pid); + +template +void tasklet_executor_task() { + auto index = priority_to_index(priority); + + while(true){ + + while(!tasklets[index].empty()){ + auto task = tasklets[index].pop(); + task.fun(task.d1, task.d2); + } + + k_print_line("executor"); + + //Wait until there is something to do + //scheduler::block_process(current_pid); + + asm volatile ("cli"); + + pcb[current_pid].state = scheduler::process_state::BLOCKED; + + switch_to_process(select_next_process()); + + asm volatile ("sti"); + + k_print_line(current_pid); + + k_print_line("executorafter"); + } +} void idle_task(){ while(true){ @@ -84,7 +129,7 @@ void idle_task(){ void gc_task(){ while(true){ //Wait until there is something to do - scheduler::block_process(scheduler::get_pid()); + scheduler::block_process(current_pid); //2. Clean up each killed process @@ -114,15 +159,17 @@ void gc_task(){ paging::unmap_pages(desc.virtual_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE); //6. Remove process from run queue + asm volatile("cli"); size_t index = 0; for(; index < run_queue(desc.priority).size(); ++index){ - std::lock_guard l(run_queue_lock(desc.priority)); + //std::lock_guard l(run_queue_lock(desc.priority)); if(run_queue(desc.priority)[index] == desc.pid){ run_queue(desc.priority).erase(index); break; } } + asm volatile("sti"); //7. Clean process desc.pid = 0; @@ -148,10 +195,16 @@ void gc_task(){ //TODO tsh should be configured somewhere void init_task(){ - //Starting from here, the logging system can output logs to file - logging::to_file(); + //Detect disks + disks::detect_disks(); - logging::log("init_task started"); + //Init the virtual file system + vfs::init(); + + //Starting from here, the logging system can output logs to file + //logging::to_file(); + + //logging::log("init_task started"); std::vector params; @@ -174,6 +227,9 @@ char init_kernel_stack[scheduler::kernel_stack_size]; char gc_stack[scheduler::user_stack_size]; char gc_kernel_stack[scheduler::kernel_stack_size]; +std::array tasklet_executor_stacks; +std::array tasklet_executor_kernel_stacks; + scheduler::process_t& new_process(){ //TODO use get_free_pid() that searchs through the PCB auto pid = next_pid++; @@ -203,8 +259,9 @@ void queue_process(scheduler::pid_t pid){ process.state = scheduler::process_state::READY; - std::lock_guard l(run_queue_lock(process.process.priority)); + asm volatile("cli"); run_queue(process.process.priority).push_back(pid); + asm volatile("sti"); } scheduler::process_t& create_kernel_task(char* user_stack, char* kernel_stack, void (*fun)()){ @@ -244,6 +301,8 @@ void create_idle_task(){ idle_process.priority = scheduler::MIN_PRIORITY; queue_process(idle_process.pid); + + idle_pid = idle_process.pid; } void create_init_task(){ @@ -253,6 +312,8 @@ void create_init_task(){ init_process.priority = scheduler::MIN_PRIORITY + 1; queue_process(init_process.pid); + + init_pid = init_process.pid; } void create_gc_task(){ @@ -266,6 +327,36 @@ void create_gc_task(){ gc_pid = gc_process.pid; } +template +std::enable_if_t<(priority > scheduler::MAX_PRIORITY)> create_tasklet_executor(){ + //Break recursion +} + +template +std::enable_if_t<(priority <= scheduler::MAX_PRIORITY)> create_tasklet_executor(){ + auto index = priority_to_index(priority); + + auto& process = create_kernel_task( + tasklet_executor_stacks[index], + tasklet_executor_kernel_stacks[index], + &tasklet_executor_task); + + process.ppid = 1; + process.priority = priority; + + queue_process(process.pid); + + pcb[process.pid].state = scheduler::process_state::BLOCKED; + + tasklets_executors_pids[index] = process.pid; + + create_tasklet_executor(); +} + +void create_tasklet_executors(){ + create_tasklet_executor(); +} + void switch_to_process(size_t pid){ auto old_pid = current_pid; current_pid = pid; @@ -289,7 +380,7 @@ size_t select_next_process(){ //1. Run a process of higher priority, if any for(size_t p = scheduler::MAX_PRIORITY; p > current_priority; --p){ - std::lock_guard l(run_queue_lock(p)); + //std::lock_guard l(run_queue_lock(p)); for(auto pid : run_queue(p)){ if(pcb[pid].state == scheduler::process_state::READY){ @@ -301,7 +392,7 @@ size_t select_next_process(){ //2. Run the next process of the same priority { - std::lock_guard l(run_queue_lock(current_priority)); + //std::lock_guard l(run_queue_lock(current_priority)); auto& current_run_queue = run_queue(current_priority); @@ -323,12 +414,10 @@ size_t select_next_process(){ } } - thor_assert(current_priority > 0, "The idle task should always be ready"); - //3. Run a process of lower priority for(size_t p = current_priority - 1; p >= scheduler::MIN_PRIORITY; --p){ - std::lock_guard l(run_queue_lock(p)); + //std::lock_guard l(run_queue_lock(p)); for(auto pid : run_queue(p)){ if(pcb[pid].state == scheduler::process_state::READY){ @@ -337,6 +426,8 @@ size_t select_next_process(){ } } + thor_assert(pcb[idle_pid].state == scheduler::process_state::READY, "The idle task should always be ready"); + thor_unreachable("No process is READY"); } @@ -588,6 +679,7 @@ void scheduler::init(){ //Create the idle task create_idle_task(); create_init_task(); create_gc_task(); + create_tasklet_executors(); current_ticks = 0; @@ -602,7 +694,33 @@ void scheduler::start(){ init_task_switch(current_pid); } +bool scheduler::is_started(){ + return started; +} + +void scheduler::irq_register_tasklet(const tasklet& task, size_t priority){ + thor_assert(started, "The scheduler must be started before irq_register_tasklet is called"); + + tasklets[priority_to_index(priority)].push(task); + + auto pid = tasklets_executors_pids[priority_to_index(priority)]; + + thor_assert(pcb[pid].state == process_state::BLOCKED || pcb[pid].state == process_state::RUNNING || pcb[pid].state == process_state::READY, "Invalid state for tasklet executor"); + + /*k_print_line("irq_register"); + k_print_line(current_pid); + k_print_line(pid); + k_print_line(priority); + k_print_line(static_cast(pcb[pid].state));*/ + + if(pcb[pid].state == process_state::BLOCKED){ + pcb[pid].state = process_state::READY; + } +} + int64_t scheduler::exec(const std::string& file, const std::vector& params){ + thor_assert(started, "The scheduler must be started before exec is called"); + std::string content; auto result = vfs::direct_read(file, content); if(result < 0){ @@ -653,12 +771,14 @@ int64_t scheduler::exec(const std::string& file, const std::vector& pcb[process.pid].working_directory.push_back(p); } - logging::logf("Exec process pid=%u, ppid=%u", process.pid, process.ppid); + //logging::logf("Exec process pid=%u, ppid=%u", process.pid, process.ppid); return process.pid; } void scheduler::sbrk(size_t inc){ + thor_assert(started, "The scheduler must be started before sbrk is called"); + auto& process = pcb[current_pid].process; size_t size = (inc + paging::PAGE_SIZE - 1) & ~(paging::PAGE_SIZE - 1); @@ -689,6 +809,8 @@ void scheduler::sbrk(size_t inc){ } void scheduler::await_termination(pid_t pid){ + thor_assert(started, "The scheduler must be started before await_termination is called"); + while(true){ bool found = false; for(auto& process : pcb){ @@ -705,12 +827,16 @@ void scheduler::await_termination(pid_t pid){ return; } + asm volatile("cli"); pcb[current_pid].state = process_state::WAITING; reschedule(); + asm volatile("sti"); } } void scheduler::kill_current_process(){ + thor_assert(started, "The scheduler must be started before kill_current_process is called"); + if(DEBUG_SCHEDULER){ printf("Kill %u\n", current_pid); } @@ -752,11 +878,11 @@ void scheduler::tick(){ if(current_ticks % QUANTUM_SIZE == 0){ auto& process = pcb[current_pid]; - if(process.rounds == TURNOVER){ + if(process.rounds == TURNOVER){ process.rounds = 0; process.state = process_state::READY; - + auto pid = select_next_process(); //If it is the same, no need to go to the switching process @@ -776,29 +902,39 @@ void scheduler::tick(){ void scheduler::reschedule(){ thor_assert(started, "No interest in rescheduling before start"); + //asm volatile ("cli"); + auto& process = pcb[current_pid]; //The process just got blocked or put to sleep, choose another one if(process.state != process_state::RUNNING){ - auto index = select_next_process(); + auto pid = select_next_process(); - switch_to_process(index); + switch_to_process(pid); } + + //asm volatile ("sti"); //At this point we just have to return to the current process } scheduler::pid_t scheduler::get_pid(){ + thor_assert(started, "The scheduler must be started before get_pid is called"); + return current_pid; } scheduler::process_t& scheduler::get_process(pid_t pid){ + thor_assert(started, "The scheduler must be started before get_process is called"); + thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); return pcb[pid].process; } void scheduler::block_process(pid_t pid){ + thor_assert(started, "The scheduler must be started before block_process is called"); + thor_assert(pid != idle_pid, "Cannot block the idle task"); thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); thor_assert(pcb[pid].state == process_state::RUNNING, "Can only block RUNNING processes"); @@ -806,12 +942,16 @@ void scheduler::block_process(pid_t pid){ printf("Block process %u\n", pid); } + asm volatile("cli"); pcb[pid].state = process_state::BLOCKED; reschedule(); + asm volatile("sti"); } void scheduler::unblock_process(pid_t pid){ + thor_assert(started, "The scheduler must be started before unblock_process is called"); + thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); thor_assert(pcb[pid].state == process_state::BLOCKED || pcb[pid].state == process_state::WAITING, "Can only unblock BLOCKED/WAITING processes"); @@ -819,10 +959,48 @@ void scheduler::unblock_process(pid_t pid){ printf("Unblock process %u\n", pid); } + asm volatile("cli"); pcb[pid].state = process_state::READY; + asm volatile("sti"); +} + +scheduler::sleep_queue_ptr* scheduler::queue_ptr(scheduler::pid_t pid){ + return &pcb[pid].queue_ptr; +} + +void scheduler::soft_block(pid_t pid){ + thor_assert(started, "The scheduler must be started before soft_block is called"); + thor_assert(pid != idle_pid, "Cannot soft block the idle task"); + + + asm volatile("cli"); + pcb[pid].state = process_state::BLOCKED; + asm volatile("sti"); +} + +void scheduler::soft_unblock(pid_t pid){ + thor_assert(started, "The scheduler must be started before soft_unblock is called"); + + asm volatile("cli"); + pcb[pid].state = process_state::READY; + asm volatile("sti"); +} + +void scheduler::soft_reschedule(pid_t pid){ + thor_assert(started, "The scheduler must be started before soft_reschedule is called"); + + asm volatile("cli"); + if(pcb[pid].state == process_state::READY){ + pcb[pid].state = process_state::RUNNING; + } else { + reschedule(); + } + asm volatile("sti"); } void scheduler::sleep_ms(pid_t pid, size_t time){ + thor_assert(started, "The scheduler must be started before sleep_ms is called"); + thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds"); thor_assert(pcb[pid].state == process_state::RUNNING, "Only RUNNING processes can sleep"); @@ -837,27 +1015,39 @@ void scheduler::sleep_ms(pid_t pid, size_t time){ } size_t scheduler::register_new_handle(const std::vector& path){ + thor_assert(started, "The scheduler must be started before new_handle is called"); + pcb[current_pid].handles.push_back(path); return pcb[current_pid].handles.size() - 1; } void scheduler::release_handle(size_t fd){ + thor_assert(started, "The scheduler must be started before release_handle is called"); + pcb[current_pid].handles[fd].clear(); } bool scheduler::has_handle(size_t fd){ + thor_assert(started, "The scheduler must be started before has_handle is called"); + return fd < pcb[current_pid].handles.size(); } const std::vector& scheduler::get_handle(size_t fd){ + thor_assert(started, "The scheduler must be started before get_handle is called"); + return pcb[current_pid].handles[fd]; } const std::vector& scheduler::get_working_directory(){ + thor_assert(started, "The scheduler must be started before get_working_directory is called"); + return pcb[current_pid].working_directory; } void scheduler::set_working_directory(const std::vector& directory){ + thor_assert(started, "The scheduler must be started before set_working_directory is called"); + pcb[current_pid].working_directory = directory; } diff --git a/kernel/src/terminal.cpp b/kernel/src/terminal.cpp index 0c20e76a..cd392159 100644 --- a/kernel/src/terminal.cpp +++ b/kernel/src/terminal.cpp @@ -21,6 +21,13 @@ bool shift = false; std::array terminals; +void tasklet_handle_input(size_t d1, size_t d2){ + auto key = static_cast(d1); + auto term = reinterpret_cast(d2); + + term->handle_input(key); +} + } //end of anonymous namespace void stdio::virtual_terminal::print(char key){ @@ -29,7 +36,18 @@ void stdio::virtual_terminal::print(char key){ } void stdio::virtual_terminal::send_input(char key){ + scheduler::tasklet task; + task.fun = &tasklet_handle_input; + task.d1 = key; + task.d2 = reinterpret_cast(this); + + scheduler::irq_register_tasklet(task, scheduler::DEFAULT_PRIORITY); +} + +void stdio::virtual_terminal::handle_input(char key){ if(canonical){ + std::lock_guard l(terminal_lock); + //Key released if(key & 0x80){ key &= ~(0x80); @@ -74,37 +92,41 @@ size_t stdio::virtual_terminal::read_input(char* buffer, size_t max){ char c; while(true){ - while(read < max && !input_buffer.empty()){ - c = input_buffer.pop(); + { + std::lock_guard l(terminal_lock); - canonical_buffer.push(c); + while(read < max && !input_buffer.empty()){ + c = input_buffer.pop(); - if(c == '\b'){ - if(read > 0){ - --read; - } - } else { - ++read; + canonical_buffer.push(c); - if(c == '\n'){ - break; - } - } - } - - if(read > 0 && (c == '\n' || read == max)){ - read = 0; - while(!canonical_buffer.empty()){ - auto value = canonical_buffer.pop(); - - if(value == '\b'){ - --read; + if(c == '\b'){ + if(read > 0){ + --read; + } } else { - buffer[read++] = value; + ++read; + + if(c == '\n'){ + break; + } } } - return read; + if(read > 0 && (c == '\n' || read == max)){ + read = 0; + while(!canonical_buffer.empty()){ + auto value = canonical_buffer.pop(); + + if(value == '\b'){ + --read; + } else { + buffer[read++] = value; + } + } + + return read; + } } input_queue.sleep(); diff --git a/tstl/include/circular_buffer.hpp b/tstl/include/circular_buffer.hpp index 9b06c9c3..a9c8f1e3 100644 --- a/tstl/include/circular_buffer.hpp +++ b/tstl/include/circular_buffer.hpp @@ -15,36 +15,30 @@ private: T buffer[Size]; - volatile size_t start; - volatile size_t end; + volatile size_t tail; + volatile size_t head; public: - circular_buffer() : start(0), end(0) { + circular_buffer() : tail(0), head(0) { //Nothing to init } bool full() const { - return (end + 1) % Size == start; + return tail - head == S; } bool empty() const { - return end == start; + return tail - head == 0; } - bool push(T value){ - if(full()){ - return false; - } else { - buffer[end] = value; - end = (end + 1) % Size; - - return true; - } + void push(T value){ + buffer[tail % S] = value; + ++tail; } T pop(){ - auto value = buffer[start]; - start = (start + 1) % Size; + auto value = buffer[head % S]; + ++head; return value; } };