various imrpovements and implementations on the kernel
This commit is contained in:
parent
61017db51c
commit
3b93b864ac
5
kernel.c
5
kernel.c
@ -54,7 +54,10 @@ void read_binary_table()
|
||||
|
||||
// create a new process for each binary found
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
7
kernel.h
7
kernel.h
@ -4,12 +4,11 @@
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#define XLEN 32 // 32 bit system
|
||||
#define PROCESS_COUNT 32 // number of concurrent processes
|
||||
#define NUM_BINARIES 16 // number of binaries loaded simultaneously
|
||||
#define PROCESS_COUNT 8 // number of concurrent processes
|
||||
#define NUM_BINARIES 4 // number of binaries loaded simultaneously
|
||||
|
||||
// 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
|
||||
extern __attribute__((__noreturn__)) void init();
|
||||
|
@ -45,7 +45,8 @@ _start:
|
||||
jal init
|
||||
|
||||
// halt machine after returning from init
|
||||
csrwi CSR_HALT, 1
|
||||
li t0, -1
|
||||
csrw CSR_HALT, t0
|
||||
1:
|
||||
j 1b
|
||||
|
||||
@ -162,4 +163,13 @@ memset:
|
||||
addi a1, a1, 32
|
||||
blt a1, a2, 1b
|
||||
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)
|
||||
{
|
||||
void* entry = (void*) args_ptr[0];
|
||||
void* args = (void*) args_ptr[1];
|
||||
void* entry = (void*) args_ptr[0]; // a0
|
||||
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;
|
||||
// void* entry, void* args
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return EINVAL;
|
||||
|
||||
}
|
||||
|
||||
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->exit_code = *args;
|
||||
|
||||
dbgln("exit", 4);
|
||||
|
||||
char msg[34] = "process exited with code ";
|
||||
|
||||
itoa(pcb->pid, &msg[8], 10);
|
||||
@ -59,28 +85,36 @@ int ecall_handle_exit(int* args, ProcessControlBlock* pcb)
|
||||
|
||||
dbgln(msg, 34);
|
||||
|
||||
// recursively kill all child processes
|
||||
kill_child_processes(pcb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
void trap_handle_ecall() {
|
||||
{
|
||||
|
||||
mark_ecall_entry();
|
||||
};
|
||||
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();
|
||||
ProcessControlBlock* pcb = get_current_process();
|
||||
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
|
||||
if (code < 0 || code > ECALL_TABLE_LEN || ecall_table[code] == 0) {
|
||||
regs[9] = ENOCODE;
|
||||
__asm__("ebreak");
|
||||
regs[REG_A0] = ENOCODE;
|
||||
} else {
|
||||
// get the corresponding ecall handler
|
||||
ecall_handler handler = ecall_table[code];
|
||||
regs[9] = handler(®s[9], pcb);
|
||||
regs[REG_A0] = handler(®s[REG_A0], pcb);
|
||||
}
|
||||
|
||||
// increment pc of this process
|
||||
@ -99,8 +133,8 @@ void trap_handle(int interrupt_bit, int code, int mtval)
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
scheduler_run_next();
|
||||
break;
|
||||
scheduler_run_next();
|
||||
break;
|
||||
default:
|
||||
// impossible
|
||||
HALT(12);
|
||||
@ -131,7 +165,7 @@ void trap_handle(int interrupt_bit, int code, int mtval)
|
||||
HALT(13);
|
||||
}
|
||||
}
|
||||
HALT(1);
|
||||
HALT(14);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@ -146,6 +180,6 @@ void init_ecall_table()
|
||||
|
||||
void handle_exception(int ecode, int mtval)
|
||||
{
|
||||
dbgln("Trap encountered!", 17);
|
||||
dbgln("exception encountered!", 17);
|
||||
__asm__("ebreak");
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ enum ecall_codes {
|
||||
ECALL_EXIT = 5,
|
||||
};
|
||||
|
||||
#define ECALL_TABLE_LEN 16
|
||||
#define ECALL_TABLE_LEN 8
|
||||
|
||||
// initializer for ecall lookup table
|
||||
void init_ecall_table();
|
||||
|
@ -44,13 +44,24 @@ char* itoa (int value, char* str, int base)
|
||||
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 {
|
||||
num = value % base;
|
||||
value = value / base;
|
||||
*str++ = alpha[num];
|
||||
digits--;
|
||||
}
|
||||
while (value > 0);
|
||||
while (digits > 0);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
#ifndef H_ktypes
|
||||
#define H_ktypes
|
||||
|
||||
#define NULL ((void*) 0)
|
||||
|
||||
/*
|
||||
* Error codes
|
||||
*/
|
||||
|
||||
enum error_code {
|
||||
ENOCODE = 1, // invalid syscall code
|
||||
EINVAL = 2, // invalid argument value
|
||||
ENOMEM = 3, // not enough memory
|
||||
ENOBUFS = 4, // no space left in buffer
|
||||
ENOCODE = 1, // invalid syscall code
|
||||
EINVAL = 2, // invalid argument value
|
||||
ENOMEM = 3, // not enough memory
|
||||
ENOBUFS = 4, // no space left in buffer
|
||||
ESRCH = 5, // no such process
|
||||
};
|
||||
|
||||
/*
|
||||
@ -37,6 +40,24 @@ struct ProcessControlBlock {
|
||||
ProcessControlBlock* waiting_for_process;
|
||||
struct loaded_binary* binary;
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
|
132
kinclude/sched.c
132
kinclude/sched.c
@ -4,10 +4,37 @@
|
||||
#include "io.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:
|
||||
ProcessControlBlock processes[PROCESS_COUNT];
|
||||
ProcessControlBlock* current_process;
|
||||
ProcessControlBlock* current_process = NULL;
|
||||
unsigned long long int scheduling_interrupted_start;
|
||||
unsigned long long int next_interrupt_scheduled_for;
|
||||
int next_process_id = 1;
|
||||
@ -15,10 +42,6 @@ int next_process_id = 1;
|
||||
void scheduler_run_next ()
|
||||
{
|
||||
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_next_interrupt();
|
||||
scheduler_switch_to(current_process);
|
||||
@ -29,6 +52,7 @@ void scheduler_try_return_to(ProcessControlBlock* pcb)
|
||||
if (pcb->status != PROC_RDY) {
|
||||
scheduler_run_next();
|
||||
} else {
|
||||
dbgln("returning to process...", 23);
|
||||
current_process = pcb;
|
||||
// add time spent in ecall handler to the processes time slice
|
||||
next_interrupt_scheduled_for = next_interrupt_scheduled_for + (read_time() - scheduling_interrupted_start);
|
||||
@ -43,12 +67,18 @@ ProcessControlBlock* scheduler_select_free()
|
||||
int i;
|
||||
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();
|
||||
int i = 1;
|
||||
ProcessControlBlock* pcb = current_process + 1;
|
||||
if (pcb > processes + PROCESS_COUNT)
|
||||
pcb = processes;
|
||||
|
||||
for (i=0; i < PROCESS_COUNT; i++) {
|
||||
ProcessControlBlock* pcb = processes + i;
|
||||
if (pcb->status == PROC_RDY && pcb != current_process)
|
||||
while (pcb != current_process) {
|
||||
if (pcb->status == PROC_RDY)
|
||||
return pcb;
|
||||
|
||||
if (pcb->status == PROC_WAIT_SLEEP) {
|
||||
@ -68,7 +98,11 @@ ProcessControlBlock* scheduler_select_free()
|
||||
timeout_available = true;
|
||||
}
|
||||
}
|
||||
pcb++;
|
||||
if (pcb > processes + PROCESS_COUNT)
|
||||
pcb = processes;
|
||||
}
|
||||
|
||||
if (current_process->status == PROC_RDY) {
|
||||
return current_process;
|
||||
}
|
||||
@ -127,13 +161,13 @@ void scheduler_switch_to(ProcessControlBlock* pcb)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
int scheduler_index_from_pid(int pid)
|
||||
ProcessControlBlock* process_from_pid(int pid)
|
||||
{
|
||||
for (int i = 0; i < PROCESS_COUNT; i++) {
|
||||
if (processes[i].pid == pid)
|
||||
return i;
|
||||
return processes + i;
|
||||
}
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int* get_current_process_registers()
|
||||
@ -168,17 +202,19 @@ optional_pcbptr find_available_pcb_slot() {
|
||||
return (optional_pcbptr) { .error = ENOBUFS };
|
||||
pcb = processes + index;
|
||||
}
|
||||
index++;
|
||||
|
||||
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
|
||||
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.error;
|
||||
return slot_or_err;
|
||||
}
|
||||
|
||||
// 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 (has_error(stack_top_or_err)) {
|
||||
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;
|
||||
@ -199,11 +235,75 @@ int create_new_process(loaded_binary* bin, int stack_size)
|
||||
pcb->pid = pid;
|
||||
pcb->pc = bin->entrypoint;
|
||||
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
|
||||
pcb->regs[1] = (int) stack_top_or_err.value;
|
||||
pcb->regs[REG_SP] = (int) stack_top_or_err.value;
|
||||
// load pid into a0 register
|
||||
pcb->regs[9] = pid;
|
||||
pcb->regs[REG_A0] = pid;
|
||||
|
||||
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
|
||||
ProcessControlBlock* scheduler_select_free();
|
||||
int scheduler_create_process(int binid);
|
||||
void set_next_interrupt();
|
||||
void __attribute__((noreturn)) scheduler_run_next();
|
||||
void __attribute__((noreturn)) scheduler_try_return_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();
|
||||
ProcessControlBlock* get_current_process();
|
||||
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
|
@ -237,4 +237,9 @@ SECTIONS
|
||||
/* End of uninitialized data segment (used by syscalls.c for heap) */
|
||||
PROVIDE( end = . );
|
||||
_end = ALIGN(8);
|
||||
|
||||
.thread_fini :
|
||||
{
|
||||
*(.thread_fini)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ INCLUDE_THESE_SECTIONS = set((
|
||||
'.text', '.stack', '.bss', '.sdata', '.rdata', '.rodata'
|
||||
'.sbss', '.data', '.stack', '.init',
|
||||
'.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
|
||||
EMPTY_SECTIONS = set((
|
||||
|
Loading…
Reference in New Issue
Block a user