initial commit
commit
8d0178aff1
@ -0,0 +1,3 @@
|
|||||||
|
out/
|
||||||
|
*.o
|
||||||
|
obj/
|
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Anton Lydike
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,45 @@
|
|||||||
|
# simple makefile for this kernel project
|
||||||
|
# This is BAD. it should be reworked before anyone else uses this.
|
||||||
|
|
||||||
|
# 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) -march=rv32im -O3
|
||||||
|
KERNEL_CFLAGS=-nostdlib
|
||||||
|
|
||||||
|
# dependencies that need to be built:
|
||||||
|
_DEPS = ecall.c csr.c mutex.c sched.c
|
||||||
|
|
||||||
|
# dependencies as object files:
|
||||||
|
_OBJ = ecall.o mutex.o sched.o boot.o csr.o
|
||||||
|
|
||||||
|
|
||||||
|
DEPS = $(patsubst %,$(KLIBDIR)/%,$(_DEPS))
|
||||||
|
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
|
||||||
|
|
||||||
|
|
||||||
|
$(ODIR)/boot.o:
|
||||||
|
$(CC) -c -o $@ $(KLIBDIR)/boot.S $(CFLAGS)
|
||||||
|
|
||||||
|
$(ODIR)/%.o:
|
||||||
|
$(CC) -c -o $@ $(KLIBDIR)/$*.c $(CFLAGS)
|
||||||
|
|
||||||
|
kernel: $(OBJ)
|
||||||
|
mkdir -p $(TARGET)
|
||||||
|
$(CC) -o $(TARGET)/$@ $^ kernel.c $(CFLAGS) $(KERNEL_CFLAGS)
|
||||||
|
|
||||||
|
kernel-dump: kernel
|
||||||
|
$(OBJDUMP) -SFlDf $(TARGET)/kernel > $(TARGET)/kernel-objects
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(ODIR) *~ $(KLIBDIR)/*~ $(TARGET)
|
@ -0,0 +1,14 @@
|
|||||||
|
# EMBARK: An Educational and Modifiable BAsic RISC-V Kernel
|
||||||
|
|
||||||
|
EMBARK is a small kernel, designed for educational projects. It has very limited scope and is designed to be extensible.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## The toolchain:
|
||||||
|
|
||||||
|
I am using the [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain), configured with `--with-arch=rv32im --disable-linux --disable-gdb --disable-multilib`.
|
||||||
|
|
||||||
|
## The Makefile:
|
||||||
|
|
||||||
|
You can build the kernel using `make kernel`. Make sure the toolchain is in your path!
|
@ -0,0 +1,45 @@
|
|||||||
|
#include "kernel.h"
|
||||||
|
#include "ecall.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
void thread_1();
|
||||||
|
|
||||||
|
extern ProcessControlBlock processes[PROCESS_COUNT];
|
||||||
|
|
||||||
|
extern void init()
|
||||||
|
{
|
||||||
|
// set up processes
|
||||||
|
processes[0].pid = 1;
|
||||||
|
processes[0].pc = (int) thread_1;
|
||||||
|
processes[0].regs[2] = 128;
|
||||||
|
processes[0].status = PROC_RDY;
|
||||||
|
processes[0].requested_lock = 0;
|
||||||
|
|
||||||
|
processes[1].pid = 2;
|
||||||
|
processes[1].pc = (int) thread_1;
|
||||||
|
processes[1].regs[2] = 256;
|
||||||
|
processes[1].status = PROC_RDY;
|
||||||
|
processes[1].requested_lock = 0;
|
||||||
|
|
||||||
|
scheduler_run_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_1() {
|
||||||
|
int a = 0; // a4
|
||||||
|
int b = 0; // a5
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
a++;
|
||||||
|
if (a > 1000000) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ebreak"
|
||||||
|
);
|
||||||
|
b++;
|
||||||
|
a = 0;
|
||||||
|
}
|
||||||
|
if (b > 1000000) {
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef H_KERNEL
|
||||||
|
#define H_KERNEL
|
||||||
|
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
|
||||||
|
#define XLEN 32 // 32 bit system
|
||||||
|
#define MUTEX_COUNT 64 // must be multiple of xlen
|
||||||
|
#define PROCESS_COUNT 64
|
||||||
|
#define MAX_INT 0x7FFFFFFF // max 32 bit signed int
|
||||||
|
|
||||||
|
// memory layout:
|
||||||
|
#define ROM_START 0x00100
|
||||||
|
#define IO_START 0x10000
|
||||||
|
#define NVM_START 0x20000
|
||||||
|
#define RAM_START 0x50000
|
||||||
|
|
||||||
|
// scheduler settings
|
||||||
|
#define TIME_SLICE_LEN 100 // number of cpu time ticks per slice
|
||||||
|
|
||||||
|
// init function
|
||||||
|
extern __attribute__((__noreturn__)) void init();
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,103 @@
|
|||||||
|
.section .stack
|
||||||
|
|
||||||
|
stack_bottom:
|
||||||
|
.space 4096
|
||||||
|
stack_top:
|
||||||
|
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
|
||||||
|
// Set up all the CSR mstatus_offsets
|
||||||
|
.set mstatus, 0x300 // machine status
|
||||||
|
.set mscratch, 0x340
|
||||||
|
.set mtvec, 0x305 // machine trap handler
|
||||||
|
.set mcause, 0x342 // machine trap cause
|
||||||
|
.set mtval, 0x343 // machine bad address or instruction
|
||||||
|
// .set misa, 0x301 // machine ISA
|
||||||
|
// .set mie, 0x304 // machine interrupt enable
|
||||||
|
// .set mip, 0x344 // machine interrupt pending
|
||||||
|
|
||||||
|
.extern init
|
||||||
|
.type init, @function
|
||||||
|
|
||||||
|
.extern trap_handle
|
||||||
|
.type trap_handle, @function
|
||||||
|
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
// setup a0 to hold |trap tbl addr|mode|
|
||||||
|
// len:| 30 | 2 |
|
||||||
|
la a0, trap_vector
|
||||||
|
csrrw zero, mtvec, a0 // write a0 into mtvec csr entry
|
||||||
|
// 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
|
||||||
|
li a0, 0x80
|
||||||
|
csrrw zero, mstatus, a0 // write to mstatus
|
||||||
|
// write
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
// init sp and gp
|
||||||
|
la sp, stack_top
|
||||||
|
la gp, __global_pointer$
|
||||||
|
.option pop
|
||||||
|
jal init
|
||||||
|
|
||||||
|
// halt machine
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
trap_vector:
|
||||||
|
// save all registers into the PCB struct
|
||||||
|
// switch contents of t6 with contents of mscratch
|
||||||
|
// mscratch holds the PCBs regs field address
|
||||||
|
csrrw t6, mscratch, t6
|
||||||
|
sw ra, 0(t6)
|
||||||
|
sw sp, 4(t6)
|
||||||
|
sw gp, 8(t6)
|
||||||
|
sw tp, 12(t6)
|
||||||
|
sw t0, 16(t6)
|
||||||
|
sw t1, 20(t6)
|
||||||
|
sw t2, 24(t6)
|
||||||
|
sw s0, 28(t6)
|
||||||
|
sw s1, 32(t6)
|
||||||
|
sw a0, 36(t6)
|
||||||
|
sw a1, 40(t6)
|
||||||
|
sw a2, 44(t6)
|
||||||
|
sw a3, 48(t6)
|
||||||
|
sw a4, 52(t6)
|
||||||
|
sw a5, 56(t6)
|
||||||
|
sw a6, 60(t6)
|
||||||
|
sw a7, 64(t6)
|
||||||
|
sw s2, 68(t6)
|
||||||
|
sw s3, 72(t6)
|
||||||
|
sw s4, 76(t6)
|
||||||
|
sw s5, 80(t6)
|
||||||
|
sw s6, 84(t6)
|
||||||
|
sw s7, 88(t6)
|
||||||
|
sw s8, 92(t6)
|
||||||
|
sw s9, 96(t6)
|
||||||
|
sw s10, 100(t6)
|
||||||
|
sw s11, 104(t6)
|
||||||
|
sw t3, 108(t6)
|
||||||
|
sw t4, 112(t6)
|
||||||
|
sw t5, 116(t6)
|
||||||
|
mv a0, t6 // save struct address to already saved register
|
||||||
|
csrrw t6, mscratch, t6 // load original t6 register from mscratch
|
||||||
|
sw t6, 120(a0) // save original t6 register
|
||||||
|
csrr a1, mcause
|
||||||
|
srli a0, a1, 31
|
||||||
|
slli a1, a1, 1
|
||||||
|
srli a1, a1, 1
|
||||||
|
csrr a2, mtval
|
||||||
|
// reinit sp and gp
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
la sp, stack_top
|
||||||
|
la gp, __global_pointer$
|
||||||
|
.option pop
|
||||||
|
jal trap_handle
|
@ -0,0 +1,40 @@
|
|||||||
|
#include "csr.h"
|
||||||
|
|
||||||
|
#ifdef TIMECMP_IN_MEMORY
|
||||||
|
|
||||||
|
void write_mtimecmp(unsigned long long int mtimecmp) {
|
||||||
|
unsigned int lower = mtimecmp;
|
||||||
|
unsigned int higher = mtimecmp >> 32;
|
||||||
|
__asm__(
|
||||||
|
"sw %2, %0\n"
|
||||||
|
"sw %3, %1" ::
|
||||||
|
"I"(TIMECMP_MEM_ADDR),"I"(TIMECMP_MEM_ADDR + 4),
|
||||||
|
"r"(lower), "r"(higher)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void write_mtimecmp(unsigned long long int mtimecmp) {
|
||||||
|
unsigned int lower = mtimecmp;
|
||||||
|
unsigned int higher = mtimecmp >> 32;
|
||||||
|
__asm__(
|
||||||
|
"csrw %0, %2\n"
|
||||||
|
"csrw %1, %3" ::
|
||||||
|
"I"(CSR_MTIMECMP),"I"(CSR_MTIMECMPH),
|
||||||
|
"r"(lower), "r"(higher)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long long int read_time() {
|
||||||
|
unsigned int lower, higher;
|
||||||
|
__asm__(
|
||||||
|
"csrr %0, 0xC01\n"
|
||||||
|
"csrr %1, 0xC81\n"
|
||||||
|
: "=r"(lower), "=r"(higher)
|
||||||
|
);
|
||||||
|
return (unsigned long long) higher << 32 | lower;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef H_CSR
|
||||||
|
#define H_CSR
|
||||||
|
|
||||||
|
#define CSR_MSTATUS 0x300 // machine status
|
||||||
|
#define CSR_MISA 0x301 // machine ISA
|
||||||
|
#define CSR_MIE 0x304 // machine interrupt enable
|
||||||
|
#define CSR_MTVEC 0x305 // machine trap handler
|
||||||
|
#define CSR_MSCRATCH 0x340 // machine scratch register
|
||||||
|
#define CSR_MEPC 0x341 // machine exception program counter
|
||||||
|
#define CSR_MCAUSE 0x342 // machine trap cause
|
||||||
|
#define CSR_MTVAL 0x343 // machine bad address or instruction
|
||||||
|
#define CSR_MIP 0x344 // machine interrupt pending
|
||||||
|
#define CSR_TIME 0xC01 // time csr
|
||||||
|
#define CSR_TIMEH 0xC81 // high bits of time
|
||||||
|
|
||||||
|
#define CSR_MTIMECMP 0x780 // mtimecmp register for timer interrupts
|
||||||
|
#define CSR_MTIMECMPH 0x781 // mtimecmph register for timer interrupts
|
||||||
|
|
||||||
|
#define CSR_HALT 0x789 // writing nonzero value here will halt the cpu
|
||||||
|
|
||||||
|
#define TIMECMP_MEM_ADDR 0xffff
|
||||||
|
|
||||||
|
#define CSR_READ(csr_id, result) {\
|
||||||
|
__asm__ ("csrr %0, %1" : "=r"((result)) : "I"((csr_id))); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CSR_WRITE(csr_id, val) {\
|
||||||
|
__asm__ ("csrw %0, %1" :: "I"((csr_id)), "r"((val))); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HALT(code) {\
|
||||||
|
__asm__("csrw %0, %1" :: "I"(CSR_HALT), "I"(code)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_mtimecmp(unsigned long long int mtimecmp);
|
||||||
|
unsigned long long int read_time();
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,80 @@
|
|||||||
|
#include "ecall.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "csr.h"
|
||||||
|
|
||||||
|
void ecall_handle_fork()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_sleep(int until)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_wait(int pid, int timeout)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_kill(int pid)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_exit(int status)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_m_create()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_m_lock(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_m_unlock(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecall_handle_m_destroy(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void trap_handle_ecall() {
|
||||||
|
int *regs = get_current_process_registers();
|
||||||
|
int code = regs[16];
|
||||||
|
__asm__("ebreak");
|
||||||
|
HALT(18);
|
||||||
|
}
|
||||||
|
|
||||||
|
void trap_handle(int interrupt_bit, int code, int mtval)
|
||||||
|
{
|
||||||
|
if (interrupt_bit) {
|
||||||
|
switch (code) {
|
||||||
|
case 7:
|
||||||
|
scheduler_run_next();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// impossible
|
||||||
|
HALT(12)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (code) {
|
||||||
|
case 8: // user ecall
|
||||||
|
trap_handle_ecall();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
HALT(13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HALT(1);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef H_ECALL
|
||||||
|
#define H_ECALL
|
||||||
|
|
||||||
|
// syscall handlers, are setup in the mtvec csr
|
||||||
|
void ecall_handle_fork();
|
||||||
|
void ecall_handle_sleep(int until);
|
||||||
|
void ecall_handle_wait(int pid, int timeout);
|
||||||
|
void ecall_handle_kill(int pid);
|
||||||
|
void ecall_handle_exit(int status);
|
||||||
|
void ecall_handle_m_create();
|
||||||
|
void ecall_handle_m_lock(int mutex_id);
|
||||||
|
void ecall_handle_m_unlock(int mutex_id);
|
||||||
|
void ecall_handle_m_destroy(int mutex_id);
|
||||||
|
|
||||||
|
void __attribute__((__noreturn__)) trap_handle(int interrupt_bit, int code, int mtval);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,37 @@
|
|||||||
|
#include "mutex.h"
|
||||||
|
#include "../kernel.h"
|
||||||
|
|
||||||
|
// mutex lock structure:
|
||||||
|
// this is a dict describing which process instantiated the lock
|
||||||
|
// it maps mutex_id -> pid (or zero for unused locks)
|
||||||
|
int locks[MUTEX_COUNT];
|
||||||
|
int locks_bitfield[MUTEX_COUNT / XLEN]; // each bit representing if the lock is
|
||||||
|
// engaged
|
||||||
|
|
||||||
|
|
||||||
|
int mutex_is_locked(int mutex_id)
|
||||||
|
{
|
||||||
|
int offset = mutex_id % XLEN;
|
||||||
|
|
||||||
|
return locks[mutex_id / XLEN] & (1 << offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mutex_create()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_lock(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_unlock(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_destroy(int mutex_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef H_MUTEX
|
||||||
|
#define H_MUTEX
|
||||||
|
|
||||||
|
// mutex operations (modifies data, no checks)
|
||||||
|
int mutex_create();
|
||||||
|
void mutex_lock(int mutex_id);
|
||||||
|
void mutex_unlock(int mutex_id);
|
||||||
|
void mutex_destroy(int mutex_id);
|
||||||
|
// mutex helpers
|
||||||
|
int mutex_is_locked(int mutex_id);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,139 @@
|
|||||||
|
#include "../kernel.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "csr.h"
|
||||||
|
|
||||||
|
|
||||||
|
// scheduling data:
|
||||||
|
ProcessControlBlock processes[PROCESS_COUNT];
|
||||||
|
int current_process_index = 1;
|
||||||
|
|
||||||
|
void scheduler_run_next ()
|
||||||
|
{
|
||||||
|
current_process_index = scheduler_select_free();
|
||||||
|
// set up timer interrupt
|
||||||
|
unsigned long long int mtimecmp = read_time() + TIME_SLICE_LEN;
|
||||||
|
write_mtimecmp(mtimecmp);
|
||||||
|
scheduler_switch_to(current_process_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int scheduler_select_free()
|
||||||
|
{
|
||||||
|
long long int mtime;
|
||||||
|
int i;
|
||||||
|
int timeout_available = false; // note if a timeout is available
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
mtime = read_time();
|
||||||
|
|
||||||
|
for (i=1; i < PROCESS_COUNT; i++) {
|
||||||
|
ProcessControlBlock *pcb = processes + ((current_process_index + i) % PROCESS_COUNT);
|
||||||
|
|
||||||
|
if (pcb->status == PROC_RDY)
|
||||||
|
return (current_process_index + i) % PROCESS_COUNT;
|
||||||
|
|
||||||
|
if (pcb->status == PROC_WAIT_SLEEP) {
|
||||||
|
if (pcb->asleep_until < mtime) {
|
||||||
|
return (current_process_index + i) % PROCESS_COUNT;
|
||||||
|
}
|
||||||
|
timeout_available = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcb->status == PROC_WAIT_PROC) {
|
||||||
|
if (pcb->asleep_until != 0) {
|
||||||
|
if (pcb->asleep_until < mtime) {
|
||||||
|
// set process return args!
|
||||||
|
return (current_process_index + i) % PROCESS_COUNT;
|
||||||
|
}
|
||||||
|
timeout_available = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcb->status == PROC_WAIT_LOCK) {
|
||||||
|
if (pcb->asleep_until != 0) {
|
||||||
|
if (pcb->asleep_until < mtime) {
|
||||||
|
// set process return args!
|
||||||
|
return (current_process_index + i) % PROCESS_COUNT;
|
||||||
|
}
|
||||||
|
timeout_available = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mutex_is_locked(pcb->requested_lock)) {
|
||||||
|
return (current_process_index + i) % PROCESS_COUNT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout_available == false) {
|
||||||
|
// either process deadlock or no processes alive.
|
||||||
|
//TODO: handle missing executable thread
|
||||||
|
HALT(22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler_switch_to(int proc_index)
|
||||||
|
{
|
||||||
|
ProcessControlBlock *pcb = processes + proc_index;
|
||||||
|
|
||||||
|
CSR_WRITE(CSR_MEPC, pcb->pc);
|
||||||
|
|
||||||
|
// set up registers
|
||||||
|
__asm__(
|
||||||
|
"mv x31, %0\n"
|
||||||
|
"csrrw zero, %1, x31\n"
|
||||||
|
"lw x1, 0(x31)\n"
|
||||||
|
"lw x2, 4(x31)\n"
|
||||||
|
"lw x3, 8(x31)\n"
|
||||||
|
"lw x4, 12(x31)\n"
|
||||||
|
"lw x5, 16(x31)\n"
|
||||||
|
"lw x6, 20(x31)\n"
|
||||||
|
"lw x7, 24(x31)\n"
|
||||||
|
"lw x8, 28(x31)\n"
|
||||||
|
"lw x9, 32(x31)\n"
|
||||||
|
"lw x10, 36(x31)\n"
|
||||||
|
"lw x11, 40(x31)\n"
|
||||||
|
"lw x12, 44(x31)\n"
|
||||||
|
"lw x13, 48(x31)\n"
|
||||||
|
"lw x14, 52(x31)\n"
|
||||||
|
"lw x15, 56(x31)\n"
|
||||||
|
"lw x16, 60(x31)\n"
|
||||||
|
"lw x17, 64(x31)\n"
|
||||||
|
"lw x18, 68(x31)\n"
|
||||||
|
"lw x19, 72(x31)\n"
|
||||||
|
"lw x20, 76(x31)\n"
|
||||||
|
"lw x21, 80(x31)\n"
|
||||||
|
"lw x22, 84(x31)\n"
|
||||||
|
"lw x23, 88(x31)\n"
|
||||||
|
"lw x24, 92(x31)\n"
|
||||||
|
"lw x25, 96(x31)\n"
|
||||||
|
"lw x26, 100(x31)\n"
|
||||||
|
"lw x27, 104(x31)\n"
|
||||||
|
"lw x28, 108(x31)\n"
|
||||||
|
"lw x29, 112(x31)\n"
|
||||||
|
"lw x30, 116(x31)\n"
|
||||||
|
"lw x31, 120(x31)\n"
|
||||||
|
"mret \n"
|
||||||
|
:: "r"(pcb->regs), "I"(CSR_MSCRATCH)
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scheduler_index_from_pid(int pid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < PROCESS_COUNT; i++) {
|
||||||
|
if (processes[i].pid == pid)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scheduler_create_process(int binid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int* get_current_process_registers()
|
||||||
|
{
|
||||||
|
return processes[current_process_index].regs;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef H_SCHED
|
||||||
|
#define H_SCHED
|
||||||
|
|
||||||
|
#include "../kernel.h"
|
||||||
|
|
||||||
|
// process statuses:
|
||||||
|
#define PROC_DEAD 0
|
||||||
|
#define PROC_RDY 1
|
||||||
|
#define PROC_WAIT_LOCK 2
|
||||||
|
#define PROC_WAIT_PROC 3
|
||||||
|
#define PROC_WAIT_SLEEP 4
|
||||||
|
|
||||||
|
// process structure:
|
||||||
|
typedef struct ProcessControlBlock ProcessControlBlock;
|
||||||
|
struct ProcessControlBlock {
|
||||||
|
int pid;
|
||||||
|
int pc;
|
||||||
|
int regs[31];
|
||||||
|
// scheduling information
|
||||||
|
int status;
|
||||||
|
int requested_lock;
|
||||||
|
ProcessControlBlock *waiting_for_process;
|
||||||
|
unsigned long long int asleep_until;
|
||||||
|
};
|
||||||
|
|
||||||
|
// scheduling data:
|
||||||
|
extern ProcessControlBlock processes[PROCESS_COUNT];
|
||||||
|
|
||||||
|
// scheduler methods
|
||||||
|
int scheduler_select_free();
|
||||||
|
int scheduler_create_process(int binid);
|
||||||
|
void __attribute__((noreturn)) scheduler_run_next();
|
||||||
|
void __attribute__((noreturn)) scheduler_switch_to(int proc_index);
|
||||||
|
int scheduler_index_from_pid(int pid);
|
||||||
|
int* get_current_process_registers();
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,69 @@
|
|||||||
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
inline void __attribute__((always_inline)) ecall() {
|
||||||
|
asm volatile("ecall");
|
||||||
|
}
|
||||||
|
|
||||||
|
// process control
|
||||||
|
int spawn(void (*child)(void*), void* args);
|
||||||
|
|
||||||
|
void sleep(int length) {
|
||||||
|
asm volatile(
|
||||||
|
"li a7, 1\n"
|
||||||
|
"ecall\n"
|
||||||
|
"mv a0, %0"
|
||||||
|
:: "r"(length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int join(int pid, int timeout) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kill(int pid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) exit(int code);
|
||||||
|
|
||||||
|
// locks
|
||||||
|
m_lock mutex_create() {
|
||||||
|
m_lock result;
|
||||||
|
asm (
|
||||||
|
"li a7, 8\n"
|
||||||
|
"ecall\n"
|
||||||
|
"mv %0, a0" :
|
||||||
|
"=r"(result)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mutex_lock(m_lock lock, int timeout) {
|
||||||
|
int result;
|
||||||
|
asm (
|
||||||
|
"li a7, 9\n"
|
||||||
|
"mv a0, %1\n"
|
||||||
|
"mv a1, %2\n"
|
||||||
|
"ecall\n"
|
||||||
|
"mv %0, a0"
|
||||||
|
: "=r"(result)
|
||||||
|
: "r"(lock), "r"(timeout)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_unlock(m_lock lock) {
|
||||||
|
asm (
|
||||||
|
"li a7, 10\n"
|
||||||
|
"mv a0, %0" ::
|
||||||
|
"r"(lock)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_destroy(m_lock lock) {
|
||||||
|
asm (
|
||||||
|
"li a7, 11\n"
|
||||||
|
"mv a0, %0" ::
|
||||||
|
"r"(lock)
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// process control
|
||||||
|
int spawn(void (*child)(void*), void* args);
|
||||||
|
|
||||||
|
void sleep(int length);
|
||||||
|
|
||||||
|
int join(int pid, int timeout);
|
||||||
|
|
||||||
|
int kill(int pid);
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) exit(int code);
|
||||||
|
|
||||||
|
// locks
|
||||||
|
typedef int m_lock;
|
||||||
|
|
||||||
|
m_lock mutex_create();
|
||||||
|
|
||||||
|
int mutex_lock(m_lock lock, int timeout);
|
||||||
|
|
||||||
|
void mutex_unlock(m_lock lock);
|
||||||
|
|
||||||
|
void mutex_destroy(m_lock lock);
|
Loading…
Reference in New Issue