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