various imrpovements and implementations on the kernel

master
Anton Lydike 3 years ago
parent 61017db51c
commit 3b93b864ac

@ -54,7 +54,10 @@ void read_binary_table()
// create a new process for each binary found // create a new process for each binary found
// it should have around 4kb stack // it should have around 4kb stack
create_new_process(binary_table+i, 1<<12); optional_pcbptr res = create_new_process(binary_table+i, 1<<12);
if (has_error(res)) {
dbgln("Error creating initial process!", 31);
}
} }
} }

@ -4,12 +4,11 @@
#define true 1 #define true 1
#define false 0 #define false 0
#define XLEN 32 // 32 bit system #define PROCESS_COUNT 8 // number of concurrent processes
#define PROCESS_COUNT 32 // number of concurrent processes #define NUM_BINARIES 4 // number of binaries loaded simultaneously
#define NUM_BINARIES 16 // number of binaries loaded simultaneously
// scheduler settings // scheduler settings
#define TIME_SLICE_LEN 100 // number of cpu time ticks per slice #define TIME_SLICE_LEN 10 // number of cpu time ticks per slice
// init function // init function
extern __attribute__((__noreturn__)) void init(); extern __attribute__((__noreturn__)) void init();

@ -45,7 +45,8 @@ _start:
jal init jal init
// halt machine after returning from init // halt machine after returning from init
csrwi CSR_HALT, 1 li t0, -1
csrw CSR_HALT, t0
1: 1:
j 1b j 1b
@ -163,3 +164,12 @@ memset:
blt a1, a2, 1b blt a1, a2, 1b
ret ret
#endif #endif
// this is where instantiated threads return to once they are finished
.section .thread_fini
.global thread_finalizer
thread_finalizer:
// just a simple exit syscall
nop
li a7, 5
ecall

@ -16,11 +16,16 @@ ecall_handler ecall_table[ECALL_TABLE_LEN] = { 0 };
int ecall_handle_spawn_thread(int* args_ptr, ProcessControlBlock* pcb) int ecall_handle_spawn_thread(int* args_ptr, ProcessControlBlock* pcb)
{ {
void* entry = (void*) args_ptr[0]; void* entry = (void*) args_ptr[0]; // a0
void* args = (void*) args_ptr[1]; void* args = (void*) args_ptr[1]; // a1
//int flags = args_ptr[2]; // a2
optional_pcbptr pcb_or_err = create_new_thread(pcb, entry, args, 1<<14);
if (has_error(pcb_or_err))
return pcb_or_err.error;
return EINVAL; return EINVAL;
// void* entry, void* args
} }
int ecall_handle_sleep(int* args, ProcessControlBlock* pcb) int ecall_handle_sleep(int* args, ProcessControlBlock* pcb)
@ -38,13 +43,32 @@ int ecall_handle_sleep(int* args, ProcessControlBlock* pcb)
int ecall_handle_join(int* args, ProcessControlBlock* pcb) int ecall_handle_join(int* args, ProcessControlBlock* pcb)
{ {
return EINVAL; int pid = args[0]; // a0
ProcessControlBlock* target = process_from_pid(pid);
if (target == NULL)
return ESRCH;
if (target->status == PROC_DEAD)
return target->exit_code;
pcb->status = PROC_WAIT_PROC;
pcb->waiting_for_process = target;
int timeout = args[1];
if (timeout <= 0)
return 0;
unsigned int len = (unsigned int) timeout;
pcb->asleep_until = read_time() + len;
return 0;
} }
int ecall_handle_kill(int* args, ProcessControlBlock* pcb) int ecall_handle_kill(int* args, ProcessControlBlock* pcb)
{ {
return EINVAL; return EINVAL;
} }
int ecall_handle_exit(int* args, ProcessControlBlock* pcb) int ecall_handle_exit(int* args, ProcessControlBlock* pcb)
@ -52,6 +76,8 @@ int ecall_handle_exit(int* args, ProcessControlBlock* pcb)
pcb->status = PROC_DEAD; pcb->status = PROC_DEAD;
pcb->exit_code = *args; pcb->exit_code = *args;
dbgln("exit", 4);
char msg[34] = "process exited with code "; char msg[34] = "process exited with code ";
itoa(pcb->pid, &msg[8], 10); itoa(pcb->pid, &msg[8], 10);
@ -59,28 +85,36 @@ int ecall_handle_exit(int* args, ProcessControlBlock* pcb)
dbgln(msg, 34); dbgln(msg, 34);
// recursively kill all child processes
kill_child_processes(pcb);
return 0; return 0;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
void trap_handle_ecall() { static void print_num(int num) {
{ char buff[16];
char* end = itoa(num, buff, 10);
dbgln(buff, (int)(end - buff));
}
void trap_handle_ecall() {
mark_ecall_entry(); mark_ecall_entry();
};
ProcessControlBlock* pcb = get_current_process(); ProcessControlBlock* pcb = get_current_process();
int *regs = pcb->regs; int *regs = pcb->regs;
int code = regs[16]; // code is inside a7 int code = regs[REG_A0 + 7]; // code is inside a7
dbgln("ecall:", 6);
print_num(code);
// check if the code is too large/small or if the handler is zero // check if the code is too large/small or if the handler is zero
if (code < 0 || code > ECALL_TABLE_LEN || ecall_table[code] == 0) { if (code < 0 || code > ECALL_TABLE_LEN || ecall_table[code] == 0) {
regs[9] = ENOCODE; regs[REG_A0] = ENOCODE;
__asm__("ebreak");
} else { } else {
// get the corresponding ecall handler // get the corresponding ecall handler
ecall_handler handler = ecall_table[code]; ecall_handler handler = ecall_table[code];
regs[9] = handler(&regs[9], pcb); regs[REG_A0] = handler(&regs[REG_A0], pcb);
} }
// increment pc of this process // increment pc of this process
@ -131,7 +165,7 @@ void trap_handle(int interrupt_bit, int code, int mtval)
HALT(13); HALT(13);
} }
} }
HALT(1); HALT(14);
__builtin_unreachable(); __builtin_unreachable();
} }
@ -146,6 +180,6 @@ void init_ecall_table()
void handle_exception(int ecode, int mtval) void handle_exception(int ecode, int mtval)
{ {
dbgln("Trap encountered!", 17); dbgln("exception encountered!", 17);
__asm__("ebreak"); __asm__("ebreak");
} }

@ -15,7 +15,7 @@ enum ecall_codes {
ECALL_EXIT = 5, ECALL_EXIT = 5,
}; };
#define ECALL_TABLE_LEN 16 #define ECALL_TABLE_LEN 8
// initializer for ecall lookup table // initializer for ecall lookup table
void init_ecall_table(); void init_ecall_table();

@ -44,13 +44,24 @@ char* itoa (int value, char* str, int base)
value *= -1; value *= -1;
} }
int num; int digits = 0;
int num = 0;
// reverse number
do {
num = num * base;
num += value % base;
value = value / base;
digits++;
} while (value > 0);
value = num;
do { do {
num = value % base; num = value % base;
value = value / base; value = value / base;
*str++ = alpha[num]; *str++ = alpha[num];
digits--;
} }
while (value > 0); while (digits > 0);
return str; return str;
} }

@ -1,6 +1,8 @@
#ifndef H_ktypes #ifndef H_ktypes
#define H_ktypes #define H_ktypes
#define NULL ((void*) 0)
/* /*
* Error codes * Error codes
*/ */
@ -10,6 +12,7 @@ enum error_code {
EINVAL = 2, // invalid argument value EINVAL = 2, // invalid argument value
ENOMEM = 3, // not enough memory ENOMEM = 3, // not enough memory
ENOBUFS = 4, // no space left in buffer ENOBUFS = 4, // no space left in buffer
ESRCH = 5, // no such process
}; };
/* /*
@ -37,6 +40,24 @@ struct ProcessControlBlock {
ProcessControlBlock* waiting_for_process; ProcessControlBlock* waiting_for_process;
struct loaded_binary* binary; struct loaded_binary* binary;
unsigned long long int asleep_until; unsigned long long int asleep_until;
// parent
ProcessControlBlock* parent;
};
enum pcb_struct_registers {
REG_RA = 0,
REG_SP = 1,
REG_GP = 2,
REG_TP = 3,
REG_T0 = 4,
REG_T1 = 5,
REG_t2 = 6,
REG_FP = 7,
REG_S0 = 7,
REG_S1 = 8,
REG_A0 = 9,
REG_S2 = 17,
REG_T3 = 27
}; };

@ -4,10 +4,37 @@
#include "io.h" #include "io.h"
#include "malloc.h" #include "malloc.h"
// use memset provided in boot.S
extern void memset(int, void*, void*);
// this is the address where threads return to
extern int thread_finalizer;
static void print_num(int num) {
char buff[16];
char* end = itoa(num, buff, 10);
dbgln(buff, (int)(end - buff));
}
static void print_processes() {
char line[12] = "============";
char alive[5] = "alive";
char dead[4] = "dead";
for (int i = 0; i < PROCESS_COUNT; i++) {
ProcessControlBlock* pcb = processes + i;
print_num(pcb->pid);
if (pcb->status == PROC_DEAD) {
dbgln(dead, 4);
} else {
dbgln(alive, 5);
}
print_num(i);
dbgln(line, 12);
}
}
// scheduling data: // scheduling data:
ProcessControlBlock processes[PROCESS_COUNT]; ProcessControlBlock processes[PROCESS_COUNT];
ProcessControlBlock* current_process; ProcessControlBlock* current_process = NULL;
unsigned long long int scheduling_interrupted_start; unsigned long long int scheduling_interrupted_start;
unsigned long long int next_interrupt_scheduled_for; unsigned long long int next_interrupt_scheduled_for;
int next_process_id = 1; int next_process_id = 1;
@ -15,10 +42,6 @@ int next_process_id = 1;
void scheduler_run_next () void scheduler_run_next ()
{ {
current_process = scheduler_select_free(); current_process = scheduler_select_free();
char msg[30] = "scheduling ";
char* end = itoa(current_process->pid, &msg[11], 10);
dbgln(msg, ((int) end) - ((int) msg));
// set up timer interrupt // set up timer interrupt
set_next_interrupt(); set_next_interrupt();
scheduler_switch_to(current_process); scheduler_switch_to(current_process);
@ -29,6 +52,7 @@ void scheduler_try_return_to(ProcessControlBlock* pcb)
if (pcb->status != PROC_RDY) { if (pcb->status != PROC_RDY) {
scheduler_run_next(); scheduler_run_next();
} else { } else {
dbgln("returning to process...", 23);
current_process = pcb; current_process = pcb;
// add time spent in ecall handler to the processes time slice // add time spent in ecall handler to the processes time slice
next_interrupt_scheduled_for = next_interrupt_scheduled_for + (read_time() - scheduling_interrupted_start); next_interrupt_scheduled_for = next_interrupt_scheduled_for + (read_time() - scheduling_interrupted_start);
@ -43,12 +67,18 @@ ProcessControlBlock* scheduler_select_free()
int i; int i;
int timeout_available = false; // note if a timeout is available int timeout_available = false; // note if a timeout is available
if (current_process == NULL)
current_process = processes + PROCESS_COUNT - 1;
while (true) { while (true) {
mtime = read_time(); mtime = read_time();
int i = 1;
ProcessControlBlock* pcb = current_process + 1;
if (pcb > processes + PROCESS_COUNT)
pcb = processes;
for (i=0; i < PROCESS_COUNT; i++) { while (pcb != current_process) {
ProcessControlBlock* pcb = processes + i; if (pcb->status == PROC_RDY)
if (pcb->status == PROC_RDY && pcb != current_process)
return pcb; return pcb;
if (pcb->status == PROC_WAIT_SLEEP) { if (pcb->status == PROC_WAIT_SLEEP) {
@ -68,7 +98,11 @@ ProcessControlBlock* scheduler_select_free()
timeout_available = true; timeout_available = true;
} }
} }
pcb++;
if (pcb > processes + PROCESS_COUNT)
pcb = processes;
} }
if (current_process->status == PROC_RDY) { if (current_process->status == PROC_RDY) {
return current_process; return current_process;
} }
@ -127,13 +161,13 @@ void scheduler_switch_to(ProcessControlBlock* pcb)
__builtin_unreachable(); __builtin_unreachable();
} }
int scheduler_index_from_pid(int pid) ProcessControlBlock* process_from_pid(int pid)
{ {
for (int i = 0; i < PROCESS_COUNT; i++) { for (int i = 0; i < PROCESS_COUNT; i++) {
if (processes[i].pid == pid) if (processes[i].pid == pid)
return i; return processes + i;
} }
return -1; return NULL;
} }
int* get_current_process_registers() int* get_current_process_registers()
@ -168,17 +202,19 @@ optional_pcbptr find_available_pcb_slot() {
return (optional_pcbptr) { .error = ENOBUFS }; return (optional_pcbptr) { .error = ENOBUFS };
pcb = processes + index; pcb = processes + index;
} }
index++;
return (optional_pcbptr) { .value = pcb }; return (optional_pcbptr) { .value = pcb };
} }
int create_new_process(loaded_binary* bin, int stack_size) optional_pcbptr create_new_process(loaded_binary* bin, int stack_size)
{ {
// try to get a position in the processes list // try to get a position in the processes list
optional_pcbptr slot_or_err = find_available_pcb_slot(); optional_pcbptr slot_or_err = find_available_pcb_slot();
// if that failed, we cannot creat a new process // if that failed, we cannot creat a new process
if (has_error(slot_or_err)) { if (has_error(slot_or_err)) {
dbgln("No more process structs!", 24); dbgln("No more process structs!", 24);
return slot_or_err.error; return slot_or_err;
} }
// allocate stack for the new process // allocate stack for the new process
@ -186,7 +222,7 @@ int create_new_process(loaded_binary* bin, int stack_size)
// if that failed, we also can't create a new process // if that failed, we also can't create a new process
if (has_error(stack_top_or_err)) { if (has_error(stack_top_or_err)) {
dbgln("Error while allocating stack for process", 40); dbgln("Error while allocating stack for process", 40);
return stack_top_or_err.error; return (optional_pcbptr) { .error = stack_top_or_err.error };
} }
ProcessControlBlock* pcb = slot_or_err.value; ProcessControlBlock* pcb = slot_or_err.value;
@ -199,11 +235,75 @@ int create_new_process(loaded_binary* bin, int stack_size)
pcb->pid = pid; pcb->pid = pid;
pcb->pc = bin->entrypoint; pcb->pc = bin->entrypoint;
pcb->binary = bin; pcb->binary = bin;
pcb->parent = NULL;
pcb->asleep_until = 0;
// zero out registers
memset(0, pcb->regs, pcb->regs + 31);
// load stack top into stack pointer register // load stack top into stack pointer register
pcb->regs[1] = (int) stack_top_or_err.value; pcb->regs[REG_SP] = (int) stack_top_or_err.value;
// load pid into a0 register // load pid into a0 register
pcb->regs[9] = pid; pcb->regs[REG_A0] = pid;
dbgln("Created new process!", 20); dbgln("Created new process!", 20);
return (optional_pcbptr) { .value = pcb };
}
optional_pcbptr create_new_thread(ProcessControlBlock* parent, void* entrypoint, void* args, int stack_size)
{
// try to get a position in the processes list
optional_pcbptr slot_or_err = find_available_pcb_slot();
// if that failed, we cannot creat a new process
if (has_error(slot_or_err)) {
dbgln("No more process structs!", 24);
return slot_or_err;
}
// allocate stack for the new process
optional_voidptr stack_top_or_err = malloc_stack(stack_size); // allocate 4Kib stack
// if that failed, we also can't create a new process
if (has_error(stack_top_or_err)) {
dbgln("Error while allocating stack for thread", 39);
return (optional_pcbptr) { .error = stack_top_or_err.error };
}
ProcessControlBlock* pcb = slot_or_err.value;
// determine next pid
int pid = next_process_id++;
// mark process as ready
pcb->status = PROC_RDY;
pcb->pid = pid;
pcb->pc = (int) entrypoint;
pcb->binary = parent->binary;
pcb->parent = parent;
pcb->asleep_until = 0;
// zero out registers
memset(0, pcb->regs, pcb->regs + 31);
// set return address to global thread finalizer
pcb->regs[REG_RA] = (int) &thread_finalizer;
// load stack top into stack pointer register
pcb->regs[REG_SP] = (int) stack_top_or_err.value;
// copy global pointer from parent
pcb->regs[REG_GP] = parent->regs[REG_GP];
// load args pointer into a0 register
pcb->regs[REG_A0] = (int) args;
dbgln("Created new thread!", 19);
return (optional_pcbptr) { .value = pcb };
}
void kill_child_processes(ProcessControlBlock* pcb)
{
for (int i = 0; i < PROCESS_COUNT; i++) {
ProcessControlBlock* proc = processes + i;
if (proc->parent != pcb)
continue;
proc->status = PROC_DEAD;
proc->exit_code = -9; // set arbitrary exit code
kill_child_processes(proc);
}
} }

@ -9,16 +9,16 @@ extern ProcessControlBlock processes[PROCESS_COUNT];
// scheduler methods // scheduler methods
ProcessControlBlock* scheduler_select_free(); ProcessControlBlock* scheduler_select_free();
int scheduler_create_process(int binid);
void set_next_interrupt(); void set_next_interrupt();
void __attribute__((noreturn)) scheduler_run_next(); void __attribute__((noreturn)) scheduler_run_next();
void __attribute__((noreturn)) scheduler_try_return_to(ProcessControlBlock*); void __attribute__((noreturn)) scheduler_try_return_to(ProcessControlBlock*);
void __attribute__((noreturn)) scheduler_switch_to(ProcessControlBlock*); void __attribute__((noreturn)) scheduler_switch_to(ProcessControlBlock*);
int scheduler_index_from_pid(int pid); ProcessControlBlock* process_from_pid(int pid);
int* get_current_process_registers(); int* get_current_process_registers();
ProcessControlBlock* get_current_process(); ProcessControlBlock* get_current_process();
void mark_ecall_entry(); void mark_ecall_entry();
int create_new_process(loaded_binary* bin, int stack_size); optional_pcbptr create_new_process(loaded_binary*, int);
optional_pcbptr create_new_thread(ProcessControlBlock*, void*, void*, int);
void kill_child_processes(ProcessControlBlock* pcb);
#endif #endif

@ -237,4 +237,9 @@ SECTIONS
/* End of uninitialized data segment (used by syscalls.c for heap) */ /* End of uninitialized data segment (used by syscalls.c for heap) */
PROVIDE( end = . ); PROVIDE( end = . );
_end = ALIGN(8); _end = ALIGN(8);
.thread_fini :
{
*(.thread_fini)
}
} }

@ -14,7 +14,7 @@ INCLUDE_THESE_SECTIONS = set((
'.text', '.stack', '.bss', '.sdata', '.rdata', '.rodata' '.text', '.stack', '.bss', '.sdata', '.rdata', '.rodata'
'.sbss', '.data', '.stack', '.init', '.sbss', '.data', '.stack', '.init',
'.fini', '.preinit_array', '.init_array', '.fini', '.preinit_array', '.init_array',
'.fini_array', '.rodata' '.fini_array', '.rodata', '.thread_fini'
)) ))
# these sections are empty, so we don't want to read the elf here # these sections are empty, so we don't want to read the elf here
EMPTY_SECTIONS = set(( EMPTY_SECTIONS = set((

Loading…
Cancel
Save