diff --git a/kernel.c b/kernel.c index 335b8e2..95352b3 100644 --- a/kernel.c +++ b/kernel.c @@ -17,12 +17,15 @@ extern void memset(unsigned int, void*, void*); extern void init() { - init_ecall_table(); - dbgln("Kernel started!", 15); - + // initialize scheduler + scheudler_init(); + // initialize tabel for associating ecall codes with their handlers + init_ecall_table(); + // read supplied binaries, this will call malloc_init with the memory layout + // then it will create a new process for each loaded binary read_binary_table(); - + // give control to the scheudler and start runnign user programs scheduler_run_next(); } diff --git a/kinclude/boot.S b/kinclude/boot.S index db28f32..8fb03a7 100644 --- a/kinclude/boot.S +++ b/kinclude/boot.S @@ -5,35 +5,32 @@ stack_bottom: .space 4096 stack_top: +// put the startup code in a special section so that the linker can position it at the start of the binary .section .text._start +// tell the linker that init is a function located elsewhere .extern init .type init, @function -.extern trap_handle -.type trap_handle, @function - - .global _start _start: + // setup mie register, enable timer and software interrupts targeting machine mode + // mie[7] MTIE = 1 - enable timer interrupts + // mie[3] MSIE = 1 - enable software interrupts + li a0, 0x88 + csrw CSR_MIE, a0 // write to mie csr + // load trap vector address into a0 + la a0, trap_vector + csrw CSR_MTVEC, a0 // write to mtvec csr // enable interrupts in mstatus - // this is the setting loaded: - // [07] MPIE = 1 - we want to enable interrupts with mret - // [03] MIE = 0 - we don't want interrupts now - // [11:12] MPP = 0 - we want to return into user mode - // all other bits should be zero + // mstatus[07] MPIE = 1 - we want to enable interrupts with mret li a0, 0x80 - csrrw zero, CSR_MSTATUS, a0 // write to mstatus - // setup a0 to hold |trap tbl addr|mode| - // len:| 30 | 2 | - la a0, trap_vector - csrrw zero, CSR_MTVEC, a0 // write a0 into mtvec csr entry - // write + csrw CSR_MSTATUS, a0 // write to mstatus csr .option push .option norelax // init sp and gp la sp, stack_top - la gp, _gp + la gp, __global_pointer$ .option pop // clear kernel bss section mv a0, zero @@ -47,9 +44,13 @@ _start: // halt machine after returning from init li t0, -1 csrw CSR_HALT, t0 + // if the halt CSR somehow didn't exit immediately trap execution in this infinite loop 1: j 1b +.extern trap_handle +.type trap_handle, @function + .align 4 trap_vector: // save all registers into the PCB struct diff --git a/kinclude/ktypes.h b/kinclude/ktypes.h index 1ac5e0b..083f9d7 100644 --- a/kinclude/ktypes.h +++ b/kinclude/ktypes.h @@ -13,6 +13,7 @@ enum error_code { ENOMEM = 3, // not enough memory ENOBUFS = 4, // no space left in buffer ESRCH = 5, // no such process + EABORT = 6 }; /* diff --git a/kinclude/sched.c b/kinclude/sched.c index 7ccf9e4..5335282 100644 --- a/kinclude/sched.c +++ b/kinclude/sched.c @@ -29,6 +29,11 @@ void scheduler_run_next() scheduler_switch_to(current_process); } +void scheudler_init() +{ + current_process = processes + PROCESS_COUNT - 1; +} + // try to return to a process void scheduler_try_return_to(ProcessControlBlock* pcb) { @@ -58,47 +63,59 @@ ProcessControlBlock* scheduler_select_free() unsigned long long int mtime; int timeout_available = false; // note if a timeout is available - if (current_process == NULL) - current_process = processes + PROCESS_COUNT - 1; - while (true) { mtime = read_time(); - ProcessControlBlock* pcb = current_process + 1; - if (pcb > processes + PROCESS_COUNT) - pcb = processes; + // start at the last scheduled process + ProcessControlBlock* pcb = current_process; + + // iterate once over the whole list + do { + // get next pcb + pcb++; + // wrap around the end of the list + if (pcb > processes + PROCESS_COUNT) + pcb = processes; - while (pcb != current_process) { + // when we find a process which is ready to be scheduled, return it! if (pcb->status == PROC_RDY) return pcb; + // if it's sleeping, check if it is time to wake it up if (pcb->status == PROC_WAIT_SLEEP) { if (pcb->asleep_until < mtime) { + pcb->status = PROC_RDY; return pcb; } timeout_available = true; } + // if it's waiting for another process, check if the process exited + // or if is waiting with a timeout, tell it the timeout expired if (pcb->status == PROC_WAIT_PROC) { + if (pcb->waiting_for_process != NULL && + pcb->waiting_for_process->status == PROC_DEAD) { + // the requested process exited, so we can set the status code and + pcb->regs[REG_A0] = pcb->waiting_for_process->exit_code; + pcb->regs[REG_A0+1] = 0; + pcb->status = PROC_RDY; + return pcb; + } if (pcb->asleep_until != 0) { if (pcb->asleep_until < mtime) { - //TODO: set process return args! + // if the timeout ran out, set an error code + pcb->regs[REG_A0 + 1] = EABORT; + pcb->status = PROC_RDY; return pcb; } timeout_available = true; } } - pcb++; - if (pcb > processes + PROCESS_COUNT) - pcb = processes; - } - - if (current_process->status == PROC_RDY) { - return current_process; - } + } while (pcb != current_process); + // when we finished iterating over all processes and no process can be scheduled we have a problem if (timeout_available == false) { - // either process deadlock or no processes alive. - //TODO: handle missing executable thread + // either process deadlock without timeout or no processes alive. + //TODO: handle deadlocks by killing a process dbgln("No thread active!", 17); HALT(22); } diff --git a/kinclude/sched.h b/kinclude/sched.h index 16ed363..bceb069 100644 --- a/kinclude/sched.h +++ b/kinclude/sched.h @@ -8,6 +8,7 @@ extern ProcessControlBlock processes[PROCESS_COUNT]; // scheduler methods +void scheudler_init(); ProcessControlBlock* scheduler_select_free(); void set_next_interrupt(); void __attribute__((noreturn)) scheduler_run_next();