changes
This commit is contained in:
parent
9c3dc94bd7
commit
29878e42e6
19
Makefile
19
Makefile
@ -5,32 +5,21 @@ P=programs
|
||||
|
||||
OBJS = \
|
||||
$K/entry.o \
|
||||
$K/start.o \
|
||||
$K/console.o \
|
||||
$K/printf.o \
|
||||
$K/uart.o \
|
||||
$K/kalloc.o \
|
||||
$K/string.o \
|
||||
$K/main.o \
|
||||
$K/vm.o \
|
||||
$K/proc.o \
|
||||
$K/swtch.o \
|
||||
$K/trampoline.o \
|
||||
$K/trap.o \
|
||||
$K/syscall.o \
|
||||
$K/sysproc.o \
|
||||
$K/bio.o \
|
||||
$K/fs.o \
|
||||
$K/log.o \
|
||||
$K/sleeplock.o \
|
||||
$K/file.o \
|
||||
$K/pipe.o \
|
||||
$K/exec.o \
|
||||
$K/sysfile.o \
|
||||
$K/kernelvec.o \
|
||||
$K/plic.o \
|
||||
$K/virtio_disk.o \
|
||||
$K/riscv.o
|
||||
$K/virtio_disk.o
|
||||
|
||||
# riscv64-unknown-elf- or riscv64-linux-gnu-
|
||||
# perhaps in /opt/riscv/bin
|
||||
@ -77,9 +66,9 @@ endif
|
||||
LDFLAGS = -z max-page-size=4096
|
||||
|
||||
$K/kernel: $(OBJS) $K/kernel.ld $U/initcode $R/src
|
||||
cargo +nightly -Z unstable-options -C $R build
|
||||
$(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a > $R/target/$(TARGET_TRIPLE)/debug/librustkernel.asm
|
||||
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a
|
||||
cargo +nightly -Z unstable-options -C $R build --release
|
||||
$(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/release/librustkernel.a > $R/target/$(TARGET_TRIPLE)/release/librustkernel.asm
|
||||
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/release/librustkernel.a
|
||||
$(OBJDUMP) -S $K/kernel > $K/kernel.asm
|
||||
$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
|
||||
|
||||
|
121
kernel/console.c
121
kernel/console.c
@ -23,117 +23,25 @@
|
||||
#include "proc.h"
|
||||
|
||||
#define BACKSPACE 0x100
|
||||
#define INPUT_BUF_SIZE 128
|
||||
#define C(x) ((x)-'@') // Control-x
|
||||
|
||||
//
|
||||
// send one character to the uart.
|
||||
// called by printf(), and to echo input characters,
|
||||
// but not from write().
|
||||
//
|
||||
void
|
||||
consputc(int c)
|
||||
{
|
||||
if(c == BACKSPACE){
|
||||
// if the user typed backspace, overwrite with a space.
|
||||
uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
|
||||
} else {
|
||||
uartputc_sync(c);
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
struct console {
|
||||
struct spinlock lock;
|
||||
|
||||
// input
|
||||
#define INPUT_BUF_SIZE 128
|
||||
char buf[INPUT_BUF_SIZE];
|
||||
uint r; // Read index
|
||||
uint w; // Write index
|
||||
uint e; // Edit index
|
||||
} cons;
|
||||
};
|
||||
|
||||
//
|
||||
// user write()s to the console go here.
|
||||
//
|
||||
int
|
||||
consolewrite(int user_src, uint64 src, int n)
|
||||
{
|
||||
int i;
|
||||
extern struct console cons;
|
||||
void consputc(int c);
|
||||
int consolewrite(int user_src, uint64 src, int n);
|
||||
int consoleread(int user_dst, uint64 dst, int n);
|
||||
|
||||
for(i = 0; i < n; i++){
|
||||
char c;
|
||||
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
||||
break;
|
||||
uartputc(c);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
//
|
||||
// user read()s from the console go here.
|
||||
// copy (up to) a whole input line to dst.
|
||||
// user_dist indicates whether dst is a user
|
||||
// or kernel address.
|
||||
//
|
||||
int
|
||||
consoleread(int user_dst, uint64 dst, int n)
|
||||
{
|
||||
uint target;
|
||||
int c;
|
||||
char cbuf;
|
||||
|
||||
target = n;
|
||||
acquire(&cons.lock);
|
||||
while(n > 0){
|
||||
// wait until interrupt handler has put some
|
||||
// input into cons.buffer.
|
||||
while(cons.r == cons.w){
|
||||
if(killed(myproc())){
|
||||
release(&cons.lock);
|
||||
return -1;
|
||||
}
|
||||
sleep(&cons.r, &cons.lock);
|
||||
}
|
||||
|
||||
c = cons.buf[cons.r++ % INPUT_BUF_SIZE];
|
||||
|
||||
if(c == C('D')){ // end-of-file
|
||||
if(n < target){
|
||||
// Save ^D for next time, to make sure
|
||||
// caller gets a 0-byte result.
|
||||
cons.r--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// copy the input byte to the user-space buffer.
|
||||
cbuf = c;
|
||||
if(either_copyout(user_dst, dst, &cbuf, 1) == -1)
|
||||
break;
|
||||
|
||||
dst++;
|
||||
--n;
|
||||
|
||||
if(c == '\n'){
|
||||
// a whole line has arrived, return to
|
||||
// the user-level read().
|
||||
break;
|
||||
}
|
||||
}
|
||||
release(&cons.lock);
|
||||
|
||||
return target - n;
|
||||
}
|
||||
|
||||
//
|
||||
// the console input interrupt handler.
|
||||
// uartintr() calls this for input character.
|
||||
// do erase/kill processing, append to cons.buf,
|
||||
// wake up consoleread() if a whole line has arrived.
|
||||
//
|
||||
void
|
||||
consoleintr(int c)
|
||||
void consoleintr(int c)
|
||||
{
|
||||
acquire(&cons.lock);
|
||||
|
||||
@ -177,16 +85,3 @@ consoleintr(int c)
|
||||
|
||||
release(&cons.lock);
|
||||
}
|
||||
|
||||
void
|
||||
consoleinit(void)
|
||||
{
|
||||
initlock(&cons.lock, "cons");
|
||||
|
||||
uartinit();
|
||||
|
||||
// connect read and write system calls
|
||||
// to consoleread and consolewrite.
|
||||
devsw[CONSOLE].read = consoleread;
|
||||
devsw[CONSOLE].write = consolewrite;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
struct buf;
|
||||
struct context;
|
||||
struct file;
|
||||
@ -18,7 +20,6 @@ void bpin(struct buf*);
|
||||
void bunpin(struct buf*);
|
||||
|
||||
// console.c
|
||||
void consoleinit(void);
|
||||
void consoleintr(int);
|
||||
void consputc(int);
|
||||
|
||||
@ -77,9 +78,12 @@ int piperead(struct pipe*, uint64, int);
|
||||
int pipewrite(struct pipe*, uint64, int);
|
||||
|
||||
// printf.c
|
||||
void printf(char*, ...);
|
||||
void panic(char*) __attribute__((noreturn));
|
||||
void printfinit(void);
|
||||
__attribute__((noreturn)) void panic(char *s);
|
||||
void printfinit(void);
|
||||
void printstr(char *s);
|
||||
void printint(int n);
|
||||
void printhex(int n);
|
||||
void printptr(uint64 p);
|
||||
|
||||
// proc.c
|
||||
int cpuid(void);
|
||||
@ -147,13 +151,7 @@ void trapinit(void);
|
||||
void trapinithart(void);
|
||||
extern struct spinlock tickslock;
|
||||
void usertrapret(void);
|
||||
|
||||
// uart.c
|
||||
void uartinit(void);
|
||||
void uartintr(void);
|
||||
void uartputc(int);
|
||||
void uartputc_sync(int);
|
||||
int uartgetc(void);
|
||||
|
||||
// vm.c
|
||||
void kvminit(void);
|
||||
|
@ -83,7 +83,7 @@ balloc(uint dev)
|
||||
}
|
||||
brelse(bp);
|
||||
}
|
||||
printf("balloc: out of blocks\n");
|
||||
printstr("balloc: out of blocks\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ ialloc(uint dev, short type)
|
||||
}
|
||||
brelse(bp);
|
||||
}
|
||||
printf("ialloc: no inodes\n");
|
||||
printstr("ialloc: no inodes\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
// Physical memory allocator, for user processes,
|
||||
// kernel stacks, page-table pages,
|
||||
// and pipe buffers. Allocates whole 4096-byte pages.
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "spinlock.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
void freerange(void *pa_start, void *pa_end);
|
||||
|
||||
extern char end[]; // first address after kernel.
|
||||
// defined by kernel.ld.
|
||||
|
||||
struct run {
|
||||
struct run *next;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct run *freelist;
|
||||
} kmem;
|
||||
|
||||
void
|
||||
kinit()
|
||||
{
|
||||
initlock(&kmem.lock, "kmem");
|
||||
freerange(end, (void*)PHYSTOP);
|
||||
}
|
||||
|
||||
void
|
||||
freerange(void *pa_start, void *pa_end)
|
||||
{
|
||||
char *p;
|
||||
p = (char*)PGROUNDUP((uint64)pa_start);
|
||||
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
// Free the page of physical memory pointed at by pa,
|
||||
// which normally should have been returned by a
|
||||
// call to kalloc(). (The exception is when
|
||||
// initializing the allocator; see kinit above.)
|
||||
void
|
||||
kfree(void *pa)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
|
||||
panic("kfree");
|
||||
|
||||
// Fill with junk to catch dangling refs.
|
||||
memset(pa, 1, PGSIZE);
|
||||
|
||||
r = (struct run*)pa;
|
||||
|
||||
acquire(&kmem.lock);
|
||||
r->next = kmem.freelist;
|
||||
kmem.freelist = r;
|
||||
release(&kmem.lock);
|
||||
}
|
||||
|
||||
// Allocate one 4096-byte page of physical memory.
|
||||
// Returns a pointer that the kernel can use.
|
||||
// Returns 0 if the memory cannot be allocated.
|
||||
void *
|
||||
kalloc(void)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
acquire(&kmem.lock);
|
||||
r = kmem.freelist;
|
||||
if(r)
|
||||
kmem.freelist = r->next;
|
||||
release(&kmem.lock);
|
||||
|
||||
if(r)
|
||||
memset((char*)r, 5, PGSIZE); // fill with junk
|
||||
return (void*)r;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
volatile static int started = 0;
|
||||
|
||||
void rust_main();
|
||||
|
||||
// start() jumps here in supervisor mode on all CPUs.
|
||||
void
|
||||
main()
|
||||
{
|
||||
if(cpuid() == 0){
|
||||
consoleinit();
|
||||
printfinit();
|
||||
printf("\n");
|
||||
printf("xv6 kernel is booting\n");
|
||||
printf("\n");
|
||||
rust_main();
|
||||
kinit(); // physical page allocator
|
||||
kvminit(); // create kernel page table
|
||||
kvminithart(); // turn on paging
|
||||
procinit(); // process table
|
||||
trapinit(); // trap vectors
|
||||
trapinithart(); // install kernel trap vector
|
||||
plicinit(); // set up interrupt controller
|
||||
plicinithart(); // ask PLIC for device interrupts
|
||||
binit(); // buffer cache
|
||||
iinit(); // inode table
|
||||
fileinit(); // file table
|
||||
virtio_disk_init(); // emulated hard disk
|
||||
userinit(); // first user process
|
||||
__sync_synchronize();
|
||||
started = 1;
|
||||
} else {
|
||||
while(started == 0)
|
||||
;
|
||||
__sync_synchronize();
|
||||
printf("hart %d starting\n", cpuid());
|
||||
kvminithart(); // turn on paging
|
||||
trapinithart(); // install kernel trap vector
|
||||
plicinithart(); // ask PLIC for device interrupts
|
||||
}
|
||||
|
||||
scheduler();
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
//
|
||||
// the riscv Platform Level Interrupt Controller (PLIC).
|
||||
//
|
||||
|
||||
void
|
||||
plicinit(void)
|
||||
{
|
||||
// set desired IRQ priorities non-zero (otherwise disabled).
|
||||
*(uint32*)(PLIC + UART0_IRQ*4) = 1;
|
||||
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
|
||||
}
|
||||
|
||||
void
|
||||
plicinithart(void)
|
||||
{
|
||||
int hart = cpuid();
|
||||
|
||||
// set enable bits for this hart's S-mode
|
||||
// for the uart and virtio disk.
|
||||
*(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
|
||||
|
||||
// set this hart's S-mode priority threshold to 0.
|
||||
*(uint32*)PLIC_SPRIORITY(hart) = 0;
|
||||
}
|
||||
|
||||
// ask the PLIC what interrupt we should serve.
|
||||
int
|
||||
plic_claim(void)
|
||||
{
|
||||
int hart = cpuid();
|
||||
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
||||
return irq;
|
||||
}
|
||||
|
||||
// tell the PLIC we've served this IRQ.
|
||||
void
|
||||
plic_complete(int irq)
|
||||
{
|
||||
int hart = cpuid();
|
||||
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
||||
}
|
139
kernel/printf.c
139
kernel/printf.c
@ -1,139 +0,0 @@
|
||||
//
|
||||
// formatted console output -- printf, panic.
|
||||
//
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
#include "proc.h"
|
||||
|
||||
volatile int panicked = 0;
|
||||
|
||||
// lock to avoid interleaving concurrent printf's.
|
||||
static struct {
|
||||
struct spinlock lock;
|
||||
int locking;
|
||||
} pr;
|
||||
|
||||
static char digits[] = "0123456789abcdef";
|
||||
|
||||
static void
|
||||
printint(int xx, int base, int sign)
|
||||
{
|
||||
char buf[16];
|
||||
int i;
|
||||
uint x;
|
||||
|
||||
if(sign && (sign = xx < 0))
|
||||
x = -xx;
|
||||
else
|
||||
x = xx;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
buf[i++] = digits[x % base];
|
||||
} while((x /= base) != 0);
|
||||
|
||||
if(sign)
|
||||
buf[i++] = '-';
|
||||
|
||||
while(--i >= 0)
|
||||
consputc(buf[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
printptr(uint64 x)
|
||||
{
|
||||
int i;
|
||||
consputc('0');
|
||||
consputc('x');
|
||||
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
|
||||
consputc(digits[x >> (sizeof(uint64) * 8 - 4)]);
|
||||
}
|
||||
|
||||
void print(char *raw) {
|
||||
printf("%s", raw);
|
||||
}
|
||||
|
||||
// Print to the console. only understands %d, %x, %p, %s.
|
||||
void
|
||||
printf(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, c, locking;
|
||||
char *s;
|
||||
|
||||
locking = pr.locking;
|
||||
if(locking)
|
||||
acquire(&pr.lock);
|
||||
|
||||
if (fmt == 0)
|
||||
panic("null fmt");
|
||||
|
||||
va_start(ap, fmt);
|
||||
for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
|
||||
if(c != '%'){
|
||||
consputc(c);
|
||||
continue;
|
||||
}
|
||||
c = fmt[++i] & 0xff;
|
||||
if(c == 0)
|
||||
break;
|
||||
switch(c){
|
||||
case 'd':
|
||||
printint(va_arg(ap, int), 10, 1);
|
||||
break;
|
||||
case 'x':
|
||||
printint(va_arg(ap, int), 16, 1);
|
||||
break;
|
||||
case 'p':
|
||||
printptr(va_arg(ap, uint64));
|
||||
break;
|
||||
case 's':
|
||||
if((s = va_arg(ap, char*)) == 0)
|
||||
s = "(null)";
|
||||
for(; *s; s++)
|
||||
consputc(*s);
|
||||
break;
|
||||
case '%':
|
||||
consputc('%');
|
||||
break;
|
||||
default:
|
||||
// Print unknown % sequence to draw attention.
|
||||
consputc('%');
|
||||
consputc(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if(locking)
|
||||
release(&pr.lock);
|
||||
}
|
||||
|
||||
void
|
||||
panic(char *s)
|
||||
{
|
||||
pr.locking = 0;
|
||||
printf("panic: ");
|
||||
printf(s);
|
||||
printf("\n");
|
||||
panicked = 1; // freeze uart output from other CPUs
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
printfinit(void)
|
||||
{
|
||||
initlock(&pr.lock, "pr");
|
||||
pr.locking = 1;
|
||||
}
|
220
kernel/proc.c
220
kernel/proc.c
@ -16,7 +16,7 @@ int nextpid = 1;
|
||||
struct spinlock pid_lock;
|
||||
|
||||
extern void forkret(void);
|
||||
static void freeproc(struct proc *p);
|
||||
void freeproc(struct proc *p);
|
||||
|
||||
extern char trampoline[]; // trampoline.S
|
||||
|
||||
@ -58,56 +58,11 @@ procinit(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Must be called with interrupts disabled,
|
||||
// to prevent race with process being moved
|
||||
// to a different CPU.
|
||||
// int
|
||||
// cpuid()
|
||||
// {
|
||||
// int id = r_tp();
|
||||
// return id;
|
||||
// }
|
||||
|
||||
// Return this CPU's cpu struct.
|
||||
// Interrupts must be disabled.
|
||||
struct cpu*
|
||||
mycpu(void)
|
||||
{
|
||||
int id = cpuid();
|
||||
struct cpu *c = &cpus[id];
|
||||
return c;
|
||||
}
|
||||
|
||||
// Return the current struct proc *, or zero if none.
|
||||
struct proc*
|
||||
myproc(void)
|
||||
{
|
||||
push_off();
|
||||
struct cpu *c = mycpu();
|
||||
struct proc *p = c->proc;
|
||||
pop_off();
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
allocpid()
|
||||
{
|
||||
int pid;
|
||||
|
||||
acquire(&pid_lock);
|
||||
pid = nextpid;
|
||||
nextpid = nextpid + 1;
|
||||
release(&pid_lock);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Look in the process table for an UNUSED proc.
|
||||
// If found, initialize state required to run in the kernel,
|
||||
// and return with p->lock held.
|
||||
// If there are no free procs, or a memory allocation fails, return 0.
|
||||
static struct proc*
|
||||
allocproc(void)
|
||||
struct proc* allocproc(void)
|
||||
{
|
||||
struct proc *p;
|
||||
|
||||
@ -149,28 +104,6 @@ found:
|
||||
return p;
|
||||
}
|
||||
|
||||
// free a proc structure and the data hanging from it,
|
||||
// including user pages.
|
||||
// p->lock must be held.
|
||||
static void
|
||||
freeproc(struct proc *p)
|
||||
{
|
||||
if(p->trapframe)
|
||||
kfree((void*)p->trapframe);
|
||||
p->trapframe = 0;
|
||||
if(p->pagetable)
|
||||
proc_freepagetable(p->pagetable, p->sz);
|
||||
p->pagetable = 0;
|
||||
p->sz = 0;
|
||||
p->pid = 0;
|
||||
p->parent = 0;
|
||||
p->name[0] = 0;
|
||||
p->chan = 0;
|
||||
p->killed = 0;
|
||||
p->xstate = 0;
|
||||
p->state = UNUSED;
|
||||
}
|
||||
|
||||
// Create a user page table for a given process, with no user memory,
|
||||
// but with trampoline and trapframe pages.
|
||||
pagetable_t
|
||||
@ -256,23 +189,7 @@ userinit(void)
|
||||
|
||||
// Grow or shrink user memory by n bytes.
|
||||
// Return 0 on success, -1 on failure.
|
||||
int
|
||||
growproc(int n)
|
||||
{
|
||||
uint64 sz;
|
||||
struct proc *p = myproc();
|
||||
|
||||
sz = p->sz;
|
||||
if(n > 0){
|
||||
if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(n < 0){
|
||||
sz = uvmdealloc(p->pagetable, sz, sz + n);
|
||||
}
|
||||
p->sz = sz;
|
||||
return 0;
|
||||
}
|
||||
int growproc(int n);
|
||||
|
||||
// Create a new process, copying the parent.
|
||||
// Sets up child kernel stack to return as if from fork() system call.
|
||||
@ -327,18 +244,7 @@ fork(void)
|
||||
|
||||
// Pass p's abandoned children to init.
|
||||
// Caller must hold wait_lock.
|
||||
void
|
||||
reparent(struct proc *p)
|
||||
{
|
||||
struct proc *pp;
|
||||
|
||||
for(pp = proc; pp < &proc[NPROC]; pp++){
|
||||
if(pp->parent == p){
|
||||
pp->parent = initproc;
|
||||
wakeup(initproc);
|
||||
}
|
||||
}
|
||||
}
|
||||
void reparent(struct proc *p);
|
||||
|
||||
// Exit the current process. Does not return.
|
||||
// An exited process remains in the zombie state
|
||||
@ -478,36 +384,10 @@ scheduler(void)
|
||||
// be proc->intena and proc->noff, but that would
|
||||
// break in the few places where a lock is held but
|
||||
// there's no process.
|
||||
void
|
||||
sched(void)
|
||||
{
|
||||
int intena;
|
||||
struct proc *p = myproc();
|
||||
|
||||
if(!holding(&p->lock))
|
||||
panic("sched p->lock");
|
||||
if(mycpu()->noff != 1)
|
||||
panic("sched locks");
|
||||
if(p->state == RUNNING)
|
||||
panic("sched running");
|
||||
if(intr_get())
|
||||
panic("sched interruptible");
|
||||
|
||||
intena = mycpu()->intena;
|
||||
swtch(&p->context, &mycpu()->context);
|
||||
mycpu()->intena = intena;
|
||||
}
|
||||
void sched(void);
|
||||
|
||||
// Give up the CPU for one scheduling round.
|
||||
void
|
||||
yield(void)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
acquire(&p->lock);
|
||||
p->state = RUNNABLE;
|
||||
sched();
|
||||
release(&p->lock);
|
||||
}
|
||||
void yield(void);
|
||||
|
||||
// A fork child's very first scheduling by scheduler()
|
||||
// will swtch to forkret.
|
||||
@ -532,39 +412,11 @@ forkret(void)
|
||||
|
||||
// Atomically release lock and sleep on chan.
|
||||
// Reacquires lock when awakened.
|
||||
void
|
||||
sleep(void *chan, struct spinlock *lk)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
|
||||
// Must acquire p->lock in order to
|
||||
// change p->state and then call sched.
|
||||
// Once we hold p->lock, we can be
|
||||
// guaranteed that we won't miss any wakeup
|
||||
// (wakeup locks p->lock),
|
||||
// so it's okay to release lk.
|
||||
|
||||
acquire(&p->lock); //DOC: sleeplock1
|
||||
release(lk);
|
||||
|
||||
// Go to sleep.
|
||||
p->chan = chan;
|
||||
p->state = SLEEPING;
|
||||
|
||||
sched();
|
||||
|
||||
// Tidy up.
|
||||
p->chan = 0;
|
||||
|
||||
// Reacquire original lock.
|
||||
release(&p->lock);
|
||||
acquire(lk);
|
||||
}
|
||||
void sleep(void *chan, struct spinlock *lk);
|
||||
|
||||
// Wake up all processes sleeping on chan.
|
||||
// Must be called without any p->lock.
|
||||
void
|
||||
wakeup(void *chan)
|
||||
void wakeup(void *chan)
|
||||
{
|
||||
struct proc *p;
|
||||
|
||||
@ -582,45 +434,9 @@ wakeup(void *chan)
|
||||
// Kill the process with the given pid.
|
||||
// The victim won't exit until it tries to return
|
||||
// to user space (see usertrap() in trap.c).
|
||||
int
|
||||
kill(int pid)
|
||||
{
|
||||
struct proc *p;
|
||||
|
||||
for(p = proc; p < &proc[NPROC]; p++){
|
||||
acquire(&p->lock);
|
||||
if(p->pid == pid){
|
||||
p->killed = 1;
|
||||
if(p->state == SLEEPING){
|
||||
// Wake process from sleep().
|
||||
p->state = RUNNABLE;
|
||||
}
|
||||
release(&p->lock);
|
||||
return 0;
|
||||
}
|
||||
release(&p->lock);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
setkilled(struct proc *p)
|
||||
{
|
||||
acquire(&p->lock);
|
||||
p->killed = 1;
|
||||
release(&p->lock);
|
||||
}
|
||||
|
||||
int
|
||||
killed(struct proc *p)
|
||||
{
|
||||
int k;
|
||||
|
||||
acquire(&p->lock);
|
||||
k = p->killed;
|
||||
release(&p->lock);
|
||||
return k;
|
||||
}
|
||||
int kill(int pid);
|
||||
void setkilled(struct proc *p);
|
||||
int killed(struct proc *p);
|
||||
|
||||
// Copy to either a user address, or kernel address,
|
||||
// depending on usr_dst.
|
||||
@ -655,8 +471,7 @@ either_copyin(void *dst, int user_src, uint64 src, uint64 len)
|
||||
// Print a process listing to console. For debugging.
|
||||
// Runs when user types ^P on console.
|
||||
// No lock to avoid wedging a stuck machine further.
|
||||
void
|
||||
procdump(void)
|
||||
void procdump(void)
|
||||
{
|
||||
static char *states[] = {
|
||||
[UNUSED] "unused",
|
||||
@ -669,7 +484,7 @@ procdump(void)
|
||||
struct proc *p;
|
||||
char *state;
|
||||
|
||||
printf("\n");
|
||||
printstr("\n");
|
||||
for(p = proc; p < &proc[NPROC]; p++){
|
||||
if(p->state == UNUSED)
|
||||
continue;
|
||||
@ -677,7 +492,12 @@ procdump(void)
|
||||
state = states[p->state];
|
||||
else
|
||||
state = "???";
|
||||
printf("%d %s %s", p->pid, state, p->name);
|
||||
printf("\n");
|
||||
|
||||
printint(p->pid);
|
||||
printstr(" ");
|
||||
printstr(state);
|
||||
printstr(" ");
|
||||
printstr(p->name);
|
||||
printstr("\n");
|
||||
}
|
||||
}
|
||||
|
@ -107,3 +107,4 @@ struct proc {
|
||||
};
|
||||
|
||||
int cpuid();
|
||||
int allocpid();
|
||||
|
@ -12,10 +12,7 @@
|
||||
#include "fs.h"
|
||||
#include "buf.h"
|
||||
|
||||
void
|
||||
ramdiskinit(void)
|
||||
{
|
||||
}
|
||||
void ramdiskinit(void);
|
||||
|
||||
// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
|
||||
// Else if B_VALID is not set, read buf from disk, set B_VALID.
|
||||
|
@ -1,30 +0,0 @@
|
||||
#include "kernel/riscv.h"
|
||||
|
||||
unsigned long rv_r_mhartid() {
|
||||
return r_mhartid();
|
||||
}
|
||||
|
||||
unsigned int rv_r_tp() {
|
||||
return r_tp();
|
||||
}
|
||||
|
||||
unsigned long rv_r_sstatus() {
|
||||
return r_sstatus();
|
||||
}
|
||||
|
||||
void rv_w_sstatus(unsigned long x) {
|
||||
w_sstatus(x);
|
||||
}
|
||||
|
||||
void rv_intr_on() {
|
||||
intr_on();
|
||||
}
|
||||
|
||||
void rv_intr_off() {
|
||||
intr_off();
|
||||
}
|
||||
|
||||
int rv_intr_get() {
|
||||
return intr_get();
|
||||
}
|
||||
|
14
kernel/rustkernel/Cargo.lock
generated
14
kernel/rustkernel/Cargo.lock
generated
@ -2,6 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "format_no_std"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6ab8bf81c8fc5032d2d3e651eb5fda8e225e278f1bc7abeda0a258b7ab5eff"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
@ -12,5 +24,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
name = "rustkernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"format_no_std",
|
||||
"libc",
|
||||
]
|
||||
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.7.4", default-features = false }
|
||||
format_no_std = "1.0.0"
|
||||
libc = { version = "0.2.149", default-features = false }
|
||||
|
||||
[lib]
|
||||
|
16
kernel/rustkernel/src/buf.rs
Normal file
16
kernel/rustkernel/src/buf.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use crate::{fs::BSIZE, sleeplock::Sleeplock};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Buf {
|
||||
/// Has data been read from disk?
|
||||
pub valid: i32,
|
||||
/// Does disk "own" buf?
|
||||
pub disk: i32,
|
||||
pub dev: u32,
|
||||
pub blockno: u32,
|
||||
pub lock: Sleeplock,
|
||||
pub refcnt: u32,
|
||||
pub prev: *mut Buf,
|
||||
pub next: *mut Buf,
|
||||
pub data: [u8; BSIZE as usize],
|
||||
}
|
225
kernel/rustkernel/src/console.rs
Normal file
225
kernel/rustkernel/src/console.rs
Normal file
@ -0,0 +1,225 @@
|
||||
//! Console input and output, to the uart.
|
||||
//
|
||||
// Reads are a line at a time.
|
||||
// Implements special input characters:
|
||||
// - newline: end of line
|
||||
// - ctrl-h: backspace
|
||||
// - ctrl-u: kill line
|
||||
// - ctrl-d: end of file
|
||||
// - ctrl-p: print process list
|
||||
|
||||
use crate::{
|
||||
file::{devsw, CONSOLE},
|
||||
proc::{killed, myproc, sleep},
|
||||
spinlock::{initlock, Spinlock},
|
||||
uart::{uartinit, uartputc, uartputc_sync},
|
||||
};
|
||||
use core::{
|
||||
ffi::{c_void, CStr},
|
||||
ptr::addr_of_mut,
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32;
|
||||
fn either_copyout(user_dst: i32, dst: u64, src: *mut c_void, len: u64) -> i32;
|
||||
|
||||
pub fn consoleintr(c: i32);
|
||||
fn wakeup(chan: *mut c_void);
|
||||
fn procdump();
|
||||
}
|
||||
|
||||
pub const BACKSPACE: i32 = 0x100;
|
||||
pub const INPUT_BUF_SIZE: u64 = 128;
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut cons: Console = Console {
|
||||
lock: unsafe { Spinlock::uninitialized() },
|
||||
buffer: [0u8; INPUT_BUF_SIZE as usize],
|
||||
read_index: 0,
|
||||
write_index: 0,
|
||||
edit_index: 0,
|
||||
};
|
||||
|
||||
/// ctrl-x
|
||||
fn ctrl_x(x: char) -> char {
|
||||
((x as u8) - b'@') as char
|
||||
}
|
||||
|
||||
/// Send one character to the UART.
|
||||
///
|
||||
/// Called by printf(), and to echo input
|
||||
/// characters but not from write().
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn consputc(c: i32) {
|
||||
if c == BACKSPACE {
|
||||
// If the user typed backspace, overwrite with a space.
|
||||
uartputc_sync('\x08' as i32);
|
||||
uartputc_sync(' ' as i32);
|
||||
uartputc_sync('\x08' as i32);
|
||||
} else {
|
||||
uartputc_sync(c);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Console {
|
||||
pub lock: Spinlock,
|
||||
pub buffer: [u8; INPUT_BUF_SIZE as usize],
|
||||
pub read_index: u32,
|
||||
pub write_index: u32,
|
||||
pub edit_index: u32,
|
||||
}
|
||||
|
||||
/// User write()s to the console go here.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 {
|
||||
for i in 0..n {
|
||||
let mut c = 0i8;
|
||||
|
||||
if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 {
|
||||
return i;
|
||||
} else {
|
||||
uartputc(c as i32);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// User read()s from the console go here.
|
||||
///
|
||||
/// Copy (up to) a whole input line to dst.
|
||||
/// user_dst indicates whether dst is a user
|
||||
/// or kernel address.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> i32 {
|
||||
let target = n;
|
||||
let mut c;
|
||||
let mut cbuf;
|
||||
|
||||
cons.lock.lock();
|
||||
|
||||
while n > 0 {
|
||||
// Wait until interrupt handler has put
|
||||
// some input into cons.buffer.
|
||||
while cons.read_index == cons.write_index {
|
||||
if killed(myproc()) != 0 {
|
||||
cons.lock.unlock();
|
||||
return -1;
|
||||
}
|
||||
sleep(
|
||||
addr_of_mut!(cons.read_index).cast(),
|
||||
addr_of_mut!(cons.lock),
|
||||
);
|
||||
}
|
||||
|
||||
c = cons.buffer[(cons.read_index % INPUT_BUF_SIZE as u32) as usize];
|
||||
cons.read_index += 1;
|
||||
|
||||
// ctrl-D or EOF
|
||||
if c == ctrl_x('D') as u8 {
|
||||
if n < target {
|
||||
// Save ctrl-D for next time, to make
|
||||
// sure caller gets a 0-byte result.
|
||||
cons.read_index -= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy the input byte to the user-space buffer.
|
||||
cbuf = c;
|
||||
if either_copyout(user_dst, dst, addr_of_mut!(cbuf).cast(), 1) == -1 {
|
||||
break;
|
||||
}
|
||||
|
||||
dst += 1;
|
||||
n -= 1;
|
||||
|
||||
if c == b'\n' {
|
||||
// A whole line has arrived,
|
||||
// return to the user-level read().
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cons.lock.unlock();
|
||||
|
||||
target - n
|
||||
}
|
||||
|
||||
// /// The console input interrupt handler.
|
||||
// ///
|
||||
// /// uartintr() calls this for input character.
|
||||
// /// Do erase/kill processing, then append to cons.buf.
|
||||
// /// Wake up consoleread() if a whole line has arrived.
|
||||
// #[no_mangle]
|
||||
// pub unsafe extern "C" fn consoleintr(c: i32) {
|
||||
// cons.lock.lock();
|
||||
//
|
||||
// let ctrl_p = ctrl_x('P') as u8 as i8 as i32;
|
||||
// let ctrl_u = ctrl_x('P') as u8 as i8 as i32;
|
||||
// let ctrl_h = ctrl_x('P') as u8 as i8 as i32;
|
||||
// let ctrl_d = ctrl_x('D') as u8 as i8 as i32;
|
||||
// let cr = '\r' as u8 as i8 as i32;
|
||||
// let nl = '\n' as u8 as i8 as i32;
|
||||
//
|
||||
// match c {
|
||||
// // Print process list.
|
||||
// ctrl_p => procdump(),
|
||||
// // Kill line
|
||||
// ctrl_u => {
|
||||
// while cons.edit_index != cons.write_index
|
||||
// && cons.buffer[((cons.edit_index - 1) % INPUT_BUF_SIZE as u32) as usize]
|
||||
// != '\n' as u8
|
||||
// {
|
||||
// cons.edit_index -= 1;
|
||||
// consputc(BACKSPACE);
|
||||
// }
|
||||
// }
|
||||
// // Backspace
|
||||
// ctrl_h => {
|
||||
// if cons.edit_index != cons.write_index {
|
||||
// cons.edit_index -= 1;
|
||||
// consputc(BACKSPACE);
|
||||
// }
|
||||
// }
|
||||
// c => {
|
||||
// if cons.edit_index - cons.read_index < INPUT_BUF_SIZE as u32 {
|
||||
// let c = if c == cr { nl } else { c };
|
||||
//
|
||||
// // Echo back to the user.
|
||||
// consputc(c);
|
||||
//
|
||||
// // Store for consumption by consoleread().
|
||||
// cons.buffer[(cons.edit_index % INPUT_BUF_SIZE as u32) as usize] = c as i8 as u8;
|
||||
// cons.edit_index += 1;
|
||||
//
|
||||
// if c == nl
|
||||
// || c == ctrl_d
|
||||
// || cons.edit_index - cons.read_index == INPUT_BUF_SIZE as u32
|
||||
// {
|
||||
// // Wake up consoleread() if a whole line (or EOF) has arrived.
|
||||
// cons.write_index = cons.edit_index;
|
||||
// wakeup(addr_of_mut!(cons.read_index).cast());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// cons.lock.unlock();
|
||||
// }
|
||||
|
||||
pub unsafe fn consoleinit() {
|
||||
initlock(
|
||||
addr_of_mut!(cons.lock),
|
||||
CStr::from_bytes_with_nul(b"cons\0")
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast_mut(),
|
||||
);
|
||||
uartinit();
|
||||
|
||||
// Connect read and write syscalls
|
||||
// to consoleread and consolewrite.
|
||||
devsw[CONSOLE].read = consoleread as usize as *const i32;
|
||||
devsw[CONSOLE].write = consolewrite as usize as *const i32;
|
||||
}
|
11
kernel/rustkernel/src/file.rs
Normal file
11
kernel/rustkernel/src/file.rs
Normal file
@ -0,0 +1,11 @@
|
||||
#[repr(C)]
|
||||
pub struct Devsw {
|
||||
pub read: *const i32,
|
||||
pub write: *const i32,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub static mut devsw: [Devsw; crate::param::NDEV];
|
||||
}
|
||||
|
||||
pub const CONSOLE: usize = 1;
|
79
kernel/rustkernel/src/fs.rs
Normal file
79
kernel/rustkernel/src/fs.rs
Normal file
@ -0,0 +1,79 @@
|
||||
//! On-disk file system forma.
|
||||
//! Both the kernel and user programs use this header file.
|
||||
|
||||
// Root inode
|
||||
pub const ROOTINO: u64 = 1;
|
||||
/// Block size.
|
||||
pub const BSIZE: u32 = 1024;
|
||||
|
||||
// Disk layout:
|
||||
// [ boot block | super block | log | inode blocks | free bit map | data blocks ]
|
||||
//
|
||||
// mkfs computes the super block and builds an initial file system.
|
||||
// The super block describes the disk layout:
|
||||
#[repr(C)]
|
||||
pub struct Superblock {
|
||||
/// Must be FSMAGIC.
|
||||
pub magic: u32,
|
||||
/// Size of file system image (blocks).
|
||||
pub size: u32,
|
||||
/// Number of data blocks.
|
||||
pub nblocks: u32,
|
||||
/// Number of inodes.
|
||||
pub ninodes: u32,
|
||||
/// Number of log blocks.
|
||||
pub nlog: u32,
|
||||
/// Block number of first log block.
|
||||
pub logstart: u32,
|
||||
/// Block number of first inode block.
|
||||
pub inodestart: u32,
|
||||
/// Block number of first free map block.
|
||||
pub bmapstart: u32,
|
||||
}
|
||||
|
||||
pub const FSMAGIC: u32 = 0x10203040;
|
||||
pub const NDIRECT: u32 = 12;
|
||||
pub const NINDIRECT: u32 = BSIZE / core::mem::size_of::<u32>() as u32;
|
||||
pub const MAXFILE: u32 = NDIRECT + NINDIRECT;
|
||||
|
||||
// On-disk inode structure;
|
||||
#[repr(C)]
|
||||
pub struct DiskInode {
|
||||
/// File type.
|
||||
pub kind: i16,
|
||||
/// Major device number (T_DEVICE only).
|
||||
pub major: i16,
|
||||
/// Minor device number (T_DEVICE only).
|
||||
pub minor: i16,
|
||||
/// Number of links to inode in file system.
|
||||
pub nlink: i16,
|
||||
/// Size of file (bytes).
|
||||
pub size: u32,
|
||||
/// Data block addresses.
|
||||
pub addrs: [u32; NDIRECT as usize + 1],
|
||||
}
|
||||
|
||||
/// Inodes per block.
|
||||
pub const IPB: u32 = BSIZE / core::mem::size_of::<DiskInode>() as u32;
|
||||
|
||||
/// Block containing inode i.
|
||||
pub fn iblock(inode: u32, superblock: &Superblock) -> u32 {
|
||||
inode / IPB + superblock.inodestart
|
||||
}
|
||||
|
||||
/// Bitmap bits per block.
|
||||
pub const BPB: u32 = BSIZE * 8;
|
||||
|
||||
/// Block of free map containing bit for block b.
|
||||
pub fn bblock(block: u32, superblock: &Superblock) -> u32 {
|
||||
block / BPB + superblock.bmapstart
|
||||
}
|
||||
|
||||
/// Directory is a file containing a sequence of DirectoryEntry structures.
|
||||
pub const DIRSIZ: usize = 14;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DirectoryEntry {
|
||||
pub inum: u16,
|
||||
pub name: [u8; DIRSIZ],
|
||||
}
|
@ -1,6 +1,104 @@
|
||||
//! Physical memory allocator, for user processes,
|
||||
//! kernel stacks, page-table pages,
|
||||
//! and pipe buffers. Allocates whole 4096-byte pages.
|
||||
|
||||
use crate::{
|
||||
riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE},
|
||||
spinlock::Spinlock,
|
||||
string::memset,
|
||||
};
|
||||
use core::{
|
||||
ffi::{c_char, CStr},
|
||||
ptr::{addr_of_mut, null_mut},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
fn kalloc() -> *mut u8;
|
||||
fn kfree(ptr: *mut u8);
|
||||
// oh my god this is so stupid why the fuck
|
||||
// this took me so long to figure out it's 3am rn
|
||||
// First address after kernel. Defined by kernel.ld.
|
||||
pub static mut end: [c_char; 0];
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut kmem: KernelMemory = KernelMemory {
|
||||
lock: unsafe { Spinlock::uninitialized() },
|
||||
freelist: null_mut(),
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Run {
|
||||
next: *mut Run,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct KernelMemory {
|
||||
pub lock: Spinlock,
|
||||
pub freelist: *mut Run,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kinit() {
|
||||
kmem.lock = Spinlock::new(
|
||||
CStr::from_bytes_with_nul(b"kmem\0")
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast_mut(),
|
||||
);
|
||||
freerange(addr_of_mut!(end).cast(), PHYSTOP as *mut u8)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn freerange(pa_start: *mut u8, pa_end: *mut u8) {
|
||||
let mut p = pg_round_up(pa_start as usize as u64) as *mut u8;
|
||||
|
||||
while p.add(PGSIZE as usize) <= pa_end {
|
||||
kfree(p.cast());
|
||||
p = p.add(PGSIZE as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Free the page of physical memory pointed at by pa,
|
||||
/// which normally should have been returned by a call
|
||||
/// to kalloc(). The exception is when initializing the
|
||||
/// allocator - see kinit above.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kfree(pa: *mut u8) {
|
||||
if (pa as usize as u64 % PGSIZE) != 0
|
||||
|| pa <= addr_of_mut!(end) as *mut u8
|
||||
|| pa >= PHYSTOP as *mut u8
|
||||
{
|
||||
panic!("kfree");
|
||||
}
|
||||
|
||||
memset(pa, 0, PGSIZE as u32);
|
||||
|
||||
let run: *mut Run = pa.cast();
|
||||
|
||||
kmem.lock.lock();
|
||||
(*run).next = kmem.freelist;
|
||||
kmem.freelist = run;
|
||||
kmem.lock.unlock();
|
||||
}
|
||||
|
||||
/// Allocate one 4096-byte page of physical memory.
|
||||
///
|
||||
/// Returns a pointer that the kernel can use.
|
||||
/// Returns 0 if the memory cannot be allocated.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kalloc() -> *mut u8 {
|
||||
kmem.lock.lock();
|
||||
|
||||
let run = kmem.freelist;
|
||||
if !run.is_null() {
|
||||
kmem.freelist = (*run).next;
|
||||
}
|
||||
|
||||
kmem.lock.unlock();
|
||||
|
||||
if !run.is_null() {
|
||||
memset(run.cast(), 0, PGSIZE as u32);
|
||||
}
|
||||
|
||||
run as *mut u8
|
||||
}
|
||||
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
|
@ -6,38 +6,93 @@
|
||||
extern crate alloc;
|
||||
extern crate core;
|
||||
|
||||
extern "C" {
|
||||
fn print(message: *const c_char);
|
||||
fn panic(panic_message: *const c_char) -> !;
|
||||
}
|
||||
|
||||
mod kalloc;
|
||||
pub mod buf;
|
||||
pub mod console;
|
||||
pub mod file;
|
||||
pub mod fs;
|
||||
pub(crate) mod kalloc;
|
||||
pub(crate) mod param;
|
||||
pub mod printf;
|
||||
pub mod proc;
|
||||
pub(crate) mod riscv;
|
||||
pub mod sleeplock;
|
||||
pub mod spinlock;
|
||||
pub mod start;
|
||||
pub mod string;
|
||||
pub mod syscall;
|
||||
pub mod sysproc;
|
||||
pub mod trap;
|
||||
pub mod uart;
|
||||
|
||||
extern "C" {
|
||||
// pub fn printfinit();
|
||||
// pub fn kinit();
|
||||
pub fn kvminit();
|
||||
pub fn kvminithart();
|
||||
pub fn procinit();
|
||||
pub fn binit();
|
||||
pub fn iinit();
|
||||
pub fn fileinit();
|
||||
pub fn virtio_disk_init();
|
||||
pub fn userinit();
|
||||
pub fn scheduler();
|
||||
}
|
||||
|
||||
use crate::{printf::print, proc::cpuid};
|
||||
use core::ffi::{c_char, CStr};
|
||||
|
||||
pub use proc::*;
|
||||
pub use spinlock::*;
|
||||
pub static mut STARTED: bool = false;
|
||||
pub static mut PANICKED: bool = false;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() {
|
||||
unsafe {
|
||||
print(
|
||||
CStr::from_bytes_with_nul(b"Hello from Rust!\n\0")
|
||||
.unwrap()
|
||||
.as_ptr(),
|
||||
);
|
||||
pub unsafe extern "C" fn main() {
|
||||
if cpuid() == 0 {
|
||||
console::consoleinit();
|
||||
printf::printfinit();
|
||||
kalloc::kinit();
|
||||
print!("\nxv6 kernel is booting\n");
|
||||
kvminit();
|
||||
kvminithart();
|
||||
procinit();
|
||||
trap::trapinit();
|
||||
trap::trapinithart();
|
||||
riscv::plic::plicinit();
|
||||
riscv::plic::plicinithart();
|
||||
binit();
|
||||
iinit();
|
||||
fileinit();
|
||||
virtio_disk_init();
|
||||
userinit();
|
||||
STARTED = true;
|
||||
} else {
|
||||
while !STARTED {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
kvminithart();
|
||||
trap::trapinithart();
|
||||
riscv::plic::plicinithart();
|
||||
}
|
||||
|
||||
scheduler();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
unsafe fn panic_wrapper(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
panic(
|
||||
CStr::from_bytes_with_nul(b"panic from rust\0")
|
||||
.unwrap_or_default()
|
||||
.as_ptr(),
|
||||
)
|
||||
fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! {
|
||||
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
|
||||
crate::printf::print!("panic: {}\n", s);
|
||||
} else {
|
||||
crate::printf::print!("kernel panic\n");
|
||||
}
|
||||
|
||||
unsafe { crate::PANICKED = true };
|
||||
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn panic(s: *const c_char) -> ! {
|
||||
let s = CStr::from_ptr(s).to_str().unwrap_or("panic from c");
|
||||
panic!("{}", s);
|
||||
}
|
||||
|
66
kernel/rustkernel/src/printf.rs
Normal file
66
kernel/rustkernel/src/printf.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use crate::spinlock::Spinlock;
|
||||
use core::ffi::{c_char, CStr};
|
||||
|
||||
pub use crate::panic;
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut PRINT_LOCK: Spinlock = unsafe { Spinlock::uninitialized() };
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PrintLock {
|
||||
pub lock: Spinlock,
|
||||
pub locking: i32,
|
||||
}
|
||||
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {{
|
||||
unsafe { $crate::printf::PRINT_LOCK.lock() };
|
||||
|
||||
// Allocate a page of memory as the buffer and release it when we're done.
|
||||
let buf = unsafe { $crate::kalloc::kalloc() as *mut [u8; 4096] };
|
||||
|
||||
let s: &str = format_no_std::show(
|
||||
unsafe { buf.as_mut() }.unwrap(),
|
||||
format_args!($($arg)*),
|
||||
).unwrap();
|
||||
|
||||
for c in s.as_bytes() {
|
||||
unsafe { $crate::console::consputc(*c as i8 as i32) };
|
||||
}
|
||||
|
||||
unsafe { $crate::kalloc::kfree(buf.cast()) };
|
||||
unsafe { $crate::printf::PRINT_LOCK.unlock() };
|
||||
}};
|
||||
}
|
||||
pub(crate) use print;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn printint(n: i32) {
|
||||
print!("{}", n);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn printhex(n: i32) {
|
||||
print!("{:0x}", n);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn printptr(p: u64) {
|
||||
print!("{:#018x}", p);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn printstr(s: *const c_char) {
|
||||
let s = CStr::from_ptr(s).to_str().unwrap_or_default();
|
||||
print!("{}", s);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn printfinit() {
|
||||
PRINT_LOCK = Spinlock::new(
|
||||
CStr::from_bytes_with_nul(b"pr\0")
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast_mut(),
|
||||
);
|
||||
}
|
@ -1,11 +1,48 @@
|
||||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
use crate::{
|
||||
riscv::{self, Pagetable},
|
||||
spinlock::Spinlock,
|
||||
kalloc::kfree,
|
||||
param::*,
|
||||
riscv::{self, Pagetable, PTE_W},
|
||||
spinlock::{pop_off, push_off, Spinlock},
|
||||
};
|
||||
use core::ffi::c_char;
|
||||
use core::{
|
||||
ffi::{c_char, c_void},
|
||||
ptr::{addr_of_mut, null_mut},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
pub static mut cpus: [Cpu; NCPU];
|
||||
pub static mut proc: [Proc; NPROC];
|
||||
pub static mut initproc: *mut Proc;
|
||||
pub static mut nextpid: i32;
|
||||
pub static mut pid_lock: Spinlock;
|
||||
/// Helps ensure that wakeups of wait()ing
|
||||
/// parents are not lost. Helps obey the
|
||||
/// memory model when using p->parent.
|
||||
/// Must be acquired before any p->lock.
|
||||
pub static mut wait_lock: Spinlock;
|
||||
// trampoline.S
|
||||
pub static mut trampoline: *mut c_char;
|
||||
|
||||
pub fn forkret();
|
||||
pub fn fork() -> i32;
|
||||
pub fn exit(status: i32) -> !;
|
||||
pub fn wait(addr: u64) -> i32;
|
||||
pub fn proc_pagetable(p: *mut Proc) -> Pagetable;
|
||||
pub fn proc_freepagetable(pagetable: Pagetable, sz: u64);
|
||||
pub fn wakeup(chan: *mut c_void);
|
||||
pub fn allocproc() -> *mut Proc;
|
||||
// pub fn freeproc(p: *mut Proc);
|
||||
pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64;
|
||||
pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64;
|
||||
// pub fn sched();
|
||||
pub fn swtch(a: *mut Context, b: *mut Context);
|
||||
}
|
||||
|
||||
/// Saved registers for kernel context switches.
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub struct Context {
|
||||
pub ra: u64,
|
||||
pub sp: u64,
|
||||
@ -37,6 +74,16 @@ pub struct Cpu {
|
||||
/// Were interrupts enabled before push_off()?
|
||||
pub intena: i32,
|
||||
}
|
||||
impl Default for Cpu {
|
||||
fn default() -> Self {
|
||||
Cpu {
|
||||
proc: null_mut(),
|
||||
context: Context::default(),
|
||||
noff: 0,
|
||||
intena: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-process data for the trap handling code in trampoline.S.
|
||||
///
|
||||
@ -52,6 +99,7 @@ pub struct Cpu {
|
||||
/// return-to-user path via usertrapret() doesn't return through
|
||||
/// the entire kernel call stack.
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub struct TrapFrame {
|
||||
/// Kernel page table.
|
||||
pub kernel_satp: u64,
|
||||
@ -97,7 +145,9 @@ pub struct TrapFrame {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Default)]
|
||||
pub enum ProcState {
|
||||
#[default]
|
||||
Unused,
|
||||
Used,
|
||||
Sleeping,
|
||||
@ -115,7 +165,7 @@ pub struct Proc {
|
||||
/// Process state
|
||||
pub state: ProcState,
|
||||
/// If non-zero, sleeping on chan
|
||||
pub chan: *mut u8,
|
||||
pub chan: *mut c_void,
|
||||
/// If non-zero, have been killed
|
||||
pub killed: i32,
|
||||
/// Exit status to be returned to parent's wait
|
||||
@ -154,9 +204,252 @@ pub unsafe extern "C" fn cpuid() -> i32 {
|
||||
riscv::r_tp() as i32
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// pub fn cpuid() -> i32;
|
||||
/// Return this CPU's cpu struct.
|
||||
/// Interrupts must be disabled.
|
||||
pub fn mycpu() -> *mut Cpu;
|
||||
/// Return this CPU's cpu struct.
|
||||
/// Interrupts must be disabled.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mycpu() -> *mut Cpu {
|
||||
let id = cpuid();
|
||||
addr_of_mut!(cpus[id as usize])
|
||||
}
|
||||
|
||||
/// Return the current struct proc *, or zero if none.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn myproc() -> *mut Proc {
|
||||
push_off();
|
||||
let c = mycpu();
|
||||
let p = (*c).proc;
|
||||
pop_off();
|
||||
p
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn allocpid() -> i32 {
|
||||
let lock = addr_of_mut!(pid_lock);
|
||||
(*lock).lock();
|
||||
let pid = nextpid;
|
||||
nextpid += 1;
|
||||
(*lock).unlock();
|
||||
pid
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/// Look in the process table for an UNUSED proc.
|
||||
/// If found, initialize state required to run in the kernel,
|
||||
/// and return with p->lock held. If there are no free procs,
|
||||
/// or a memory allocation fails, return 0.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn allocproc() -> *mut Proc {
|
||||
for p in &mut proc {
|
||||
let lock = addr_of_mut!(p.lock);
|
||||
(*lock).lock();
|
||||
|
||||
if p.state != ProcState::Unused {
|
||||
(*lock).unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
let p = addr_of_mut!(*p);
|
||||
(*p).pid = allocpid();
|
||||
(*p).state = ProcState::Used;
|
||||
|
||||
// Allocate a trapframe page and
|
||||
// create an empty user page table.
|
||||
(*p).trapframe = kalloc().cast();
|
||||
(*p).pagetable = proc_pagetable(p);
|
||||
|
||||
if (*p).trapframe.is_null() || (*p).pagetable.is_null() {
|
||||
freeproc(p);
|
||||
(*p).lock.unlock();
|
||||
return null_mut();
|
||||
}
|
||||
|
||||
// Set up new context to start executing
|
||||
// at forkret which returns to user space.
|
||||
memset(addr_of_mut!((*p).context).cast(), 0, size_of::<Context>() as u32);
|
||||
// TODO: convert fn pointer to u64
|
||||
(*p).context.ra = forkret as usize as u64;
|
||||
(*p).context.sp = (*p).kstack + PGSIZE;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
null_mut()
|
||||
}
|
||||
*/
|
||||
|
||||
/// Free a proc structure and the data hanging from it, including user pages.
|
||||
/// p->lock must be held.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn freeproc(p: *mut Proc) {
|
||||
if !(*p).trapframe.is_null() {
|
||||
kfree((*p).trapframe.cast());
|
||||
}
|
||||
(*p).trapframe = null_mut();
|
||||
if !(*p).pagetable.is_null() {
|
||||
proc_freepagetable((*p).pagetable, (*p).sz);
|
||||
}
|
||||
(*p).pagetable = null_mut();
|
||||
(*p).sz = 0;
|
||||
(*p).pid = 0;
|
||||
(*p).parent = null_mut();
|
||||
(*p).name[0] = 0;
|
||||
(*p).chan = null_mut();
|
||||
(*p).killed = 0;
|
||||
(*p).xstate = 0;
|
||||
(*p).state = ProcState::Unused;
|
||||
}
|
||||
|
||||
// /// Wake up all processes sleeping on chan.
|
||||
// /// Must be called without any p->lock.
|
||||
// #[no_mangle]
|
||||
// pub unsafe extern "C" fn wakeup(chan: *mut c_void) {
|
||||
// for p in &mut proc {
|
||||
// let p: *mut Proc = addr_of_mut!(*p);
|
||||
//
|
||||
// if p != myproc() {
|
||||
// (*p).lock.lock();
|
||||
// if (*p).state == ProcState::Sleeping && (*p).chan == chan {
|
||||
// (*p).state = ProcState::Runnable;
|
||||
// }
|
||||
// (*p).lock.unlock();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Pass p's abandoned children to init.
|
||||
/// Caller must hold wait_lock.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn reparent(p: *mut Proc) {
|
||||
for pp in proc.iter_mut().map(|p: &mut Proc| addr_of_mut!(*p)) {
|
||||
if (*pp).parent == p {
|
||||
(*pp).parent = initproc;
|
||||
wakeup(initproc.cast());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Grow or shrink user memory by n bytes.
|
||||
/// Return 0 on success, -1 on failure.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn growproc(n: i32) -> i32 {
|
||||
let p = myproc();
|
||||
let mut sz = (*p).sz;
|
||||
|
||||
if n > 0 {
|
||||
sz = uvmalloc((*p).pagetable, sz, sz.wrapping_add(n as u64), PTE_W as i32);
|
||||
if sz == 0 {
|
||||
return -1;
|
||||
}
|
||||
} else if n < 0 {
|
||||
sz = uvmdealloc((*p).pagetable, sz, sz.wrapping_add(n as u64));
|
||||
}
|
||||
(*p).sz = sz;
|
||||
0
|
||||
}
|
||||
|
||||
/// Give up the CPU for one scheduling round.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn r#yield() {
|
||||
let p = myproc();
|
||||
(*p).lock.lock();
|
||||
(*p).state = ProcState::Runnable;
|
||||
sched();
|
||||
(*p).lock.unlock();
|
||||
}
|
||||
|
||||
/// Switch to scheduler. Must hold only p->lock
|
||||
/// and have changed proc->state. Saves and restores
|
||||
/// intena because intena is a property of this
|
||||
/// kernel thread, not this CPU. It should
|
||||
/// be proc->intena and proc->noff, but that would
|
||||
/// break in the few places where a lock is held but
|
||||
/// there's no process.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sched() {
|
||||
let p = myproc();
|
||||
let c = mycpu();
|
||||
|
||||
if !(*p).lock.held_by_current_cpu() {
|
||||
panic!("sched p->lock");
|
||||
} else if (*c).noff != 1 {
|
||||
panic!("sched locks");
|
||||
} else if (*p).state == ProcState::Running {
|
||||
panic!("sched running");
|
||||
} else if riscv::intr_get() > 0 {
|
||||
panic!("sched interruptible");
|
||||
}
|
||||
|
||||
let intena = (*c).intena;
|
||||
swtch(addr_of_mut!((*p).context), addr_of_mut!((*c).context));
|
||||
(*c).intena = intena;
|
||||
}
|
||||
|
||||
/// Atomically release lock and sleep on chan.
|
||||
/// Reacquires lock when awakened.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sleep(chan: *mut c_void, lock: *mut Spinlock) {
|
||||
let p = myproc();
|
||||
|
||||
// Must acquire p->lock in order to
|
||||
// change p->state and then call sched.
|
||||
// Once we hold p->lock, we can be
|
||||
// guaranteed that we won't miss any wakeup
|
||||
// (wakeup locks p->lock),
|
||||
// so it's okay to release lk.
|
||||
|
||||
(*p).lock.lock();
|
||||
(*lock).unlock();
|
||||
|
||||
// Go to sleep.
|
||||
(*p).chan = chan;
|
||||
(*p).state = ProcState::Sleeping;
|
||||
|
||||
sched();
|
||||
|
||||
// Tidy up.
|
||||
(*p).chan = null_mut();
|
||||
|
||||
// Reacquire original lock.
|
||||
(*p).lock.unlock();
|
||||
(*lock).lock();
|
||||
}
|
||||
|
||||
/// Kill the process with the given pid.
|
||||
/// The victim won't exit until it tries to return
|
||||
/// to user space (see usertrap() in trap.c).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kill(pid: i32) -> i32 {
|
||||
for p in &mut proc {
|
||||
p.lock.lock();
|
||||
|
||||
if p.pid == pid {
|
||||
p.killed = 1;
|
||||
|
||||
if p.state == ProcState::Sleeping {
|
||||
// Wake process from sleep().
|
||||
p.state = ProcState::Runnable;
|
||||
}
|
||||
|
||||
p.lock.unlock();
|
||||
return 0;
|
||||
}
|
||||
p.lock.unlock();
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setkilled(p: *mut Proc) {
|
||||
(*p).lock.lock();
|
||||
(*p).killed = 1;
|
||||
(*p).lock.unlock();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn killed(p: *mut Proc) -> i32 {
|
||||
(*p).lock.lock();
|
||||
let k = (*p).killed;
|
||||
(*p).lock.unlock();
|
||||
k
|
||||
}
|
||||
|
8
kernel/rustkernel/src/ramdisk.rs
Normal file
8
kernel/rustkernel/src/ramdisk.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//! Ramdisk that uses the disk image loaded by qemu -initrd fs.img
|
||||
|
||||
extern "C" {
|
||||
pub fn ramdiskrw(buffer: *mut Buf);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ramdiskinit() {}
|
@ -159,7 +159,6 @@ pub unsafe fn w_mscratch(x: u64) {
|
||||
asm!("csrw mscratch, {}", in(reg) x);
|
||||
}
|
||||
|
||||
|
||||
// Supervisor Trap Cause
|
||||
#[inline(always)]
|
||||
pub unsafe fn r_scause() -> u64 {
|
||||
|
84
kernel/rustkernel/src/riscv/memlayout.rs
Normal file
84
kernel/rustkernel/src/riscv/memlayout.rs
Normal file
@ -0,0 +1,84 @@
|
||||
// Physical memory layout
|
||||
|
||||
// QEMU -machine virt is setup like this,
|
||||
// based on QEMU's hw/riscv/virt.c
|
||||
//
|
||||
// 00001000 - boot ROM, provided by qemu
|
||||
// 02000000 - CLINT
|
||||
// 0C000000 - PLIC
|
||||
// 10000000 - uart0
|
||||
// 10001000 - virtio disk
|
||||
// 80000000 - boot ROM jumps here in machine mode (kernel loads the kernel here)
|
||||
// unused after 8000000
|
||||
|
||||
// The kernel uses physical memory as so:
|
||||
// 80000000 - entry.S, then kernel text and data
|
||||
// end - start of kernel page allocation data
|
||||
// PHYSTOP - end of RAM used by the kernel
|
||||
|
||||
use super::{MAXVA, PGSIZE};
|
||||
|
||||
// QEMU puts UART registers here in physical memory.
|
||||
pub const UART0: u64 = 0x10000000;
|
||||
pub const UART0_IRQ: i32 = 10;
|
||||
|
||||
// Virtio MMIO interface
|
||||
pub const VIRTIO0: u64 = 0x10001000;
|
||||
pub const VIRTIO0_IRQ: i32 = 1;
|
||||
|
||||
// Core Local Interrupter (CLINT), which contains the timer.
|
||||
pub const CLINT: u64 = 0x2000000;
|
||||
pub const CLINT_MTIME: u64 = CLINT + 0xbff8;
|
||||
pub fn clint_mtimecmp(hartid: u64) -> u64 {
|
||||
CLINT + 0x4000 + (8 * hartid)
|
||||
}
|
||||
|
||||
// QEMU puts platform-level interrupt controller (PLIC) here.
|
||||
pub const PLIC: u64 = 0x0c000000;
|
||||
pub const PLIC_PRIORITY: u64 = PLIC;
|
||||
pub const PLIC_PENDING: u64 = PLIC + 0x1000;
|
||||
pub fn plic_menable(hartid: u64) -> u64 {
|
||||
PLIC + 0x2000 + (0x100 * hartid)
|
||||
}
|
||||
pub fn plic_senable(hartid: u64) -> u64 {
|
||||
PLIC + 0x2080 + (0x100 * hartid)
|
||||
}
|
||||
pub fn plic_mpriority(hartid: u64) -> u64 {
|
||||
PLIC + 0x200000 + (0x2000 * hartid)
|
||||
}
|
||||
pub fn plic_spriority(hartid: u64) -> u64 {
|
||||
PLIC + 0x201000 + (0x2000 * hartid)
|
||||
}
|
||||
pub fn plic_mclaim(hartid: u64) -> u64 {
|
||||
PLIC + 0x200004 + (0x2000 * hartid)
|
||||
}
|
||||
pub fn plic_sclaim(hartid: u64) -> u64 {
|
||||
PLIC + 0x201004 + (0x2000 * hartid)
|
||||
}
|
||||
|
||||
// The kernel expects there to be RAM
|
||||
// for use by the kernel and user pages
|
||||
// from physical address 0x80000000 to PHYSTOP.
|
||||
pub const KERNBASE: u64 = 0x80000000;
|
||||
pub const PHYSTOP: u64 = KERNBASE + 128 * 1024 * 1024;
|
||||
|
||||
// Map the trampoline page to the highest address,
|
||||
// in both user and kernel space.
|
||||
pub const TRAMPOLINE: u64 = MAXVA - PGSIZE;
|
||||
|
||||
// Map kernel stacks beneath the trampoline,
|
||||
// each surrouned by invalid guard pages.
|
||||
pub fn kstack(p: u64) -> u64 {
|
||||
TRAMPOLINE - (p + 1) * 2 * PGSIZE
|
||||
}
|
||||
|
||||
// User memory layout.
|
||||
// Address zero first:
|
||||
// - text
|
||||
// - original data and bss
|
||||
// - fixed-size stack
|
||||
// - expandable heap
|
||||
// ...
|
||||
// - TRAPFRAME (p->trapframe, used by the trampoline)
|
||||
// - TRAMPOLINE (the same page as in the kernel)
|
||||
pub const TRAPFRAME: u64 = TRAMPOLINE - PGSIZE;
|
@ -1,6 +1,9 @@
|
||||
pub mod asm;
|
||||
pub mod memlayout;
|
||||
pub mod plic;
|
||||
|
||||
pub use asm::*;
|
||||
pub use memlayout::*;
|
||||
|
||||
pub type Pte = u64;
|
||||
pub type Pagetable = *mut [Pte; 512];
|
||||
@ -45,8 +48,38 @@ pub const PGSIZE: u64 = 4096;
|
||||
/// Bits of offset within a page
|
||||
pub const PGSHIFT: u64 = 12;
|
||||
|
||||
pub fn pg_round_up(sz: u64) -> u64 {
|
||||
(sz + PGSIZE - 1) & !(PGSIZE - 1)
|
||||
}
|
||||
pub fn pg_round_down(a: u64) -> u64 {
|
||||
a & !(PGSIZE - 1)
|
||||
}
|
||||
|
||||
// Valid.
|
||||
pub const PTE_V: u64 = 1 << 0;
|
||||
pub const PTE_R: u64 = 1 << 1;
|
||||
pub const PTE_W: u64 = 1 << 2;
|
||||
pub const PTE_X: u64 = 1 << 3;
|
||||
// User can access.
|
||||
pub const PTE_U: u64 = 1 << 4;
|
||||
|
||||
/*
|
||||
// shift a physical address to the right place for a PTE.
|
||||
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
||||
|
||||
#define PTE2PA(pte) (((pte) >> 10) << 12)
|
||||
|
||||
#define PTE_FLAGS(pte) ((pte) & 0x3FF)
|
||||
|
||||
// extract the three 9-bit page table indices from a virtual address.
|
||||
#define PXMASK 0x1FF // 9 bits
|
||||
#define PXSHIFT(level) (PGSHIFT+(9*(level)))
|
||||
#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)
|
||||
*/
|
||||
|
||||
/// One beyond the highest possible virtual address.
|
||||
///
|
||||
/// MAXVA is actually one bit less than the max allowed by
|
||||
/// Sv39, to avoid having to sign-extend virtual addresses
|
||||
/// that have the high bit set.
|
||||
pub const MAXVA: u64 = 1u64 << (9 + 9 + 9 + 12 - 1);
|
||||
|
39
kernel/rustkernel/src/riscv/plic.rs
Normal file
39
kernel/rustkernel/src/riscv/plic.rs
Normal file
@ -0,0 +1,39 @@
|
||||
//! The RISC-V Platform Level Interrupt Controller (PLIC)
|
||||
|
||||
use crate::{
|
||||
proc::cpuid,
|
||||
riscv::{plic_sclaim, plic_senable, plic_spriority, PLIC, UART0_IRQ, VIRTIO0_IRQ},
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn plicinit() {
|
||||
// Set desired IRQ priorities non-zero (otherwise disabled).
|
||||
*((PLIC + UART0_IRQ as u64 * 4) as *mut u32) = 1;
|
||||
*((PLIC + VIRTIO0_IRQ as u64 * 4) as *mut u32) = 1;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn plicinithart() {
|
||||
let hart = cpuid() as u64;
|
||||
|
||||
// Set enable bits for this hart's S-mode
|
||||
// for the UART and VIRTIO disk.
|
||||
*(plic_senable(hart) as *mut u32) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
|
||||
|
||||
// Set this hart's S-mode priority threshold to 0.
|
||||
*(plic_spriority(hart) as *mut u32) = 0;
|
||||
}
|
||||
|
||||
/// Ask the PLIC what interrupt we should serve.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn plic_claim() -> i32 {
|
||||
let hart = cpuid() as u64;
|
||||
*(plic_sclaim(hart) as *const i32)
|
||||
}
|
||||
|
||||
/// Tell the PLIC we've served this IRQ.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn plic_complete(irq: i32) {
|
||||
let hart = cpuid() as u64;
|
||||
*(plic_sclaim(hart) as *mut i32) = irq;
|
||||
}
|
53
kernel/rustkernel/src/sleeplock.rs
Normal file
53
kernel/rustkernel/src/sleeplock.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::{
|
||||
proc::{myproc, sleep, wakeup},
|
||||
spinlock::{self, Spinlock},
|
||||
};
|
||||
use core::{ffi::c_char, ptr::addr_of_mut};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Sleeplock {
|
||||
pub locked: u32,
|
||||
pub inner: Spinlock,
|
||||
pub name: *mut c_char,
|
||||
pub pid: i32,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn initsleeplock(lock: *mut Sleeplock, name: *mut c_char) {
|
||||
spinlock::initlock(addr_of_mut!((*lock).inner), name);
|
||||
(*lock).name = name;
|
||||
(*lock).locked = 0;
|
||||
(*lock).pid = 0;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn acquiresleep(lock: *mut Sleeplock) {
|
||||
(*lock).inner.lock();
|
||||
while (*lock).locked > 0 {
|
||||
sleep(lock.cast(), addr_of_mut!((*lock).inner));
|
||||
}
|
||||
(*lock).locked = 1;
|
||||
(*lock).pid = (*myproc()).pid;
|
||||
(*lock).inner.unlock()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn releasesleep(lock: *mut Sleeplock) {
|
||||
(*lock).inner.lock();
|
||||
(*lock).locked = 0;
|
||||
(*lock).pid = 0;
|
||||
wakeup(lock.cast());
|
||||
(*lock).inner.unlock();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn holdingsleep(lock: *mut Sleeplock) -> i32 {
|
||||
(*lock).inner.lock();
|
||||
let holding = ((*lock).locked > 0) && ((*lock).pid == (*myproc()).pid);
|
||||
(*lock).inner.unlock();
|
||||
if holding {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
@ -15,6 +15,13 @@ pub struct Spinlock {
|
||||
pub cpu: *mut Cpu,
|
||||
}
|
||||
impl Spinlock {
|
||||
pub const unsafe fn uninitialized() -> Spinlock {
|
||||
Spinlock {
|
||||
locked: AtomicBool::new(false),
|
||||
name: null_mut(),
|
||||
cpu: null_mut(),
|
||||
}
|
||||
}
|
||||
/// Initializes a `Spinlock`.
|
||||
pub fn new(name: *mut c_char) -> Spinlock {
|
||||
Spinlock {
|
||||
|
83
kernel/rustkernel/src/start.rs
Normal file
83
kernel/rustkernel/src/start.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use crate::{main, param::NCPU, riscv::*};
|
||||
use core::{arch::asm, ptr::addr_of};
|
||||
|
||||
extern "C" {
|
||||
pub fn timervec();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut timer_scratch: [[u64; 5]; NCPU] = [[0u64; 5]; NCPU];
|
||||
|
||||
// The original C has this aligned to 16 - hopefully that's not a problem.
|
||||
#[no_mangle]
|
||||
pub static mut stack0: [u8; 4096 * NCPU] = [0u8; 4096 * NCPU];
|
||||
|
||||
// entry.S jumps here in machine mode on stack0
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn start() {
|
||||
// Set M Previous Privilege mode to Supervisor, for mret.
|
||||
let mut x = r_mstatus();
|
||||
x &= !MSTATUS_MPP_MASK;
|
||||
x |= MSTATUS_MPP_S;
|
||||
w_mstatus(x);
|
||||
|
||||
// Set M Exception Program Counter to main, for mret.
|
||||
w_mepc(main as usize as u64);
|
||||
|
||||
// Disable paging for now.
|
||||
w_satp(0);
|
||||
|
||||
// Delegate all interrupts and exceptions to supervisor mode.
|
||||
w_medeleg(0xffffu64);
|
||||
w_mideleg(0xffffu64);
|
||||
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
||||
|
||||
// Configure Physical Memory Protection to give
|
||||
// supervisor mode access to all of physical memory.
|
||||
w_pmpaddr0(0x3fffffffffffffu64);
|
||||
w_pmpcfg0(0xf);
|
||||
|
||||
// Ask for clock interrupts.
|
||||
timerinit();
|
||||
|
||||
// Keep each CPU's hartid in its tp register, for cpuid().
|
||||
w_tp(r_mhartid());
|
||||
|
||||
// Switch to supervisor mode and jump to main().
|
||||
asm!("mret");
|
||||
}
|
||||
|
||||
/// Arrange to receive timer interrupts.
|
||||
///
|
||||
/// They will arrive in machine mode at
|
||||
/// at timervec in kernelvec.S,
|
||||
/// which turns them into software interrupts for
|
||||
/// devintr() in trap.c.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn timerinit() {
|
||||
// Each CPU has a separate source of timer interrupts.
|
||||
let id = r_mhartid();
|
||||
|
||||
// Ask the CLINT for a timer interrupt.
|
||||
// cycles, about 1/10th second in qemu
|
||||
let interval = 1_000_000u64;
|
||||
*(clint_mtimecmp(id) as *mut u64) = *(CLINT_MTIME as *const u64) + interval;
|
||||
|
||||
// Prepare information in scratch[] for timervec.
|
||||
// scratch[0..=2]: Space for timervec to save registers.
|
||||
// scratch[3]: Address of CLINT MTIMECMP register.
|
||||
// scratch[4]: Desired interval (in cycles) between timer interrupts.
|
||||
let scratch: &mut [u64; 5] = &mut timer_scratch[id as usize];
|
||||
scratch[3] = clint_mtimecmp(id);
|
||||
scratch[4] = interval;
|
||||
w_mscratch(addr_of!(scratch[0]) as usize as u64);
|
||||
|
||||
// Set the machine-mode trap handler.
|
||||
w_mtvec(timervec as usize as u64);
|
||||
|
||||
// Enable machine-mode interrupts.
|
||||
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
||||
|
||||
// Enable machine-mode timer interrupts.
|
||||
w_mie(r_mie() | MIE_MTIE);
|
||||
}
|
133
kernel/rustkernel/src/string.rs
Normal file
133
kernel/rustkernel/src/string.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use core::{ffi::c_char, option::Option};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memset(dst: *mut u8, data: i32, max_bytes: u32) -> *mut u8 {
|
||||
for i in 0..max_bytes {
|
||||
*dst.add(i as usize) = data as u8;
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memcmp(mut a: *const u8, mut b: *const u8, max_bytes: u32) -> i32 {
|
||||
for _ in 0..max_bytes {
|
||||
if *a != *b {
|
||||
return (*a - *b) as i32;
|
||||
} else {
|
||||
a = a.add(1);
|
||||
b = b.add(1);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memmove(mut dst: *mut u8, mut src: *const u8, max_bytes: u32) -> *mut u8 {
|
||||
if max_bytes == 0 {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// If src starts before dst and src + max_bytes
|
||||
// is after d, the memory regions overlap.
|
||||
if src < dst && src.add(max_bytes as usize) > dst {
|
||||
dst = dst.add(max_bytes as usize);
|
||||
src = src.add(max_bytes as usize);
|
||||
|
||||
for _ in 0..max_bytes {
|
||||
dst = dst.sub(1);
|
||||
src = src.sub(1);
|
||||
*dst = *src;
|
||||
}
|
||||
} else {
|
||||
for _ in 0..max_bytes {
|
||||
*dst = *src;
|
||||
dst = dst.add(1);
|
||||
src = src.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, max_bytes: u32) -> *mut u8 {
|
||||
memmove(dst, src, max_bytes)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn strlen_checked(s: *const c_char, max_chars: usize) -> Option<i32> {
|
||||
for len in 0..max_chars {
|
||||
if (*s.add(len)) == '\0' as i8 {
|
||||
return Some(len.try_into().unwrap_or(i32::MAX));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strlen(s: *const c_char) -> i32 {
|
||||
strlen_checked(s, usize::MAX).unwrap_or(i32::MAX)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncmp(mut a: *const u8, mut b: *const u8, mut max_chars: u32) -> i32 {
|
||||
while max_chars > 0 && *a != 0 && *a == *b {
|
||||
max_chars -= 1;
|
||||
a = a.add(1);
|
||||
b = b.add(1);
|
||||
}
|
||||
if max_chars == 0 {
|
||||
0
|
||||
} else {
|
||||
(*a - *b) as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncpy(
|
||||
mut a: *mut u8,
|
||||
mut b: *const u8,
|
||||
mut max_chars: i32,
|
||||
) -> *const u8 {
|
||||
let original_a = a;
|
||||
while max_chars > 0 && *b != 0 {
|
||||
*a = *b;
|
||||
max_chars -= 1;
|
||||
a = a.add(1);
|
||||
b = b.add(1);
|
||||
}
|
||||
|
||||
while max_chars > 0 {
|
||||
*a = 0;
|
||||
max_chars -= 1;
|
||||
a = a.add(1);
|
||||
}
|
||||
|
||||
original_a
|
||||
}
|
||||
|
||||
/// Like strncpy but guaranteed to null-terminate.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn safestrcpy(
|
||||
mut a: *mut u8,
|
||||
mut b: *const u8,
|
||||
mut max_chars: i32,
|
||||
) -> *const u8 {
|
||||
let original_a = a;
|
||||
|
||||
if max_chars <= 0 {
|
||||
return a;
|
||||
} else {
|
||||
max_chars -= 1;
|
||||
}
|
||||
|
||||
while max_chars > 0 && *b != 0 {
|
||||
*a = *b;
|
||||
max_chars -= 1;
|
||||
a = a.add(1);
|
||||
b = b.add(1);
|
||||
}
|
||||
|
||||
*a = 0;
|
||||
|
||||
original_a
|
||||
}
|
231
kernel/rustkernel/src/syscall.rs
Normal file
231
kernel/rustkernel/src/syscall.rs
Normal file
@ -0,0 +1,231 @@
|
||||
use crate::{printf::print, proc::myproc, riscv::Pagetable, string::strlen, sysproc};
|
||||
use core::{mem::size_of, ptr::addr_of_mut};
|
||||
|
||||
extern "C" {
|
||||
fn copyin(pagetable: Pagetable, dst: *mut u8, srcva: u64, len: u64) -> i32;
|
||||
fn copyinstr(pagetable: Pagetable, dst: *mut u8, srcva: u64, len: u64) -> i32;
|
||||
// fn syscall();
|
||||
fn sys_pipe() -> u64;
|
||||
fn sys_read() -> u64;
|
||||
fn sys_exec() -> u64;
|
||||
fn sys_fstat() -> u64;
|
||||
fn sys_chdir() -> u64;
|
||||
fn sys_dup() -> u64;
|
||||
fn sys_open() -> u64;
|
||||
fn sys_write() -> u64;
|
||||
fn sys_mknod() -> u64;
|
||||
fn sys_unlink() -> u64;
|
||||
fn sys_link() -> u64;
|
||||
fn sys_mkdir() -> u64;
|
||||
fn sys_close() -> u64;
|
||||
}
|
||||
|
||||
pub enum Syscall {
|
||||
Fork,
|
||||
Exit,
|
||||
Wait,
|
||||
Pipe,
|
||||
Read,
|
||||
Kill,
|
||||
Exec,
|
||||
Fstat,
|
||||
Chdir,
|
||||
Dup,
|
||||
Getpid,
|
||||
Sbrk,
|
||||
Sleep,
|
||||
Uptime,
|
||||
Open,
|
||||
Write,
|
||||
Mknod,
|
||||
Unlink,
|
||||
Link,
|
||||
Mkdir,
|
||||
Close,
|
||||
}
|
||||
impl Syscall {
|
||||
pub unsafe fn call(&self) -> u64 {
|
||||
match self {
|
||||
Syscall::Fork => sysproc::sys_fork(),
|
||||
Syscall::Exit => sysproc::sys_exit(),
|
||||
Syscall::Wait => sysproc::sys_wait(),
|
||||
Syscall::Pipe => sys_pipe(),
|
||||
Syscall::Read => sys_read(),
|
||||
Syscall::Kill => sysproc::sys_kill(),
|
||||
Syscall::Exec => sys_exec(),
|
||||
Syscall::Fstat => sys_fstat(),
|
||||
Syscall::Chdir => sys_chdir(),
|
||||
Syscall::Dup => sys_dup(),
|
||||
Syscall::Getpid => sysproc::sys_getpid(),
|
||||
Syscall::Sbrk => sysproc::sys_sbrk(),
|
||||
Syscall::Sleep => sysproc::sys_sleep(),
|
||||
Syscall::Uptime => sysproc::sys_uptime(),
|
||||
Syscall::Open => sys_open(),
|
||||
Syscall::Write => sys_write(),
|
||||
Syscall::Mknod => sys_mknod(),
|
||||
Syscall::Unlink => sys_unlink(),
|
||||
Syscall::Link => sys_link(),
|
||||
Syscall::Mkdir => sys_mkdir(),
|
||||
Syscall::Close => sys_close(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<usize> for Syscall {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: usize) -> core::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(Syscall::Fork),
|
||||
2 => Ok(Syscall::Exit),
|
||||
3 => Ok(Syscall::Wait),
|
||||
4 => Ok(Syscall::Pipe),
|
||||
5 => Ok(Syscall::Read),
|
||||
6 => Ok(Syscall::Kill),
|
||||
7 => Ok(Syscall::Exec),
|
||||
8 => Ok(Syscall::Fstat),
|
||||
9 => Ok(Syscall::Chdir),
|
||||
10 => Ok(Syscall::Dup),
|
||||
11 => Ok(Syscall::Getpid),
|
||||
12 => Ok(Syscall::Sbrk),
|
||||
13 => Ok(Syscall::Sleep),
|
||||
14 => Ok(Syscall::Uptime),
|
||||
15 => Ok(Syscall::Open),
|
||||
16 => Ok(Syscall::Write),
|
||||
17 => Ok(Syscall::Mknod),
|
||||
18 => Ok(Syscall::Unlink),
|
||||
19 => Ok(Syscall::Link),
|
||||
20 => Ok(Syscall::Mkdir),
|
||||
21 => Ok(Syscall::Close),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Syscall> for usize {
|
||||
fn from(syscall: Syscall) -> usize {
|
||||
match syscall {
|
||||
Syscall::Fork => 1,
|
||||
Syscall::Exit => 2,
|
||||
Syscall::Wait => 3,
|
||||
Syscall::Pipe => 4,
|
||||
Syscall::Read => 5,
|
||||
Syscall::Kill => 6,
|
||||
Syscall::Exec => 7,
|
||||
Syscall::Fstat => 8,
|
||||
Syscall::Chdir => 9,
|
||||
Syscall::Dup => 10,
|
||||
Syscall::Getpid => 11,
|
||||
Syscall::Sbrk => 12,
|
||||
Syscall::Sleep => 13,
|
||||
Syscall::Uptime => 14,
|
||||
Syscall::Open => 15,
|
||||
Syscall::Write => 16,
|
||||
Syscall::Mknod => 17,
|
||||
Syscall::Unlink => 18,
|
||||
Syscall::Link => 19,
|
||||
Syscall::Mkdir => 20,
|
||||
Syscall::Close => 21,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the u64 at addr from the current process.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fetchaddr(addr: u64, ip: *mut u64) -> i32 {
|
||||
let p = myproc();
|
||||
|
||||
// Both tests needed, in case of overflow.
|
||||
if addr >= (*p).sz
|
||||
|| addr + size_of::<u64>() as u64 > (*p).sz
|
||||
|| copyin(
|
||||
(*p).pagetable,
|
||||
ip.cast(),
|
||||
addr,
|
||||
size_of::<*mut u64>() as u64,
|
||||
) != 0
|
||||
{
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the null-terminated string at addr from the current process.
|
||||
///
|
||||
/// Returns length of string, not including null, or -1 for error.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fetchstr(addr: u64, buf: *mut u8, max: i32) -> i32 {
|
||||
let p = myproc();
|
||||
if copyinstr((*p).pagetable, buf, addr, max as u64) < 0 {
|
||||
-1
|
||||
} else {
|
||||
strlen(buf.cast())
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn argraw(n: i32) -> u64 {
|
||||
let p = myproc();
|
||||
match n {
|
||||
0 => (*(*p).trapframe).a0,
|
||||
1 => (*(*p).trapframe).a1,
|
||||
2 => (*(*p).trapframe).a2,
|
||||
3 => (*(*p).trapframe).a3,
|
||||
4 => (*(*p).trapframe).a4,
|
||||
5 => (*(*p).trapframe).a5,
|
||||
_ => panic!("argraw"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the n-th 32-bit syscall argument.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn argint(n: i32, ip: *mut i32) {
|
||||
*ip = argraw(n) as i32;
|
||||
}
|
||||
|
||||
/// Retrieve an argument as a pointer.
|
||||
///
|
||||
/// Doesn't check for legality, since
|
||||
/// copyin/copyout will do that.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn argaddr(n: i32, ip: *mut u64) {
|
||||
*ip = argraw(n);
|
||||
}
|
||||
|
||||
/// Fetch the n-th word-sized syscall argument as a null-terminated string.
|
||||
///
|
||||
/// Copies into buf, at most max.
|
||||
/// Returns string length if ok (including null), -1 if error.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn argstr(n: i32, buf: *mut u8, max: i32) -> i32 {
|
||||
let mut addr = 0u64;
|
||||
argaddr(n, addr_of_mut!(addr));
|
||||
fetchstr(addr, buf, max)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn syscall() {
|
||||
let p = myproc();
|
||||
let num = (*(*p).trapframe).a7;
|
||||
|
||||
// print!("syscall {}\n", num);
|
||||
|
||||
(*(*p).trapframe).a0 = match TryInto::<Syscall>::try_into(num as usize) {
|
||||
Ok(syscall) => syscall.call(),
|
||||
Err(_) => {
|
||||
print!("{} unknown syscall {}\n", (*p).pid, num);
|
||||
-1i64 as u64
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// #[no_mangle]
|
||||
// pub unsafe extern "C" fn rust_syscall(num: u64) -> u64 {
|
||||
// match TryInto::<Syscall>::try_into(num as usize) {
|
||||
// Ok(syscall) => syscall.call(),
|
||||
// Err(_) => {
|
||||
// print!("unknown syscall {}\n", num);
|
||||
// -1i64 as u64
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
79
kernel/rustkernel/src/sysproc.rs
Normal file
79
kernel/rustkernel/src/sysproc.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use crate::{
|
||||
proc::{exit, fork, growproc, kill, killed, myproc, sleep, wait},
|
||||
syscall::{argaddr, argint},
|
||||
};
|
||||
use core::ptr::addr_of_mut;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_exit() -> u64 {
|
||||
let mut n = 0i32;
|
||||
argint(0, addr_of_mut!(n));
|
||||
exit(n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_getpid() -> u64 {
|
||||
(*myproc()).pid as u64
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_fork() -> u64 {
|
||||
fork() as u64
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_wait() -> u64 {
|
||||
let mut p = 0u64;
|
||||
argaddr(0, addr_of_mut!(p));
|
||||
wait(p) as u64
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_sbrk() -> u64 {
|
||||
let mut n = 0i32;
|
||||
argint(0, addr_of_mut!(n));
|
||||
let addr = (*myproc()).sz;
|
||||
|
||||
if growproc(n) < 0 {
|
||||
-1i64 as u64
|
||||
} else {
|
||||
addr
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_sleep() -> u64 {
|
||||
let mut n = 0i32;
|
||||
argint(0, addr_of_mut!(n));
|
||||
|
||||
crate::trap::tickslock.lock();
|
||||
let ticks = crate::trap::ticks;
|
||||
while crate::trap::ticks < ticks + n as u32 {
|
||||
if killed(myproc()) > 0 {
|
||||
crate::trap::tickslock.unlock();
|
||||
return -1i64 as u64;
|
||||
}
|
||||
sleep(
|
||||
addr_of_mut!(crate::trap::ticks).cast(),
|
||||
addr_of_mut!(crate::trap::tickslock).cast(),
|
||||
)
|
||||
}
|
||||
crate::trap::tickslock.unlock();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_kill() -> u64 {
|
||||
let mut pid = 0i32;
|
||||
argint(0, addr_of_mut!(pid));
|
||||
kill(pid) as u64
|
||||
}
|
||||
|
||||
/// Returns how many clock tick interrupts have occurred since start.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sys_uptime() -> u64 {
|
||||
crate::trap::tickslock.lock();
|
||||
let ticks = crate::trap::ticks;
|
||||
crate::trap::tickslock.unlock();
|
||||
ticks as u64
|
||||
}
|
144
kernel/rustkernel/src/trap.rs
Normal file
144
kernel/rustkernel/src/trap.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use crate::{
|
||||
printf::print,
|
||||
proc::{cpuid, wakeup},
|
||||
riscv::*,
|
||||
spinlock::Spinlock,
|
||||
};
|
||||
use core::{ffi::CStr, ptr::addr_of_mut};
|
||||
|
||||
extern "C" {
|
||||
pub fn kernelvec();
|
||||
pub fn usertrap();
|
||||
pub fn usertrapret();
|
||||
fn syscall();
|
||||
fn virtio_disk_intr();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut tickslock: Spinlock = unsafe { Spinlock::uninitialized() };
|
||||
#[no_mangle]
|
||||
pub static mut ticks: u32 = 0;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn trapinit() {
|
||||
tickslock = Spinlock::new(
|
||||
CStr::from_bytes_with_nul(b"time\0")
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Set up to take exceptions and traps while in the kernel.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn trapinithart() {
|
||||
w_stvec(kernelvec as usize as u64);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn clockintr() {
|
||||
tickslock.lock();
|
||||
ticks += 1;
|
||||
wakeup(addr_of_mut!(ticks).cast());
|
||||
tickslock.unlock();
|
||||
}
|
||||
|
||||
// /// Handle an interrupt, exception, or syscall from user space.
|
||||
// /// Called from trampoline.S.
|
||||
// #[no_mangle]
|
||||
// pub unsafe extern "C" fn usertrap() {
|
||||
// if r_sstatus() & SSTATUS_SPP != 0 {
|
||||
// panic!("usertrap: not from user mode");
|
||||
// }
|
||||
//
|
||||
// // Send interrupts and exceptions to kerneltrap(),
|
||||
// // since we're now in the kernel.
|
||||
// w_stvec(kernelvec as usize as u64);
|
||||
//
|
||||
// let p = myproc();
|
||||
//
|
||||
// // Save user program counter.
|
||||
// (*(*p).trapframe).epc = r_sepc();
|
||||
//
|
||||
// if r_scause() == 8 {
|
||||
// // Syscall
|
||||
//
|
||||
// if killed(p) > 0 {
|
||||
// exit(-1);
|
||||
// }
|
||||
//
|
||||
// // sepc points to the ecall instruction,
|
||||
// // but we want to return to the next instruction.
|
||||
// (*(*p).trapframe).epc += 4;
|
||||
//
|
||||
// // An interrupt will change sepc, scause, and sstatus,
|
||||
// // so enable only now that we're done with those registers.
|
||||
// intr_on();
|
||||
//
|
||||
// syscall();
|
||||
// } else {
|
||||
// let which_dev = devintr();
|
||||
// if which_dev == 0 {
|
||||
// print!("usertrap(): unexpected scause {:#018x} pid={}\n", r_scause(), (*p).pid);
|
||||
// print!(" sepc={:#018x} stval={:#018x}\n", r_sepc(), r_stval());
|
||||
// setkilled(p);
|
||||
// }
|
||||
// if killed(p) > 0 {
|
||||
// exit(-1);
|
||||
// }
|
||||
//
|
||||
// // Give up the CPU if this is a timer interrupt.
|
||||
// if which_dev == 2 {
|
||||
// r#yield();
|
||||
// }
|
||||
//
|
||||
// usertrapret();
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Check if it's an external interrupt or software interrupt and handle it.
|
||||
///
|
||||
/// Returns 2 if timer interrupt, 1 if other device, 0 if not recognized.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn devintr() -> i32 {
|
||||
let scause = r_scause();
|
||||
|
||||
if (scause & 0x8000000000000000 > 0) && (scause & 0xff) == 9 {
|
||||
// This is a supervisor external interrupt, via PLIC.
|
||||
|
||||
// IRQ indicates which device interrupted.
|
||||
let irq = plic::plic_claim();
|
||||
|
||||
if irq == UART0_IRQ {
|
||||
crate::uart::uartintr();
|
||||
} else if irq == VIRTIO0_IRQ {
|
||||
virtio_disk_intr();
|
||||
} else if irq > 0 {
|
||||
print!("unexpected interrupt irq={}\n", irq);
|
||||
}
|
||||
|
||||
// The PLIC allows each device to raise at most one
|
||||
// interrupt at a time; tell the PLIC the device is
|
||||
// now allowed to interrupt again.
|
||||
if irq > 0 {
|
||||
plic::plic_complete(irq);
|
||||
}
|
||||
|
||||
1
|
||||
} else if scause == 0x8000000000000001 {
|
||||
// Software interrupt from a machine-mode timer interrupt,
|
||||
// forwarded by timervec in kernelvec.S.
|
||||
|
||||
if cpuid() == 0 {
|
||||
clockintr();
|
||||
}
|
||||
|
||||
// Acknowledge the software interrupt by
|
||||
// clearing the SSIP bit in sip.
|
||||
w_sip(r_sip() & !2);
|
||||
|
||||
2
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
204
kernel/rustkernel/src/uart.rs
Normal file
204
kernel/rustkernel/src/uart.rs
Normal file
@ -0,0 +1,204 @@
|
||||
//! Low-level driver routines for 16550a UART.
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use crate::{
|
||||
console::consoleintr,
|
||||
proc::{sleep, wakeup},
|
||||
riscv::memlayout::UART0,
|
||||
spinlock::{pop_off, push_off, Spinlock},
|
||||
};
|
||||
use core::{ffi::CStr, ptr::addr_of_mut};
|
||||
|
||||
/// The UART control registers are memory-mapped
|
||||
/// at address UART0. This function returns the
|
||||
/// address of one of the registers.
|
||||
#[inline(always)]
|
||||
fn get_register_addr<N: Into<u64>>(register: N) -> *mut u8 {
|
||||
let register: u64 = register.into();
|
||||
(UART0 + register) as *mut u8
|
||||
}
|
||||
|
||||
// The UART control registers.
|
||||
// Some have different meanings for read vs write.
|
||||
// See http://byterunner.com/16550.html
|
||||
|
||||
/// Receive Holding Register (for input bytes)
|
||||
const RHR: u8 = 0;
|
||||
/// Transmit Holding Register (for output bytes)
|
||||
const THR: u8 = 0;
|
||||
/// Interrupt Enable Register
|
||||
const IER: u8 = 1;
|
||||
const IER_RX_ENABLE: u8 = 1 << 0;
|
||||
const IER_TX_ENABLE: u8 = 1 << 1;
|
||||
/// FIFO control register
|
||||
const FCR: u8 = 2;
|
||||
const FCR_FIFO_ENABLE: u8 = 1 << 0;
|
||||
/// Clear the content of the two FIFOs.
|
||||
const FCR_FIFO_CLEAR: u8 = 3 << 1;
|
||||
/// Interrupt Status Register
|
||||
const ISR: u8 = 2;
|
||||
/// Line Control Register
|
||||
const LCR: u8 = 2;
|
||||
const LCR_EIGHT_BITS: u8 = 3;
|
||||
/// Special mode to set baud rate
|
||||
const LCR_BAUD_LATCH: u8 = 1 << 7;
|
||||
/// Line Status Register
|
||||
const LSR: u8 = 5;
|
||||
/// Input is waiting to be read from RHR
|
||||
const LSR_RX_READY: u8 = 1 << 0;
|
||||
/// THR can accept another character to send
|
||||
const LSR_TX_IDLE: u8 = 1 << 5;
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn read_register<N: Into<u64>>(register: N) -> u8 {
|
||||
*get_register_addr(register)
|
||||
}
|
||||
#[inline(always)]
|
||||
unsafe fn write_register<N: Into<u64>>(register: N, value: u8) {
|
||||
*get_register_addr(register) = value;
|
||||
}
|
||||
|
||||
static mut uart_tx_lock: Spinlock = unsafe { Spinlock::uninitialized() };
|
||||
const UART_TX_BUF_SIZE: u64 = 32;
|
||||
static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE as usize] = [0u8; UART_TX_BUF_SIZE as usize];
|
||||
/// Write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
|
||||
static mut uart_tx_w: u64 = 0;
|
||||
/// Read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
|
||||
static mut uart_tx_r: u64 = 0;
|
||||
|
||||
pub(crate) unsafe fn uartinit() {
|
||||
// Disable interrupts.
|
||||
write_register(IER, 0x00);
|
||||
// Special mode to set baud rate.
|
||||
write_register(LCR, LCR_BAUD_LATCH);
|
||||
// LSB for baud rate of 38.4K.
|
||||
write_register(0u8, 0x03);
|
||||
// MSB for baud rate of 38.4K.
|
||||
write_register(1u8, 0x00);
|
||||
// Leave set-baud mode and set
|
||||
// word length to 8 bits, no parity.
|
||||
write_register(LCR, LCR_EIGHT_BITS);
|
||||
// Reset and enable FIFOs.
|
||||
write_register(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
|
||||
// Enable transmit and receive interrupts.
|
||||
write_register(IER, IER_TX_ENABLE | IER_RX_ENABLE);
|
||||
|
||||
uart_tx_lock = Spinlock::new(
|
||||
CStr::from_bytes_with_nul(b"uart\0")
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a character to the output buffer and tell the
|
||||
/// UART to start sending if it isn't already.
|
||||
/// Blocks if the output buffer is full.
|
||||
/// Because it may block, it can't be called
|
||||
/// from interrupts, it's only suitable for use
|
||||
/// by write().
|
||||
pub(crate) unsafe fn uartputc(c: i32) {
|
||||
uart_tx_lock.lock();
|
||||
|
||||
if crate::PANICKED {
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
while uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE {
|
||||
// Buffer is full.
|
||||
// Wait for uartstart() to open up space in the buffer.
|
||||
sleep(
|
||||
addr_of_mut!(uart_tx_r).cast(),
|
||||
addr_of_mut!(uart_tx_lock).cast(),
|
||||
);
|
||||
}
|
||||
|
||||
uart_tx_buf[(uart_tx_w % UART_TX_BUF_SIZE) as usize] = c as i8 as u8;
|
||||
uart_tx_w += 1;
|
||||
uartstart();
|
||||
uart_tx_lock.unlock();
|
||||
}
|
||||
|
||||
/// Alternate version of uartputc() that doesn't
|
||||
/// use interrupts, for use by kernel printf() and
|
||||
/// to echo characters. It spins waiting for the UART's
|
||||
/// output register to be empty.
|
||||
pub(crate) unsafe fn uartputc_sync(c: i32) {
|
||||
push_off();
|
||||
|
||||
if crate::PANICKED {
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for Transmit Holding Empty to be set in LSR.
|
||||
while read_register(LSR) & LSR_TX_IDLE == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
write_register(THR, c as i8 as u8);
|
||||
|
||||
pop_off();
|
||||
}
|
||||
|
||||
/// If the UART is idle, and a character is waiting
|
||||
/// in the transmit buffer, send it.
|
||||
/// Caller must hold uart_tx_lock.
|
||||
/// Called from both the top and bottom halves.
|
||||
unsafe fn uartstart() {
|
||||
loop {
|
||||
if uart_tx_w == uart_tx_r {
|
||||
// Transmit buffer is ready.
|
||||
return;
|
||||
}
|
||||
|
||||
if read_register(LSR) & LSR_TX_IDLE == 0 {
|
||||
// The UART transmit holding register is full,
|
||||
// so we cannot give it another byte.
|
||||
// It will interrupt when it's ready for a new byte.
|
||||
return;
|
||||
}
|
||||
|
||||
let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize];
|
||||
uart_tx_r += 1;
|
||||
|
||||
// Maybe uartputc() is waiting for space in the buffer.
|
||||
wakeup(addr_of_mut!(uart_tx_r).cast());
|
||||
|
||||
write_register(THR, c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read one input character from the UART.
|
||||
/// Return -1 if nothing is waiting.
|
||||
unsafe fn uartgetc() -> i32 {
|
||||
if read_register(LSR) & 0x01 != 0 {
|
||||
// Input data is ready.
|
||||
read_register(RHR) as i32
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a UART interrupt, raised because input has
|
||||
/// arrived, or the uart is ready for more output, or
|
||||
/// both. Called from devintr().
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn uartintr() {
|
||||
// Read and process incoming characters.
|
||||
loop {
|
||||
let c = uartgetc();
|
||||
if c == -1 {
|
||||
break;
|
||||
}
|
||||
consoleintr(c);
|
||||
}
|
||||
|
||||
// Send buffered characters.
|
||||
uart_tx_lock.lock();
|
||||
uartstart();
|
||||
uart_tx_lock.unlock();
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// Sleeping locks
|
||||
|
||||
#include "types.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "spinlock.h"
|
||||
#include "proc.h"
|
||||
#include "sleeplock.h"
|
||||
|
||||
void
|
||||
initsleeplock(struct sleeplock *lk, char *name)
|
||||
{
|
||||
initlock(&lk->lk, "sleep lock");
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
acquiresleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
while (lk->locked) {
|
||||
sleep(lk, &lk->lk);
|
||||
}
|
||||
lk->locked = 1;
|
||||
lk->pid = myproc()->pid;
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
void
|
||||
releasesleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
wakeup(lk);
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
int
|
||||
holdingsleep(struct sleeplock *lk)
|
||||
{
|
||||
int r;
|
||||
|
||||
acquire(&lk->lk);
|
||||
r = lk->locked && (lk->pid == myproc()->pid);
|
||||
release(&lk->lk);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,89 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
void main();
|
||||
void timerinit();
|
||||
|
||||
// entry.S needs one stack per CPU.
|
||||
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
|
||||
|
||||
// a scratch area per CPU for machine-mode timer interrupts.
|
||||
uint64 timer_scratch[NCPU][5];
|
||||
|
||||
// assembly code in kernelvec.S for machine-mode timer interrupt.
|
||||
extern void timervec();
|
||||
|
||||
// entry.S jumps here in machine mode on stack0.
|
||||
void
|
||||
start()
|
||||
{
|
||||
// set M Previous Privilege mode to Supervisor, for mret.
|
||||
unsigned long x = r_mstatus();
|
||||
x &= ~MSTATUS_MPP_MASK;
|
||||
x |= MSTATUS_MPP_S;
|
||||
w_mstatus(x);
|
||||
|
||||
// set M Exception Program Counter to main, for mret.
|
||||
// requires gcc -mcmodel=medany
|
||||
w_mepc((uint64)main);
|
||||
|
||||
// disable paging for now.
|
||||
w_satp(0);
|
||||
|
||||
// delegate all interrupts and exceptions to supervisor mode.
|
||||
w_medeleg(0xffff);
|
||||
w_mideleg(0xffff);
|
||||
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
||||
|
||||
// configure Physical Memory Protection to give supervisor mode
|
||||
// access to all of physical memory.
|
||||
w_pmpaddr0(0x3fffffffffffffull);
|
||||
w_pmpcfg0(0xf);
|
||||
|
||||
// ask for clock interrupts.
|
||||
timerinit();
|
||||
|
||||
// keep each CPU's hartid in its tp register, for cpuid().
|
||||
int id = r_mhartid();
|
||||
w_tp(id);
|
||||
|
||||
// switch to supervisor mode and jump to main().
|
||||
asm volatile("mret");
|
||||
}
|
||||
|
||||
// arrange to receive timer interrupts.
|
||||
// they will arrive in machine mode at
|
||||
// at timervec in kernelvec.S,
|
||||
// which turns them into software interrupts for
|
||||
// devintr() in trap.c.
|
||||
void
|
||||
timerinit()
|
||||
{
|
||||
// each CPU has a separate source of timer interrupts.
|
||||
int id = r_mhartid();
|
||||
|
||||
// ask the CLINT for a timer interrupt.
|
||||
int interval = 1000000; // cycles; about 1/10th second in qemu.
|
||||
*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;
|
||||
|
||||
// prepare information in scratch[] for timervec.
|
||||
// scratch[0..2] : space for timervec to save registers.
|
||||
// scratch[3] : address of CLINT MTIMECMP register.
|
||||
// scratch[4] : desired interval (in cycles) between timer interrupts.
|
||||
uint64 *scratch = &timer_scratch[id][0];
|
||||
scratch[3] = CLINT_MTIMECMP(id);
|
||||
scratch[4] = interval;
|
||||
w_mscratch((uint64)scratch);
|
||||
|
||||
// set the machine-mode trap handler.
|
||||
w_mtvec((uint64)timervec);
|
||||
|
||||
// enable machine-mode interrupts.
|
||||
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
||||
|
||||
// enable machine-mode timer interrupts.
|
||||
w_mie(r_mie() | MIE_MTIE);
|
||||
}
|
107
kernel/string.c
107
kernel/string.c
@ -1,107 +0,0 @@
|
||||
#include "types.h"
|
||||
|
||||
void*
|
||||
memset(void *dst, int c, uint n)
|
||||
{
|
||||
char *cdst = (char *) dst;
|
||||
int i;
|
||||
for(i = 0; i < n; i++){
|
||||
cdst[i] = c;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, uint n)
|
||||
{
|
||||
const uchar *s1, *s2;
|
||||
|
||||
s1 = v1;
|
||||
s2 = v2;
|
||||
while(n-- > 0){
|
||||
if(*s1 != *s2)
|
||||
return *s1 - *s2;
|
||||
s1++, s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void*
|
||||
memmove(void *dst, const void *src, uint n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
if(n == 0)
|
||||
return dst;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if(s < d && s + n > d){
|
||||
s += n;
|
||||
d += n;
|
||||
while(n-- > 0)
|
||||
*--d = *--s;
|
||||
} else
|
||||
while(n-- > 0)
|
||||
*d++ = *s++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// memcpy exists to placate GCC. Use memmove.
|
||||
void*
|
||||
memcpy(void *dst, const void *src, uint n)
|
||||
{
|
||||
return memmove(dst, src, n);
|
||||
}
|
||||
|
||||
int
|
||||
strncmp(const char *p, const char *q, uint n)
|
||||
{
|
||||
while(n > 0 && *p && *p == *q)
|
||||
n--, p++, q++;
|
||||
if(n == 0)
|
||||
return 0;
|
||||
return (uchar)*p - (uchar)*q;
|
||||
}
|
||||
|
||||
char*
|
||||
strncpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os;
|
||||
|
||||
os = s;
|
||||
while(n-- > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
while(n-- > 0)
|
||||
*s++ = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
// Like strncpy but guaranteed to NUL-terminate.
|
||||
char*
|
||||
safestrcpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os;
|
||||
|
||||
os = s;
|
||||
if(n <= 0)
|
||||
return os;
|
||||
while(--n > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
*s = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
int
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for(n = 0; s[n]; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
147
kernel/syscall.c
147
kernel/syscall.c
@ -1,147 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "spinlock.h"
|
||||
#include "proc.h"
|
||||
#include "syscall.h"
|
||||
#include "defs.h"
|
||||
|
||||
// Fetch the uint64 at addr from the current process.
|
||||
int
|
||||
fetchaddr(uint64 addr, uint64 *ip)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow
|
||||
return -1;
|
||||
if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fetch the nul-terminated string at addr from the current process.
|
||||
// Returns length of string, not including nul, or -1 for error.
|
||||
int
|
||||
fetchstr(uint64 addr, char *buf, int max)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
if(copyinstr(p->pagetable, buf, addr, max) < 0)
|
||||
return -1;
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static uint64
|
||||
argraw(int n)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
switch (n) {
|
||||
case 0:
|
||||
return p->trapframe->a0;
|
||||
case 1:
|
||||
return p->trapframe->a1;
|
||||
case 2:
|
||||
return p->trapframe->a2;
|
||||
case 3:
|
||||
return p->trapframe->a3;
|
||||
case 4:
|
||||
return p->trapframe->a4;
|
||||
case 5:
|
||||
return p->trapframe->a5;
|
||||
}
|
||||
panic("argraw");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fetch the nth 32-bit system call argument.
|
||||
void
|
||||
argint(int n, int *ip)
|
||||
{
|
||||
*ip = argraw(n);
|
||||
}
|
||||
|
||||
// Retrieve an argument as a pointer.
|
||||
// Doesn't check for legality, since
|
||||
// copyin/copyout will do that.
|
||||
void
|
||||
argaddr(int n, uint64 *ip)
|
||||
{
|
||||
*ip = argraw(n);
|
||||
}
|
||||
|
||||
// Fetch the nth word-sized system call argument as a null-terminated string.
|
||||
// Copies into buf, at most max.
|
||||
// Returns string length if OK (including nul), -1 if error.
|
||||
int
|
||||
argstr(int n, char *buf, int max)
|
||||
{
|
||||
uint64 addr;
|
||||
argaddr(n, &addr);
|
||||
return fetchstr(addr, buf, max);
|
||||
}
|
||||
|
||||
// Prototypes for the functions that handle system calls.
|
||||
extern uint64 sys_fork(void);
|
||||
extern uint64 sys_exit(void);
|
||||
extern uint64 sys_wait(void);
|
||||
extern uint64 sys_pipe(void);
|
||||
extern uint64 sys_read(void);
|
||||
extern uint64 sys_kill(void);
|
||||
extern uint64 sys_exec(void);
|
||||
extern uint64 sys_fstat(void);
|
||||
extern uint64 sys_chdir(void);
|
||||
extern uint64 sys_dup(void);
|
||||
extern uint64 sys_getpid(void);
|
||||
extern uint64 sys_sbrk(void);
|
||||
extern uint64 sys_sleep(void);
|
||||
extern uint64 sys_uptime(void);
|
||||
extern uint64 sys_open(void);
|
||||
extern uint64 sys_write(void);
|
||||
extern uint64 sys_mknod(void);
|
||||
extern uint64 sys_unlink(void);
|
||||
extern uint64 sys_link(void);
|
||||
extern uint64 sys_mkdir(void);
|
||||
extern uint64 sys_close(void);
|
||||
|
||||
// An array mapping syscall numbers from syscall.h
|
||||
// to the function that handles the system call.
|
||||
static uint64 (*syscalls[])(void) = {
|
||||
[SYS_fork] sys_fork,
|
||||
[SYS_exit] sys_exit,
|
||||
[SYS_wait] sys_wait,
|
||||
[SYS_pipe] sys_pipe,
|
||||
[SYS_read] sys_read,
|
||||
[SYS_kill] sys_kill,
|
||||
[SYS_exec] sys_exec,
|
||||
[SYS_fstat] sys_fstat,
|
||||
[SYS_chdir] sys_chdir,
|
||||
[SYS_dup] sys_dup,
|
||||
[SYS_getpid] sys_getpid,
|
||||
[SYS_sbrk] sys_sbrk,
|
||||
[SYS_sleep] sys_sleep,
|
||||
[SYS_uptime] sys_uptime,
|
||||
[SYS_open] sys_open,
|
||||
[SYS_write] sys_write,
|
||||
[SYS_mknod] sys_mknod,
|
||||
[SYS_unlink] sys_unlink,
|
||||
[SYS_link] sys_link,
|
||||
[SYS_mkdir] sys_mkdir,
|
||||
[SYS_close] sys_close,
|
||||
};
|
||||
|
||||
void
|
||||
syscall(void)
|
||||
{
|
||||
int num;
|
||||
struct proc *p = myproc();
|
||||
|
||||
num = p->trapframe->a7;
|
||||
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
|
||||
// Use num to lookup the system call function for num, call it,
|
||||
// and store its return value in p->trapframe->a0
|
||||
p->trapframe->a0 = syscalls[num]();
|
||||
} else {
|
||||
printf("%d %s: unknown sys call %d\n",
|
||||
p->pid, p->name, num);
|
||||
p->trapframe->a0 = -1;
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "spinlock.h"
|
||||
#include "proc.h"
|
||||
|
||||
uint64
|
||||
sys_exit(void)
|
||||
{
|
||||
int n;
|
||||
argint(0, &n);
|
||||
exit(n);
|
||||
return 0; // not reached
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_getpid(void)
|
||||
{
|
||||
return myproc()->pid;
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_fork(void)
|
||||
{
|
||||
return fork();
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_wait(void)
|
||||
{
|
||||
uint64 p;
|
||||
argaddr(0, &p);
|
||||
return wait(p);
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_sbrk(void)
|
||||
{
|
||||
uint64 addr;
|
||||
int n;
|
||||
|
||||
argint(0, &n);
|
||||
addr = myproc()->sz;
|
||||
if(growproc(n) < 0)
|
||||
return -1;
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_sleep(void)
|
||||
{
|
||||
int n;
|
||||
uint ticks0;
|
||||
|
||||
argint(0, &n);
|
||||
acquire(&tickslock);
|
||||
ticks0 = ticks;
|
||||
while(ticks - ticks0 < n){
|
||||
if(killed(myproc())){
|
||||
release(&tickslock);
|
||||
return -1;
|
||||
}
|
||||
sleep(&ticks, &tickslock);
|
||||
}
|
||||
release(&tickslock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64
|
||||
sys_kill(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
argint(0, &pid);
|
||||
return kill(pid);
|
||||
}
|
||||
|
||||
// return how many clock tick interrupts have occurred
|
||||
// since start.
|
||||
uint64
|
||||
sys_uptime(void)
|
||||
{
|
||||
uint xticks;
|
||||
|
||||
acquire(&tickslock);
|
||||
xticks = ticks;
|
||||
release(&tickslock);
|
||||
return xticks;
|
||||
}
|
@ -6,9 +6,6 @@
|
||||
#include "proc.h"
|
||||
#include "defs.h"
|
||||
|
||||
struct spinlock tickslock;
|
||||
uint ticks;
|
||||
|
||||
extern char trampoline[], uservec[], userret[];
|
||||
|
||||
// in kernelvec.S, calls kerneltrap().
|
||||
@ -16,25 +13,11 @@ void kernelvec();
|
||||
|
||||
extern int devintr();
|
||||
|
||||
void
|
||||
trapinit(void)
|
||||
{
|
||||
initlock(&tickslock, "time");
|
||||
}
|
||||
|
||||
// set up to take exceptions and traps while in the kernel.
|
||||
void
|
||||
trapinithart(void)
|
||||
{
|
||||
w_stvec((uint64)kernelvec);
|
||||
}
|
||||
|
||||
//
|
||||
// handle an interrupt, exception, or system call from user space.
|
||||
// called from trampoline.S
|
||||
//
|
||||
void
|
||||
usertrap(void)
|
||||
void usertrap(void)
|
||||
{
|
||||
int which_dev = 0;
|
||||
|
||||
@ -68,8 +51,15 @@ usertrap(void)
|
||||
} else if((which_dev = devintr()) != 0){
|
||||
// ok
|
||||
} else {
|
||||
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
|
||||
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
|
||||
printstr("usertrap(): unexepected scause ");
|
||||
printptr(r_scause());
|
||||
printstr(" pid=");
|
||||
printint(p->pid);
|
||||
printstr(" sepc=");
|
||||
printptr(r_sepc());
|
||||
printstr(" stval=");
|
||||
printptr(r_stval());
|
||||
printstr("\n");
|
||||
setkilled(p);
|
||||
}
|
||||
|
||||
@ -145,8 +135,13 @@ kerneltrap()
|
||||
panic("kerneltrap: interrupts enabled");
|
||||
|
||||
if((which_dev = devintr()) == 0){
|
||||
printf("scause %p\n", scause);
|
||||
printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
|
||||
printstr("scause ");
|
||||
printptr(scause);
|
||||
printstr("\nsepc=");
|
||||
printptr(r_sepc());
|
||||
printstr(" stval=");
|
||||
printptr(r_stval());
|
||||
printstr("\n");
|
||||
panic("kerneltrap");
|
||||
}
|
||||
|
||||
@ -159,63 +154,3 @@ kerneltrap()
|
||||
w_sepc(sepc);
|
||||
w_sstatus(sstatus);
|
||||
}
|
||||
|
||||
void
|
||||
clockintr()
|
||||
{
|
||||
acquire(&tickslock);
|
||||
ticks++;
|
||||
wakeup(&ticks);
|
||||
release(&tickslock);
|
||||
}
|
||||
|
||||
// check if it's an external interrupt or software interrupt,
|
||||
// and handle it.
|
||||
// returns 2 if timer interrupt,
|
||||
// 1 if other device,
|
||||
// 0 if not recognized.
|
||||
int
|
||||
devintr()
|
||||
{
|
||||
uint64 scause = r_scause();
|
||||
|
||||
if((scause & 0x8000000000000000L) &&
|
||||
(scause & 0xff) == 9){
|
||||
// this is a supervisor external interrupt, via PLIC.
|
||||
|
||||
// irq indicates which device interrupted.
|
||||
int irq = plic_claim();
|
||||
|
||||
if(irq == UART0_IRQ){
|
||||
uartintr();
|
||||
} else if(irq == VIRTIO0_IRQ){
|
||||
virtio_disk_intr();
|
||||
} else if(irq){
|
||||
printf("unexpected interrupt irq=%d\n", irq);
|
||||
}
|
||||
|
||||
// the PLIC allows each device to raise at most one
|
||||
// interrupt at a time; tell the PLIC the device is
|
||||
// now allowed to interrupt again.
|
||||
if(irq)
|
||||
plic_complete(irq);
|
||||
|
||||
return 1;
|
||||
} else if(scause == 0x8000000000000001L){
|
||||
// software interrupt from a machine-mode timer interrupt,
|
||||
// forwarded by timervec in kernelvec.S.
|
||||
|
||||
if(cpuid() == 0){
|
||||
clockintr();
|
||||
}
|
||||
|
||||
// acknowledge the software interrupt by clearing
|
||||
// the SSIP bit in sip.
|
||||
w_sip(r_sip() & ~2);
|
||||
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
190
kernel/uart.c
190
kernel/uart.c
@ -1,190 +0,0 @@
|
||||
//
|
||||
// low-level driver routines for 16550a UART.
|
||||
//
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "spinlock.h"
|
||||
#include "proc.h"
|
||||
#include "defs.h"
|
||||
|
||||
// the UART control registers are memory-mapped
|
||||
// at address UART0. this macro returns the
|
||||
// address of one of the registers.
|
||||
#define Reg(reg) ((volatile unsigned char *)(UART0 + reg))
|
||||
|
||||
// the UART control registers.
|
||||
// some have different meanings for
|
||||
// read vs write.
|
||||
// see http://byterunner.com/16550.html
|
||||
#define RHR 0 // receive holding register (for input bytes)
|
||||
#define THR 0 // transmit holding register (for output bytes)
|
||||
#define IER 1 // interrupt enable register
|
||||
#define IER_RX_ENABLE (1<<0)
|
||||
#define IER_TX_ENABLE (1<<1)
|
||||
#define FCR 2 // FIFO control register
|
||||
#define FCR_FIFO_ENABLE (1<<0)
|
||||
#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
|
||||
#define ISR 2 // interrupt status register
|
||||
#define LCR 3 // line control register
|
||||
#define LCR_EIGHT_BITS (3<<0)
|
||||
#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
|
||||
#define LSR 5 // line status register
|
||||
#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR
|
||||
#define LSR_TX_IDLE (1<<5) // THR can accept another character to send
|
||||
|
||||
#define ReadReg(reg) (*(Reg(reg)))
|
||||
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
|
||||
|
||||
// the transmit output buffer.
|
||||
struct spinlock uart_tx_lock;
|
||||
#define UART_TX_BUF_SIZE 32
|
||||
char uart_tx_buf[UART_TX_BUF_SIZE];
|
||||
uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
|
||||
uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
|
||||
|
||||
extern volatile int panicked; // from printf.c
|
||||
|
||||
void uartstart();
|
||||
|
||||
void
|
||||
uartinit(void)
|
||||
{
|
||||
// disable interrupts.
|
||||
WriteReg(IER, 0x00);
|
||||
|
||||
// special mode to set baud rate.
|
||||
WriteReg(LCR, LCR_BAUD_LATCH);
|
||||
|
||||
// LSB for baud rate of 38.4K.
|
||||
WriteReg(0, 0x03);
|
||||
|
||||
// MSB for baud rate of 38.4K.
|
||||
WriteReg(1, 0x00);
|
||||
|
||||
// leave set-baud mode,
|
||||
// and set word length to 8 bits, no parity.
|
||||
WriteReg(LCR, LCR_EIGHT_BITS);
|
||||
|
||||
// reset and enable FIFOs.
|
||||
WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
|
||||
|
||||
// enable transmit and receive interrupts.
|
||||
WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
|
||||
|
||||
initlock(&uart_tx_lock, "uart");
|
||||
}
|
||||
|
||||
// add a character to the output buffer and tell the
|
||||
// UART to start sending if it isn't already.
|
||||
// blocks if the output buffer is full.
|
||||
// because it may block, it can't be called
|
||||
// from interrupts; it's only suitable for use
|
||||
// by write().
|
||||
void
|
||||
uartputc(int c)
|
||||
{
|
||||
acquire(&uart_tx_lock);
|
||||
|
||||
if(panicked){
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){
|
||||
// buffer is full.
|
||||
// wait for uartstart() to open up space in the buffer.
|
||||
sleep(&uart_tx_r, &uart_tx_lock);
|
||||
}
|
||||
uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
|
||||
uart_tx_w += 1;
|
||||
uartstart();
|
||||
release(&uart_tx_lock);
|
||||
}
|
||||
|
||||
|
||||
// alternate version of uartputc() that doesn't
|
||||
// use interrupts, for use by kernel printf() and
|
||||
// to echo characters. it spins waiting for the uart's
|
||||
// output register to be empty.
|
||||
void
|
||||
uartputc_sync(int c)
|
||||
{
|
||||
push_off();
|
||||
|
||||
if(panicked){
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
|
||||
// wait for Transmit Holding Empty to be set in LSR.
|
||||
while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
|
||||
;
|
||||
WriteReg(THR, c);
|
||||
|
||||
pop_off();
|
||||
}
|
||||
|
||||
// if the UART is idle, and a character is waiting
|
||||
// in the transmit buffer, send it.
|
||||
// caller must hold uart_tx_lock.
|
||||
// called from both the top- and bottom-half.
|
||||
void
|
||||
uartstart()
|
||||
{
|
||||
while(1){
|
||||
if(uart_tx_w == uart_tx_r){
|
||||
// transmit buffer is empty.
|
||||
return;
|
||||
}
|
||||
|
||||
if((ReadReg(LSR) & LSR_TX_IDLE) == 0){
|
||||
// the UART transmit holding register is full,
|
||||
// so we cannot give it another byte.
|
||||
// it will interrupt when it's ready for a new byte.
|
||||
return;
|
||||
}
|
||||
|
||||
int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
|
||||
uart_tx_r += 1;
|
||||
|
||||
// maybe uartputc() is waiting for space in the buffer.
|
||||
wakeup(&uart_tx_r);
|
||||
|
||||
WriteReg(THR, c);
|
||||
}
|
||||
}
|
||||
|
||||
// read one input character from the UART.
|
||||
// return -1 if none is waiting.
|
||||
int
|
||||
uartgetc(void)
|
||||
{
|
||||
if(ReadReg(LSR) & 0x01){
|
||||
// input data is ready.
|
||||
return ReadReg(RHR);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// handle a uart interrupt, raised because input has
|
||||
// arrived, or the uart is ready for more output, or
|
||||
// both. called from devintr().
|
||||
void
|
||||
uartintr(void)
|
||||
{
|
||||
// read and process incoming characters.
|
||||
while(1){
|
||||
int c = uartgetc();
|
||||
if(c == -1)
|
||||
break;
|
||||
consoleintr(c);
|
||||
}
|
||||
|
||||
// send buffered characters.
|
||||
acquire(&uart_tx_lock);
|
||||
uartstart();
|
||||
release(&uart_tx_lock);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user