This commit is contained in:
Garen Tyler 2023-10-16 17:28:59 -06:00
parent 9c3dc94bd7
commit 29878e42e6
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
42 changed files with 2050 additions and 1484 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -107,3 +107,4 @@ struct proc {
};
int cpuid();
int allocpid();

View File

@ -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.

View File

@ -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();
}

View File

@ -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",
]

View File

@ -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]

View 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],
}

View 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;
}

View 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;

View 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],
}

View File

@ -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};

View File

@ -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);
}

View 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(),
);
}

View File

@ -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
}

View 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() {}

View File

@ -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 {

View 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;

View File

@ -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);

View 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;
}

View 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
}
}

View File

@ -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 {

View 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);
}

View 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
}

View 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
// }
// }
// }
//

View 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
}

View 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
}
}

View 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();
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}