From bb13cbeca592055291d0918793747bf373d8fa5f Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Thu, 30 Sep 2021 21:55:28 +0200 Subject: [PATCH] a lot of cleanup, added pmp --- Makefile | 41 ++++++++++++++++++++++------------------- kernel.c | 41 +++++++++++++++++++++++++++++++++-------- kinclude/csr.c | 37 +++++++++++++++++++------------------ kinclude/csr.h | 19 +++++++++++-------- kinclude/ecall.c | 30 +++++++++++++++++++++++++----- kinclude/io.c | 14 ++++++++++++++ kinclude/ktypes.h | 6 ++++++ kinclude/malloc.c | 9 +++++++-- kinclude/malloc.h | 6 +++--- kinclude/sched.c | 27 ++++++++++++++++++--------- kinclude/sched.h | 4 ++-- package.py | 32 +++++++++++++++++++++----------- programs/Makefile | 2 +- programs/spawn.c | 22 +++++----------------- programs/threads.c | 12 ++++++------ programs/threads.h | 13 ++++++++++--- 16 files changed, 203 insertions(+), 112 deletions(-) diff --git a/Makefile b/Makefile index 746c92e..f5e44af 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,9 @@ # small makefile for compiling the kernel -# kernel lib dir -KLIBDIR=kinclude -# object file dir -ODIR=obj -# target dir -TARGET=out - -GCC_PREF=riscv32-unknown-elf- - -CC=$(GCC_PREF)gcc -OBJDUMP=$(GCC_PREF)objdump -CFLAGS=-I$(KLIBDIR) -MD -mcmodel=medany -Wall -Wextra -pedantic-errors -Wno-builtin-declaration-mismatch -KERNEL_CFLAGS=-nostdlib -T linker.ld -ARCH = rv32im - ### Build configuration: -# Define the maximum number of running processes +# process and binary count PROCESS_COUNT = 8 +PACKAGED_BINARY_COUNT = 8 # Define the maximum number of binaries packaged with the kernel PACKAGED_BINARY_COUNT = 4 @@ -25,17 +11,34 @@ PACKAGED_BINARY_COUNT = 4 # Comment this out if you don't have any text IO device memory mapped CFLAGS += -DTEXT_IO_ADDR=0xff0000 -DTEXT_IO_BUFLEN=64 -# Uncomment these to build with only the rv32i standard +# If you want to build without any extension, you can uncomment the next line #CFLAGS += -D__risc_no_ext=1 -#ARCH = rv32i +# also change this to represent your target RISC-V architecture and extensions +ARCH = rv32im # Configure if mtime is memory-mapped or inside a CSR: # replace 0xFF11FF22FF33 with the correct address #CFLAGS += -DTIMECMP_IN_MEMORY=1 -DTIMECMP_MEM_ADDR=0xFF11FF22 +# Set this to the first out-of-bounds memory address +END_OF_USABLE_MEM=0xff0000 ### End configuration -CFLAGS += -march=$(ARCH) -DPROCESS_COUNT=$(PROCESS_COUNT) -DNUM_BINARIES=$(PACKAGED_BINARY_COUNT) + +# kernel lib dir +KLIBDIR=kinclude +# object file dir +ODIR=obj +# target dir +TARGET=out + +GCC_PREF=riscv32-unknown-elf- + +CC=$(GCC_PREF)gcc +OBJDUMP=$(GCC_PREF)objdump +CFLAGS+=-I$(KLIBDIR) -MD -mcmodel=medany -Wall -Wextra -pedantic-errors -Wno-builtin-declaration-mismatch -march=$(ARCH) +KERNEL_CFLAGS=-nostdlib -T linker.ld +CFLAGS+=-DPROCESS_COUNT=$(PROCESS_COUNT) -DPACKAGED_BINARY_COUNT=$(PACKAGED_BINARY_COUNT) -DEND_OF_USABLE_MEM=$(END_OF_USABLE_MEM) # dependencies that need to be built: _DEPS = ecall.c csr.c sched.c io.c malloc.c diff --git a/kernel.c b/kernel.c index 084629a..73afc9f 100644 --- a/kernel.c +++ b/kernel.c @@ -4,20 +4,26 @@ #include "sched.h" #include "io.h" #include "malloc.h" +#include "csr.h" void read_binary_table(); - -extern struct process_control_block processes[PROCESS_COUNT]; +void setup_mem_protection(); // this array is populated when the memory image is built, therefore it should // resign in a section which is not overwritten with zeros on startup -loaded_binary binary_table[NUM_BINARIES] __attribute__ ((section(".data"))); +loaded_binary binary_table[PACKAGED_BINARY_COUNT] __attribute__ ((section(".data"))); +// access the memset function defined in boot.S extern void memset(unsigned int, void*, void*); +// access linker symbols: +extern byte* _end, _ftext; + extern void init() { dbgln("Kernel started!", 15); + // setup phsycial memory protection + setup_mem_protection(); // initialize scheduler scheudler_init(); // initialize tabel for associating ecall codes with their handlers @@ -33,13 +39,13 @@ void read_binary_table() { char msg[28] = "found bin with id 0 at pos 0"; - malloc_info info = { - .allocate_memory_end = (void*) 0xFF0000, + struct malloc_info info = { + .allocate_memory_end = (void*) END_OF_USABLE_MEM, .allocate_memory_start = (void*) 0 }; // calculate the end of loaded binaries - for (int i = 0; i < NUM_BINARIES; i++) { + for (int i = 0; i < PACKAGED_BINARY_COUNT; i++) { if (binary_table[i].binid == 0) break; @@ -56,15 +62,34 @@ void read_binary_table() // initialize malloc malloc_init(&info); - for (int i = 0; i < NUM_BINARIES; i++) { + for (int i = 0; i < PACKAGED_BINARY_COUNT; i++) { if (binary_table[i].binid == 0) break; // create a new process for each binary found // it should have around 4kb stack - optional_pcbptr res = create_new_process(binary_table + i, 1 << 12); + optional_pcbptr res = create_new_process(binary_table + i); if (has_error(res)) { dbgln("Error creating initial process!", 31); } } } + +void setup_mem_protection() +{ + // this pmp config uses Top-of-Range mode - read more in the privileged spec p.49 + // we disallow all access to 0x0-0x100 from user and machine mode + // and all access to 0x100-kernel_end from user mode + // to do this, we must first calculate the kernel bin length + uint32 kernel_bin_len = ((uint32) &_end) - ((uint32) &_ftext); + + CSR_WRITE(CSR_PMPADDR, 0x100); + CSR_WRITE(CSR_PMPADDR + 1, 0x100 + kernel_bin_len); + + // this contains two pmp configs: + // fields: L A RWX + // pmpcfg0: 0b1_00_01_000 <- disallow RWX from U and M mode + // pmpcfg1: 0b0_00_01_000 <- disallow RWX from U mode + // the resulint number is 0b0000100010001000, hex 0x888 + CSR_WRITE(CSR_PMPCFG, 0x888); +} diff --git a/kinclude/csr.c b/kinclude/csr.c index 87f173e..48f3eba 100644 --- a/kinclude/csr.c +++ b/kinclude/csr.c @@ -1,3 +1,4 @@ +#include "ktypes.h" #include "csr.h" #ifdef TIMECMP_IN_MEMORY @@ -6,31 +7,31 @@ #error "You set TIMECMP_IN_MEMORY but did not provide a memory addres in TIMECMP_MEM_ADDR!" #endif -void write_mtimecmp(unsigned long long int mtimecmp) +void write_mtimecmp(uint64 mtimecmp) { - unsigned int lo = mtimecmp & 0xffffffff; - unsigned int hi = mtimecmp >> 32; - - __asm__ volatile( - "li t0, %0\n" - "sw %1, 0(t0)\n" - "sw %2, 4(t0)" :: - "i"(TIMECMP_MEM_ADDR), "r"(lo), "r"(hi) + uint32 lo = mtimecmp & 0xffffffff; + uint32 hi = mtimecmp >> 32; + + __asm__ volatile ( + "li t0, %0\n" + "sw %1, 0(t0)\n" + "sw %2, 4(t0)" :: + "i"(TIMECMP_MEM_ADDR), "r"(lo), "r"(hi) ); } #else -void write_mtimecmp(unsigned long long int mtimecmp) +void write_mtimecmp(uint64 mtimecmp) { - unsigned int lower = mtimecmp & 0xffffffff; - unsigned int higher = mtimecmp >> 32; - - __asm__ volatile( - "csrw %0, %2\n" - "csrw %1, %3" :: - "I"(CSR_MTIMECMP),"I"(CSR_MTIMECMPH), - "r"(lower), "r"(higher) + uint32 lower = mtimecmp & 0xffffffff; + uint32 higher = mtimecmp >> 32; + + __asm__ volatile ( + "csrw %0, %2\n" + "csrw %1, %3" :: + "I"(CSR_MTIMECMP),"I"(CSR_MTIMECMPH), + "r"(lower), "r"(higher) ); } diff --git a/kinclude/csr.h b/kinclude/csr.h index e8b94a3..290c116 100644 --- a/kinclude/csr.h +++ b/kinclude/csr.h @@ -16,6 +16,9 @@ #define CSR_MTIMECMP 0x780 // mtimecmp register for timer interrupts #define CSR_MTIMECMPH 0x781 // mtimecmph register for timer interrupts +#define CSR_PMPCFG 0x3A0 // start of physical memory protection config +#define CSR_PMPADDR 0x3B0 // start of pmp addresses + #ifndef CSR_HALT #define CSR_HALT 0x789 // writing nonzero value here will halt the cpu #endif @@ -35,19 +38,19 @@ __asm__ ("csrw %0, %1" :: "I"(CSR_HALT), "I"(code)); \ } -void write_mtimecmp(unsigned long long int mtimecmp); +void write_mtimecmp(uint64 mtimecmp); -inline __attribute__((always_inline)) unsigned long long int read_time() +inline __attribute__((always_inline)) uint64 read_time() { unsigned int lower, higher; - __asm__ volatile( - "csrr %0, %2\n" - "csrr %1, %3\n" - : "=r"(lower), "=r"(higher) - : "i"(CSR_TIME), "i"(CSR_TIMEH) + __asm__ volatile ( + "csrr %0, %2\n" + "csrr %1, %3\n" + : "=r"(lower), "=r"(higher) + : "i"(CSR_TIME), "i"(CSR_TIMEH) ); - return (unsigned long long) higher << 32 | lower; + return (uint64) higher << 32 | lower; } #endif diff --git a/kinclude/ecall.c b/kinclude/ecall.c index ddb7e31..5df1c56 100644 --- a/kinclude/ecall.c +++ b/kinclude/ecall.c @@ -16,12 +16,15 @@ ecall_handler ecall_table[ECALL_TABLE_LEN] = { 0 }; optional_int ecall_handle_spawn_thread(int* args_ptr, struct process_control_block* pcb) { + // args_ptr is a pointer to the pcb->regs field, starting at a0. + // args_ptr[0] is a0, args_ptr[1] is a1, etc. 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); + // create a new thread + optional_pcbptr pcb_or_err = create_new_thread(pcb, entry, args); + // if an error occured, pass it along to the process if (has_error(pcb_or_err)) return (optional_int) { .error = pcb_or_err.error }; @@ -30,12 +33,15 @@ optional_int ecall_handle_spawn_thread(int* args_ptr, struct process_control_blo optional_int ecall_handle_sleep(int* args, struct process_control_block* pcb) { + // read len from a0 int len = args[0]; + // only allow sleeping for positive intervals if (len < 0) { return (optional_int) { .error = EINVAL }; } + // if a positive interval is given, calculate the wakeup time if (len > 0) { pcb->asleep_until = read_time() + len; pcb->status = PROC_WAIT_SLEEP; @@ -46,28 +52,35 @@ optional_int ecall_handle_sleep(int* args, struct process_control_block* pcb) optional_int ecall_handle_join(int* args, struct process_control_block* pcb) { - int pid = args[0]; // a0 + int pid = args[0]; // read pid from processes a0 register + // find the referenced process struct process_control_block* target = process_from_pid(pid); if (target == NULL) return (optional_int) { .error = ESRCH }; + // if the process is dead, join can return immediately if (target->status == PROC_DEAD) return (optional_int) { .value = target->exit_code }; + // mark the current process as waiting for the target process pcb->status = PROC_WAIT_PROC; pcb->waiting_for_process = target; + // check if a valid timeout was passed in register a1 int timeout = args[1]; if (timeout <= 0) return (optional_int) { .value = 0 }; + // set the asleep_until field unsigned int len = (unsigned int) timeout; pcb->asleep_until = read_time() + len; + // here we can return whatever value we want, as it is overwritten when + // the process is awoken again return (optional_int) { .value = 0 }; } @@ -100,6 +113,7 @@ optional_int ecall_handle_exit(int* args, struct process_control_block* pcb) pcb->status = PROC_DEAD; pcb->exit_code = *args; + // print a message if debugging is enabled if (DEBUGGING) { char msg[34] = "process exited with code "; @@ -119,10 +133,12 @@ optional_int ecall_handle_exit(int* args, struct process_control_block* pcb) void trap_handle_ecall() { + // save current clock so we don't waste too much process time mark_ecall_entry(); + // get the current process struct process_control_block* pcb = get_current_process(); int *regs = pcb->regs; - int code = regs[REG_A0 + 7]; // code is stored inside a7 + int code = regs[REG_A0 + 7]; // syscall code is stored inside a7 // check if the code is too large/small or if the handler is zero if (code < 0 || code > ECALL_TABLE_LEN || ecall_table[code] == NULL) { @@ -154,7 +170,7 @@ void trap_handle(int interrupt_bit, int code, int mtval) scheduler_run_next(); break; default: - // impossible + // any other interrupt is not supported currently HALT(12); break; } @@ -187,6 +203,8 @@ void trap_handle(int interrupt_bit, int code, int mtval) __builtin_unreachable(); } +// this writes the function pointers to the ecall table +// it's called from the kernels init() function void init_ecall_table() { ecall_table[ECALL_SPAWN] = ecall_handle_spawn_thread; @@ -196,6 +214,8 @@ void init_ecall_table() ecall_table[ECALL_EXIT] = ecall_handle_exit; } +// this exception handler is crude and just kills off any process who +// causes an exception. void handle_exception(int ecode, int mtval) { // kill off offending process diff --git a/kinclude/io.c b/kinclude/io.c index 1de3eed..8277d36 100644 --- a/kinclude/io.c +++ b/kinclude/io.c @@ -1,23 +1,33 @@ #include "io.h" +// this file should only be used for debugging purposes. Most functions here +// could easily corrupt kernel memory if used with untrusted input. +// ALWAYS check your buffer lengths! #ifdef TEXT_IO_ADDR +// this function writes a string to the TEXT_IO buffer +// it adds a newline at the end and splits the passed string into smaller chunks +// if it is larger than TEXT_IO_BUFLEN. void dbgln(char* text, int len) { + // if the passed text is longer than TEXT_IO_BUFLEN, print it in chunks while (len > TEXT_IO_BUFLEN) { dbgln(text, TEXT_IO_BUFLEN); text += TEXT_IO_BUFLEN; len -= TEXT_IO_BUFLEN; } + // this is the address of the textIO char* ioaddr = (char*) TEXT_IO_ADDR + 4; + // write message bytewise to buffer (this could be implemented faster) for (int i = 0; i < len; i++) { if (*text == 0) break; *ioaddr++ = *text++; } + // add a newline if (len < TEXT_IO_BUFLEN) *ioaddr = '\n'; @@ -28,13 +38,16 @@ void dbgln(char* text, int len) /* alphabet for itoa */ char alpha[16] = "0123456789abcdef"; +// convert int to str char* itoa(int value, char* str, int base) { + // fail on unknown base if (base > 16 || base < 2) { *str++ = '?'; return str; } + // handle negative numbers if (value < 0) { *str++ = '-'; value *= -1; @@ -51,6 +64,7 @@ char* itoa(int value, char* str, int base) digits++; } while (value > 0); + // write reversed number to the buffer value = num; do { num = value % base; diff --git a/kinclude/ktypes.h b/kinclude/ktypes.h index 5bb6b8a..674ffa6 100644 --- a/kinclude/ktypes.h +++ b/kinclude/ktypes.h @@ -1,8 +1,14 @@ #ifndef H_ktypes #define H_ktypes +// define the nullpointer #define NULL ((void*) 0) +// types with explicit bit-widths +typedef unsigned int uint32; +typedef unsigned long long uint64; +typedef unsigned char byte; + /* * Error codes */ diff --git a/kinclude/malloc.c b/kinclude/malloc.c index e275f01..c2ed91a 100644 --- a/kinclude/malloc.c +++ b/kinclude/malloc.c @@ -5,7 +5,7 @@ #include "io.h" // information about the systems memory layout is stored here -static malloc_info global_malloc_info = { 0 }; +static struct malloc_info global_malloc_info = { 0 }; // this pointer points to the end of unused memory static void* allocate_memory_end; // this stack holds currently unused program stacks @@ -22,8 +22,11 @@ void stash_stack(void* stack_top) } } -void malloc_init(malloc_info* given_info) +// this function is called by the kernels init() function after it parsed the +// list of loaded binaries and calculated the available memory for general allocation +void malloc_init(struct malloc_info* given_info) { + // save the passed info global_malloc_info = *given_info; allocate_memory_end = given_info->allocate_memory_end; allocate_memory_end = (void*) (((int) allocate_memory_end) - PROCESS_COUNT); @@ -53,6 +56,8 @@ optional_voidptr malloc_stack() return (optional_voidptr) { .value = stack_top }; } +// a stack is not returned to the general memory pool (since EMBARK has no such +// pool) but rather just added to the pool of free process stacks. void free_stack(void* stack_top) { stash_stack(stack_top); diff --git a/kinclude/malloc.h b/kinclude/malloc.h index c48bf85..a593a66 100644 --- a/kinclude/malloc.h +++ b/kinclude/malloc.h @@ -3,14 +3,14 @@ #include "ktypes.h" -typedef struct malloc_info { +struct malloc_info { void* allocate_memory_end; void* allocate_memory_start; -} malloc_info; +}; optional_voidptr malloc_stack(); void free_stack(void* stack_top); -void malloc_init(malloc_info* info); +void malloc_init(struct malloc_info* info); #endif diff --git a/kinclude/sched.c b/kinclude/sched.c index 0c8ff51..fbcd63c 100644 --- a/kinclude/sched.c +++ b/kinclude/sched.c @@ -15,8 +15,8 @@ struct process_control_block processes[PROCESS_COUNT]; // pointer to the currently scheduled process struct process_control_block* current_process = NULL; // timer variables to add kernel time back to the processes time slice -unsigned long long int scheduling_interrupted_start; -unsigned long long int next_interrupt_scheduled_for; +uint64 scheduling_interrupted_start; +uint64 next_interrupt_scheduled_for; // this counter generates process ids int next_process_id = 1; @@ -60,7 +60,7 @@ void scheduler_try_return_to(struct process_control_block* pcb) // select a new process to run next struct process_control_block* scheduler_select_free() { - unsigned long long int mtime; + uint64 mtime; int timeout_available = 0; // note if a timeout is available while (1) { @@ -122,6 +122,7 @@ struct process_control_block* scheduler_select_free() } } +// performs the context switch from kernel to userspace mode void scheduler_switch_to(struct process_control_block* pcb) { CSR_WRITE(CSR_MEPC, pcb->pc); @@ -167,6 +168,7 @@ void scheduler_switch_to(struct process_control_block* pcb) __builtin_unreachable(); } +// get a PCB from a pid struct process_control_block* process_from_pid(int pid) { for (int i = 0; i < PROCESS_COUNT; i++) { @@ -186,6 +188,7 @@ struct process_control_block* get_current_process() return current_process; } +// this method sets up the mtimecmp register to trigger the next timer interrupt void set_next_interrupt() { next_interrupt_scheduled_for = read_time() + TIME_SLICE_LEN; @@ -197,14 +200,20 @@ void mark_ecall_entry() scheduling_interrupted_start = read_time(); } +// this function selects an unused entry in the processes list +// it tries to select slots which have been unused the longest optional_pcbptr find_available_pcb_slot() { + // this method loops over the process list using an index which persists + // over multiple function calls, wrapping when it reaches the end. This + // makes free space selection relatively fair. static int index = 0; int start_index = index; struct process_control_block* pcb = processes + index; while (pcb->status != PROC_DEAD) { index = (index + 1) % PROCESS_COUNT; + // if we iterated over the whole list and found nothing, we have no space left! if (index == start_index) return (optional_pcbptr) { .error = ENOBUFS }; pcb = processes + index; @@ -214,9 +223,9 @@ optional_pcbptr find_available_pcb_slot() return (optional_pcbptr) { .value = pcb }; } -optional_pcbptr create_new_process(loaded_binary* bin, int stack_size) +optional_pcbptr create_new_process(loaded_binary* bin) { - // try to get a position in the processes list + // try to get an unused entry in the processes list optional_pcbptr slot_or_err = find_available_pcb_slot(); // if that failed, we cannot creat a new process @@ -226,7 +235,7 @@ optional_pcbptr create_new_process(loaded_binary* bin, int stack_size) } // allocate stack for the new process - optional_voidptr stack_top_or_err = malloc_stack(stack_size); // allocate 4Kib stack + optional_voidptr stack_top_or_err = malloc_stack(); // allocate stack // if that failed, we also can't create a new process if (has_error(stack_top_or_err)) { @@ -259,9 +268,9 @@ optional_pcbptr create_new_process(loaded_binary* bin, int stack_size) return (optional_pcbptr) { .value = pcb }; } -optional_pcbptr create_new_thread(struct process_control_block* parent, void* entrypoint, void* args, int stack_size) +optional_pcbptr create_new_thread(struct process_control_block* parent, void* entrypoint, void* args) { - // try to get a position in the processes list + // try to get an unused entry in the processes list optional_pcbptr slot_or_err = find_available_pcb_slot(); // if that failed, we cannot creat a new process @@ -271,7 +280,7 @@ optional_pcbptr create_new_thread(struct process_control_block* parent, void* en } // allocate stack for the new process - optional_voidptr stack_top_or_err = malloc_stack(stack_size); // allocate 4Kib stack + optional_voidptr stack_top_or_err = malloc_stack(); // allocate stack // if that failed, we also can't create a new process if (has_error(stack_top_or_err)) { diff --git a/kinclude/sched.h b/kinclude/sched.h index da549ff..cb75754 100644 --- a/kinclude/sched.h +++ b/kinclude/sched.h @@ -20,8 +20,8 @@ struct process_control_block* get_current_process(); void mark_ecall_entry(); // process creation / destruction -optional_pcbptr create_new_process(loaded_binary*, int); -optional_pcbptr create_new_thread(struct process_control_block*, void*, void*, int); +optional_pcbptr create_new_process(loaded_binary*); +optional_pcbptr create_new_thread(struct process_control_block*, void*, void*); void destroy_process(struct process_control_block* pcb); void kill_child_processes(struct process_control_block* pcb); #endif diff --git a/package.py b/package.py index b391039..af2bdc6 100644 --- a/package.py +++ b/package.py @@ -14,17 +14,8 @@ SECTOR_SIZE = 512 # start address MEM_START = 0x100 -# this is the name of the global variable holding the list of loaded binaries -KERNEL_BINARY_TABLE = 'binary_table' -# loaded_binary struct size (4 integers) -KERNEL_BINARY_TABLE_ENTRY_SIZE = 4 * 4 - -# overwrite this function to generate the entries for the loaded binary list -def create_loaded_bin_struct(binid: int, entrypoint: int, start: int, end: int): - """ - Creates the binary data to populate the KERNEL_BINARY_TABLE structs - """ - return b''.join(num.to_bytes(4, 'little') for num in (binid, entrypoint, start, end)) +# address where the userspace binaries should be located (-1 to start directly after the kernel) +USR_BIN_START = -1 ## end of config @@ -40,7 +31,23 @@ EMPTY_SECTIONS = set(( '.bss', '.sbss', '.stack' )) +# this is the name of the global variable holding the list of loaded binaries +KERNEL_BINARY_TABLE = 'binary_table' +# loaded_binary struct size (4 integers) +KERNEL_BINARY_TABLE_ENTRY_SIZE = 4 * 4 + +# overwrite this function to generate the entries for the loaded binary list +def create_loaded_bin_struct(binid: int, entrypoint: int, start: int, end: int): + """ + Creates the binary data to populate the KERNEL_BINARY_TABLE structs + """ + return b''.join(num.to_bytes(4, 'little') for num in (binid, entrypoint, start, end)) + + def overlaps(p1, l1, p2, l2) -> bool: + """ + check if the intervals (p1, p1+l1) and (p2, p2+l2) overlap + """ return (p1 <= p2 and p1 + l1 > p2) or (p2 <= p1 and p2 + l2 > p1) class Section: @@ -202,6 +209,9 @@ def package(kernel: str, binaries: List[str], out: str): img.putBin(kernel) + if USR_BIN_START > 0: + img.seek(USR_BIN_START) + binid = 0 for bin_name in binaries: img.align(8) # align to eight bytes diff --git a/programs/Makefile b/programs/Makefile index 7545a48..1e0c876 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -11,5 +11,5 @@ spawn: thread: $(CC) $(CFLAGS) -o threads threads.c -all: simple spawn threads +all: simple spawn thread diff --git a/programs/spawn.c b/programs/spawn.c index 4179944..c851326 100644 --- a/programs/spawn.c +++ b/programs/spawn.c @@ -110,22 +110,6 @@ char* itoa(int value, char* str, int base) return str; } -void wrap_main() -{ - dbgln("start", 5); - - register int code asm ("s1") = main(); - - dbgln("end", 3); - - __asm__ __volatile__ ( - "mv a0, s1\n" - "li a7, 5\n" - "ecall\n" - "ebreak\n" - ); -} - void _start() { __asm__ __volatile__ ( @@ -134,6 +118,10 @@ void _start() " la gp, _gp\n" ".option pop\n" ); + __asm__ __volatile__ ( + "mv a0, %0\n" + "li a7, 5\n" + "ecall\n" :: "r"(main()) + ); - wrap_main(); } diff --git a/programs/threads.c b/programs/threads.c index ee8c581..4c790f6 100644 --- a/programs/threads.c +++ b/programs/threads.c @@ -9,12 +9,13 @@ int main() struct optional_int t1 = spawn(thread, &arg1); if (has_error(t1)) { - __asm__("ebreak"); + __asm__ ("ebreak"); return 1; } struct optional_int t2 = spawn(thread, &arg2); + if (has_error(t2)) { - __asm__("ebreak"); + __asm__ ("ebreak"); return 2; } @@ -36,6 +37,7 @@ int thread(void* args) { // read value int arg = *((int*) args); + //char buff[64] = "sleeping for "; //char* end = itoa(arg, &buff[13], 10); @@ -118,12 +120,10 @@ void _start() " la gp, _gp\n" ".option pop\n" ); - - register int code asm ("s1") = main(); + main(); __asm__ __volatile__ ( - "mv a0, s1\n" "li a7, 5\n" "ecall\n" - "ebreak\n" ); + __builtin_unreachable(); } diff --git a/programs/threads.h b/programs/threads.h index c15d51c..fceabd2 100644 --- a/programs/threads.h +++ b/programs/threads.h @@ -26,7 +26,11 @@ char* itoa(int value, char* str, int base); int thread(void* args); -__attribute__((naked)) struct optional_int spawn(int (*target)(void*), void* args) { +// ignore unused parameter errors only for these functions +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +__attribute__((naked)) struct optional_int spawn(int (*target)(void*), void* args) +{ __asm__ ( "li a7, 1\n" "ecall\n" @@ -35,7 +39,8 @@ __attribute__((naked)) struct optional_int spawn(int (*target)(void*), void* arg __builtin_unreachable(); } -__attribute__((naked)) struct optional_int join(int pid, int timeout) { +__attribute__((naked)) struct optional_int join(int pid, int timeout) +{ __asm__ ( "li a7, 3\n" "ecall\n" @@ -45,7 +50,8 @@ __attribute__((naked)) struct optional_int join(int pid, int timeout) { } -__attribute__((naked)) struct optional_int sleep(int timeout) { +__attribute__((naked)) struct optional_int sleep(int timeout) +{ __asm__ ( "li a7, 2\n" "ecall\n" @@ -53,3 +59,4 @@ __attribute__((naked)) struct optional_int sleep(int timeout) { ); __builtin_unreachable(); } +#pragma GCC diagnostic pop