a lot of cleanup, added pmp
This commit is contained in:
parent
e69cfa8a5a
commit
bb13cbeca5
51
Makefile
51
Makefile
@ -1,5 +1,30 @@
|
||||
# small makefile for compiling the kernel
|
||||
|
||||
### Build configuration:
|
||||
# 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
|
||||
|
||||
# Comment this out if you don't have any text IO device memory mapped
|
||||
CFLAGS += -DTEXT_IO_ADDR=0xff0000 -DTEXT_IO_BUFLEN=64
|
||||
|
||||
# If you want to build without any extension, you can uncomment the next line
|
||||
#CFLAGS += -D__risc_no_ext=1
|
||||
# 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
|
||||
|
||||
# kernel lib dir
|
||||
KLIBDIR=kinclude
|
||||
# object file dir
|
||||
@ -11,31 +36,9 @@ 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
|
||||
CFLAGS+=-I$(KLIBDIR) -MD -mcmodel=medany -Wall -Wextra -pedantic-errors -Wno-builtin-declaration-mismatch -march=$(ARCH)
|
||||
KERNEL_CFLAGS=-nostdlib -T linker.ld
|
||||
ARCH = rv32im
|
||||
|
||||
### Build configuration:
|
||||
# Define the maximum number of running processes
|
||||
PROCESS_COUNT = 8
|
||||
|
||||
# Define the maximum number of binaries packaged with the kernel
|
||||
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
|
||||
#CFLAGS += -D__risc_no_ext=1
|
||||
#ARCH = rv32i
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
### End configuration
|
||||
CFLAGS += -march=$(ARCH) -DPROCESS_COUNT=$(PROCESS_COUNT) -DNUM_BINARIES=$(PACKAGED_BINARY_COUNT)
|
||||
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
|
||||
|
41
kernel.c
41
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);
|
||||
}
|
||||
|
@ -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;
|
||||
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)
|
||||
__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;
|
||||
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)
|
||||
__asm__ volatile (
|
||||
"csrw %0, %2\n"
|
||||
"csrw %1, %3" ::
|
||||
"I"(CSR_MTIMECMP),"I"(CSR_MTIMECMPH),
|
||||
"r"(lower), "r"(higher)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
32
package.py
32
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
|
||||
|
@ -11,5 +11,5 @@ spawn:
|
||||
thread:
|
||||
$(CC) $(CFLAGS) -o threads threads.c
|
||||
|
||||
all: simple spawn threads
|
||||
all: simple spawn thread
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user