Compare commits

..

No commits in common. "4cd2466900323c27937d2994b3b702266f840e46" and "96047832c62e6a172c92f9ead6715b553d4c2271" have entirely different histories.

55 changed files with 7683 additions and 7050 deletions

View File

@ -1,5 +0,0 @@
[build]
target = "riscv64gc-unknown-none-elf"
[target.riscv64-unknown-elf]
linker = "/opt/riscv/bin/riscv64-unknown-elf-gcc"

View File

@ -1,4 +0,0 @@
SortIncludes: Never
UseTab: Always
IndentWidth: 4
TabWidth: 4

View File

@ -4,4 +4,19 @@ root = true
[*] [*]
end_of_line = lf end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
[*.{c,h}]
indent_size = 2
[*.S]
indent_size = 8
[*.ld]
indent_size = 2
[Makefile]
indent_style = tab indent_style = tab
indent_size = 8

View File

@ -1 +0,0 @@
hard_tabs = true

View File

@ -1,8 +1,5 @@
K=kernel K=kernel
U=user U=user
R=rust
RTARGET = riscv64gc-unknown-none-elf
OBJS = \ OBJS = \
$K/entry.o \ $K/entry.o \
@ -33,9 +30,6 @@ OBJS = \
$K/plic.o \ $K/plic.o \
$K/virtio_disk.o $K/virtio_disk.o
RLIBS = \
$R/foo/target/$(RTARGET)/release/libfoo.a
# riscv64-unknown-elf- or riscv64-linux-gnu- # riscv64-unknown-elf- or riscv64-linux-gnu-
# perhaps in /opt/riscv/bin # perhaps in /opt/riscv/bin
#TOOLPREFIX = #TOOLPREFIX =
@ -79,8 +73,8 @@ endif
LDFLAGS = -z max-page-size=4096 LDFLAGS = -z max-page-size=4096
$K/kernel: $(OBJS) $K/kernel.ld $U/initcode rustlibs $K/kernel: $(OBJS) $K/kernel.ld $U/initcode
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(RLIBS) $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS)
$(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -S $K/kernel > $K/kernel.asm
$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
@ -144,9 +138,6 @@ fs.img: mkfs/mkfs README $(UPROGS)
-include kernel/*.d user/*.d -include kernel/*.d user/*.d
rustlibs:
cargo build -r --manifest-path $R/foo/Cargo.toml
clean: clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
*/*.o */*.d */*.asm */*.sym \ */*.o */*.d */*.asm */*.sym \
@ -154,7 +145,6 @@ clean:
mkfs/mkfs .gdbinit \ mkfs/mkfs .gdbinit \
$U/usys.S \ $U/usys.S \
$(UPROGS) $(UPROGS)
cargo clean --manifest-path $R/foo/Cargo.toml
# try to generate a unique GDB port # try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000) GDBPORT = $(shell expr `id -u` % 5000 + 25000)

36
README
View File

@ -6,7 +6,7 @@ ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides 2000)). See also https://pdos.csail.mit.edu/6.828/, which provides
pointers to on-line resources for v6. pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching, The following people have made contributions: Russ Cox (context switching,
@ -14,31 +14,29 @@ locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements. Clements.
We are also grateful for the bug reports and patches contributed by We are also grateful for the bug reports and patches contributed by
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, Ian Chen, Dan
Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi, Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi, eyalz800, Nelson
eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, flespark,
Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt Peter Froehlich, Yakir Goaron, Shivam Handa, Matt Harvey, Bryan Henry,
Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John jaichenhengjie, Jim Huang, Matúš Jókay, Alexander Kapshuk, Anders
Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller, Kaseorg, kehao95, Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt,
Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin Eddie Kohler, Vadim Kolontsov, Austin Liew, l0stman, Pavan
Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi
Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider,
Nider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude OptimisticSide, Greg Price, Jude Rich, Ayan Shafqat, Eldar Sehayek,
Rich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya Yongming Shen, Fumiya Shigemitsu, Cam Tenny, tyfkda, Warren Toomey,
Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal, Stephen Tu, Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, Keiichi
Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe, Watanabe, Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy
Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, Zheng, ZhUyU1997, and Zou Chang Wei.
ZhUyU1997, and Zou Chang Wei.
The code in the files that constitute xv6 is The code in the files that constitute xv6 is
Copyright 2006-2022 Frans Kaashoek, Robert Morris, and Russ Cox. Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching (kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.1810, so we are more interested in operating system for MIT's 6.S081, so we are more interested in
simplifications and clarifications than new features. simplifications and clarifications than new features.
BUILDING AND RUNNING XV6 BUILDING AND RUNNING XV6

View File

@ -13,6 +13,7 @@
// * Only one process at a time can use a buffer, // * Only one process at a time can use a buffer,
// so do not keep them longer than necessary. // so do not keep them longer than necessary.
#include "types.h" #include "types.h"
#include "param.h" #include "param.h"
#include "spinlock.h" #include "spinlock.h"
@ -23,116 +24,130 @@
#include "buf.h" #include "buf.h"
struct { struct {
struct spinlock lock; struct spinlock lock;
struct buf buf[NBUF]; struct buf buf[NBUF];
// Linked list of all buffers, through prev/next. // Linked list of all buffers, through prev/next.
// Sorted by how recently the buffer was used. // Sorted by how recently the buffer was used.
// head.next is most recent, head.prev is least. // head.next is most recent, head.prev is least.
struct buf head; struct buf head;
} bcache; } bcache;
void binit(void) { void
struct buf *b; binit(void)
{
struct buf *b;
initlock(&bcache.lock, "bcache"); initlock(&bcache.lock, "bcache");
// Create linked list of buffers // Create linked list of buffers
bcache.head.prev = &bcache.head; bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head; bcache.head.next = &bcache.head;
for (b = bcache.buf; b < bcache.buf + NBUF; b++) { for(b = bcache.buf; b < bcache.buf+NBUF; b++){
b->next = bcache.head.next; b->next = bcache.head.next;
b->prev = &bcache.head; b->prev = &bcache.head;
initsleeplock(&b->lock, "buffer"); initsleeplock(&b->lock, "buffer");
bcache.head.next->prev = b; bcache.head.next->prev = b;
bcache.head.next = b; bcache.head.next = b;
} }
} }
// Look through buffer cache for block on device dev. // Look through buffer cache for block on device dev.
// If not found, allocate a buffer. // If not found, allocate a buffer.
// In either case, return locked buffer. // In either case, return locked buffer.
static struct buf *bget(uint dev, uint blockno) { static struct buf*
struct buf *b; bget(uint dev, uint blockno)
{
struct buf *b;
acquire(&bcache.lock); acquire(&bcache.lock);
// Is the block already cached? // Is the block already cached?
for (b = bcache.head.next; b != &bcache.head; b = b->next) { for(b = bcache.head.next; b != &bcache.head; b = b->next){
if (b->dev == dev && b->blockno == blockno) { if(b->dev == dev && b->blockno == blockno){
b->refcnt++; b->refcnt++;
release(&bcache.lock); release(&bcache.lock);
acquiresleep(&b->lock); acquiresleep(&b->lock);
return b; return b;
} }
} }
// Not cached. // Not cached.
// Recycle the least recently used (LRU) unused buffer. // Recycle the least recently used (LRU) unused buffer.
for (b = bcache.head.prev; b != &bcache.head; b = b->prev) { for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
if (b->refcnt == 0) { if(b->refcnt == 0) {
b->dev = dev; b->dev = dev;
b->blockno = blockno; b->blockno = blockno;
b->valid = 0; b->valid = 0;
b->refcnt = 1; b->refcnt = 1;
release(&bcache.lock); release(&bcache.lock);
acquiresleep(&b->lock); acquiresleep(&b->lock);
return b; return b;
} }
} }
panic("bget: no buffers"); panic("bget: no buffers");
} }
// Return a locked buf with the contents of the indicated block. // Return a locked buf with the contents of the indicated block.
struct buf *bread(uint dev, uint blockno) { struct buf*
struct buf *b; bread(uint dev, uint blockno)
{
struct buf *b;
b = bget(dev, blockno); b = bget(dev, blockno);
if (!b->valid) { if(!b->valid) {
virtio_disk_rw(b, 0); virtio_disk_rw(b, 0);
b->valid = 1; b->valid = 1;
} }
return b; return b;
} }
// Write b's contents to disk. Must be locked. // Write b's contents to disk. Must be locked.
void bwrite(struct buf *b) { void
if (!holdingsleep(&b->lock)) bwrite(struct buf *b)
panic("bwrite"); {
virtio_disk_rw(b, 1); if(!holdingsleep(&b->lock))
panic("bwrite");
virtio_disk_rw(b, 1);
} }
// Release a locked buffer. // Release a locked buffer.
// Move to the head of the most-recently-used list. // Move to the head of the most-recently-used list.
void brelse(struct buf *b) { void
if (!holdingsleep(&b->lock)) brelse(struct buf *b)
panic("brelse"); {
if(!holdingsleep(&b->lock))
panic("brelse");
releasesleep(&b->lock); releasesleep(&b->lock);
acquire(&bcache.lock); acquire(&bcache.lock);
b->refcnt--; b->refcnt--;
if (b->refcnt == 0) { if (b->refcnt == 0) {
// no one is waiting for it. // no one is waiting for it.
b->next->prev = b->prev; b->next->prev = b->prev;
b->prev->next = b->next; b->prev->next = b->next;
b->next = bcache.head.next; b->next = bcache.head.next;
b->prev = &bcache.head; b->prev = &bcache.head;
bcache.head.next->prev = b; bcache.head.next->prev = b;
bcache.head.next = b; bcache.head.next = b;
} }
release(&bcache.lock); release(&bcache.lock);
} }
void bpin(struct buf *b) { void
acquire(&bcache.lock); bpin(struct buf *b) {
b->refcnt++; acquire(&bcache.lock);
release(&bcache.lock); b->refcnt++;
release(&bcache.lock);
} }
void bunpin(struct buf *b) { void
acquire(&bcache.lock); bunpin(struct buf *b) {
b->refcnt--; acquire(&bcache.lock);
release(&bcache.lock); b->refcnt--;
release(&bcache.lock);
} }

View File

@ -23,49 +23,51 @@
#include "proc.h" #include "proc.h"
#define BACKSPACE 0x100 #define BACKSPACE 0x100
#define C(x) ((x) - '@') // Control-x #define C(x) ((x)-'@') // Control-x
// //
// send one character to the uart. // send one character to the uart.
// called by printf(), and to echo input characters, // called by printf(), and to echo input characters,
// but not from write(). // but not from write().
// //
void consputc(int c) { void
if (c == BACKSPACE) { consputc(int c)
// if the user typed backspace, overwrite with a space. {
uartputc_sync('\b'); if(c == BACKSPACE){
uartputc_sync(' '); // if the user typed backspace, overwrite with a space.
uartputc_sync('\b'); uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
} else { } else {
uartputc_sync(c); uartputc_sync(c);
} }
} }
struct { struct {
struct spinlock lock; struct spinlock lock;
// input // input
#define INPUT_BUF_SIZE 128 #define INPUT_BUF_SIZE 128
char buf[INPUT_BUF_SIZE]; char buf[INPUT_BUF_SIZE];
uint r; // Read index uint r; // Read index
uint w; // Write index uint w; // Write index
uint e; // Edit index uint e; // Edit index
} cons; } cons;
// //
// user write()s to the console go here. // user write()s to the console go here.
// //
int consolewrite(int user_src, uint64 src, int n) { int
int i; consolewrite(int user_src, uint64 src, int n)
{
int i;
for (i = 0; i < n; i++) { for(i = 0; i < n; i++){
char c; char c;
if (either_copyin(&c, user_src, src + i, 1) == -1) if(either_copyin(&c, user_src, src+i, 1) == -1)
break; break;
uartputc(c); uartputc(c);
} }
return i; return i;
} }
// //
@ -74,52 +76,54 @@ int consolewrite(int user_src, uint64 src, int n) {
// user_dist indicates whether dst is a user // user_dist indicates whether dst is a user
// or kernel address. // or kernel address.
// //
int consoleread(int user_dst, uint64 dst, int n) { int
uint target; consoleread(int user_dst, uint64 dst, int n)
int c; {
char cbuf; uint target;
int c;
char cbuf;
target = n; target = n;
acquire(&cons.lock); acquire(&cons.lock);
while (n > 0) { while(n > 0){
// wait until interrupt handler has put some // wait until interrupt handler has put some
// input into cons.buffer. // input into cons.buffer.
while (cons.r == cons.w) { while(cons.r == cons.w){
if (killed(myproc())) { if(killed(myproc())){
release(&cons.lock); release(&cons.lock);
return -1; return -1;
} }
sleep(&cons.r, &cons.lock); sleep(&cons.r, &cons.lock);
} }
c = cons.buf[cons.r++ % INPUT_BUF_SIZE]; c = cons.buf[cons.r++ % INPUT_BUF_SIZE];
if (c == C('D')) { // end-of-file if(c == C('D')){ // end-of-file
if (n < target) { if(n < target){
// Save ^D for next time, to make sure // Save ^D for next time, to make sure
// caller gets a 0-byte result. // caller gets a 0-byte result.
cons.r--; cons.r--;
} }
break; break;
} }
// copy the input byte to the user-space buffer. // copy the input byte to the user-space buffer.
cbuf = c; cbuf = c;
if (either_copyout(user_dst, dst, &cbuf, 1) == -1) if(either_copyout(user_dst, dst, &cbuf, 1) == -1)
break; break;
dst++; dst++;
--n; --n;
if (c == '\n') { if(c == '\n'){
// a whole line has arrived, return to // a whole line has arrived, return to
// the user-level read(). // the user-level read().
break; break;
} }
} }
release(&cons.lock); release(&cons.lock);
return target - n; return target - n;
} }
// //
@ -128,57 +132,61 @@ int consoleread(int user_dst, uint64 dst, int n) {
// do erase/kill processing, append to cons.buf, // do erase/kill processing, append to cons.buf,
// wake up consoleread() if a whole line has arrived. // wake up consoleread() if a whole line has arrived.
// //
void consoleintr(int c) { void
acquire(&cons.lock); consoleintr(int c)
{
acquire(&cons.lock);
switch (c) { switch(c){
case C('P'): // Print process list. case C('P'): // Print process list.
procdump(); procdump();
break; break;
case C('U'): // Kill line. case C('U'): // Kill line.
while (cons.e != cons.w && while(cons.e != cons.w &&
cons.buf[(cons.e - 1) % INPUT_BUF_SIZE] != '\n') { cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){
cons.e--; cons.e--;
consputc(BACKSPACE); consputc(BACKSPACE);
} }
break; break;
case C('H'): // Backspace case C('H'): // Backspace
case '\x7f': // Delete key case '\x7f': // Delete key
if (cons.e != cons.w) { if(cons.e != cons.w){
cons.e--; cons.e--;
consputc(BACKSPACE); consputc(BACKSPACE);
} }
break; break;
default: default:
if (c != 0 && cons.e - cons.r < INPUT_BUF_SIZE) { if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){
c = (c == '\r') ? '\n' : c; c = (c == '\r') ? '\n' : c;
// echo back to the user. // echo back to the user.
consputc(c); consputc(c);
// store for consumption by consoleread(). // store for consumption by consoleread().
cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; cons.buf[cons.e++ % INPUT_BUF_SIZE] = c;
if (c == '\n' || c == C('D') || cons.e - cons.r == INPUT_BUF_SIZE) { if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){
// wake up consoleread() if a whole line (or end-of-file) // wake up consoleread() if a whole line (or end-of-file)
// has arrived. // has arrived.
cons.w = cons.e; cons.w = cons.e;
wakeup(&cons.r); wakeup(&cons.r);
} }
} }
break; break;
} }
release(&cons.lock); release(&cons.lock);
} }
void consoleinit(void) { void
initlock(&cons.lock, "cons"); consoleinit(void)
{
initlock(&cons.lock, "cons");
uartinit(); uartinit();
// connect read and write system calls // connect read and write system calls
// to consoleread and consolewrite. // to consoleread and consolewrite.
devsw[CONSOLE].read = consoleread; devsw[CONSOLE].read = consoleread;
devsw[CONSOLE].write = consolewrite; devsw[CONSOLE].write = consolewrite;
} }

View File

@ -9,155 +9,158 @@
static int loadseg(pde_t *, uint64, struct inode *, uint, uint); static int loadseg(pde_t *, uint64, struct inode *, uint, uint);
int flags2perm(int flags) { int flags2perm(int flags)
int perm = 0; {
if (flags & 0x1) int perm = 0;
perm = PTE_X; if(flags & 0x1)
if (flags & 0x2) perm = PTE_X;
perm |= PTE_W; if(flags & 0x2)
return perm; perm |= PTE_W;
return perm;
} }
int exec(char *path, char **argv) { int
char *s, *last; exec(char *path, char **argv)
int i, off; {
uint64 argc, sz = 0, sp, ustack[MAXARG], stackbase; char *s, *last;
struct elfhdr elf; int i, off;
struct inode *ip; uint64 argc, sz = 0, sp, ustack[MAXARG], stackbase;
struct proghdr ph; struct elfhdr elf;
pagetable_t pagetable = 0, oldpagetable; struct inode *ip;
struct proc *p = myproc(); struct proghdr ph;
pagetable_t pagetable = 0, oldpagetable;
struct proc *p = myproc();
begin_op(); begin_op();
if ((ip = namei(path)) == 0) { if((ip = namei(path)) == 0){
end_op(); end_op();
return -1; return -1;
} }
ilock(ip); ilock(ip);
// Check ELF header // Check ELF header
if (readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
goto bad; goto bad;
if (elf.magic != ELF_MAGIC) if(elf.magic != ELF_MAGIC)
goto bad; goto bad;
if ((pagetable = proc_pagetable(p)) == 0) if((pagetable = proc_pagetable(p)) == 0)
goto bad; goto bad;
// Load program into memory. // Load program into memory.
for (i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) { for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if (readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph)) if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad; goto bad;
if (ph.type != ELF_PROG_LOAD) if(ph.type != ELF_PROG_LOAD)
continue; continue;
if (ph.memsz < ph.filesz) if(ph.memsz < ph.filesz)
goto bad; goto bad;
if (ph.vaddr + ph.memsz < ph.vaddr) if(ph.vaddr + ph.memsz < ph.vaddr)
goto bad; goto bad;
if (ph.vaddr % PGSIZE != 0) if(ph.vaddr % PGSIZE != 0)
goto bad; goto bad;
uint64 sz1; uint64 sz1;
if ((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, flags2perm(ph.flags))) == 0)
flags2perm(ph.flags))) == 0) goto bad;
goto bad; sz = sz1;
sz = sz1; if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
if (loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0) goto bad;
goto bad; }
} iunlockput(ip);
iunlockput(ip); end_op();
end_op(); ip = 0;
ip = 0;
p = myproc(); p = myproc();
uint64 oldsz = p->sz; uint64 oldsz = p->sz;
// Allocate two pages at the next page boundary. // Allocate two pages at the next page boundary.
// Make the first inaccessible as a stack guard. // Make the first inaccessible as a stack guard.
// Use the second as the user stack. // Use the second as the user stack.
sz = PGROUNDUP(sz); sz = PGROUNDUP(sz);
uint64 sz1; uint64 sz1;
if ((sz1 = uvmalloc(pagetable, sz, sz + 2 * PGSIZE, PTE_W)) == 0) if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE, PTE_W)) == 0)
goto bad; goto bad;
sz = sz1; sz = sz1;
uvmclear(pagetable, sz - 2 * PGSIZE); uvmclear(pagetable, sz-2*PGSIZE);
sp = sz; sp = sz;
stackbase = sp - PGSIZE; stackbase = sp - PGSIZE;
// Push argument strings, prepare rest of stack in ustack. // Push argument strings, prepare rest of stack in ustack.
for (argc = 0; argv[argc]; argc++) { for(argc = 0; argv[argc]; argc++) {
if (argc >= MAXARG) if(argc >= MAXARG)
goto bad; goto bad;
sp -= strlen(argv[argc]) + 1; sp -= strlen(argv[argc]) + 1;
sp -= sp % 16; // riscv sp must be 16-byte aligned sp -= sp % 16; // riscv sp must be 16-byte aligned
if (sp < stackbase) if(sp < stackbase)
goto bad; goto bad;
if (copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
goto bad; goto bad;
ustack[argc] = sp; ustack[argc] = sp;
} }
ustack[argc] = 0; ustack[argc] = 0;
// push the array of argv[] pointers. // push the array of argv[] pointers.
sp -= (argc + 1) * sizeof(uint64); sp -= (argc+1) * sizeof(uint64);
sp -= sp % 16; sp -= sp % 16;
if (sp < stackbase) if(sp < stackbase)
goto bad; goto bad;
if (copyout(pagetable, sp, (char *)ustack, (argc + 1) * sizeof(uint64)) < 0) if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
goto bad; goto bad;
// arguments to user main(argc, argv) // arguments to user main(argc, argv)
// argc is returned via the system call return // argc is returned via the system call return
// value, which goes in a0. // value, which goes in a0.
p->trapframe->a1 = sp; p->trapframe->a1 = sp;
// Save program name for debugging. // Save program name for debugging.
for (last = s = path; *s; s++) for(last=s=path; *s; s++)
if (*s == '/') if(*s == '/')
last = s + 1; last = s+1;
safestrcpy(p->name, last, sizeof(p->name)); safestrcpy(p->name, last, sizeof(p->name));
// Commit to the user image.
oldpagetable = p->pagetable;
p->pagetable = pagetable;
p->sz = sz;
p->trapframe->epc = elf.entry; // initial program counter = main
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
// Commit to the user image. return argc; // this ends up in a0, the first argument to main(argc, argv)
oldpagetable = p->pagetable;
p->pagetable = pagetable;
p->sz = sz;
p->trapframe->epc = elf.entry; // initial program counter = main
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
return argc; // this ends up in a0, the first argument to main(argc, argv) bad:
if(pagetable)
bad: proc_freepagetable(pagetable, sz);
if (pagetable) if(ip){
proc_freepagetable(pagetable, sz); iunlockput(ip);
if (ip) { end_op();
iunlockput(ip); }
end_op(); return -1;
}
return -1;
} }
// Load a program segment into pagetable at virtual address va. // Load a program segment into pagetable at virtual address va.
// va must be page-aligned // va must be page-aligned
// and the pages from va to va+sz must already be mapped. // and the pages from va to va+sz must already be mapped.
// Returns 0 on success, -1 on failure. // Returns 0 on success, -1 on failure.
static int loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, static int
uint offset, uint sz) { loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz)
uint i, n; {
uint64 pa; uint i, n;
uint64 pa;
for (i = 0; i < sz; i += PGSIZE) { for(i = 0; i < sz; i += PGSIZE){
pa = walkaddr(pagetable, va + i); pa = walkaddr(pagetable, va + i);
if (pa == 0) if(pa == 0)
panic("loadseg: address should exist"); panic("loadseg: address should exist");
if (sz - i < PGSIZE) if(sz - i < PGSIZE)
n = sz - i; n = sz - i;
else else
n = PGSIZE; n = PGSIZE;
if (readi(ip, 0, (uint64)pa, offset + i, n) != n) if(readi(ip, 0, (uint64)pa, offset+i, n) != n)
return -1; return -1;
} }
return 0; return 0;
} }

View File

@ -15,151 +15,168 @@
struct devsw devsw[NDEV]; struct devsw devsw[NDEV];
struct { struct {
struct spinlock lock; struct spinlock lock;
struct file file[NFILE]; struct file file[NFILE];
} ftable; } ftable;
void fileinit(void) { initlock(&ftable.lock, "ftable"); } void
fileinit(void)
{
initlock(&ftable.lock, "ftable");
}
// Allocate a file structure. // Allocate a file structure.
struct file *filealloc(void) { struct file*
struct file *f; filealloc(void)
{
struct file *f;
acquire(&ftable.lock); acquire(&ftable.lock);
for (f = ftable.file; f < ftable.file + NFILE; f++) { for(f = ftable.file; f < ftable.file + NFILE; f++){
if (f->ref == 0) { if(f->ref == 0){
f->ref = 1; f->ref = 1;
release(&ftable.lock); release(&ftable.lock);
return f; return f;
} }
} }
release(&ftable.lock); release(&ftable.lock);
return 0; return 0;
} }
// Increment ref count for file f. // Increment ref count for file f.
struct file *filedup(struct file *f) { struct file*
acquire(&ftable.lock); filedup(struct file *f)
if (f->ref < 1) {
panic("filedup"); acquire(&ftable.lock);
f->ref++; if(f->ref < 1)
release(&ftable.lock); panic("filedup");
return f; f->ref++;
release(&ftable.lock);
return f;
} }
// Close file f. (Decrement ref count, close when reaches 0.) // Close file f. (Decrement ref count, close when reaches 0.)
void fileclose(struct file *f) { void
struct file ff; fileclose(struct file *f)
{
struct file ff;
acquire(&ftable.lock); acquire(&ftable.lock);
if (f->ref < 1) if(f->ref < 1)
panic("fileclose"); panic("fileclose");
if (--f->ref > 0) { if(--f->ref > 0){
release(&ftable.lock); release(&ftable.lock);
return; return;
} }
ff = *f; ff = *f;
f->ref = 0; f->ref = 0;
f->type = FD_NONE; f->type = FD_NONE;
release(&ftable.lock); release(&ftable.lock);
if (ff.type == FD_PIPE) { if(ff.type == FD_PIPE){
pipeclose(ff.pipe, ff.writable); pipeclose(ff.pipe, ff.writable);
} else if (ff.type == FD_INODE || ff.type == FD_DEVICE) { } else if(ff.type == FD_INODE || ff.type == FD_DEVICE){
begin_op(); begin_op();
iput(ff.ip); iput(ff.ip);
end_op(); end_op();
} }
} }
// Get metadata about file f. // Get metadata about file f.
// addr is a user virtual address, pointing to a struct stat. // addr is a user virtual address, pointing to a struct stat.
int filestat(struct file *f, uint64 addr) { int
struct proc *p = myproc(); filestat(struct file *f, uint64 addr)
struct stat st; {
struct proc *p = myproc();
if (f->type == FD_INODE || f->type == FD_DEVICE) { struct stat st;
ilock(f->ip);
stati(f->ip, &st); if(f->type == FD_INODE || f->type == FD_DEVICE){
iunlock(f->ip); ilock(f->ip);
if (copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) stati(f->ip, &st);
return -1; iunlock(f->ip);
return 0; if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
} return -1;
return -1; return 0;
}
return -1;
} }
// Read from file f. // Read from file f.
// addr is a user virtual address. // addr is a user virtual address.
int fileread(struct file *f, uint64 addr, int n) { int
int r = 0; fileread(struct file *f, uint64 addr, int n)
{
int r = 0;
if (f->readable == 0) if(f->readable == 0)
return -1; return -1;
if (f->type == FD_PIPE) { if(f->type == FD_PIPE){
r = piperead(f->pipe, addr, n); r = piperead(f->pipe, addr, n);
} else if (f->type == FD_DEVICE) { } else if(f->type == FD_DEVICE){
if (f->major < 0 || f->major >= NDEV || !devsw[f->major].read) if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read)
return -1; return -1;
r = devsw[f->major].read(1, addr, n); r = devsw[f->major].read(1, addr, n);
} else if (f->type == FD_INODE) { } else if(f->type == FD_INODE){
ilock(f->ip); ilock(f->ip);
if ((r = readi(f->ip, 1, addr, f->off, n)) > 0) if((r = readi(f->ip, 1, addr, f->off, n)) > 0)
f->off += r; f->off += r;
iunlock(f->ip); iunlock(f->ip);
} else { } else {
panic("fileread"); panic("fileread");
} }
return r; return r;
} }
// Write to file f. // Write to file f.
// addr is a user virtual address. // addr is a user virtual address.
int filewrite(struct file *f, uint64 addr, int n) { int
int r, ret = 0; filewrite(struct file *f, uint64 addr, int n)
{
int r, ret = 0;
if (f->writable == 0) if(f->writable == 0)
return -1; return -1;
if (f->type == FD_PIPE) { if(f->type == FD_PIPE){
ret = pipewrite(f->pipe, addr, n); ret = pipewrite(f->pipe, addr, n);
} else if (f->type == FD_DEVICE) { } else if(f->type == FD_DEVICE){
if (f->major < 0 || f->major >= NDEV || !devsw[f->major].write) if(f->major < 0 || f->major >= NDEV || !devsw[f->major].write)
return -1; return -1;
ret = devsw[f->major].write(1, addr, n); ret = devsw[f->major].write(1, addr, n);
} else if (f->type == FD_INODE) { } else if(f->type == FD_INODE){
// write a few blocks at a time to avoid exceeding // write a few blocks at a time to avoid exceeding
// the maximum log transaction size, including // the maximum log transaction size, including
// i-node, indirect block, allocation blocks, // i-node, indirect block, allocation blocks,
// and 2 blocks of slop for non-aligned writes. // and 2 blocks of slop for non-aligned writes.
// this really belongs lower down, since writei() // this really belongs lower down, since writei()
// might be writing a device like the console. // might be writing a device like the console.
int max = ((MAXOPBLOCKS - 1 - 1 - 2) / 2) * BSIZE; int max = ((MAXOPBLOCKS-1-1-2) / 2) * BSIZE;
int i = 0; int i = 0;
while (i < n) { while(i < n){
int n1 = n - i; int n1 = n - i;
if (n1 > max) if(n1 > max)
n1 = max; n1 = max;
begin_op(); begin_op();
ilock(f->ip); ilock(f->ip);
if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0) if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0)
f->off += r; f->off += r;
iunlock(f->ip); iunlock(f->ip);
end_op(); end_op();
if (r != n1) { if(r != n1){
// error from writei // error from writei
break; break;
} }
i += r; i += r;
} }
ret = (i == n ? n : -1); ret = (i == n ? n : -1);
} else { } else {
panic("filewrite"); panic("filewrite");
} }
return ret; return ret;
} }

View File

@ -24,75 +24,84 @@
#define min(a, b) ((a) < (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b))
// there should be one superblock per disk device, but we run with // there should be one superblock per disk device, but we run with
// only one device // only one device
struct superblock sb; struct superblock sb;
// Read the super block. // Read the super block.
static void readsb(int dev, struct superblock *sb) { static void
struct buf *bp; readsb(int dev, struct superblock *sb)
{
struct buf *bp;
bp = bread(dev, 1); bp = bread(dev, 1);
memmove(sb, bp->data, sizeof(*sb)); memmove(sb, bp->data, sizeof(*sb));
brelse(bp); brelse(bp);
} }
// Init fs // Init fs
void fsinit(int dev) { void
readsb(dev, &sb); fsinit(int dev) {
if (sb.magic != FSMAGIC) readsb(dev, &sb);
panic("invalid file system"); if(sb.magic != FSMAGIC)
initlog(dev, &sb); panic("invalid file system");
initlog(dev, &sb);
} }
// Zero a block. // Zero a block.
static void bzero(int dev, int bno) { static void
struct buf *bp; bzero(int dev, int bno)
{
struct buf *bp;
bp = bread(dev, bno); bp = bread(dev, bno);
memset(bp->data, 0, BSIZE); memset(bp->data, 0, BSIZE);
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
// Blocks. // Blocks.
// Allocate a zeroed disk block. // Allocate a zeroed disk block.
// returns 0 if out of disk space. // returns 0 if out of disk space.
static uint balloc(uint dev) { static uint
int b, bi, m; balloc(uint dev)
struct buf *bp; {
int b, bi, m;
struct buf *bp;
bp = 0; bp = 0;
for (b = 0; b < sb.size; b += BPB) { for(b = 0; b < sb.size; b += BPB){
bp = bread(dev, BBLOCK(b, sb)); bp = bread(dev, BBLOCK(b, sb));
for (bi = 0; bi < BPB && b + bi < sb.size; bi++) { for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
m = 1 << (bi % 8); m = 1 << (bi % 8);
if ((bp->data[bi / 8] & m) == 0) { // Is block free? if((bp->data[bi/8] & m) == 0){ // Is block free?
bp->data[bi / 8] |= m; // Mark block in use. bp->data[bi/8] |= m; // Mark block in use.
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
bzero(dev, b + bi); bzero(dev, b + bi);
return b + bi; return b + bi;
} }
} }
brelse(bp); brelse(bp);
} }
printf("balloc: out of blocks\n"); printf("balloc: out of blocks\n");
return 0; return 0;
} }
// Free a disk block. // Free a disk block.
static void bfree(int dev, uint b) { static void
struct buf *bp; bfree(int dev, uint b)
int bi, m; {
struct buf *bp;
int bi, m;
bp = bread(dev, BBLOCK(b, sb)); bp = bread(dev, BBLOCK(b, sb));
bi = b % BPB; bi = b % BPB;
m = 1 << (bi % 8); m = 1 << (bi % 8);
if ((bp->data[bi / 8] & m) == 0) if((bp->data[bi/8] & m) == 0)
panic("freeing free block"); panic("freeing free block");
bp->data[bi / 8] &= ~m; bp->data[bi/8] &= ~m;
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
// Inodes. // Inodes.
@ -165,142 +174,156 @@ static void bfree(int dev, uint b) {
// read or write that inode's ip->valid, ip->size, ip->type, &c. // read or write that inode's ip->valid, ip->size, ip->type, &c.
struct { struct {
struct spinlock lock; struct spinlock lock;
struct inode inode[NINODE]; struct inode inode[NINODE];
} itable; } itable;
void iinit() { void
int i = 0; iinit()
{
initlock(&itable.lock, "itable"); int i = 0;
for (i = 0; i < NINODE; i++) {
initsleeplock(&itable.inode[i].lock, "inode"); initlock(&itable.lock, "itable");
} for(i = 0; i < NINODE; i++) {
initsleeplock(&itable.inode[i].lock, "inode");
}
} }
static struct inode *iget(uint dev, uint inum); static struct inode* iget(uint dev, uint inum);
// Allocate an inode on device dev. // Allocate an inode on device dev.
// Mark it as allocated by giving it type type. // Mark it as allocated by giving it type type.
// Returns an unlocked but allocated and referenced inode, // Returns an unlocked but allocated and referenced inode,
// or NULL if there is no free inode. // or NULL if there is no free inode..
struct inode *ialloc(uint dev, short type) { struct inode*
int inum; ialloc(uint dev, short type)
struct buf *bp; {
struct dinode *dip; int inum;
struct buf *bp;
struct dinode *dip;
for (inum = 1; inum < sb.ninodes; inum++) { for(inum = 1; inum < sb.ninodes; inum++){
bp = bread(dev, IBLOCK(inum, sb)); bp = bread(dev, IBLOCK(inum, sb));
dip = (struct dinode *)bp->data + inum % IPB; dip = (struct dinode*)bp->data + inum%IPB;
if (dip->type == 0) { // a free inode if(dip->type == 0){ // a free inode
memset(dip, 0, sizeof(*dip)); memset(dip, 0, sizeof(*dip));
dip->type = type; dip->type = type;
log_write(bp); // mark it allocated on the disk log_write(bp); // mark it allocated on the disk
brelse(bp); brelse(bp);
return iget(dev, inum); return iget(dev, inum);
} }
brelse(bp); brelse(bp);
} }
printf("ialloc: no inodes\n"); printf("ialloc: no inodes\n");
return 0; return 0;
} }
// Copy a modified in-memory inode to disk. // Copy a modified in-memory inode to disk.
// Must be called after every change to an ip->xxx field // Must be called after every change to an ip->xxx field
// that lives on disk. // that lives on disk.
// Caller must hold ip->lock. // Caller must hold ip->lock.
void iupdate(struct inode *ip) { void
struct buf *bp; iupdate(struct inode *ip)
struct dinode *dip; {
struct buf *bp;
struct dinode *dip;
bp = bread(ip->dev, IBLOCK(ip->inum, sb)); bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode *)bp->data + ip->inum % IPB; dip = (struct dinode*)bp->data + ip->inum%IPB;
dip->type = ip->type; dip->type = ip->type;
dip->major = ip->major; dip->major = ip->major;
dip->minor = ip->minor; dip->minor = ip->minor;
dip->nlink = ip->nlink; dip->nlink = ip->nlink;
dip->size = ip->size; dip->size = ip->size;
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
// Find the inode with number inum on device dev // Find the inode with number inum on device dev
// and return the in-memory copy. Does not lock // and return the in-memory copy. Does not lock
// the inode and does not read it from disk. // the inode and does not read it from disk.
static struct inode *iget(uint dev, uint inum) { static struct inode*
struct inode *ip, *empty; iget(uint dev, uint inum)
{
struct inode *ip, *empty;
acquire(&itable.lock); acquire(&itable.lock);
// Is the inode already in the table? // Is the inode already in the table?
empty = 0; empty = 0;
for (ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++) { for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){
if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) { if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
ip->ref++; ip->ref++;
release(&itable.lock); release(&itable.lock);
return ip; return ip;
} }
if (empty == 0 && ip->ref == 0) // Remember empty slot. if(empty == 0 && ip->ref == 0) // Remember empty slot.
empty = ip; empty = ip;
} }
// Recycle an inode entry. // Recycle an inode entry.
if (empty == 0) if(empty == 0)
panic("iget: no inodes"); panic("iget: no inodes");
ip = empty; ip = empty;
ip->dev = dev; ip->dev = dev;
ip->inum = inum; ip->inum = inum;
ip->ref = 1; ip->ref = 1;
ip->valid = 0; ip->valid = 0;
release(&itable.lock); release(&itable.lock);
return ip; return ip;
} }
// Increment reference count for ip. // Increment reference count for ip.
// Returns ip to enable ip = idup(ip1) idiom. // Returns ip to enable ip = idup(ip1) idiom.
struct inode *idup(struct inode *ip) { struct inode*
acquire(&itable.lock); idup(struct inode *ip)
ip->ref++; {
release(&itable.lock); acquire(&itable.lock);
return ip; ip->ref++;
release(&itable.lock);
return ip;
} }
// Lock the given inode. // Lock the given inode.
// Reads the inode from disk if necessary. // Reads the inode from disk if necessary.
void ilock(struct inode *ip) { void
struct buf *bp; ilock(struct inode *ip)
struct dinode *dip; {
struct buf *bp;
struct dinode *dip;
if (ip == 0 || ip->ref < 1) if(ip == 0 || ip->ref < 1)
panic("ilock"); panic("ilock");
acquiresleep(&ip->lock); acquiresleep(&ip->lock);
if (ip->valid == 0) { if(ip->valid == 0){
bp = bread(ip->dev, IBLOCK(ip->inum, sb)); bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode *)bp->data + ip->inum % IPB; dip = (struct dinode*)bp->data + ip->inum%IPB;
ip->type = dip->type; ip->type = dip->type;
ip->major = dip->major; ip->major = dip->major;
ip->minor = dip->minor; ip->minor = dip->minor;
ip->nlink = dip->nlink; ip->nlink = dip->nlink;
ip->size = dip->size; ip->size = dip->size;
memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
brelse(bp); brelse(bp);
ip->valid = 1; ip->valid = 1;
if (ip->type == 0) if(ip->type == 0)
panic("ilock: no type"); panic("ilock: no type");
} }
} }
// Unlock the given inode. // Unlock the given inode.
void iunlock(struct inode *ip) { void
if (ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1) iunlock(struct inode *ip)
panic("iunlock"); {
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
panic("iunlock");
releasesleep(&ip->lock); releasesleep(&ip->lock);
} }
// Drop a reference to an in-memory inode. // Drop a reference to an in-memory inode.
@ -310,36 +333,40 @@ void iunlock(struct inode *ip) {
// to it, free the inode (and its content) on disk. // to it, free the inode (and its content) on disk.
// All calls to iput() must be inside a transaction in // All calls to iput() must be inside a transaction in
// case it has to free the inode. // case it has to free the inode.
void iput(struct inode *ip) { void
acquire(&itable.lock); iput(struct inode *ip)
{
acquire(&itable.lock);
if (ip->ref == 1 && ip->valid && ip->nlink == 0) { if(ip->ref == 1 && ip->valid && ip->nlink == 0){
// inode has no links and no other references: truncate and free. // inode has no links and no other references: truncate and free.
// ip->ref == 1 means no other process can have ip locked, // ip->ref == 1 means no other process can have ip locked,
// so this acquiresleep() won't block (or deadlock). // so this acquiresleep() won't block (or deadlock).
acquiresleep(&ip->lock); acquiresleep(&ip->lock);
release(&itable.lock); release(&itable.lock);
itrunc(ip); itrunc(ip);
ip->type = 0; ip->type = 0;
iupdate(ip); iupdate(ip);
ip->valid = 0; ip->valid = 0;
releasesleep(&ip->lock); releasesleep(&ip->lock);
acquire(&itable.lock); acquire(&itable.lock);
} }
ip->ref--; ip->ref--;
release(&itable.lock); release(&itable.lock);
} }
// Common idiom: unlock, then put. // Common idiom: unlock, then put.
void iunlockput(struct inode *ip) { void
iunlock(ip); iunlockput(struct inode *ip)
iput(ip); {
iunlock(ip);
iput(ip);
} }
// Inode content // Inode content
@ -352,112 +379,120 @@ void iunlockput(struct inode *ip) {
// Return the disk block address of the nth block in inode ip. // Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one. // If there is no such block, bmap allocates one.
// returns 0 if out of disk space. // returns 0 if out of disk space.
static uint bmap(struct inode *ip, uint bn) { static uint
uint addr, *a; bmap(struct inode *ip, uint bn)
struct buf *bp; {
uint addr, *a;
struct buf *bp;
if (bn < NDIRECT) { if(bn < NDIRECT){
if ((addr = ip->addrs[bn]) == 0) { if((addr = ip->addrs[bn]) == 0){
addr = balloc(ip->dev); addr = balloc(ip->dev);
if (addr == 0) if(addr == 0)
return 0; return 0;
ip->addrs[bn] = addr; ip->addrs[bn] = addr;
} }
return addr; return addr;
} }
bn -= NDIRECT; bn -= NDIRECT;
if (bn < NINDIRECT) { if(bn < NINDIRECT){
// Load indirect block, allocating if necessary. // Load indirect block, allocating if necessary.
if ((addr = ip->addrs[NDIRECT]) == 0) { if((addr = ip->addrs[NDIRECT]) == 0){
addr = balloc(ip->dev); addr = balloc(ip->dev);
if (addr == 0) if(addr == 0)
return 0; return 0;
ip->addrs[NDIRECT] = addr; ip->addrs[NDIRECT] = addr;
} }
bp = bread(ip->dev, addr); bp = bread(ip->dev, addr);
a = (uint *)bp->data; a = (uint*)bp->data;
if ((addr = a[bn]) == 0) { if((addr = a[bn]) == 0){
addr = balloc(ip->dev); addr = balloc(ip->dev);
if (addr) { if(addr){
a[bn] = addr; a[bn] = addr;
log_write(bp); log_write(bp);
} }
} }
brelse(bp); brelse(bp);
return addr; return addr;
} }
panic("bmap: out of range"); panic("bmap: out of range");
} }
// Truncate inode (discard contents). // Truncate inode (discard contents).
// Caller must hold ip->lock. // Caller must hold ip->lock.
void itrunc(struct inode *ip) { void
int i, j; itrunc(struct inode *ip)
struct buf *bp; {
uint *a; int i, j;
struct buf *bp;
uint *a;
for (i = 0; i < NDIRECT; i++) { for(i = 0; i < NDIRECT; i++){
if (ip->addrs[i]) { if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]); bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0; ip->addrs[i] = 0;
} }
} }
if (ip->addrs[NDIRECT]) { if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]); bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint *)bp->data; a = (uint*)bp->data;
for (j = 0; j < NINDIRECT; j++) { for(j = 0; j < NINDIRECT; j++){
if (a[j]) if(a[j])
bfree(ip->dev, a[j]); bfree(ip->dev, a[j]);
} }
brelse(bp); brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]); bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0; ip->addrs[NDIRECT] = 0;
} }
ip->size = 0; ip->size = 0;
iupdate(ip); iupdate(ip);
} }
// Copy stat information from inode. // Copy stat information from inode.
// Caller must hold ip->lock. // Caller must hold ip->lock.
void stati(struct inode *ip, struct stat *st) { void
st->dev = ip->dev; stati(struct inode *ip, struct stat *st)
st->ino = ip->inum; {
st->type = ip->type; st->dev = ip->dev;
st->nlink = ip->nlink; st->ino = ip->inum;
st->size = ip->size; st->type = ip->type;
st->nlink = ip->nlink;
st->size = ip->size;
} }
// Read data from inode. // Read data from inode.
// Caller must hold ip->lock. // Caller must hold ip->lock.
// If user_dst==1, then dst is a user virtual address; // If user_dst==1, then dst is a user virtual address;
// otherwise, dst is a kernel address. // otherwise, dst is a kernel address.
int readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) { int
uint tot, m; readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
struct buf *bp; {
uint tot, m;
struct buf *bp;
if (off > ip->size || off + n < off) if(off > ip->size || off + n < off)
return 0; return 0;
if (off + n > ip->size) if(off + n > ip->size)
n = ip->size - off; n = ip->size - off;
for (tot = 0; tot < n; tot += m, off += m, dst += m) { for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
uint addr = bmap(ip, off / BSIZE); uint addr = bmap(ip, off/BSIZE);
if (addr == 0) if(addr == 0)
break; break;
bp = bread(ip->dev, addr); bp = bread(ip->dev, addr);
m = min(n - tot, BSIZE - off % BSIZE); m = min(n - tot, BSIZE - off%BSIZE);
if (either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) { if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) {
brelse(bp); brelse(bp);
tot = -1; tot = -1;
break; break;
} }
brelse(bp); brelse(bp);
} }
return tot; return tot;
} }
// Write data to inode. // Write data to inode.
@ -467,97 +502,107 @@ int readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) {
// Returns the number of bytes successfully written. // Returns the number of bytes successfully written.
// If the return value is less than the requested n, // If the return value is less than the requested n,
// there was an error of some kind. // there was an error of some kind.
int writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) { int
uint tot, m; writei(struct inode *ip, int user_src, uint64 src, uint off, uint n)
struct buf *bp; {
uint tot, m;
struct buf *bp;
if (off > ip->size || off + n < off) if(off > ip->size || off + n < off)
return -1; return -1;
if (off + n > MAXFILE * BSIZE) if(off + n > MAXFILE*BSIZE)
return -1; return -1;
for (tot = 0; tot < n; tot += m, off += m, src += m) { for(tot=0; tot<n; tot+=m, off+=m, src+=m){
uint addr = bmap(ip, off / BSIZE); uint addr = bmap(ip, off/BSIZE);
if (addr == 0) if(addr == 0)
break; break;
bp = bread(ip->dev, addr); bp = bread(ip->dev, addr);
m = min(n - tot, BSIZE - off % BSIZE); m = min(n - tot, BSIZE - off%BSIZE);
if (either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) { if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) {
brelse(bp); brelse(bp);
break; break;
} }
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
if (off > ip->size) if(off > ip->size)
ip->size = off; ip->size = off;
// write the i-node back to disk even if the size didn't change // write the i-node back to disk even if the size didn't change
// because the loop above might have called bmap() and added a new // because the loop above might have called bmap() and added a new
// block to ip->addrs[]. // block to ip->addrs[].
iupdate(ip); iupdate(ip);
return tot; return tot;
} }
// Directories // Directories
int namecmp(const char *s, const char *t) { return strncmp(s, t, DIRSIZ); } int
namecmp(const char *s, const char *t)
{
return strncmp(s, t, DIRSIZ);
}
// Look for a directory entry in a directory. // Look for a directory entry in a directory.
// If found, set *poff to byte offset of entry. // If found, set *poff to byte offset of entry.
struct inode *dirlookup(struct inode *dp, char *name, uint *poff) { struct inode*
uint off, inum; dirlookup(struct inode *dp, char *name, uint *poff)
struct dirent de; {
uint off, inum;
struct dirent de;
if (dp->type != T_DIR) if(dp->type != T_DIR)
panic("dirlookup not DIR"); panic("dirlookup not DIR");
for (off = 0; off < dp->size; off += sizeof(de)) { for(off = 0; off < dp->size; off += sizeof(de)){
if (readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("dirlookup read"); panic("dirlookup read");
if (de.inum == 0) if(de.inum == 0)
continue; continue;
if (namecmp(name, de.name) == 0) { if(namecmp(name, de.name) == 0){
// entry matches path element // entry matches path element
if (poff) if(poff)
*poff = off; *poff = off;
inum = de.inum; inum = de.inum;
return iget(dp->dev, inum); return iget(dp->dev, inum);
} }
} }
return 0; return 0;
} }
// Write a new directory entry (name, inum) into the directory dp. // Write a new directory entry (name, inum) into the directory dp.
// Returns 0 on success, -1 on failure (e.g. out of disk blocks). // Returns 0 on success, -1 on failure (e.g. out of disk blocks).
int dirlink(struct inode *dp, char *name, uint inum) { int
int off; dirlink(struct inode *dp, char *name, uint inum)
struct dirent de; {
struct inode *ip; int off;
struct dirent de;
struct inode *ip;
// Check that name is not present. // Check that name is not present.
if ((ip = dirlookup(dp, name, 0)) != 0) { if((ip = dirlookup(dp, name, 0)) != 0){
iput(ip); iput(ip);
return -1; return -1;
} }
// Look for an empty dirent. // Look for an empty dirent.
for (off = 0; off < dp->size; off += sizeof(de)) { for(off = 0; off < dp->size; off += sizeof(de)){
if (readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("dirlink read"); panic("dirlink read");
if (de.inum == 0) if(de.inum == 0)
break; break;
} }
strncpy(de.name, name, DIRSIZ); strncpy(de.name, name, DIRSIZ);
de.inum = inum; de.inum = inum;
if (writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
return -1; return -1;
return 0; return 0;
} }
// Paths // Paths
@ -574,71 +619,79 @@ int dirlink(struct inode *dp, char *name, uint inum) {
// skipelem("a", name) = "", setting name = "a" // skipelem("a", name) = "", setting name = "a"
// skipelem("", name) = skipelem("////", name) = 0 // skipelem("", name) = skipelem("////", name) = 0
// //
static char *skipelem(char *path, char *name) { static char*
char *s; skipelem(char *path, char *name)
int len; {
char *s;
int len;
while (*path == '/') while(*path == '/')
path++; path++;
if (*path == 0) if(*path == 0)
return 0; return 0;
s = path; s = path;
while (*path != '/' && *path != 0) while(*path != '/' && *path != 0)
path++; path++;
len = path - s; len = path - s;
if (len >= DIRSIZ) if(len >= DIRSIZ)
memmove(name, s, DIRSIZ); memmove(name, s, DIRSIZ);
else { else {
memmove(name, s, len); memmove(name, s, len);
name[len] = 0; name[len] = 0;
} }
while (*path == '/') while(*path == '/')
path++; path++;
return path; return path;
} }
// Look up and return the inode for a path name. // Look up and return the inode for a path name.
// If parent != 0, return the inode for the parent and copy the final // If parent != 0, return the inode for the parent and copy the final
// path element into name, which must have room for DIRSIZ bytes. // path element into name, which must have room for DIRSIZ bytes.
// Must be called inside a transaction since it calls iput(). // Must be called inside a transaction since it calls iput().
static struct inode *namex(char *path, int nameiparent, char *name) { static struct inode*
struct inode *ip, *next; namex(char *path, int nameiparent, char *name)
{
struct inode *ip, *next;
if (*path == '/') if(*path == '/')
ip = iget(ROOTDEV, ROOTINO); ip = iget(ROOTDEV, ROOTINO);
else else
ip = idup(myproc()->cwd); ip = idup(myproc()->cwd);
while ((path = skipelem(path, name)) != 0) { while((path = skipelem(path, name)) != 0){
ilock(ip); ilock(ip);
if (ip->type != T_DIR) { if(ip->type != T_DIR){
iunlockput(ip); iunlockput(ip);
return 0; return 0;
} }
if (nameiparent && *path == '\0') { if(nameiparent && *path == '\0'){
// Stop one level early. // Stop one level early.
iunlock(ip); iunlock(ip);
return ip; return ip;
} }
if ((next = dirlookup(ip, name, 0)) == 0) { if((next = dirlookup(ip, name, 0)) == 0){
iunlockput(ip); iunlockput(ip);
return 0; return 0;
} }
iunlockput(ip); iunlockput(ip);
ip = next; ip = next;
} }
if (nameiparent) { if(nameiparent){
iput(ip); iput(ip);
return 0; return 0;
} }
return ip; return ip;
} }
struct inode *namei(char *path) { struct inode*
char name[DIRSIZ]; namei(char *path)
return namex(path, 0, name); {
char name[DIRSIZ];
return namex(path, 0, name);
} }
struct inode *nameiparent(char *path, char *name) { struct inode*
return namex(path, 1, name); nameiparent(char *path, char *name)
{
return namex(path, 1, name);
} }

View File

@ -12,63 +12,71 @@
void freerange(void *pa_start, void *pa_end); void freerange(void *pa_start, void *pa_end);
extern char end[]; // first address after kernel. extern char end[]; // first address after kernel.
// defined by kernel.ld. // defined by kernel.ld.
struct run { struct run {
struct run *next; struct run *next;
}; };
struct { struct {
struct spinlock lock; struct spinlock lock;
struct run *freelist; struct run *freelist;
} kmem; } kmem;
void kinit() { void
initlock(&kmem.lock, "kmem"); kinit()
freerange(end, (void *)PHYSTOP); {
initlock(&kmem.lock, "kmem");
freerange(end, (void*)PHYSTOP);
} }
void freerange(void *pa_start, void *pa_end) { void
char *p; freerange(void *pa_start, void *pa_end)
p = (char *)PGROUNDUP((uint64)pa_start); {
for (; p + PGSIZE <= (char *)pa_end; p += PGSIZE) char *p;
kfree(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, // Free the page of physical memory pointed at by pa,
// which normally should have been returned by a // which normally should have been returned by a
// call to kalloc(). (The exception is when // call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.) // initializing the allocator; see kinit above.)
void kfree(void *pa) { void
struct run *r; kfree(void *pa)
{
struct run *r;
if (((uint64)pa % PGSIZE) != 0 || (char *)pa < end || (uint64)pa >= PHYSTOP) if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree"); panic("kfree");
// Fill with junk to catch dangling refs. // Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE); memset(pa, 1, PGSIZE);
r = (struct run *)pa; r = (struct run*)pa;
acquire(&kmem.lock); acquire(&kmem.lock);
r->next = kmem.freelist; r->next = kmem.freelist;
kmem.freelist = r; kmem.freelist = r;
release(&kmem.lock); release(&kmem.lock);
} }
// Allocate one 4096-byte page of physical memory. // Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use. // Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated. // Returns 0 if the memory cannot be allocated.
void *kalloc(void) { void *
struct run *r; kalloc(void)
{
struct run *r;
acquire(&kmem.lock); acquire(&kmem.lock);
r = kmem.freelist; r = kmem.freelist;
if (r) if(r)
kmem.freelist = r->next; kmem.freelist = r->next;
release(&kmem.lock); release(&kmem.lock);
if (r) if(r)
memset((char *)r, 5, PGSIZE); // fill with junk memset((char*)r, 5, PGSIZE); // fill with junk
return (void *)r; return (void*)r;
} }

View File

@ -33,156 +33,173 @@
// Contents of the header block, used for both the on-disk header block // Contents of the header block, used for both the on-disk header block
// and to keep track in memory of logged block# before commit. // and to keep track in memory of logged block# before commit.
struct logheader { struct logheader {
int n; int n;
int block[LOGSIZE]; int block[LOGSIZE];
}; };
struct log { struct log {
struct spinlock lock; struct spinlock lock;
int start; int start;
int size; int size;
int outstanding; // how many FS sys calls are executing. int outstanding; // how many FS sys calls are executing.
int committing; // in commit(), please wait. int committing; // in commit(), please wait.
int dev; int dev;
struct logheader lh; struct logheader lh;
}; };
struct log log; struct log log;
static void recover_from_log(void); static void recover_from_log(void);
static void commit(); static void commit();
void initlog(int dev, struct superblock *sb) { void
if (sizeof(struct logheader) >= BSIZE) initlog(int dev, struct superblock *sb)
panic("initlog: too big logheader"); {
if (sizeof(struct logheader) >= BSIZE)
panic("initlog: too big logheader");
initlock(&log.lock, "log"); initlock(&log.lock, "log");
log.start = sb->logstart; log.start = sb->logstart;
log.size = sb->nlog; log.size = sb->nlog;
log.dev = dev; log.dev = dev;
recover_from_log(); recover_from_log();
} }
// Copy committed blocks from log to their home location // Copy committed blocks from log to their home location
static void install_trans(int recovering) { static void
int tail; install_trans(int recovering)
{
int tail;
for (tail = 0; tail < log.lh.n; tail++) { for (tail = 0; tail < log.lh.n; tail++) {
struct buf *lbuf = struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
bread(log.dev, log.start + tail + 1); // read log block struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst bwrite(dbuf); // write dst to disk
bwrite(dbuf); // write dst to disk if(recovering == 0)
if (recovering == 0) bunpin(dbuf);
bunpin(dbuf); brelse(lbuf);
brelse(lbuf); brelse(dbuf);
brelse(dbuf); }
}
} }
// Read the log header from disk into the in-memory log header // Read the log header from disk into the in-memory log header
static void read_head(void) { static void
struct buf *buf = bread(log.dev, log.start); read_head(void)
struct logheader *lh = (struct logheader *)(buf->data); {
int i; struct buf *buf = bread(log.dev, log.start);
log.lh.n = lh->n; struct logheader *lh = (struct logheader *) (buf->data);
for (i = 0; i < log.lh.n; i++) { int i;
log.lh.block[i] = lh->block[i]; log.lh.n = lh->n;
} for (i = 0; i < log.lh.n; i++) {
brelse(buf); log.lh.block[i] = lh->block[i];
}
brelse(buf);
} }
// Write in-memory log header to disk. // Write in-memory log header to disk.
// This is the true point at which the // This is the true point at which the
// current transaction commits. // current transaction commits.
static void write_head(void) { static void
struct buf *buf = bread(log.dev, log.start); write_head(void)
struct logheader *hb = (struct logheader *)(buf->data); {
int i; struct buf *buf = bread(log.dev, log.start);
hb->n = log.lh.n; struct logheader *hb = (struct logheader *) (buf->data);
for (i = 0; i < log.lh.n; i++) { int i;
hb->block[i] = log.lh.block[i]; hb->n = log.lh.n;
} for (i = 0; i < log.lh.n; i++) {
bwrite(buf); hb->block[i] = log.lh.block[i];
brelse(buf); }
bwrite(buf);
brelse(buf);
} }
static void recover_from_log(void) { static void
read_head(); recover_from_log(void)
install_trans(1); // if committed, copy from log to disk {
log.lh.n = 0; read_head();
write_head(); // clear the log install_trans(1); // if committed, copy from log to disk
log.lh.n = 0;
write_head(); // clear the log
} }
// called at the start of each FS system call. // called at the start of each FS system call.
void begin_op(void) { void
acquire(&log.lock); begin_op(void)
while (1) { {
if (log.committing) { acquire(&log.lock);
sleep(&log, &log.lock); while(1){
} else if (log.lh.n + (log.outstanding + 1) * MAXOPBLOCKS > LOGSIZE) { if(log.committing){
// this op might exhaust log space; wait for commit. sleep(&log, &log.lock);
sleep(&log, &log.lock); } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
} else { // this op might exhaust log space; wait for commit.
log.outstanding += 1; sleep(&log, &log.lock);
release(&log.lock); } else {
break; log.outstanding += 1;
} release(&log.lock);
} break;
}
}
} }
// called at the end of each FS system call. // called at the end of each FS system call.
// commits if this was the last outstanding operation. // commits if this was the last outstanding operation.
void end_op(void) { void
int do_commit = 0; end_op(void)
{
int do_commit = 0;
acquire(&log.lock); acquire(&log.lock);
log.outstanding -= 1; log.outstanding -= 1;
if (log.committing) if(log.committing)
panic("log.committing"); panic("log.committing");
if (log.outstanding == 0) { if(log.outstanding == 0){
do_commit = 1; do_commit = 1;
log.committing = 1; log.committing = 1;
} else { } else {
// begin_op() may be waiting for log space, // begin_op() may be waiting for log space,
// and decrementing log.outstanding has decreased // and decrementing log.outstanding has decreased
// the amount of reserved space. // the amount of reserved space.
wakeup(&log); wakeup(&log);
} }
release(&log.lock); release(&log.lock);
if (do_commit) { if(do_commit){
// call commit w/o holding locks, since not allowed // call commit w/o holding locks, since not allowed
// to sleep with locks. // to sleep with locks.
commit(); commit();
acquire(&log.lock); acquire(&log.lock);
log.committing = 0; log.committing = 0;
wakeup(&log); wakeup(&log);
release(&log.lock); release(&log.lock);
} }
} }
// Copy modified blocks from cache to log. // Copy modified blocks from cache to log.
static void write_log(void) { static void
int tail; write_log(void)
{
int tail;
for (tail = 0; tail < log.lh.n; tail++) { for (tail = 0; tail < log.lh.n; tail++) {
struct buf *to = bread(log.dev, log.start + tail + 1); // log block struct buf *to = bread(log.dev, log.start+tail+1); // log block
struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
memmove(to->data, from->data, BSIZE); memmove(to->data, from->data, BSIZE);
bwrite(to); // write the log bwrite(to); // write the log
brelse(from); brelse(from);
brelse(to); brelse(to);
} }
} }
static void commit() { static void
if (log.lh.n > 0) { commit()
write_log(); // Write modified blocks from cache to log {
write_head(); // Write header to disk -- the real commit if (log.lh.n > 0) {
install_trans(0); // Now install writes to home locations write_log(); // Write modified blocks from cache to log
log.lh.n = 0; write_head(); // Write header to disk -- the real commit
write_head(); // Erase the transaction from the log install_trans(0); // Now install writes to home locations
} log.lh.n = 0;
write_head(); // Erase the transaction from the log
}
} }
// Caller has modified b->data and is done with the buffer. // Caller has modified b->data and is done with the buffer.
@ -194,23 +211,26 @@ static void commit() {
// modify bp->data[] // modify bp->data[]
// log_write(bp) // log_write(bp)
// brelse(bp) // brelse(bp)
void log_write(struct buf *b) { void
int i; log_write(struct buf *b)
{
int i;
acquire(&log.lock); acquire(&log.lock);
if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
panic("too big a transaction"); panic("too big a transaction");
if (log.outstanding < 1) if (log.outstanding < 1)
panic("log_write outside of trans"); panic("log_write outside of trans");
for (i = 0; i < log.lh.n; i++) { for (i = 0; i < log.lh.n; i++) {
if (log.lh.block[i] == b->blockno) // log absorption if (log.lh.block[i] == b->blockno) // log absorption
break; break;
} }
log.lh.block[i] = b->blockno; log.lh.block[i] = b->blockno;
if (i == log.lh.n) { // Add new block to log? if (i == log.lh.n) { // Add new block to log?
bpin(b); bpin(b);
log.lh.n++; log.lh.n++;
} }
release(&log.lock); release(&log.lock);
} }

View File

@ -3,46 +3,43 @@
#include "memlayout.h" #include "memlayout.h"
#include "riscv.h" #include "riscv.h"
#include "defs.h" #include "defs.h"
#include <stdint.h>
extern int32_t add(int32_t right, int32_t left);
volatile static int started = 0; volatile static int started = 0;
// start() jumps here in supervisor mode on all CPUs. // start() jumps here in supervisor mode on all CPUs.
void main() { void
if (cpuid() == 0) { main()
consoleinit(); {
printfinit(); if(cpuid() == 0){
printf("\n"); consoleinit();
printf("xv6 kernel is booting\n"); printfinit();
printf("\n"); printf("\n");
printf("rust library call: add(1, 2) = %d\n", add(1, 2)); printf("xv6 kernel is booting\n");
printf("\n"); printf("\n");
kinit(); // physical page allocator kinit(); // physical page allocator
kvminit(); // create kernel page table kvminit(); // create kernel page table
kvminithart(); // turn on paging kvminithart(); // turn on paging
procinit(); // process table procinit(); // process table
trapinit(); // trap vectors trapinit(); // trap vectors
trapinithart(); // install kernel trap vector trapinithart(); // install kernel trap vector
plicinit(); // set up interrupt controller plicinit(); // set up interrupt controller
plicinithart(); // ask PLIC for device interrupts plicinithart(); // ask PLIC for device interrupts
binit(); // buffer cache binit(); // buffer cache
iinit(); // inode table iinit(); // inode table
fileinit(); // file table fileinit(); // file table
virtio_disk_init(); // emulated hard disk virtio_disk_init(); // emulated hard disk
userinit(); // first user process userinit(); // first user process
__sync_synchronize(); __sync_synchronize();
started = 1; started = 1;
} else { } else {
while (started == 0) while(started == 0)
; ;
__sync_synchronize(); __sync_synchronize();
printf("hart %d starting\n", cpuid()); printf("hart %d starting\n", cpuid());
kvminithart(); // turn on paging kvminithart(); // turn on paging
trapinithart(); // install kernel trap vector trapinithart(); // install kernel trap vector
plicinithart(); // ask PLIC for device interrupts plicinithart(); // ask PLIC for device interrupts
} }
scheduler(); scheduler();
} }

View File

@ -11,112 +11,120 @@
#define PIPESIZE 512 #define PIPESIZE 512
struct pipe { struct pipe {
struct spinlock lock; struct spinlock lock;
char data[PIPESIZE]; char data[PIPESIZE];
uint nread; // number of bytes read uint nread; // number of bytes read
uint nwrite; // number of bytes written uint nwrite; // number of bytes written
int readopen; // read fd is still open int readopen; // read fd is still open
int writeopen; // write fd is still open int writeopen; // write fd is still open
}; };
int pipealloc(struct file **f0, struct file **f1) { int
struct pipe *pi; pipealloc(struct file **f0, struct file **f1)
{
struct pipe *pi;
pi = 0; pi = 0;
*f0 = *f1 = 0; *f0 = *f1 = 0;
if ((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
goto bad; goto bad;
if ((pi = (struct pipe *)kalloc()) == 0) if((pi = (struct pipe*)kalloc()) == 0)
goto bad; goto bad;
pi->readopen = 1; pi->readopen = 1;
pi->writeopen = 1; pi->writeopen = 1;
pi->nwrite = 0; pi->nwrite = 0;
pi->nread = 0; pi->nread = 0;
initlock(&pi->lock, "pipe"); initlock(&pi->lock, "pipe");
(*f0)->type = FD_PIPE; (*f0)->type = FD_PIPE;
(*f0)->readable = 1; (*f0)->readable = 1;
(*f0)->writable = 0; (*f0)->writable = 0;
(*f0)->pipe = pi; (*f0)->pipe = pi;
(*f1)->type = FD_PIPE; (*f1)->type = FD_PIPE;
(*f1)->readable = 0; (*f1)->readable = 0;
(*f1)->writable = 1; (*f1)->writable = 1;
(*f1)->pipe = pi; (*f1)->pipe = pi;
return 0; return 0;
bad: bad:
if (pi) if(pi)
kfree((char *)pi); kfree((char*)pi);
if (*f0) if(*f0)
fileclose(*f0); fileclose(*f0);
if (*f1) if(*f1)
fileclose(*f1); fileclose(*f1);
return -1; return -1;
} }
void pipeclose(struct pipe *pi, int writable) { void
acquire(&pi->lock); pipeclose(struct pipe *pi, int writable)
if (writable) { {
pi->writeopen = 0; acquire(&pi->lock);
wakeup(&pi->nread); if(writable){
} else { pi->writeopen = 0;
pi->readopen = 0; wakeup(&pi->nread);
wakeup(&pi->nwrite); } else {
} pi->readopen = 0;
if (pi->readopen == 0 && pi->writeopen == 0) { wakeup(&pi->nwrite);
release(&pi->lock); }
kfree((char *)pi); if(pi->readopen == 0 && pi->writeopen == 0){
} else release(&pi->lock);
release(&pi->lock); kfree((char*)pi);
} else
release(&pi->lock);
} }
int pipewrite(struct pipe *pi, uint64 addr, int n) { int
int i = 0; pipewrite(struct pipe *pi, uint64 addr, int n)
struct proc *pr = myproc(); {
int i = 0;
struct proc *pr = myproc();
acquire(&pi->lock); acquire(&pi->lock);
while (i < n) { while(i < n){
if (pi->readopen == 0 || killed(pr)) { if(pi->readopen == 0 || killed(pr)){
release(&pi->lock); release(&pi->lock);
return -1; return -1;
} }
if (pi->nwrite == pi->nread + PIPESIZE) { // DOC: pipewrite-full if(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full
wakeup(&pi->nread); wakeup(&pi->nread);
sleep(&pi->nwrite, &pi->lock); sleep(&pi->nwrite, &pi->lock);
} else { } else {
char ch; char ch;
if (copyin(pr->pagetable, &ch, addr + i, 1) == -1) if(copyin(pr->pagetable, &ch, addr + i, 1) == -1)
break; break;
pi->data[pi->nwrite++ % PIPESIZE] = ch; pi->data[pi->nwrite++ % PIPESIZE] = ch;
i++; i++;
} }
} }
wakeup(&pi->nread); wakeup(&pi->nread);
release(&pi->lock); release(&pi->lock);
return i; return i;
} }
int piperead(struct pipe *pi, uint64 addr, int n) { int
int i; piperead(struct pipe *pi, uint64 addr, int n)
struct proc *pr = myproc(); {
char ch; int i;
struct proc *pr = myproc();
char ch;
acquire(&pi->lock); acquire(&pi->lock);
while (pi->nread == pi->nwrite && pi->writeopen) { // DOC: pipe-empty while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty
if (killed(pr)) { if(killed(pr)){
release(&pi->lock); release(&pi->lock);
return -1; return -1;
} }
sleep(&pi->nread, &pi->lock); // DOC: piperead-sleep sleep(&pi->nread, &pi->lock); //DOC: piperead-sleep
} }
for (i = 0; i < n; i++) { // DOC: piperead-copy for(i = 0; i < n; i++){ //DOC: piperead-copy
if (pi->nread == pi->nwrite) if(pi->nread == pi->nwrite)
break; break;
ch = pi->data[pi->nread++ % PIPESIZE]; ch = pi->data[pi->nread++ % PIPESIZE];
if (copyout(pr->pagetable, addr + i, &ch, 1) == -1) if(copyout(pr->pagetable, addr + i, &ch, 1) == -1)
break; break;
} }
wakeup(&pi->nwrite); // DOC: piperead-wakeup wakeup(&pi->nwrite); //DOC: piperead-wakeup
release(&pi->lock); release(&pi->lock);
return i; return i;
} }

View File

@ -8,32 +8,40 @@
// the riscv Platform Level Interrupt Controller (PLIC). // the riscv Platform Level Interrupt Controller (PLIC).
// //
void plicinit(void) { void
// set desired IRQ priorities non-zero (otherwise disabled). plicinit(void)
*(uint32 *)(PLIC + UART0_IRQ * 4) = 1; {
*(uint32 *)(PLIC + VIRTIO0_IRQ * 4) = 1; // set desired IRQ priorities non-zero (otherwise disabled).
*(uint32*)(PLIC + UART0_IRQ*4) = 1;
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
} }
void plicinithart(void) { void
int hart = cpuid(); 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 enable bits for this hart's S-mode // set this hart's S-mode priority threshold to 0.
// for the uart and virtio disk. *(uint32*)PLIC_SPRIORITY(hart) = 0;
*(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. // ask the PLIC what interrupt we should serve.
int plic_claim(void) { int
int hart = cpuid(); plic_claim(void)
int irq = *(uint32 *)PLIC_SCLAIM(hart); {
return irq; int hart = cpuid();
int irq = *(uint32*)PLIC_SCLAIM(hart);
return irq;
} }
// tell the PLIC we've served this IRQ. // tell the PLIC we've served this IRQ.
void plic_complete(int irq) { void
int hart = cpuid(); plic_complete(int irq)
*(uint32 *)PLIC_SCLAIM(hart) = irq; {
int hart = cpuid();
*(uint32*)PLIC_SCLAIM(hart) = irq;
} }

View File

@ -19,107 +19,117 @@ volatile int panicked = 0;
// lock to avoid interleaving concurrent printf's. // lock to avoid interleaving concurrent printf's.
static struct { static struct {
struct spinlock lock; struct spinlock lock;
int locking; int locking;
} pr; } pr;
static char digits[] = "0123456789abcdef"; static char digits[] = "0123456789abcdef";
static void printint(int xx, int base, int sign) { static void
char buf[16]; printint(int xx, int base, int sign)
int i; {
uint x; char buf[16];
int i;
uint x;
if (sign && (sign = xx < 0)) if(sign && (sign = xx < 0))
x = -xx; x = -xx;
else else
x = xx; x = xx;
i = 0; i = 0;
do { do {
buf[i++] = digits[x % base]; buf[i++] = digits[x % base];
} while ((x /= base) != 0); } while((x /= base) != 0);
if (sign) if(sign)
buf[i++] = '-'; buf[i++] = '-';
while (--i >= 0) while(--i >= 0)
consputc(buf[i]); consputc(buf[i]);
} }
static void printptr(uint64 x) { static void
int i; printptr(uint64 x)
consputc('0'); {
consputc('x'); int i;
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) consputc('0');
consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); consputc('x');
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
consputc(digits[x >> (sizeof(uint64) * 8 - 4)]);
} }
// Print to the console. only understands %d, %x, %p, %s. // Print to the console. only understands %d, %x, %p, %s.
void printf(char *fmt, ...) { void
va_list ap; printf(char *fmt, ...)
int i, c, locking; {
char *s; va_list ap;
int i, c, locking;
char *s;
locking = pr.locking; locking = pr.locking;
if (locking) if(locking)
acquire(&pr.lock); acquire(&pr.lock);
if (fmt == 0) if (fmt == 0)
panic("null fmt"); panic("null fmt");
va_start(ap, fmt); va_start(ap, fmt);
for (i = 0; (c = fmt[i] & 0xff) != 0; i++) { for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
if (c != '%') { if(c != '%'){
consputc(c); consputc(c);
continue; continue;
} }
c = fmt[++i] & 0xff; c = fmt[++i] & 0xff;
if (c == 0) if(c == 0)
break; break;
switch (c) { switch(c){
case 'd': case 'd':
printint(va_arg(ap, int), 10, 1); printint(va_arg(ap, int), 10, 1);
break; break;
case 'x': case 'x':
printint(va_arg(ap, int), 16, 1); printint(va_arg(ap, int), 16, 1);
break; break;
case 'p': case 'p':
printptr(va_arg(ap, uint64)); printptr(va_arg(ap, uint64));
break; break;
case 's': case 's':
if ((s = va_arg(ap, char *)) == 0) if((s = va_arg(ap, char*)) == 0)
s = "(null)"; s = "(null)";
for (; *s; s++) for(; *s; s++)
consputc(*s); consputc(*s);
break; break;
case '%': case '%':
consputc('%'); consputc('%');
break; break;
default: default:
// Print unknown % sequence to draw attention. // Print unknown % sequence to draw attention.
consputc('%'); consputc('%');
consputc(c); consputc(c);
break; break;
} }
} }
va_end(ap); va_end(ap);
if (locking) if(locking)
release(&pr.lock); release(&pr.lock);
} }
void panic(char *s) { void
pr.locking = 0; panic(char *s)
printf("panic: "); {
printf(s); pr.locking = 0;
printf("\n"); printf("panic: ");
panicked = 1; // freeze uart output from other CPUs printf(s);
for (;;) printf("\n");
; panicked = 1; // freeze uart output from other CPUs
for(;;)
;
} }
void printfinit(void) { void
initlock(&pr.lock, "pr"); printfinit(void)
pr.locking = 1; {
initlock(&pr.lock, "pr");
pr.locking = 1;
} }

File diff suppressed because it is too large Load Diff

View File

@ -12,29 +12,34 @@
#include "fs.h" #include "fs.h"
#include "buf.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. // 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. // Else if B_VALID is not set, read buf from disk, set B_VALID.
void ramdiskrw(struct buf *b) { void
if (!holdingsleep(&b->lock)) ramdiskrw(struct buf *b)
panic("ramdiskrw: buf not locked"); {
if ((b->flags & (B_VALID | B_DIRTY)) == B_VALID) if(!holdingsleep(&b->lock))
panic("ramdiskrw: nothing to do"); panic("ramdiskrw: buf not locked");
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
panic("ramdiskrw: nothing to do");
if (b->blockno >= FSSIZE) if(b->blockno >= FSSIZE)
panic("ramdiskrw: blockno too big"); panic("ramdiskrw: blockno too big");
uint64 diskaddr = b->blockno * BSIZE; uint64 diskaddr = b->blockno * BSIZE;
char *addr = (char *)RAMDISK + diskaddr; char *addr = (char *)RAMDISK + diskaddr;
if (b->flags & B_DIRTY) { if(b->flags & B_DIRTY){
// write // write
memmove(addr, b->data, BSIZE); memmove(addr, b->data, BSIZE);
b->flags &= ~B_DIRTY; b->flags &= ~B_DIRTY;
} else { } else {
// read // read
memmove(b->data, addr, BSIZE); memmove(b->data, addr, BSIZE);
b->flags |= B_VALID; b->flags |= B_VALID;
} }
} }

View File

@ -9,36 +9,47 @@
#include "proc.h" #include "proc.h"
#include "sleeplock.h" #include "sleeplock.h"
void initsleeplock(struct sleeplock *lk, char *name) { void
initlock(&lk->lk, "sleep lock"); initsleeplock(struct sleeplock *lk, char *name)
lk->name = name; {
lk->locked = 0; initlock(&lk->lk, "sleep lock");
lk->pid = 0; lk->name = name;
lk->locked = 0;
lk->pid = 0;
} }
void acquiresleep(struct sleeplock *lk) { void
acquire(&lk->lk); acquiresleep(struct sleeplock *lk)
while (lk->locked) { {
sleep(lk, &lk->lk); acquire(&lk->lk);
} while (lk->locked) {
lk->locked = 1; sleep(lk, &lk->lk);
lk->pid = myproc()->pid; }
release(&lk->lk); lk->locked = 1;
lk->pid = myproc()->pid;
release(&lk->lk);
} }
void releasesleep(struct sleeplock *lk) { void
acquire(&lk->lk); releasesleep(struct sleeplock *lk)
lk->locked = 0; {
lk->pid = 0; acquire(&lk->lk);
wakeup(lk); lk->locked = 0;
release(&lk->lk); lk->pid = 0;
wakeup(lk);
release(&lk->lk);
} }
int holdingsleep(struct sleeplock *lk) { int
int r; holdingsleep(struct sleeplock *lk)
{
acquire(&lk->lk); int r;
r = lk->locked && (lk->pid == myproc()->pid);
release(&lk->lk); acquire(&lk->lk);
return r; r = lk->locked && (lk->pid == myproc()->pid);
release(&lk->lk);
return r;
} }

View File

@ -8,91 +8,103 @@
#include "proc.h" #include "proc.h"
#include "defs.h" #include "defs.h"
void initlock(struct spinlock *lk, char *name) { void
lk->name = name; initlock(struct spinlock *lk, char *name)
lk->locked = 0; {
lk->cpu = 0; lk->name = name;
lk->locked = 0;
lk->cpu = 0;
} }
// Acquire the lock. // Acquire the lock.
// Loops (spins) until the lock is acquired. // Loops (spins) until the lock is acquired.
void acquire(struct spinlock *lk) { void
push_off(); // disable interrupts to avoid deadlock. acquire(struct spinlock *lk)
if (holding(lk)) {
panic("acquire"); push_off(); // disable interrupts to avoid deadlock.
if(holding(lk))
panic("acquire");
// On RISC-V, sync_lock_test_and_set turns into an atomic swap: // On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1 // a5 = 1
// s1 = &lk->locked // s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1) // amoswap.w.aq a5, a5, (s1)
while (__sync_lock_test_and_set(&lk->locked, 1) != 0) while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
; ;
// Tell the C compiler and the processor to not move loads or stores // Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory // past this point, to ensure that the critical section's memory
// references happen strictly after the lock is acquired. // references happen strictly after the lock is acquired.
// On RISC-V, this emits a fence instruction. // On RISC-V, this emits a fence instruction.
__sync_synchronize(); __sync_synchronize();
// Record info about lock acquisition for holding() and debugging. // Record info about lock acquisition for holding() and debugging.
lk->cpu = mycpu(); lk->cpu = mycpu();
} }
// Release the lock. // Release the lock.
void release(struct spinlock *lk) { void
if (!holding(lk)) release(struct spinlock *lk)
panic("release"); {
if(!holding(lk))
panic("release");
lk->cpu = 0; lk->cpu = 0;
// Tell the C compiler and the CPU to not move loads or stores // Tell the C compiler and the CPU to not move loads or stores
// past this point, to ensure that all the stores in the critical // past this point, to ensure that all the stores in the critical
// section are visible to other CPUs before the lock is released, // section are visible to other CPUs before the lock is released,
// and that loads in the critical section occur strictly before // and that loads in the critical section occur strictly before
// the lock is released. // the lock is released.
// On RISC-V, this emits a fence instruction. // On RISC-V, this emits a fence instruction.
__sync_synchronize(); __sync_synchronize();
// Release the lock, equivalent to lk->locked = 0. // Release the lock, equivalent to lk->locked = 0.
// This code doesn't use a C assignment, since the C standard // This code doesn't use a C assignment, since the C standard
// implies that an assignment might be implemented with // implies that an assignment might be implemented with
// multiple store instructions. // multiple store instructions.
// On RISC-V, sync_lock_release turns into an atomic swap: // On RISC-V, sync_lock_release turns into an atomic swap:
// s1 = &lk->locked // s1 = &lk->locked
// amoswap.w zero, zero, (s1) // amoswap.w zero, zero, (s1)
__sync_lock_release(&lk->locked); __sync_lock_release(&lk->locked);
pop_off(); pop_off();
} }
// Check whether this cpu is holding the lock. // Check whether this cpu is holding the lock.
// Interrupts must be off. // Interrupts must be off.
int holding(struct spinlock *lk) { int
int r; holding(struct spinlock *lk)
r = (lk->locked && lk->cpu == mycpu()); {
return r; int r;
r = (lk->locked && lk->cpu == mycpu());
return r;
} }
// push_off/pop_off are like intr_off()/intr_on() except that they are matched: // push_off/pop_off are like intr_off()/intr_on() except that they are matched:
// it takes two pop_off()s to undo two push_off()s. Also, if interrupts // it takes two pop_off()s to undo two push_off()s. Also, if interrupts
// are initially off, then push_off, pop_off leaves them off. // are initially off, then push_off, pop_off leaves them off.
void push_off(void) { void
int old = intr_get(); push_off(void)
{
int old = intr_get();
intr_off(); intr_off();
if (mycpu()->noff == 0) if(mycpu()->noff == 0)
mycpu()->intena = old; mycpu()->intena = old;
mycpu()->noff += 1; mycpu()->noff += 1;
} }
void pop_off(void) { void
struct cpu *c = mycpu(); pop_off(void)
if (intr_get()) {
panic("pop_off - interruptible"); struct cpu *c = mycpu();
if (c->noff < 1) if(intr_get())
panic("pop_off"); panic("pop_off - interruptible");
c->noff -= 1; if(c->noff < 1)
if (c->noff == 0 && c->intena) panic("pop_off");
intr_on(); c->noff -= 1;
if(c->noff == 0 && c->intena)
intr_on();
} }

View File

@ -8,7 +8,7 @@ void main();
void timerinit(); void timerinit();
// entry.S needs one stack per CPU. // entry.S needs one stack per CPU.
__attribute__((aligned(16))) char stack0[4096 * NCPU]; __attribute__ ((aligned (16))) char stack0[4096 * NCPU];
// a scratch area per CPU for machine-mode timer interrupts. // a scratch area per CPU for machine-mode timer interrupts.
uint64 timer_scratch[NCPU][5]; uint64 timer_scratch[NCPU][5];
@ -17,39 +17,41 @@ uint64 timer_scratch[NCPU][5];
extern void timervec(); extern void timervec();
// entry.S jumps here in machine mode on stack0. // entry.S jumps here in machine mode on stack0.
void start() { void
// set M Previous Privilege mode to Supervisor, for mret. start()
unsigned long x = r_mstatus(); {
x &= ~MSTATUS_MPP_MASK; // set M Previous Privilege mode to Supervisor, for mret.
x |= MSTATUS_MPP_S; unsigned long x = r_mstatus();
w_mstatus(x); x &= ~MSTATUS_MPP_MASK;
x |= MSTATUS_MPP_S;
w_mstatus(x);
// set M Exception Program Counter to main, for mret. // set M Exception Program Counter to main, for mret.
// requires gcc -mcmodel=medany // requires gcc -mcmodel=medany
w_mepc((uint64)main); w_mepc((uint64)main);
// disable paging for now. // disable paging for now.
w_satp(0); w_satp(0);
// delegate all interrupts and exceptions to supervisor mode. // delegate all interrupts and exceptions to supervisor mode.
w_medeleg(0xffff); w_medeleg(0xffff);
w_mideleg(0xffff); w_mideleg(0xffff);
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
// configure Physical Memory Protection to give supervisor mode // configure Physical Memory Protection to give supervisor mode
// access to all of physical memory. // access to all of physical memory.
w_pmpaddr0(0x3fffffffffffffull); w_pmpaddr0(0x3fffffffffffffull);
w_pmpcfg0(0xf); w_pmpcfg0(0xf);
// ask for clock interrupts. // ask for clock interrupts.
timerinit(); timerinit();
// keep each CPU's hartid in its tp register, for cpuid(). // keep each CPU's hartid in its tp register, for cpuid().
int id = r_mhartid(); int id = r_mhartid();
w_tp(id); w_tp(id);
// switch to supervisor mode and jump to main(). // switch to supervisor mode and jump to main().
asm volatile("mret"); asm volatile("mret");
} }
// arrange to receive timer interrupts. // arrange to receive timer interrupts.
@ -57,29 +59,31 @@ void start() {
// at timervec in kernelvec.S, // at timervec in kernelvec.S,
// which turns them into software interrupts for // which turns them into software interrupts for
// devintr() in trap.c. // devintr() in trap.c.
void timerinit() { void
// each CPU has a separate source of timer interrupts. timerinit()
int id = r_mhartid(); {
// each CPU has a separate source of timer interrupts.
int id = r_mhartid();
// ask the CLINT for a timer interrupt. // ask the CLINT for a timer interrupt.
int interval = 1000000; // cycles; about 1/10th second in qemu. int interval = 1000000; // cycles; about 1/10th second in qemu.
*(uint64 *)CLINT_MTIMECMP(id) = *(uint64 *)CLINT_MTIME + interval; *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;
// prepare information in scratch[] for timervec. // prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers. // scratch[0..2] : space for timervec to save registers.
// scratch[3] : address of CLINT MTIMECMP register. // scratch[3] : address of CLINT MTIMECMP register.
// scratch[4] : desired interval (in cycles) between timer interrupts. // scratch[4] : desired interval (in cycles) between timer interrupts.
uint64 *scratch = &timer_scratch[id][0]; uint64 *scratch = &timer_scratch[id][0];
scratch[3] = CLINT_MTIMECMP(id); scratch[3] = CLINT_MTIMECMP(id);
scratch[4] = interval; scratch[4] = interval;
w_mscratch((uint64)scratch); w_mscratch((uint64)scratch);
// set the machine-mode trap handler. // set the machine-mode trap handler.
w_mtvec((uint64)timervec); w_mtvec((uint64)timervec);
// enable machine-mode interrupts. // enable machine-mode interrupts.
w_mstatus(r_mstatus() | MSTATUS_MIE); w_mstatus(r_mstatus() | MSTATUS_MIE);
// enable machine-mode timer interrupts. // enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE); w_mie(r_mie() | MIE_MTIE);
} }

View File

@ -1,90 +1,107 @@
#include "types.h" #include "types.h"
void *memset(void *dst, int c, uint n) { void*
char *cdst = (char *)dst; memset(void *dst, int c, uint n)
int i; {
for (i = 0; i < n; i++) { char *cdst = (char *) dst;
cdst[i] = c; int i;
} for(i = 0; i < n; i++){
return dst; cdst[i] = c;
}
return dst;
} }
int memcmp(const void *v1, const void *v2, uint n) { int
const uchar *s1, *s2; memcmp(const void *v1, const void *v2, uint n)
{
const uchar *s1, *s2;
s1 = v1; s1 = v1;
s2 = v2; s2 = v2;
while (n-- > 0) { while(n-- > 0){
if (*s1 != *s2) if(*s1 != *s2)
return *s1 - *s2; return *s1 - *s2;
s1++, s2++; s1++, s2++;
} }
return 0; return 0;
} }
void *memmove(void *dst, const void *src, uint n) { void*
const char *s; memmove(void *dst, const void *src, uint n)
char *d; {
const char *s;
char *d;
if (n == 0) if(n == 0)
return dst; 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++;
s = src; return dst;
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. // memcpy exists to placate GCC. Use memmove.
void *memcpy(void *dst, const void *src, uint n) { void*
return memmove(dst, src, n); memcpy(void *dst, const void *src, uint n)
{
return memmove(dst, src, n);
} }
int strncmp(const char *p, const char *q, uint n) { int
while (n > 0 && *p && *p == *q) strncmp(const char *p, const char *q, uint n)
n--, p++, q++; {
if (n == 0) while(n > 0 && *p && *p == *q)
return 0; n--, p++, q++;
return (uchar)*p - (uchar)*q; if(n == 0)
return 0;
return (uchar)*p - (uchar)*q;
} }
char *strncpy(char *s, const char *t, int n) { char*
char *os; strncpy(char *s, const char *t, int n)
{
char *os;
os = s; os = s;
while (n-- > 0 && (*s++ = *t++) != 0) while(n-- > 0 && (*s++ = *t++) != 0)
; ;
while (n-- > 0) while(n-- > 0)
*s++ = 0; *s++ = 0;
return os; return os;
} }
// Like strncpy but guaranteed to NUL-terminate. // Like strncpy but guaranteed to NUL-terminate.
char *safestrcpy(char *s, const char *t, int n) { char*
char *os; safestrcpy(char *s, const char *t, int n)
{
char *os;
os = s; os = s;
if (n <= 0) if(n <= 0)
return os; return os;
while (--n > 0 && (*s++ = *t++) != 0) while(--n > 0 && (*s++ = *t++) != 0)
; ;
*s = 0; *s = 0;
return os; return os;
} }
int strlen(const char *s) { int
int n; strlen(const char *s)
{
int n;
for (n = 0; s[n]; n++) for(n = 0; s[n]; n++)
; ;
return n; return n;
} }

View File

@ -8,63 +8,77 @@
#include "defs.h" #include "defs.h"
// Fetch the uint64 at addr from the current process. // Fetch the uint64 at addr from the current process.
int fetchaddr(uint64 addr, uint64 *ip) { int
struct proc *p = myproc(); fetchaddr(uint64 addr, uint64 *ip)
if (addr >= p->sz || {
addr + sizeof(uint64) > p->sz) // both tests needed, in case of overflow struct proc *p = myproc();
return -1; if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow
if (copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) return -1;
return -1; if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
return 0; return -1;
return 0;
} }
// Fetch the nul-terminated string at addr from the current process. // Fetch the nul-terminated string at addr from the current process.
// Returns length of string, not including nul, or -1 for error. // Returns length of string, not including nul, or -1 for error.
int fetchstr(uint64 addr, char *buf, int max) { int
struct proc *p = myproc(); fetchstr(uint64 addr, char *buf, int max)
if (copyinstr(p->pagetable, buf, addr, max) < 0) {
return -1; struct proc *p = myproc();
return strlen(buf); if(copyinstr(p->pagetable, buf, addr, max) < 0)
return -1;
return strlen(buf);
} }
static uint64 argraw(int n) { static uint64
struct proc *p = myproc(); argraw(int n)
switch (n) { {
case 0: struct proc *p = myproc();
return p->trapframe->a0; switch (n) {
case 1: case 0:
return p->trapframe->a1; return p->trapframe->a0;
case 2: case 1:
return p->trapframe->a2; return p->trapframe->a1;
case 3: case 2:
return p->trapframe->a3; return p->trapframe->a2;
case 4: case 3:
return p->trapframe->a4; return p->trapframe->a3;
case 5: case 4:
return p->trapframe->a5; return p->trapframe->a4;
} case 5:
panic("argraw"); return p->trapframe->a5;
return -1; }
panic("argraw");
return -1;
} }
// Fetch the nth 32-bit system call argument. // Fetch the nth 32-bit system call argument.
void argint(int n, int *ip) { *ip = argraw(n); } void
argint(int n, int *ip)
{
*ip = argraw(n);
}
// Retrieve an argument as a pointer. // Retrieve an argument as a pointer.
// Doesn't check for legality, since // Doesn't check for legality, since
// copyin/copyout will do that. // copyin/copyout will do that.
void argaddr(int n, uint64 *ip) { *ip = argraw(n); } void
argaddr(int n, uint64 *ip)
{
*ip = argraw(n);
}
// Fetch the nth word-sized system call argument as a null-terminated string. // Fetch the nth word-sized system call argument as a null-terminated string.
// Copies into buf, at most max. // Copies into buf, at most max.
// Returns string length if OK (including nul), -1 if error. // Returns string length if OK (including nul), -1 if error.
int argstr(int n, char *buf, int max) { int
uint64 addr; argstr(int n, char *buf, int max)
argaddr(n, &addr); {
return fetchstr(addr, buf, 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_fork(void);
extern uint64 sys_exit(void); extern uint64 sys_exit(void);
extern uint64 sys_wait(void); extern uint64 sys_wait(void);
@ -87,29 +101,42 @@ extern uint64 sys_link(void);
extern uint64 sys_mkdir(void); extern uint64 sys_mkdir(void);
extern uint64 sys_close(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) = { static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork, [SYS_exit] sys_exit, [SYS_wait] sys_wait, [SYS_fork] sys_fork,
[SYS_pipe] sys_pipe, [SYS_read] sys_read, [SYS_kill] sys_kill, [SYS_exit] sys_exit,
[SYS_exec] sys_exec, [SYS_fstat] sys_fstat, [SYS_chdir] sys_chdir, [SYS_wait] sys_wait,
[SYS_dup] sys_dup, [SYS_getpid] sys_getpid, [SYS_sbrk] sys_sbrk, [SYS_pipe] sys_pipe,
[SYS_sleep] sys_sleep, [SYS_uptime] sys_uptime, [SYS_open] sys_open, [SYS_read] sys_read,
[SYS_write] sys_write, [SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, [SYS_kill] sys_kill,
[SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, [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) { void
int num; syscall(void)
struct proc *p = myproc(); {
int num;
struct proc *p = myproc();
num = p->trapframe->a7; num = p->trapframe->a7;
if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
// Use num to lookup the system call function for num, call it, p->trapframe->a0 = syscalls[num]();
// and store its return value in p->trapframe->a0 } else {
p->trapframe->a0 = syscalls[num](); printf("%d %s: unknown sys call %d\n",
} else { p->pid, p->name, num);
printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); p->trapframe->a0 = -1;
p->trapframe->a0 = -1; }
}
} }

View File

@ -18,455 +18,488 @@
// Fetch the nth word-sized system call argument as a file descriptor // Fetch the nth word-sized system call argument as a file descriptor
// and return both the descriptor and the corresponding struct file. // and return both the descriptor and the corresponding struct file.
static int argfd(int n, int *pfd, struct file **pf) { static int
int fd; argfd(int n, int *pfd, struct file **pf)
struct file *f; {
int fd;
struct file *f;
argint(n, &fd); argint(n, &fd);
if (fd < 0 || fd >= NOFILE || (f = myproc()->ofile[fd]) == 0) if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0)
return -1; return -1;
if (pfd) if(pfd)
*pfd = fd; *pfd = fd;
if (pf) if(pf)
*pf = f; *pf = f;
return 0; return 0;
} }
// Allocate a file descriptor for the given file. // Allocate a file descriptor for the given file.
// Takes over file reference from caller on success. // Takes over file reference from caller on success.
static int fdalloc(struct file *f) { static int
int fd; fdalloc(struct file *f)
struct proc *p = myproc(); {
int fd;
struct proc *p = myproc();
for (fd = 0; fd < NOFILE; fd++) { for(fd = 0; fd < NOFILE; fd++){
if (p->ofile[fd] == 0) { if(p->ofile[fd] == 0){
p->ofile[fd] = f; p->ofile[fd] = f;
return fd; return fd;
} }
} }
return -1; return -1;
} }
uint64 sys_dup(void) { uint64
struct file *f; sys_dup(void)
int fd; {
struct file *f;
int fd;
if (argfd(0, 0, &f) < 0) if(argfd(0, 0, &f) < 0)
return -1; return -1;
if ((fd = fdalloc(f)) < 0) if((fd=fdalloc(f)) < 0)
return -1; return -1;
filedup(f); filedup(f);
return fd; return fd;
} }
uint64 sys_read(void) { uint64
struct file *f; sys_read(void)
int n; {
uint64 p; struct file *f;
int n;
uint64 p;
argaddr(1, &p); argaddr(1, &p);
argint(2, &n); argint(2, &n);
if (argfd(0, 0, &f) < 0) if(argfd(0, 0, &f) < 0)
return -1; return -1;
return fileread(f, p, n); return fileread(f, p, n);
} }
uint64 sys_write(void) { uint64
struct file *f; sys_write(void)
int n; {
uint64 p; struct file *f;
int n;
uint64 p;
argaddr(1, &p);
argint(2, &n);
if(argfd(0, 0, &f) < 0)
return -1;
argaddr(1, &p); return filewrite(f, p, n);
argint(2, &n);
if (argfd(0, 0, &f) < 0)
return -1;
return filewrite(f, p, n);
} }
uint64 sys_close(void) { uint64
int fd; sys_close(void)
struct file *f; {
int fd;
struct file *f;
if (argfd(0, &fd, &f) < 0) if(argfd(0, &fd, &f) < 0)
return -1; return -1;
myproc()->ofile[fd] = 0; myproc()->ofile[fd] = 0;
fileclose(f); fileclose(f);
return 0; return 0;
} }
uint64 sys_fstat(void) { uint64
struct file *f; sys_fstat(void)
uint64 st; // user pointer to struct stat {
struct file *f;
uint64 st; // user pointer to struct stat
argaddr(1, &st); argaddr(1, &st);
if (argfd(0, 0, &f) < 0) if(argfd(0, 0, &f) < 0)
return -1; return -1;
return filestat(f, st); return filestat(f, st);
} }
// Create the path new as a link to the same inode as old. // Create the path new as a link to the same inode as old.
uint64 sys_link(void) { uint64
char name[DIRSIZ], new[MAXPATH], old[MAXPATH]; sys_link(void)
struct inode *dp, *ip; {
char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
struct inode *dp, *ip;
if (argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0) if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)
return -1; return -1;
begin_op(); begin_op();
if ((ip = namei(old)) == 0) { if((ip = namei(old)) == 0){
end_op(); end_op();
return -1; return -1;
} }
ilock(ip); ilock(ip);
if (ip->type == T_DIR) { if(ip->type == T_DIR){
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return -1; return -1;
} }
ip->nlink++; ip->nlink++;
iupdate(ip); iupdate(ip);
iunlock(ip); iunlock(ip);
if ((dp = nameiparent(new, name)) == 0) if((dp = nameiparent(new, name)) == 0)
goto bad; goto bad;
ilock(dp); ilock(dp);
if (dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0) { if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
iunlockput(dp); iunlockput(dp);
goto bad; goto bad;
} }
iunlockput(dp); iunlockput(dp);
iput(ip); iput(ip);
end_op(); end_op();
return 0; return 0;
bad: bad:
ilock(ip); ilock(ip);
ip->nlink--; ip->nlink--;
iupdate(ip); iupdate(ip);
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return -1; return -1;
} }
// Is the directory dp empty except for "." and ".." ? // Is the directory dp empty except for "." and ".." ?
static int isdirempty(struct inode *dp) { static int
int off; isdirempty(struct inode *dp)
struct dirent de; {
int off;
struct dirent de;
for (off = 2 * sizeof(de); off < dp->size; off += sizeof(de)) { for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
if (readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("isdirempty: readi"); panic("isdirempty: readi");
if (de.inum != 0) if(de.inum != 0)
return 0; return 0;
} }
return 1; return 1;
} }
uint64 sys_unlink(void) { uint64
struct inode *ip, *dp; sys_unlink(void)
struct dirent de; {
char name[DIRSIZ], path[MAXPATH]; struct inode *ip, *dp;
uint off; struct dirent de;
char name[DIRSIZ], path[MAXPATH];
uint off;
if (argstr(0, path, MAXPATH) < 0) if(argstr(0, path, MAXPATH) < 0)
return -1; return -1;
begin_op(); begin_op();
if ((dp = nameiparent(path, name)) == 0) { if((dp = nameiparent(path, name)) == 0){
end_op(); end_op();
return -1; return -1;
} }
ilock(dp); ilock(dp);
// Cannot unlink "." or "..". // Cannot unlink "." or "..".
if (namecmp(name, ".") == 0 || namecmp(name, "..") == 0) if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
goto bad; goto bad;
if ((ip = dirlookup(dp, name, &off)) == 0) if((ip = dirlookup(dp, name, &off)) == 0)
goto bad; goto bad;
ilock(ip); ilock(ip);
if (ip->nlink < 1) if(ip->nlink < 1)
panic("unlink: nlink < 1"); panic("unlink: nlink < 1");
if (ip->type == T_DIR && !isdirempty(ip)) { if(ip->type == T_DIR && !isdirempty(ip)){
iunlockput(ip); iunlockput(ip);
goto bad; goto bad;
} }
memset(&de, 0, sizeof(de)); memset(&de, 0, sizeof(de));
if (writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("unlink: writei"); panic("unlink: writei");
if (ip->type == T_DIR) { if(ip->type == T_DIR){
dp->nlink--; dp->nlink--;
iupdate(dp); iupdate(dp);
} }
iunlockput(dp); iunlockput(dp);
ip->nlink--; ip->nlink--;
iupdate(ip); iupdate(ip);
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return 0; return 0;
bad: bad:
iunlockput(dp); iunlockput(dp);
end_op(); end_op();
return -1; return -1;
} }
static struct inode *create(char *path, short type, short major, short minor) { static struct inode*
struct inode *ip, *dp; create(char *path, short type, short major, short minor)
char name[DIRSIZ]; {
struct inode *ip, *dp;
char name[DIRSIZ];
if ((dp = nameiparent(path, name)) == 0) if((dp = nameiparent(path, name)) == 0)
return 0; return 0;
ilock(dp); ilock(dp);
if ((ip = dirlookup(dp, name, 0)) != 0) { if((ip = dirlookup(dp, name, 0)) != 0){
iunlockput(dp); iunlockput(dp);
ilock(ip); ilock(ip);
if (type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE)) if(type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE))
return ip; return ip;
iunlockput(ip); iunlockput(ip);
return 0; return 0;
} }
if ((ip = ialloc(dp->dev, type)) == 0) { if((ip = ialloc(dp->dev, type)) == 0){
iunlockput(dp); iunlockput(dp);
return 0; return 0;
} }
ilock(ip); ilock(ip);
ip->major = major; ip->major = major;
ip->minor = minor; ip->minor = minor;
ip->nlink = 1; ip->nlink = 1;
iupdate(ip); iupdate(ip);
if (type == T_DIR) { // Create . and .. entries. if(type == T_DIR){ // Create . and .. entries.
// No ip->nlink++ for ".": avoid cyclic ref count. // No ip->nlink++ for ".": avoid cyclic ref count.
if (dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
goto fail; goto fail;
} }
if (dirlink(dp, name, ip->inum) < 0) if(dirlink(dp, name, ip->inum) < 0)
goto fail; goto fail;
if (type == T_DIR) { if(type == T_DIR){
// now that success is guaranteed: // now that success is guaranteed:
dp->nlink++; // for ".." dp->nlink++; // for ".."
iupdate(dp); iupdate(dp);
} }
iunlockput(dp); iunlockput(dp);
return ip; return ip;
fail: fail:
// something went wrong. de-allocate ip. // something went wrong. de-allocate ip.
ip->nlink = 0; ip->nlink = 0;
iupdate(ip); iupdate(ip);
iunlockput(ip); iunlockput(ip);
iunlockput(dp); iunlockput(dp);
return 0; return 0;
} }
uint64 sys_open(void) { uint64
char path[MAXPATH]; sys_open(void)
int fd, omode; {
struct file *f; char path[MAXPATH];
struct inode *ip; int fd, omode;
int n; struct file *f;
struct inode *ip;
int n;
argint(1, &omode); argint(1, &omode);
if ((n = argstr(0, path, MAXPATH)) < 0) if((n = argstr(0, path, MAXPATH)) < 0)
return -1; return -1;
begin_op(); begin_op();
if (omode & O_CREATE) { if(omode & O_CREATE){
ip = create(path, T_FILE, 0, 0); ip = create(path, T_FILE, 0, 0);
if (ip == 0) { if(ip == 0){
end_op(); end_op();
return -1; return -1;
} }
} else { } else {
if ((ip = namei(path)) == 0) { if((ip = namei(path)) == 0){
end_op(); end_op();
return -1; return -1;
} }
ilock(ip); ilock(ip);
if (ip->type == T_DIR && omode != O_RDONLY) { if(ip->type == T_DIR && omode != O_RDONLY){
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return -1; return -1;
} }
} }
if (ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)) { if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return -1; return -1;
} }
if ((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0) { if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
if (f) if(f)
fileclose(f); fileclose(f);
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return -1; return -1;
} }
if (ip->type == T_DEVICE) { if(ip->type == T_DEVICE){
f->type = FD_DEVICE; f->type = FD_DEVICE;
f->major = ip->major; f->major = ip->major;
} else { } else {
f->type = FD_INODE; f->type = FD_INODE;
f->off = 0; f->off = 0;
} }
f->ip = ip; f->ip = ip;
f->readable = !(omode & O_WRONLY); f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR); f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
if ((omode & O_TRUNC) && ip->type == T_FILE) { if((omode & O_TRUNC) && ip->type == T_FILE){
itrunc(ip); itrunc(ip);
} }
iunlock(ip); iunlock(ip);
end_op(); end_op();
return fd; return fd;
} }
uint64 sys_mkdir(void) { uint64
char path[MAXPATH]; sys_mkdir(void)
struct inode *ip; {
char path[MAXPATH];
struct inode *ip;
begin_op(); begin_op();
if (argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0) { if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
end_op(); end_op();
return -1; return -1;
} }
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return 0; return 0;
} }
uint64 sys_mknod(void) { uint64
struct inode *ip; sys_mknod(void)
char path[MAXPATH]; {
int major, minor; struct inode *ip;
char path[MAXPATH];
int major, minor;
begin_op(); begin_op();
argint(1, &major); argint(1, &major);
argint(2, &minor); argint(2, &minor);
if ((argstr(0, path, MAXPATH)) < 0 || if((argstr(0, path, MAXPATH)) < 0 ||
(ip = create(path, T_DEVICE, major, minor)) == 0) { (ip = create(path, T_DEVICE, major, minor)) == 0){
end_op(); end_op();
return -1; return -1;
} }
iunlockput(ip); iunlockput(ip);
end_op(); end_op();
return 0; return 0;
} }
uint64 sys_chdir(void) { uint64
char path[MAXPATH]; sys_chdir(void)
struct inode *ip; {
struct proc *p = myproc(); char path[MAXPATH];
struct inode *ip;
begin_op(); struct proc *p = myproc();
if (argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0) {
end_op(); begin_op();
return -1; if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){
} end_op();
ilock(ip); return -1;
if (ip->type != T_DIR) { }
iunlockput(ip); ilock(ip);
end_op(); if(ip->type != T_DIR){
return -1; iunlockput(ip);
} end_op();
iunlock(ip); return -1;
iput(p->cwd); }
end_op(); iunlock(ip);
p->cwd = ip; iput(p->cwd);
return 0; end_op();
p->cwd = ip;
return 0;
} }
uint64 sys_exec(void) { uint64
char path[MAXPATH], *argv[MAXARG]; sys_exec(void)
int i; {
uint64 uargv, uarg; char path[MAXPATH], *argv[MAXARG];
int i;
uint64 uargv, uarg;
argaddr(1, &uargv); argaddr(1, &uargv);
if (argstr(0, path, MAXPATH) < 0) { if(argstr(0, path, MAXPATH) < 0) {
return -1; return -1;
} }
memset(argv, 0, sizeof(argv)); memset(argv, 0, sizeof(argv));
for (i = 0;; i++) { for(i=0;; i++){
if (i >= NELEM(argv)) { if(i >= NELEM(argv)){
goto bad; goto bad;
} }
if (fetchaddr(uargv + sizeof(uint64) * i, (uint64 *)&uarg) < 0) { if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
goto bad; goto bad;
} }
if (uarg == 0) { if(uarg == 0){
argv[i] = 0; argv[i] = 0;
break; break;
} }
argv[i] = kalloc(); argv[i] = kalloc();
if (argv[i] == 0) if(argv[i] == 0)
goto bad; goto bad;
if (fetchstr(uarg, argv[i], PGSIZE) < 0) if(fetchstr(uarg, argv[i], PGSIZE) < 0)
goto bad; goto bad;
} }
int ret = exec(path, argv); int ret = exec(path, argv);
for (i = 0; i < NELEM(argv) && argv[i] != 0; i++) for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]); kfree(argv[i]);
return ret; return ret;
bad: bad:
for (i = 0; i < NELEM(argv) && argv[i] != 0; i++) for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]); kfree(argv[i]);
return -1; return -1;
} }
uint64 sys_pipe(void) { uint64
uint64 fdarray; // user pointer to array of two integers sys_pipe(void)
struct file *rf, *wf; {
int fd0, fd1; uint64 fdarray; // user pointer to array of two integers
struct proc *p = myproc(); struct file *rf, *wf;
int fd0, fd1;
struct proc *p = myproc();
argaddr(0, &fdarray); argaddr(0, &fdarray);
if (pipealloc(&rf, &wf) < 0) if(pipealloc(&rf, &wf) < 0)
return -1; return -1;
fd0 = -1; fd0 = -1;
if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) { if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
if (fd0 >= 0) if(fd0 >= 0)
p->ofile[fd0] = 0; p->ofile[fd0] = 0;
fileclose(rf); fileclose(rf);
fileclose(wf); fileclose(wf);
return -1; return -1;
} }
if (copyout(p->pagetable, fdarray, (char *)&fd0, sizeof(fd0)) < 0 || if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||
copyout(p->pagetable, fdarray + sizeof(fd0), (char *)&fd1, copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){
sizeof(fd1)) < 0) { p->ofile[fd0] = 0;
p->ofile[fd0] = 0; p->ofile[fd1] = 0;
p->ofile[fd1] = 0; fileclose(rf);
fileclose(rf); fileclose(wf);
fileclose(wf); return -1;
return -1; }
} return 0;
return 0;
} }

View File

@ -6,66 +6,86 @@
#include "spinlock.h" #include "spinlock.h"
#include "proc.h" #include "proc.h"
uint64 sys_exit(void) { uint64
int n; sys_exit(void)
argint(0, &n); {
exit(n); int n;
return 0; // not reached argint(0, &n);
exit(n);
return 0; // not reached
} }
uint64 sys_getpid(void) { return myproc()->pid; } uint64
sys_getpid(void)
uint64 sys_fork(void) { return fork(); } {
return myproc()->pid;
uint64 sys_wait(void) {
uint64 p;
argaddr(0, &p);
return wait(p);
} }
uint64 sys_sbrk(void) { uint64
uint64 addr; sys_fork(void)
int n; {
return fork();
argint(0, &n);
addr = myproc()->sz;
if (growproc(n) < 0)
return -1;
return addr;
} }
uint64 sys_sleep(void) { uint64
int n; sys_wait(void)
uint ticks0; {
uint64 p;
argint(0, &n); argaddr(0, &p);
acquire(&tickslock); return wait(p);
ticks0 = ticks;
while (ticks - ticks0 < n) {
if (killed(myproc())) {
release(&tickslock);
return -1;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
} }
uint64 sys_kill(void) { uint64
int pid; sys_sbrk(void)
{
uint64 addr;
int n;
argint(0, &pid); argint(0, &n);
return kill(pid); 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 // return how many clock tick interrupts have occurred
// since start. // since start.
uint64 sys_uptime(void) { uint64
uint xticks; sys_uptime(void)
{
uint xticks;
acquire(&tickslock); acquire(&tickslock);
xticks = ticks; xticks = ticks;
release(&tickslock); release(&tickslock);
return xticks; return xticks;
} }

View File

@ -80,18 +80,9 @@ uservec:
# load the address of usertrap(), from p->trapframe->kernel_trap # load the address of usertrap(), from p->trapframe->kernel_trap
ld t0, 16(a0) ld t0, 16(a0)
# load the kernel page table, from p->trapframe->kernel_satp
# fetch the kernel page table address, from p->trapframe->kernel_satp.
ld t1, 0(a0) ld t1, 0(a0)
# wait for any previous memory operations to complete, so that
# they use the user page table.
sfence.vma zero, zero
# install the kernel page table.
csrw satp, t1 csrw satp, t1
# flush now-stale user entries from the TLB.
sfence.vma zero, zero sfence.vma zero, zero
# jump to usertrap(), which does not return # jump to usertrap(), which does not return
@ -105,7 +96,6 @@ userret:
# a0: user page table, for satp. # a0: user page table, for satp.
# switch to the user page table. # switch to the user page table.
sfence.vma zero, zero
csrw satp, a0 csrw satp, a0
sfence.vma zero, zero sfence.vma zero, zero

View File

@ -16,141 +16,157 @@ void kernelvec();
extern int devintr(); extern int devintr();
void trapinit(void) { initlock(&tickslock, "time"); } void
trapinit(void)
{
initlock(&tickslock, "time");
}
// set up to take exceptions and traps while in the kernel. // set up to take exceptions and traps while in the kernel.
void trapinithart(void) { w_stvec((uint64)kernelvec); } void
trapinithart(void)
{
w_stvec((uint64)kernelvec);
}
// //
// handle an interrupt, exception, or system call from user space. // handle an interrupt, exception, or system call from user space.
// called from trampoline.S // called from trampoline.S
// //
void usertrap(void) { void
int which_dev = 0; usertrap(void)
{
int which_dev = 0;
if ((r_sstatus() & SSTATUS_SPP) != 0) if((r_sstatus() & SSTATUS_SPP) != 0)
panic("usertrap: not from user mode"); panic("usertrap: not from user mode");
// send interrupts and exceptions to kerneltrap(), // send interrupts and exceptions to kerneltrap(),
// since we're now in the kernel. // since we're now in the kernel.
w_stvec((uint64)kernelvec); w_stvec((uint64)kernelvec);
struct proc *p = myproc(); struct proc *p = myproc();
// save user program counter.
p->trapframe->epc = r_sepc();
if(r_scause() == 8){
// system call
// save user program counter. if(killed(p))
p->trapframe->epc = r_sepc(); exit(-1);
if (r_scause() == 8) { // sepc points to the ecall instruction,
// system call // but we want to return to the next instruction.
p->trapframe->epc += 4;
if (killed(p)) // an interrupt will change sepc, scause, and sstatus,
exit(-1); // so enable only now that we're done with those registers.
intr_on();
// sepc points to the ecall instruction, syscall();
// but we want to return to the next instruction. } else if((which_dev = devintr()) != 0){
p->trapframe->epc += 4; // ok
} else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
setkilled(p);
}
// an interrupt will change sepc, scause, and sstatus, if(killed(p))
// so enable only now that we're done with those registers. exit(-1);
intr_on();
syscall(); // give up the CPU if this is a timer interrupt.
} else if ((which_dev = devintr()) != 0) { if(which_dev == 2)
// ok yield();
} else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
setkilled(p);
}
if (killed(p)) usertrapret();
exit(-1);
// give up the CPU if this is a timer interrupt.
if (which_dev == 2)
yield();
usertrapret();
} }
// //
// return to user space // return to user space
// //
void usertrapret(void) { void
struct proc *p = myproc(); usertrapret(void)
{
struct proc *p = myproc();
// we're about to switch the destination of traps from // we're about to switch the destination of traps from
// kerneltrap() to usertrap(), so turn off interrupts until // kerneltrap() to usertrap(), so turn off interrupts until
// we're back in user space, where usertrap() is correct. // we're back in user space, where usertrap() is correct.
intr_off(); intr_off();
// send syscalls, interrupts, and exceptions to uservec in trampoline.S // send syscalls, interrupts, and exceptions to uservec in trampoline.S
uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline); uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
w_stvec(trampoline_uservec); w_stvec(trampoline_uservec);
// set up trapframe values that uservec will need when // set up trapframe values that uservec will need when
// the process next traps into the kernel. // the process next traps into the kernel.
p->trapframe->kernel_satp = r_satp(); // kernel page table p->trapframe->kernel_satp = r_satp(); // kernel page table
p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
p->trapframe->kernel_trap = (uint64)usertrap; p->trapframe->kernel_trap = (uint64)usertrap;
p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid() p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
// set up the registers that trampoline.S's sret will use // set up the registers that trampoline.S's sret will use
// to get to user space. // to get to user space.
// set S Previous Privilege mode to User.
unsigned long x = r_sstatus();
x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
x |= SSTATUS_SPIE; // enable interrupts in user mode
w_sstatus(x);
// set S Previous Privilege mode to User. // set S Exception Program Counter to the saved user pc.
unsigned long x = r_sstatus(); w_sepc(p->trapframe->epc);
x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
x |= SSTATUS_SPIE; // enable interrupts in user mode
w_sstatus(x);
// set S Exception Program Counter to the saved user pc. // tell trampoline.S the user page table to switch to.
w_sepc(p->trapframe->epc); uint64 satp = MAKE_SATP(p->pagetable);
// tell trampoline.S the user page table to switch to. // jump to userret in trampoline.S at the top of memory, which
uint64 satp = MAKE_SATP(p->pagetable); // switches to the user page table, restores user registers,
// and switches to user mode with sret.
// jump to userret in trampoline.S at the top of memory, which uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
// switches to the user page table, restores user registers, ((void (*)(uint64))trampoline_userret)(satp);
// and switches to user mode with sret.
uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
((void (*)(uint64))trampoline_userret)(satp);
} }
// interrupts and exceptions from kernel code go here via kernelvec, // interrupts and exceptions from kernel code go here via kernelvec,
// on whatever the current kernel stack is. // on whatever the current kernel stack is.
void kerneltrap() { void
int which_dev = 0; kerneltrap()
uint64 sepc = r_sepc(); {
uint64 sstatus = r_sstatus(); int which_dev = 0;
uint64 scause = r_scause(); uint64 sepc = r_sepc();
uint64 sstatus = r_sstatus();
uint64 scause = r_scause();
if((sstatus & SSTATUS_SPP) == 0)
panic("kerneltrap: not from supervisor mode");
if(intr_get() != 0)
panic("kerneltrap: interrupts enabled");
if ((sstatus & SSTATUS_SPP) == 0) if((which_dev = devintr()) == 0){
panic("kerneltrap: not from supervisor mode"); printf("scause %p\n", scause);
if (intr_get() != 0) printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
panic("kerneltrap: interrupts enabled"); panic("kerneltrap");
}
if ((which_dev = devintr()) == 0) { // give up the CPU if this is a timer interrupt.
printf("scause %p\n", scause); if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); yield();
panic("kerneltrap");
}
// give up the CPU if this is a timer interrupt. // the yield() may have caused some traps to occur,
if (which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) // so restore trap registers for use by kernelvec.S's sepc instruction.
yield(); w_sepc(sepc);
w_sstatus(sstatus);
// the yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
} }
void clockintr() { void
acquire(&tickslock); clockintr()
ticks++; {
wakeup(&ticks); acquire(&tickslock);
release(&tickslock); ticks++;
wakeup(&ticks);
release(&tickslock);
} }
// check if it's an external interrupt or software interrupt, // check if it's an external interrupt or software interrupt,
@ -158,44 +174,48 @@ void clockintr() {
// returns 2 if timer interrupt, // returns 2 if timer interrupt,
// 1 if other device, // 1 if other device,
// 0 if not recognized. // 0 if not recognized.
int devintr() { int
uint64 scause = r_scause(); devintr()
{
uint64 scause = r_scause();
if ((scause & 0x8000000000000000L) && (scause & 0xff) == 9) { if((scause & 0x8000000000000000L) &&
// this is a supervisor external interrupt, via PLIC. (scause & 0xff) == 9){
// this is a supervisor external interrupt, via PLIC.
// irq indicates which device interrupted. // irq indicates which device interrupted.
int irq = plic_claim(); int irq = plic_claim();
if (irq == UART0_IRQ) { if(irq == UART0_IRQ){
uartintr(); uartintr();
} else if (irq == VIRTIO0_IRQ) { } else if(irq == VIRTIO0_IRQ){
virtio_disk_intr(); virtio_disk_intr();
} else if (irq) { } else if(irq){
printf("unexpected interrupt irq=%d\n", irq); printf("unexpected interrupt irq=%d\n", irq);
} }
// the PLIC allows each device to raise at most one // the PLIC allows each device to raise at most one
// interrupt at a time; tell the PLIC the device is // interrupt at a time; tell the PLIC the device is
// now allowed to interrupt again. // now allowed to interrupt again.
if (irq) if(irq)
plic_complete(irq); plic_complete(irq);
return 1; return 1;
} else if (scause == 0x8000000000000001L) { } else if(scause == 0x8000000000000001L){
// software interrupt from a machine-mode timer interrupt, // software interrupt from a machine-mode timer interrupt,
// forwarded by timervec in kernelvec.S. // forwarded by timervec in kernelvec.S.
if (cpuid() == 0) { if(cpuid() == 0){
clockintr(); clockintr();
} }
// acknowledge the software interrupt by clearing
// the SSIP bit in sip.
w_sip(r_sip() & ~2);
// acknowledge the software interrupt by clearing return 2;
// the SSIP bit in sip. } else {
w_sip(r_sip() & ~2); return 0;
}
return 2;
} else {
return 0;
}
} }

View File

@ -19,21 +19,21 @@
// some have different meanings for // some have different meanings for
// read vs write. // read vs write.
// see http://byterunner.com/16550.html // see http://byterunner.com/16550.html
#define RHR 0 // receive holding register (for input bytes) #define RHR 0 // receive holding register (for input bytes)
#define THR 0 // transmit holding register (for output bytes) #define THR 0 // transmit holding register (for output bytes)
#define IER 1 // interrupt enable register #define IER 1 // interrupt enable register
#define IER_RX_ENABLE (1 << 0) #define IER_RX_ENABLE (1<<0)
#define IER_TX_ENABLE (1 << 1) #define IER_TX_ENABLE (1<<1)
#define FCR 2 // FIFO control register #define FCR 2 // FIFO control register
#define FCR_FIFO_ENABLE (1 << 0) #define FCR_FIFO_ENABLE (1<<0)
#define FCR_FIFO_CLEAR (3 << 1) // clear the content of the two FIFOs #define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
#define ISR 2 // interrupt status register #define ISR 2 // interrupt status register
#define LCR 3 // line control register #define LCR 3 // line control register
#define LCR_EIGHT_BITS (3 << 0) #define LCR_EIGHT_BITS (3<<0)
#define LCR_BAUD_LATCH (1 << 7) // special mode to set baud rate #define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
#define LSR 5 // line status register #define LSR 5 // line status register
#define LSR_RX_READY (1 << 0) // input is waiting to be read from RHR #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 LSR_TX_IDLE (1<<5) // THR can accept another character to send
#define ReadReg(reg) (*(Reg(reg))) #define ReadReg(reg) (*(Reg(reg)))
#define WriteReg(reg, v) (*(Reg(reg)) = (v)) #define WriteReg(reg, v) (*(Reg(reg)) = (v))
@ -49,30 +49,32 @@ extern volatile int panicked; // from printf.c
void uartstart(); void uartstart();
void uartinit(void) { void
// disable interrupts. uartinit(void)
WriteReg(IER, 0x00); {
// disable interrupts.
WriteReg(IER, 0x00);
// special mode to set baud rate. // special mode to set baud rate.
WriteReg(LCR, LCR_BAUD_LATCH); WriteReg(LCR, LCR_BAUD_LATCH);
// LSB for baud rate of 38.4K. // LSB for baud rate of 38.4K.
WriteReg(0, 0x03); WriteReg(0, 0x03);
// MSB for baud rate of 38.4K. // MSB for baud rate of 38.4K.
WriteReg(1, 0x00); WriteReg(1, 0x00);
// leave set-baud mode, // leave set-baud mode,
// and set word length to 8 bits, no parity. // and set word length to 8 bits, no parity.
WriteReg(LCR, LCR_EIGHT_BITS); WriteReg(LCR, LCR_EIGHT_BITS);
// reset and enable FIFOs. // reset and enable FIFOs.
WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
// enable transmit and receive interrupts. // enable transmit and receive interrupts.
WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
initlock(&uart_tx_lock, "uart"); initlock(&uart_tx_lock, "uart");
} }
// add a character to the output buffer and tell the // add a character to the output buffer and tell the
@ -81,97 +83,108 @@ void uartinit(void) {
// because it may block, it can't be called // because it may block, it can't be called
// from interrupts; it's only suitable for use // from interrupts; it's only suitable for use
// by write(). // by write().
void uartputc(int c) { void
acquire(&uart_tx_lock); uartputc(int c)
{
acquire(&uart_tx_lock);
if (panicked) { if(panicked){
for (;;) for(;;)
; ;
} }
while (uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE) { while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){
// buffer is full. // buffer is full.
// wait for uartstart() to open up space in the buffer. // wait for uartstart() to open up space in the buffer.
sleep(&uart_tx_r, &uart_tx_lock); sleep(&uart_tx_r, &uart_tx_lock);
} }
uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
uart_tx_w += 1; uart_tx_w += 1;
uartstart(); uartstart();
release(&uart_tx_lock); release(&uart_tx_lock);
} }
// alternate version of uartputc() that doesn't
// alternate version of uartputc() that doesn't
// use interrupts, for use by kernel printf() and // use interrupts, for use by kernel printf() and
// to echo characters. it spins waiting for the uart's // to echo characters. it spins waiting for the uart's
// output register to be empty. // output register to be empty.
void uartputc_sync(int c) { void
push_off(); uartputc_sync(int c)
{
push_off();
if (panicked) { if(panicked){
for (;;) for(;;)
; ;
} }
// wait for Transmit Holding Empty to be set in LSR. // wait for Transmit Holding Empty to be set in LSR.
while ((ReadReg(LSR) & LSR_TX_IDLE) == 0) while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
; ;
WriteReg(THR, c); WriteReg(THR, c);
pop_off(); pop_off();
} }
// if the UART is idle, and a character is waiting // if the UART is idle, and a character is waiting
// in the transmit buffer, send it. // in the transmit buffer, send it.
// caller must hold uart_tx_lock. // caller must hold uart_tx_lock.
// called from both the top- and bottom-half. // called from both the top- and bottom-half.
void uartstart() { void
while (1) { uartstart()
if (uart_tx_w == uart_tx_r) { {
// transmit buffer is empty. while(1){
return; 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. if((ReadReg(LSR) & LSR_TX_IDLE) == 0){
// it will interrupt when it's ready for a new byte. // the UART transmit holding register is full,
return; // 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;
int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
// maybe uartputc() is waiting for space in the buffer. uart_tx_r += 1;
wakeup(&uart_tx_r);
// maybe uartputc() is waiting for space in the buffer.
WriteReg(THR, c); wakeup(&uart_tx_r);
}
WriteReg(THR, c);
}
} }
// read one input character from the UART. // read one input character from the UART.
// return -1 if none is waiting. // return -1 if none is waiting.
int uartgetc(void) { int
if (ReadReg(LSR) & 0x01) { uartgetc(void)
// input data is ready. {
return ReadReg(RHR); if(ReadReg(LSR) & 0x01){
} else { // input data is ready.
return -1; return ReadReg(RHR);
} } else {
return -1;
}
} }
// handle a uart interrupt, raised because input has // handle a uart interrupt, raised because input has
// arrived, or the uart is ready for more output, or // arrived, or the uart is ready for more output, or
// both. called from devintr(). // both. called from devintr().
void uartintr(void) { void
// read and process incoming characters. uartintr(void)
while (1) { {
int c = uartgetc(); // read and process incoming characters.
if (c == -1) while(1){
break; int c = uartgetc();
consoleintr(c); if(c == -1)
} break;
consoleintr(c);
}
// send buffered characters. // send buffered characters.
acquire(&uart_tx_lock); acquire(&uart_tx_lock);
uartstart(); uartstart();
release(&uart_tx_lock); release(&uart_tx_lock);
} }

View File

@ -2,8 +2,7 @@
// driver for qemu's virtio disk device. // driver for qemu's virtio disk device.
// uses qemu's mmio interface to virtio. // uses qemu's mmio interface to virtio.
// //
// qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device // qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
// virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
// //
#include "types.h" #include "types.h"
@ -21,293 +20,308 @@
#define R(r) ((volatile uint32 *)(VIRTIO0 + (r))) #define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))
static struct disk { static struct disk {
// a set (not a ring) of DMA descriptors, with which the // a set (not a ring) of DMA descriptors, with which the
// driver tells the device where to read and write individual // driver tells the device where to read and write individual
// disk operations. there are NUM descriptors. // disk operations. there are NUM descriptors.
// most commands consist of a "chain" (a linked list) of a couple of // most commands consist of a "chain" (a linked list) of a couple of
// these descriptors. // these descriptors.
struct virtq_desc *desc; struct virtq_desc *desc;
// a ring in which the driver writes descriptor numbers // a ring in which the driver writes descriptor numbers
// that the driver would like the device to process. it only // that the driver would like the device to process. it only
// includes the head descriptor of each chain. the ring has // includes the head descriptor of each chain. the ring has
// NUM elements. // NUM elements.
struct virtq_avail *avail; struct virtq_avail *avail;
// a ring in which the device writes descriptor numbers that // a ring in which the device writes descriptor numbers that
// the device has finished processing (just the head of each chain). // the device has finished processing (just the head of each chain).
// there are NUM used ring entries. // there are NUM used ring entries.
struct virtq_used *used; struct virtq_used *used;
// our own book-keeping. // our own book-keeping.
char free[NUM]; // is a descriptor free? char free[NUM]; // is a descriptor free?
uint16 used_idx; // we've looked this far in used[2..NUM]. uint16 used_idx; // we've looked this far in used[2..NUM].
// track info about in-flight operations, // track info about in-flight operations,
// for use when completion interrupt arrives. // for use when completion interrupt arrives.
// indexed by first descriptor index of chain. // indexed by first descriptor index of chain.
struct { struct {
struct buf *b; struct buf *b;
char status; char status;
} info[NUM]; } info[NUM];
// disk command headers.
// one-for-one with descriptors, for convenience.
struct virtio_blk_req ops[NUM];
struct spinlock vdisk_lock;
// disk command headers.
// one-for-one with descriptors, for convenience.
struct virtio_blk_req ops[NUM];
struct spinlock vdisk_lock;
} disk; } disk;
void virtio_disk_init(void) { void
uint32 status = 0; virtio_disk_init(void)
{
uint32 status = 0;
initlock(&disk.vdisk_lock, "virtio_disk"); initlock(&disk.vdisk_lock, "virtio_disk");
if (*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||
*R(VIRTIO_MMIO_VERSION) != 2 || *R(VIRTIO_MMIO_DEVICE_ID) != 2 || *R(VIRTIO_MMIO_VERSION) != 2 ||
*R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551) { *R(VIRTIO_MMIO_DEVICE_ID) != 2 ||
panic("could not find virtio disk"); *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){
} panic("could not find virtio disk");
}
// reset device
*R(VIRTIO_MMIO_STATUS) = status;
// reset device // set ACKNOWLEDGE status bit
*R(VIRTIO_MMIO_STATUS) = status; status |= VIRTIO_CONFIG_S_ACKNOWLEDGE;
*R(VIRTIO_MMIO_STATUS) = status;
// set ACKNOWLEDGE status bit // set DRIVER status bit
status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; status |= VIRTIO_CONFIG_S_DRIVER;
*R(VIRTIO_MMIO_STATUS) = status; *R(VIRTIO_MMIO_STATUS) = status;
// set DRIVER status bit // negotiate features
status |= VIRTIO_CONFIG_S_DRIVER; uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES);
*R(VIRTIO_MMIO_STATUS) = status; features &= ~(1 << VIRTIO_BLK_F_RO);
features &= ~(1 << VIRTIO_BLK_F_SCSI);
features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);
features &= ~(1 << VIRTIO_BLK_F_MQ);
features &= ~(1 << VIRTIO_F_ANY_LAYOUT);
features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
*R(VIRTIO_MMIO_DRIVER_FEATURES) = features;
// negotiate features // tell device that feature negotiation is complete.
uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES); status |= VIRTIO_CONFIG_S_FEATURES_OK;
features &= ~(1 << VIRTIO_BLK_F_RO); *R(VIRTIO_MMIO_STATUS) = status;
features &= ~(1 << VIRTIO_BLK_F_SCSI);
features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);
features &= ~(1 << VIRTIO_BLK_F_MQ);
features &= ~(1 << VIRTIO_F_ANY_LAYOUT);
features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
*R(VIRTIO_MMIO_DRIVER_FEATURES) = features;
// tell device that feature negotiation is complete. // re-read status to ensure FEATURES_OK is set.
status |= VIRTIO_CONFIG_S_FEATURES_OK; status = *R(VIRTIO_MMIO_STATUS);
*R(VIRTIO_MMIO_STATUS) = status; if(!(status & VIRTIO_CONFIG_S_FEATURES_OK))
panic("virtio disk FEATURES_OK unset");
// re-read status to ensure FEATURES_OK is set. // initialize queue 0.
status = *R(VIRTIO_MMIO_STATUS); *R(VIRTIO_MMIO_QUEUE_SEL) = 0;
if (!(status & VIRTIO_CONFIG_S_FEATURES_OK))
panic("virtio disk FEATURES_OK unset");
// initialize queue 0. // ensure queue 0 is not in use.
*R(VIRTIO_MMIO_QUEUE_SEL) = 0; if(*R(VIRTIO_MMIO_QUEUE_READY))
panic("virtio disk should not be ready");
// ensure queue 0 is not in use. // check maximum queue size.
if (*R(VIRTIO_MMIO_QUEUE_READY)) uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX);
panic("virtio disk should not be ready"); if(max == 0)
panic("virtio disk has no queue 0");
if(max < NUM)
panic("virtio disk max queue too short");
// check maximum queue size. // allocate and zero queue memory.
uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX); disk.desc = kalloc();
if (max == 0) disk.avail = kalloc();
panic("virtio disk has no queue 0"); disk.used = kalloc();
if (max < NUM) if(!disk.desc || !disk.avail || !disk.used)
panic("virtio disk max queue too short"); panic("virtio disk kalloc");
memset(disk.desc, 0, PGSIZE);
memset(disk.avail, 0, PGSIZE);
memset(disk.used, 0, PGSIZE);
// allocate and zero queue memory. // set queue size.
disk.desc = kalloc(); *R(VIRTIO_MMIO_QUEUE_NUM) = NUM;
disk.avail = kalloc();
disk.used = kalloc();
if (!disk.desc || !disk.avail || !disk.used)
panic("virtio disk kalloc");
memset(disk.desc, 0, PGSIZE);
memset(disk.avail, 0, PGSIZE);
memset(disk.used, 0, PGSIZE);
// set queue size. // write physical addresses.
*R(VIRTIO_MMIO_QUEUE_NUM) = NUM; *R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc;
*R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32;
*R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail;
*R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32;
*R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used;
*R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32;
// write physical addresses. // queue is ready.
*R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc; *R(VIRTIO_MMIO_QUEUE_READY) = 0x1;
*R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32;
*R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail;
*R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32;
*R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used;
*R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32;
// queue is ready. // all NUM descriptors start out unused.
*R(VIRTIO_MMIO_QUEUE_READY) = 0x1; for(int i = 0; i < NUM; i++)
disk.free[i] = 1;
// all NUM descriptors start out unused. // tell device we're completely ready.
for (int i = 0; i < NUM; i++) status |= VIRTIO_CONFIG_S_DRIVER_OK;
disk.free[i] = 1; *R(VIRTIO_MMIO_STATUS) = status;
// tell device we're completely ready. // plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
status |= VIRTIO_CONFIG_S_DRIVER_OK;
*R(VIRTIO_MMIO_STATUS) = status;
// plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
} }
// find a free descriptor, mark it non-free, return its index. // find a free descriptor, mark it non-free, return its index.
static int alloc_desc() { static int
for (int i = 0; i < NUM; i++) { alloc_desc()
if (disk.free[i]) { {
disk.free[i] = 0; for(int i = 0; i < NUM; i++){
return i; if(disk.free[i]){
} disk.free[i] = 0;
} return i;
return -1; }
}
return -1;
} }
// mark a descriptor as free. // mark a descriptor as free.
static void free_desc(int i) { static void
if (i >= NUM) free_desc(int i)
panic("free_desc 1"); {
if (disk.free[i]) if(i >= NUM)
panic("free_desc 2"); panic("free_desc 1");
disk.desc[i].addr = 0; if(disk.free[i])
disk.desc[i].len = 0; panic("free_desc 2");
disk.desc[i].flags = 0; disk.desc[i].addr = 0;
disk.desc[i].next = 0; disk.desc[i].len = 0;
disk.free[i] = 1; disk.desc[i].flags = 0;
wakeup(&disk.free[0]); disk.desc[i].next = 0;
disk.free[i] = 1;
wakeup(&disk.free[0]);
} }
// free a chain of descriptors. // free a chain of descriptors.
static void free_chain(int i) { static void
while (1) { free_chain(int i)
int flag = disk.desc[i].flags; {
int nxt = disk.desc[i].next; while(1){
free_desc(i); int flag = disk.desc[i].flags;
if (flag & VRING_DESC_F_NEXT) int nxt = disk.desc[i].next;
i = nxt; free_desc(i);
else if(flag & VRING_DESC_F_NEXT)
break; i = nxt;
} else
break;
}
} }
// allocate three descriptors (they need not be contiguous). // allocate three descriptors (they need not be contiguous).
// disk transfers always use three descriptors. // disk transfers always use three descriptors.
static int alloc3_desc(int *idx) { static int
for (int i = 0; i < 3; i++) { alloc3_desc(int *idx)
idx[i] = alloc_desc(); {
if (idx[i] < 0) { for(int i = 0; i < 3; i++){
for (int j = 0; j < i; j++) idx[i] = alloc_desc();
free_desc(idx[j]); if(idx[i] < 0){
return -1; for(int j = 0; j < i; j++)
} free_desc(idx[j]);
} return -1;
return 0; }
}
return 0;
} }
void virtio_disk_rw(struct buf *b, int write) { void
uint64 sector = b->blockno * (BSIZE / 512); virtio_disk_rw(struct buf *b, int write)
{
uint64 sector = b->blockno * (BSIZE / 512);
acquire(&disk.vdisk_lock); acquire(&disk.vdisk_lock);
// the spec's Section 5.2 says that legacy block operations use // the spec's Section 5.2 says that legacy block operations use
// three descriptors: one for type/reserved/sector, one for the // three descriptors: one for type/reserved/sector, one for the
// data, one for a 1-byte status result. // data, one for a 1-byte status result.
// allocate the three descriptors. // allocate the three descriptors.
int idx[3]; int idx[3];
while (1) { while(1){
if (alloc3_desc(idx) == 0) { if(alloc3_desc(idx) == 0) {
break; break;
} }
sleep(&disk.free[0], &disk.vdisk_lock); sleep(&disk.free[0], &disk.vdisk_lock);
} }
// format the three descriptors. // format the three descriptors.
// qemu's virtio-blk.c reads them. // qemu's virtio-blk.c reads them.
struct virtio_blk_req *buf0 = &disk.ops[idx[0]]; struct virtio_blk_req *buf0 = &disk.ops[idx[0]];
if (write) if(write)
buf0->type = VIRTIO_BLK_T_OUT; // write the disk buf0->type = VIRTIO_BLK_T_OUT; // write the disk
else else
buf0->type = VIRTIO_BLK_T_IN; // read the disk buf0->type = VIRTIO_BLK_T_IN; // read the disk
buf0->reserved = 0; buf0->reserved = 0;
buf0->sector = sector; buf0->sector = sector;
disk.desc[idx[0]].addr = (uint64)buf0; disk.desc[idx[0]].addr = (uint64) buf0;
disk.desc[idx[0]].len = sizeof(struct virtio_blk_req); disk.desc[idx[0]].len = sizeof(struct virtio_blk_req);
disk.desc[idx[0]].flags = VRING_DESC_F_NEXT; disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;
disk.desc[idx[0]].next = idx[1]; disk.desc[idx[0]].next = idx[1];
disk.desc[idx[1]].addr = (uint64)b->data; disk.desc[idx[1]].addr = (uint64) b->data;
disk.desc[idx[1]].len = BSIZE; disk.desc[idx[1]].len = BSIZE;
if (write) if(write)
disk.desc[idx[1]].flags = 0; // device reads b->data disk.desc[idx[1]].flags = 0; // device reads b->data
else else
disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data
disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT; disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT;
disk.desc[idx[1]].next = idx[2]; disk.desc[idx[1]].next = idx[2];
disk.info[idx[0]].status = 0xff; // device writes 0 on success disk.info[idx[0]].status = 0xff; // device writes 0 on success
disk.desc[idx[2]].addr = (uint64)&disk.info[idx[0]].status; disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status;
disk.desc[idx[2]].len = 1; disk.desc[idx[2]].len = 1;
disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status
disk.desc[idx[2]].next = 0; disk.desc[idx[2]].next = 0;
// record struct buf for virtio_disk_intr(). // record struct buf for virtio_disk_intr().
b->disk = 1; b->disk = 1;
disk.info[idx[0]].b = b; disk.info[idx[0]].b = b;
// tell the device the first index in our chain of descriptors. // tell the device the first index in our chain of descriptors.
disk.avail->ring[disk.avail->idx % NUM] = idx[0]; disk.avail->ring[disk.avail->idx % NUM] = idx[0];
__sync_synchronize(); __sync_synchronize();
// tell the device another avail ring entry is available. // tell the device another avail ring entry is available.
disk.avail->idx += 1; // not % NUM ... disk.avail->idx += 1; // not % NUM ...
__sync_synchronize(); __sync_synchronize();
*R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number
// Wait for virtio_disk_intr() to say request has finished. // Wait for virtio_disk_intr() to say request has finished.
while (b->disk == 1) { while(b->disk == 1) {
sleep(b, &disk.vdisk_lock); sleep(b, &disk.vdisk_lock);
} }
disk.info[idx[0]].b = 0; disk.info[idx[0]].b = 0;
free_chain(idx[0]); free_chain(idx[0]);
release(&disk.vdisk_lock); release(&disk.vdisk_lock);
} }
void virtio_disk_intr() { void
acquire(&disk.vdisk_lock); virtio_disk_intr()
{
acquire(&disk.vdisk_lock);
// the device won't raise another interrupt until we tell it // the device won't raise another interrupt until we tell it
// we've seen this interrupt, which the following line does. // we've seen this interrupt, which the following line does.
// this may race with the device writing new entries to // this may race with the device writing new entries to
// the "used" ring, in which case we may process the new // the "used" ring, in which case we may process the new
// completion entries in this interrupt, and have nothing to do // completion entries in this interrupt, and have nothing to do
// in the next interrupt, which is harmless. // in the next interrupt, which is harmless.
*R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3;
__sync_synchronize(); __sync_synchronize();
// the device increments disk.used->idx when it // the device increments disk.used->idx when it
// adds an entry to the used ring. // adds an entry to the used ring.
while (disk.used_idx != disk.used->idx) { while(disk.used_idx != disk.used->idx){
__sync_synchronize(); __sync_synchronize();
int id = disk.used->ring[disk.used_idx % NUM].id; int id = disk.used->ring[disk.used_idx % NUM].id;
if (disk.info[id].status != 0) if(disk.info[id].status != 0)
panic("virtio_disk_intr status"); panic("virtio_disk_intr status");
struct buf *b = disk.info[id].b; struct buf *b = disk.info[id].b;
b->disk = 0; // disk is done with buf b->disk = 0; // disk is done with buf
wakeup(b); wakeup(b);
disk.used_idx += 1; disk.used_idx += 1;
} }
release(&disk.vdisk_lock); release(&disk.vdisk_lock);
} }

View File

@ -11,56 +11,58 @@
*/ */
pagetable_t kernel_pagetable; pagetable_t kernel_pagetable;
extern char etext[]; // kernel.ld sets this to end of kernel code. extern char etext[]; // kernel.ld sets this to end of kernel code.
extern char trampoline[]; // trampoline.S extern char trampoline[]; // trampoline.S
// Make a direct-map page table for the kernel. // Make a direct-map page table for the kernel.
pagetable_t kvmmake(void) { pagetable_t
pagetable_t kpgtbl; kvmmake(void)
{
pagetable_t kpgtbl;
kpgtbl = (pagetable_t)kalloc(); kpgtbl = (pagetable_t) kalloc();
memset(kpgtbl, 0, PGSIZE); memset(kpgtbl, 0, PGSIZE);
// uart registers // uart registers
kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W); kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W);
// virtio mmio disk interface // virtio mmio disk interface
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
// PLIC // PLIC
kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W); kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
// map kernel text executable and read-only. // map kernel text executable and read-only.
kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext - KERNBASE, PTE_R | PTE_X); kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
// map kernel data and the physical RAM we'll make use of. // map kernel data and the physical RAM we'll make use of.
kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP - (uint64)etext, kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
PTE_R | PTE_W);
// map the trampoline for trap entry/exit to // map the trampoline for trap entry/exit to
// the highest virtual address in the kernel. // the highest virtual address in the kernel.
kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
// allocate and map a kernel stack for each process. // allocate and map a kernel stack for each process.
proc_mapstacks(kpgtbl); proc_mapstacks(kpgtbl);
return kpgtbl; return kpgtbl;
} }
// Initialize the one kernel_pagetable // Initialize the one kernel_pagetable
void kvminit(void) { kernel_pagetable = kvmmake(); } void
kvminit(void)
{
kernel_pagetable = kvmmake();
}
// Switch h/w page table register to the kernel's page table, // Switch h/w page table register to the kernel's page table,
// and enable paging. // and enable paging.
void kvminithart() { void
// wait for any previous writes to the page table memory to finish. kvminithart()
sfence_vma(); {
w_satp(MAKE_SATP(kernel_pagetable));
w_satp(MAKE_SATP(kernel_pagetable)); sfence_vma();
// flush stale entries from the TLB.
sfence_vma();
} }
// Return the address of the PTE in page table pagetable // Return the address of the PTE in page table pagetable
@ -75,198 +77,218 @@ void kvminithart() {
// 21..29 -- 9 bits of level-1 index. // 21..29 -- 9 bits of level-1 index.
// 12..20 -- 9 bits of level-0 index. // 12..20 -- 9 bits of level-0 index.
// 0..11 -- 12 bits of byte offset within the page. // 0..11 -- 12 bits of byte offset within the page.
pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) { pte_t *
if (va >= MAXVA) walk(pagetable_t pagetable, uint64 va, int alloc)
panic("walk"); {
if(va >= MAXVA)
panic("walk");
for (int level = 2; level > 0; level--) { for(int level = 2; level > 0; level--) {
pte_t *pte = &pagetable[PX(level, va)]; pte_t *pte = &pagetable[PX(level, va)];
if (*pte & PTE_V) { if(*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte); pagetable = (pagetable_t)PTE2PA(*pte);
} else { } else {
if (!alloc || (pagetable = (pde_t *)kalloc()) == 0) if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0; return 0;
memset(pagetable, 0, PGSIZE); memset(pagetable, 0, PGSIZE);
*pte = PA2PTE(pagetable) | PTE_V; *pte = PA2PTE(pagetable) | PTE_V;
} }
} }
return &pagetable[PX(0, va)]; return &pagetable[PX(0, va)];
} }
// Look up a virtual address, return the physical address, // Look up a virtual address, return the physical address,
// or 0 if not mapped. // or 0 if not mapped.
// Can only be used to look up user pages. // Can only be used to look up user pages.
uint64 walkaddr(pagetable_t pagetable, uint64 va) { uint64
pte_t *pte; walkaddr(pagetable_t pagetable, uint64 va)
uint64 pa; {
pte_t *pte;
uint64 pa;
if (va >= MAXVA) if(va >= MAXVA)
return 0; return 0;
pte = walk(pagetable, va, 0); pte = walk(pagetable, va, 0);
if (pte == 0) if(pte == 0)
return 0; return 0;
if ((*pte & PTE_V) == 0) if((*pte & PTE_V) == 0)
return 0; return 0;
if ((*pte & PTE_U) == 0) if((*pte & PTE_U) == 0)
return 0; return 0;
pa = PTE2PA(*pte); pa = PTE2PA(*pte);
return pa; return pa;
} }
// add a mapping to the kernel page table. // add a mapping to the kernel page table.
// only used when booting. // only used when booting.
// does not flush TLB or enable paging. // does not flush TLB or enable paging.
void kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm) { void
if (mappages(kpgtbl, va, sz, pa, perm) != 0) kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm)
panic("kvmmap"); {
if(mappages(kpgtbl, va, sz, pa, perm) != 0)
panic("kvmmap");
} }
// Create PTEs for virtual addresses starting at va that refer to // Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not // physical addresses starting at pa. va and size might not
// be page-aligned. Returns 0 on success, -1 if walk() couldn't // be page-aligned. Returns 0 on success, -1 if walk() couldn't
// allocate a needed page-table page. // allocate a needed page-table page.
int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int
int perm) { mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
uint64 a, last; {
pte_t *pte; uint64 a, last;
pte_t *pte;
if (size == 0) if(size == 0)
panic("mappages: size"); panic("mappages: size");
a = PGROUNDDOWN(va); a = PGROUNDDOWN(va);
last = PGROUNDDOWN(va + size - 1); last = PGROUNDDOWN(va + size - 1);
for (;;) { for(;;){
if ((pte = walk(pagetable, a, 1)) == 0) if((pte = walk(pagetable, a, 1)) == 0)
return -1; return -1;
if (*pte & PTE_V) if(*pte & PTE_V)
panic("mappages: remap"); panic("mappages: remap");
*pte = PA2PTE(pa) | perm | PTE_V; *pte = PA2PTE(pa) | perm | PTE_V;
if (a == last) if(a == last)
break; break;
a += PGSIZE; a += PGSIZE;
pa += PGSIZE; pa += PGSIZE;
} }
return 0; return 0;
} }
// Remove npages of mappings starting from va. va must be // Remove npages of mappings starting from va. va must be
// page-aligned. The mappings must exist. // page-aligned. The mappings must exist.
// Optionally free the physical memory. // Optionally free the physical memory.
void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { void
uint64 a; uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
pte_t *pte; {
uint64 a;
pte_t *pte;
if ((va % PGSIZE) != 0) if((va % PGSIZE) != 0)
panic("uvmunmap: not aligned"); panic("uvmunmap: not aligned");
for (a = va; a < va + npages * PGSIZE; a += PGSIZE) { for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if ((pte = walk(pagetable, a, 0)) == 0) if((pte = walk(pagetable, a, 0)) == 0)
panic("uvmunmap: walk"); panic("uvmunmap: walk");
if ((*pte & PTE_V) == 0) if((*pte & PTE_V) == 0)
panic("uvmunmap: not mapped"); panic("uvmunmap: not mapped");
if (PTE_FLAGS(*pte) == PTE_V) if(PTE_FLAGS(*pte) == PTE_V)
panic("uvmunmap: not a leaf"); panic("uvmunmap: not a leaf");
if (do_free) { if(do_free){
uint64 pa = PTE2PA(*pte); uint64 pa = PTE2PA(*pte);
kfree((void *)pa); kfree((void*)pa);
} }
*pte = 0; *pte = 0;
} }
} }
// create an empty user page table. // create an empty user page table.
// returns 0 if out of memory. // returns 0 if out of memory.
pagetable_t uvmcreate() { pagetable_t
pagetable_t pagetable; uvmcreate()
pagetable = (pagetable_t)kalloc(); {
if (pagetable == 0) pagetable_t pagetable;
return 0; pagetable = (pagetable_t) kalloc();
memset(pagetable, 0, PGSIZE); if(pagetable == 0)
return pagetable; return 0;
memset(pagetable, 0, PGSIZE);
return pagetable;
} }
// Load the user initcode into address 0 of pagetable, // Load the user initcode into address 0 of pagetable,
// for the very first process. // for the very first process.
// sz must be less than a page. // sz must be less than a page.
void uvmfirst(pagetable_t pagetable, uchar *src, uint sz) { void
char *mem; uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
{
char *mem;
if (sz >= PGSIZE) if(sz >= PGSIZE)
panic("uvmfirst: more than a page"); panic("uvmfirst: more than a page");
mem = kalloc(); mem = kalloc();
memset(mem, 0, PGSIZE); memset(mem, 0, PGSIZE);
mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W | PTE_R | PTE_X | PTE_U); mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
memmove(mem, src, sz); memmove(mem, src, sz);
} }
// Allocate PTEs and physical memory to grow process from oldsz to // Allocate PTEs and physical memory to grow process from oldsz to
// newsz, which need not be page aligned. Returns new size or 0 on error. // newsz, which need not be page aligned. Returns new size or 0 on error.
uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) { uint64
char *mem; uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
uint64 a; {
char *mem;
uint64 a;
if (newsz < oldsz) if(newsz < oldsz)
return oldsz; return oldsz;
oldsz = PGROUNDUP(oldsz); oldsz = PGROUNDUP(oldsz);
for (a = oldsz; a < newsz; a += PGSIZE) { for(a = oldsz; a < newsz; a += PGSIZE){
mem = kalloc(); mem = kalloc();
if (mem == 0) { if(mem == 0){
uvmdealloc(pagetable, a, oldsz); uvmdealloc(pagetable, a, oldsz);
return 0; return 0;
} }
memset(mem, 0, PGSIZE); memset(mem, 0, PGSIZE);
if (mappages(pagetable, a, PGSIZE, (uint64)mem, if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
PTE_R | PTE_U | xperm) != 0) { kfree(mem);
kfree(mem); uvmdealloc(pagetable, a, oldsz);
uvmdealloc(pagetable, a, oldsz); return 0;
return 0; }
} }
} return newsz;
return newsz;
} }
// Deallocate user pages to bring the process size from oldsz to // Deallocate user pages to bring the process size from oldsz to
// newsz. oldsz and newsz need not be page-aligned, nor does newsz // newsz. oldsz and newsz need not be page-aligned, nor does newsz
// need to be less than oldsz. oldsz can be larger than the actual // need to be less than oldsz. oldsz can be larger than the actual
// process size. Returns the new process size. // process size. Returns the new process size.
uint64 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) { uint64
if (newsz >= oldsz) uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
return oldsz; {
if(newsz >= oldsz)
return oldsz;
if (PGROUNDUP(newsz) < PGROUNDUP(oldsz)) { if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE; int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1); uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
} }
return newsz; return newsz;
} }
// Recursively free page-table pages. // Recursively free page-table pages.
// All leaf mappings must already have been removed. // All leaf mappings must already have been removed.
void freewalk(pagetable_t pagetable) { void
// there are 2^9 = 512 PTEs in a page table. freewalk(pagetable_t pagetable)
for (int i = 0; i < 512; i++) { {
pte_t pte = pagetable[i]; // there are 2^9 = 512 PTEs in a page table.
if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0) { for(int i = 0; i < 512; i++){
// this PTE points to a lower-level page table. pte_t pte = pagetable[i];
uint64 child = PTE2PA(pte); if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
freewalk((pagetable_t)child); // this PTE points to a lower-level page table.
pagetable[i] = 0; uint64 child = PTE2PA(pte);
} else if (pte & PTE_V) { freewalk((pagetable_t)child);
panic("freewalk: leaf"); pagetable[i] = 0;
} } else if(pte & PTE_V){
} panic("freewalk: leaf");
kfree((void *)pagetable); }
}
kfree((void*)pagetable);
} }
// Free user memory pages, // Free user memory pages,
// then free page-table pages. // then free page-table pages.
void uvmfree(pagetable_t pagetable, uint64 sz) { void
if (sz > 0) uvmfree(pagetable_t pagetable, uint64 sz)
uvmunmap(pagetable, 0, PGROUNDUP(sz) / PGSIZE, 1); {
freewalk(pagetable); if(sz > 0)
uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
freewalk(pagetable);
} }
// Given a parent process's page table, copy // Given a parent process's page table, copy
@ -275,128 +297,138 @@ void uvmfree(pagetable_t pagetable, uint64 sz) {
// physical memory. // physical memory.
// returns 0 on success, -1 on failure. // returns 0 on success, -1 on failure.
// frees any allocated pages on failure. // frees any allocated pages on failure.
int uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { int
pte_t *pte; uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
uint64 pa, i; {
uint flags; pte_t *pte;
char *mem; uint64 pa, i;
uint flags;
char *mem;
for (i = 0; i < sz; i += PGSIZE) { for(i = 0; i < sz; i += PGSIZE){
if ((pte = walk(old, i, 0)) == 0) if((pte = walk(old, i, 0)) == 0)
panic("uvmcopy: pte should exist"); panic("uvmcopy: pte should exist");
if ((*pte & PTE_V) == 0) if((*pte & PTE_V) == 0)
panic("uvmcopy: page not present"); panic("uvmcopy: page not present");
pa = PTE2PA(*pte); pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte); flags = PTE_FLAGS(*pte);
if ((mem = kalloc()) == 0) if((mem = kalloc()) == 0)
goto err; goto err;
memmove(mem, (char *)pa, PGSIZE); memmove(mem, (char*)pa, PGSIZE);
if (mappages(new, i, PGSIZE, (uint64)mem, flags) != 0) { if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem); kfree(mem);
goto err; goto err;
} }
} }
return 0; return 0;
err: err:
uvmunmap(new, 0, i / PGSIZE, 1); uvmunmap(new, 0, i / PGSIZE, 1);
return -1; return -1;
} }
// mark a PTE invalid for user access. // mark a PTE invalid for user access.
// used by exec for the user stack guard page. // used by exec for the user stack guard page.
void uvmclear(pagetable_t pagetable, uint64 va) { void
pte_t *pte; uvmclear(pagetable_t pagetable, uint64 va)
{
pte = walk(pagetable, va, 0); pte_t *pte;
if (pte == 0)
panic("uvmclear"); pte = walk(pagetable, va, 0);
*pte &= ~PTE_U; if(pte == 0)
panic("uvmclear");
*pte &= ~PTE_U;
} }
// Copy from kernel to user. // Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table. // Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error. // Return 0 on success, -1 on error.
int copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) { int
uint64 n, va0, pa0; copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
uint64 n, va0, pa0;
while (len > 0) { while(len > 0){
va0 = PGROUNDDOWN(dstva); va0 = PGROUNDDOWN(dstva);
pa0 = walkaddr(pagetable, va0); pa0 = walkaddr(pagetable, va0);
if (pa0 == 0) if(pa0 == 0)
return -1; return -1;
n = PGSIZE - (dstva - va0); n = PGSIZE - (dstva - va0);
if (n > len) if(n > len)
n = len; n = len;
memmove((void *)(pa0 + (dstva - va0)), src, n); memmove((void *)(pa0 + (dstva - va0)), src, n);
len -= n; len -= n;
src += n; src += n;
dstva = va0 + PGSIZE; dstva = va0 + PGSIZE;
} }
return 0; return 0;
} }
// Copy from user to kernel. // Copy from user to kernel.
// Copy len bytes to dst from virtual address srcva in a given page table. // Copy len bytes to dst from virtual address srcva in a given page table.
// Return 0 on success, -1 on error. // Return 0 on success, -1 on error.
int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) { int
uint64 n, va0, pa0; copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
uint64 n, va0, pa0;
while (len > 0) { while(len > 0){
va0 = PGROUNDDOWN(srcva); va0 = PGROUNDDOWN(srcva);
pa0 = walkaddr(pagetable, va0); pa0 = walkaddr(pagetable, va0);
if (pa0 == 0) if(pa0 == 0)
return -1; return -1;
n = PGSIZE - (srcva - va0); n = PGSIZE - (srcva - va0);
if (n > len) if(n > len)
n = len; n = len;
memmove(dst, (void *)(pa0 + (srcva - va0)), n); memmove(dst, (void *)(pa0 + (srcva - va0)), n);
len -= n; len -= n;
dst += n; dst += n;
srcva = va0 + PGSIZE; srcva = va0 + PGSIZE;
} }
return 0; return 0;
} }
// Copy a null-terminated string from user to kernel. // Copy a null-terminated string from user to kernel.
// Copy bytes to dst from virtual address srcva in a given page table, // Copy bytes to dst from virtual address srcva in a given page table,
// until a '\0', or max. // until a '\0', or max.
// Return 0 on success, -1 on error. // Return 0 on success, -1 on error.
int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) { int
uint64 n, va0, pa0; copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
int got_null = 0; {
uint64 n, va0, pa0;
int got_null = 0;
while (got_null == 0 && max > 0) { while(got_null == 0 && max > 0){
va0 = PGROUNDDOWN(srcva); va0 = PGROUNDDOWN(srcva);
pa0 = walkaddr(pagetable, va0); pa0 = walkaddr(pagetable, va0);
if (pa0 == 0) if(pa0 == 0)
return -1; return -1;
n = PGSIZE - (srcva - va0); n = PGSIZE - (srcva - va0);
if (n > max) if(n > max)
n = max; n = max;
char *p = (char *)(pa0 + (srcva - va0)); char *p = (char *) (pa0 + (srcva - va0));
while (n > 0) { while(n > 0){
if (*p == '\0') { if(*p == '\0'){
*dst = '\0'; *dst = '\0';
got_null = 1; got_null = 1;
break; break;
} else { } else {
*dst = *p; *dst = *p;
} }
--n; --n;
--max; --max;
p++; p++;
dst++; dst++;
} }
srcva = va0 + PGSIZE; srcva = va0 + PGSIZE;
} }
if (got_null) { if(got_null){
return 0; return 0;
} else { } else {
return -1; return -1;
} }
} }

View File

@ -5,19 +5,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <assert.h> #include <assert.h>
#define stat xv6_stat // avoid clash with host struct stat #define stat xv6_stat // avoid clash with host struct stat
#include "kernel/types.h" #include "kernel/types.h"
#include "kernel/fs.h" #include "kernel/fs.h"
#include "kernel/stat.h" #include "kernel/stat.h"
#include "kernel/param.h" #include "kernel/param.h"
#ifndef static_assert #ifndef static_assert
#define static_assert(a, b) \ #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0)
do { \
switch (0) \
case 0: \
case (a):; \
} while (0)
#endif #endif
#define NINODES 200 #define NINODES 200
@ -25,11 +20,11 @@
// Disk layout: // Disk layout:
// [ boot block | sb block | log | inode blocks | free bit map | data blocks ] // [ boot block | sb block | log | inode blocks | free bit map | data blocks ]
int nbitmap = FSSIZE / (BSIZE * 8) + 1; int nbitmap = FSSIZE/(BSIZE*8) + 1;
int ninodeblocks = NINODES / IPB + 1; int ninodeblocks = NINODES / IPB + 1;
int nlog = LOGSIZE; int nlog = LOGSIZE;
int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
int nblocks; // Number of data blocks int nblocks; // Number of data blocks
int fsfd; int fsfd;
struct superblock sb; struct superblock sb;
@ -37,9 +32,10 @@ char zeroes[BSIZE];
uint freeinode = 1; uint freeinode = 1;
uint freeblock; uint freeblock;
void balloc(int); void balloc(int);
void wsect(uint, void *); void wsect(uint, void*);
void winode(uint, struct dinode *); void winode(uint, struct dinode*);
void rinode(uint inum, struct dinode *ip); void rinode(uint inum, struct dinode *ip);
void rsect(uint sec, void *buf); void rsect(uint sec, void *buf);
uint ialloc(ushort type); uint ialloc(ushort type);
@ -47,237 +43,259 @@ void iappend(uint inum, void *p, int n);
void die(const char *); void die(const char *);
// convert to riscv byte order // convert to riscv byte order
ushort xshort(ushort x) { ushort
ushort y; xshort(ushort x)
uchar *a = (uchar *)&y; {
a[0] = x; ushort y;
a[1] = x >> 8; uchar *a = (uchar*)&y;
return y; a[0] = x;
a[1] = x >> 8;
return y;
} }
uint xint(uint x) { uint
uint y; xint(uint x)
uchar *a = (uchar *)&y; {
a[0] = x; uint y;
a[1] = x >> 8; uchar *a = (uchar*)&y;
a[2] = x >> 16; a[0] = x;
a[3] = x >> 24; a[1] = x >> 8;
return y; a[2] = x >> 16;
a[3] = x >> 24;
return y;
} }
int main(int argc, char *argv[]) { int
int i, cc, fd; main(int argc, char *argv[])
uint rootino, inum, off; {
struct dirent de; int i, cc, fd;
char buf[BSIZE]; uint rootino, inum, off;
struct dinode din; struct dirent de;
char buf[BSIZE];
struct dinode din;
static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");
if (argc < 2) { static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");
fprintf(stderr, "Usage: mkfs fs.img files...\n");
exit(1);
}
assert((BSIZE % sizeof(struct dinode)) == 0); if(argc < 2){
assert((BSIZE % sizeof(struct dirent)) == 0); fprintf(stderr, "Usage: mkfs fs.img files...\n");
exit(1);
}
fsfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666); assert((BSIZE % sizeof(struct dinode)) == 0);
if (fsfd < 0) assert((BSIZE % sizeof(struct dirent)) == 0);
die(argv[1]);
// 1 fs block = 1 disk sector fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
nmeta = 2 + nlog + ninodeblocks + nbitmap; if(fsfd < 0)
nblocks = FSSIZE - nmeta; die(argv[1]);
sb.magic = FSMAGIC; // 1 fs block = 1 disk sector
sb.size = xint(FSSIZE); nmeta = 2 + nlog + ninodeblocks + nbitmap;
sb.nblocks = xint(nblocks); nblocks = FSSIZE - nmeta;
sb.ninodes = xint(NINODES);
sb.nlog = xint(nlog);
sb.logstart = xint(2);
sb.inodestart = xint(2 + nlog);
sb.bmapstart = xint(2 + nlog + ninodeblocks);
printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap " sb.magic = FSMAGIC;
"blocks %u) blocks %d total %d\n", sb.size = xint(FSSIZE);
nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); sb.nblocks = xint(nblocks);
sb.ninodes = xint(NINODES);
sb.nlog = xint(nlog);
sb.logstart = xint(2);
sb.inodestart = xint(2+nlog);
sb.bmapstart = xint(2+nlog+ninodeblocks);
freeblock = nmeta; // the first free block that we can allocate printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n",
nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE);
for (i = 0; i < FSSIZE; i++) freeblock = nmeta; // the first free block that we can allocate
wsect(i, zeroes);
memset(buf, 0, sizeof(buf)); for(i = 0; i < FSSIZE; i++)
memmove(buf, &sb, sizeof(sb)); wsect(i, zeroes);
wsect(1, buf);
rootino = ialloc(T_DIR); memset(buf, 0, sizeof(buf));
assert(rootino == ROOTINO); memmove(buf, &sb, sizeof(sb));
wsect(1, buf);
bzero(&de, sizeof(de)); rootino = ialloc(T_DIR);
de.inum = xshort(rootino); assert(rootino == ROOTINO);
strcpy(de.name, ".");
iappend(rootino, &de, sizeof(de));
bzero(&de, sizeof(de)); bzero(&de, sizeof(de));
de.inum = xshort(rootino); de.inum = xshort(rootino);
strcpy(de.name, ".."); strcpy(de.name, ".");
iappend(rootino, &de, sizeof(de)); iappend(rootino, &de, sizeof(de));
for (i = 2; i < argc; i++) { bzero(&de, sizeof(de));
// get rid of "user/" de.inum = xshort(rootino);
char *shortname; strcpy(de.name, "..");
if (strncmp(argv[i], "user/", 5) == 0) iappend(rootino, &de, sizeof(de));
shortname = argv[i] + 5;
else
shortname = argv[i];
assert(index(shortname, '/') == 0); for(i = 2; i < argc; i++){
// get rid of "user/"
char *shortname;
if(strncmp(argv[i], "user/", 5) == 0)
shortname = argv[i] + 5;
else
shortname = argv[i];
assert(index(shortname, '/') == 0);
if ((fd = open(argv[i], 0)) < 0) if((fd = open(argv[i], 0)) < 0)
die(argv[i]); die(argv[i]);
// Skip leading _ in name when writing to file system. // Skip leading _ in name when writing to file system.
// The binaries are named _rm, _cat, etc. to keep the // The binaries are named _rm, _cat, etc. to keep the
// build operating system from trying to execute them // build operating system from trying to execute them
// in place of system binaries like rm and cat. // in place of system binaries like rm and cat.
if (shortname[0] == '_') if(shortname[0] == '_')
shortname += 1; shortname += 1;
inum = ialloc(T_FILE); inum = ialloc(T_FILE);
bzero(&de, sizeof(de)); bzero(&de, sizeof(de));
de.inum = xshort(inum); de.inum = xshort(inum);
strncpy(de.name, shortname, DIRSIZ); strncpy(de.name, shortname, DIRSIZ);
iappend(rootino, &de, sizeof(de)); iappend(rootino, &de, sizeof(de));
while ((cc = read(fd, buf, sizeof(buf))) > 0) while((cc = read(fd, buf, sizeof(buf))) > 0)
iappend(inum, buf, cc); iappend(inum, buf, cc);
close(fd); close(fd);
} }
// fix size of root inode dir // fix size of root inode dir
rinode(rootino, &din); rinode(rootino, &din);
off = xint(din.size); off = xint(din.size);
off = ((off / BSIZE) + 1) * BSIZE; off = ((off/BSIZE) + 1) * BSIZE;
din.size = xint(off); din.size = xint(off);
winode(rootino, &din); winode(rootino, &din);
balloc(freeblock); balloc(freeblock);
exit(0); exit(0);
} }
void wsect(uint sec, void *buf) { void
if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) wsect(uint sec, void *buf)
die("lseek"); {
if (write(fsfd, buf, BSIZE) != BSIZE) if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE)
die("write"); die("lseek");
if(write(fsfd, buf, BSIZE) != BSIZE)
die("write");
} }
void winode(uint inum, struct dinode *ip) { void
char buf[BSIZE]; winode(uint inum, struct dinode *ip)
uint bn; {
struct dinode *dip; char buf[BSIZE];
uint bn;
struct dinode *dip;
bn = IBLOCK(inum, sb); bn = IBLOCK(inum, sb);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode *)buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*dip = *ip; *dip = *ip;
wsect(bn, buf); wsect(bn, buf);
} }
void rinode(uint inum, struct dinode *ip) { void
char buf[BSIZE]; rinode(uint inum, struct dinode *ip)
uint bn; {
struct dinode *dip; char buf[BSIZE];
uint bn;
struct dinode *dip;
bn = IBLOCK(inum, sb); bn = IBLOCK(inum, sb);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode *)buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*ip = *dip; *ip = *dip;
} }
void rsect(uint sec, void *buf) { void
if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) rsect(uint sec, void *buf)
die("lseek"); {
if (read(fsfd, buf, BSIZE) != BSIZE) if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE)
die("read"); die("lseek");
if(read(fsfd, buf, BSIZE) != BSIZE)
die("read");
} }
uint ialloc(ushort type) { uint
uint inum = freeinode++; ialloc(ushort type)
struct dinode din; {
uint inum = freeinode++;
struct dinode din;
bzero(&din, sizeof(din)); bzero(&din, sizeof(din));
din.type = xshort(type); din.type = xshort(type);
din.nlink = xshort(1); din.nlink = xshort(1);
din.size = xint(0); din.size = xint(0);
winode(inum, &din); winode(inum, &din);
return inum; return inum;
} }
void balloc(int used) { void
uchar buf[BSIZE]; balloc(int used)
int i; {
uchar buf[BSIZE];
int i;
printf("balloc: first %d blocks have been allocated\n", used); printf("balloc: first %d blocks have been allocated\n", used);
assert(used < BSIZE * 8); assert(used < BSIZE*8);
bzero(buf, BSIZE); bzero(buf, BSIZE);
for (i = 0; i < used; i++) { for(i = 0; i < used; i++){
buf[i / 8] = buf[i / 8] | (0x1 << (i % 8)); buf[i/8] = buf[i/8] | (0x1 << (i%8));
} }
printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); printf("balloc: write bitmap block at sector %d\n", sb.bmapstart);
wsect(sb.bmapstart, buf); wsect(sb.bmapstart, buf);
} }
#define min(a, b) ((a) < (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b))
void iappend(uint inum, void *xp, int n) { void
char *p = (char *)xp; iappend(uint inum, void *xp, int n)
uint fbn, off, n1; {
struct dinode din; char *p = (char*)xp;
char buf[BSIZE]; uint fbn, off, n1;
uint indirect[NINDIRECT]; struct dinode din;
uint x; char buf[BSIZE];
uint indirect[NINDIRECT];
uint x;
rinode(inum, &din); rinode(inum, &din);
off = xint(din.size); off = xint(din.size);
// printf("append inum %d at off %d sz %d\n", inum, off, n); // printf("append inum %d at off %d sz %d\n", inum, off, n);
while (n > 0) { while(n > 0){
fbn = off / BSIZE; fbn = off / BSIZE;
assert(fbn < MAXFILE); assert(fbn < MAXFILE);
if (fbn < NDIRECT) { if(fbn < NDIRECT){
if (xint(din.addrs[fbn]) == 0) { if(xint(din.addrs[fbn]) == 0){
din.addrs[fbn] = xint(freeblock++); din.addrs[fbn] = xint(freeblock++);
} }
x = xint(din.addrs[fbn]); x = xint(din.addrs[fbn]);
} else { } else {
if (xint(din.addrs[NDIRECT]) == 0) { if(xint(din.addrs[NDIRECT]) == 0){
din.addrs[NDIRECT] = xint(freeblock++); din.addrs[NDIRECT] = xint(freeblock++);
} }
rsect(xint(din.addrs[NDIRECT]), (char *)indirect); rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
if (indirect[fbn - NDIRECT] == 0) { if(indirect[fbn - NDIRECT] == 0){
indirect[fbn - NDIRECT] = xint(freeblock++); indirect[fbn - NDIRECT] = xint(freeblock++);
wsect(xint(din.addrs[NDIRECT]), (char *)indirect); wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
} }
x = xint(indirect[fbn - NDIRECT]); x = xint(indirect[fbn-NDIRECT]);
} }
n1 = min(n, (fbn + 1) * BSIZE - off); n1 = min(n, (fbn + 1) * BSIZE - off);
rsect(x, buf); rsect(x, buf);
bcopy(p, buf + off - (fbn * BSIZE), n1); bcopy(p, buf + off - (fbn * BSIZE), n1);
wsect(x, buf); wsect(x, buf);
n -= n1; n -= n1;
off += n1; off += n1;
p += n1; p += n1;
} }
din.size = xint(off); din.size = xint(off);
winode(inum, &din); winode(inum, &din);
} }
void die(const char *s) { void
perror(s); die(const char *s)
exit(1); {
perror(s);
exit(1);
} }

1
rust/foo/.gitignore vendored
View File

@ -1 +0,0 @@
/target

7
rust/foo/Cargo.lock generated
View File

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "foo"
version = "0.1.0"

View File

@ -1,14 +0,0 @@
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = [ "staticlib" ]
[profile.release]
panic = "abort"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,13 +0,0 @@
#![no_std]
#![crate_type = "staticlib"]
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
#[panic_handler]
#[no_mangle]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

View File

@ -4,36 +4,40 @@
char buf[512]; char buf[512];
void cat(int fd) { void
int n; cat(int fd)
{
int n;
while ((n = read(fd, buf, sizeof(buf))) > 0) { while((n = read(fd, buf, sizeof(buf))) > 0) {
if (write(1, buf, n) != n) { if (write(1, buf, n) != n) {
fprintf(2, "cat: write error\n"); fprintf(2, "cat: write error\n");
exit(1); exit(1);
} }
} }
if (n < 0) { if(n < 0){
fprintf(2, "cat: read error\n"); fprintf(2, "cat: read error\n");
exit(1); exit(1);
} }
} }
int main(int argc, char *argv[]) { int
int fd, i; main(int argc, char *argv[])
{
int fd, i;
if (argc <= 1) { if(argc <= 1){
cat(0); cat(0);
exit(0); exit(0);
} }
for (i = 1; i < argc; i++) { for(i = 1; i < argc; i++){
if ((fd = open(argv[i], 0)) < 0) { if((fd = open(argv[i], 0)) < 0){
fprintf(2, "cat: cannot open %s\n", argv[i]); fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }
cat(fd); cat(fd);
close(fd); close(fd);
} }
exit(0); exit(0);
} }

View File

@ -2,16 +2,18 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(int argc, char *argv[]) { int
int i; main(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++) { for(i = 1; i < argc; i++){
write(1, argv[i], strlen(argv[i])); write(1, argv[i], strlen(argv[i]));
if (i + 1 < argc) { if(i + 1 < argc){
write(1, " ", 1); write(1, " ", 1);
} else { } else {
write(1, "\n", 1); write(1, "\n", 1);
} }
} }
exit(0); exit(0);
} }

View File

@ -5,44 +5,52 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
#define N 1000 #define N 1000
void print(const char *s) { write(1, s, strlen(s)); } void
print(const char *s)
void forktest(void) { {
int n, pid; write(1, s, strlen(s));
print("fork test\n");
for (n = 0; n < N; n++) {
pid = fork();
if (pid < 0)
break;
if (pid == 0)
exit(0);
}
if (n == N) {
print("fork claimed to work N times!\n");
exit(1);
}
for (; n > 0; n--) {
if (wait(0) < 0) {
print("wait stopped early\n");
exit(1);
}
}
if (wait(0) != -1) {
print("wait got too many\n");
exit(1);
}
print("fork test OK\n");
} }
int main(void) { void
forktest(); forktest(void)
exit(0); {
int n, pid;
print("fork test\n");
for(n=0; n<N; n++){
pid = fork();
if(pid < 0)
break;
if(pid == 0)
exit(0);
}
if(n == N){
print("fork claimed to work N times!\n");
exit(1);
}
for(; n > 0; n--){
if(wait(0) < 0){
print("wait stopped early\n");
exit(1);
}
}
if(wait(0) != -1){
print("wait got too many\n");
exit(1);
}
print("fork test OK\n");
}
int
main(void)
{
forktest();
exit(0);
} }

View File

@ -5,93 +5,102 @@
#include "user/user.h" #include "user/user.h"
char buf[1024]; char buf[1024];
int match(char *, char *); int match(char*, char*);
void grep(char *pattern, int fd) { void
int n, m; grep(char *pattern, int fd)
char *p, *q; {
int n, m;
char *p, *q;
m = 0; m = 0;
while ((n = read(fd, buf + m, sizeof(buf) - m - 1)) > 0) { while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){
m += n; m += n;
buf[m] = '\0'; buf[m] = '\0';
p = buf; p = buf;
while ((q = strchr(p, '\n')) != 0) { while((q = strchr(p, '\n')) != 0){
*q = 0; *q = 0;
if (match(pattern, p)) { if(match(pattern, p)){
*q = '\n'; *q = '\n';
write(1, p, q + 1 - p); write(1, p, q+1 - p);
} }
p = q + 1; p = q+1;
} }
if (m > 0) { if(m > 0){
m -= p - buf; m -= p - buf;
memmove(buf, p, m); memmove(buf, p, m);
} }
} }
} }
int main(int argc, char *argv[]) { int
int fd, i; main(int argc, char *argv[])
char *pattern; {
int fd, i;
char *pattern;
if (argc <= 1) { if(argc <= 1){
fprintf(2, "usage: grep pattern [file ...]\n"); fprintf(2, "usage: grep pattern [file ...]\n");
exit(1); exit(1);
} }
pattern = argv[1]; pattern = argv[1];
if (argc <= 2) { if(argc <= 2){
grep(pattern, 0); grep(pattern, 0);
exit(0); exit(0);
} }
for (i = 2; i < argc; i++) { for(i = 2; i < argc; i++){
if ((fd = open(argv[i], 0)) < 0) { if((fd = open(argv[i], 0)) < 0){
printf("grep: cannot open %s\n", argv[i]); printf("grep: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }
grep(pattern, fd); grep(pattern, fd);
close(fd); close(fd);
} }
exit(0); exit(0);
} }
// Regexp matcher from Kernighan & Pike, // Regexp matcher from Kernighan & Pike,
// The Practice of Programming, Chapter 9, or // The Practice of Programming, Chapter 9, or
// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html // https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
int matchhere(char *, char *); int matchhere(char*, char*);
int matchstar(int, char *, char *); int matchstar(int, char*, char*);
int match(char *re, char *text) { int
if (re[0] == '^') match(char *re, char *text)
return matchhere(re + 1, text); {
do { // must look at empty string if(re[0] == '^')
if (matchhere(re, text)) return matchhere(re+1, text);
return 1; do{ // must look at empty string
} while (*text++ != '\0'); if(matchhere(re, text))
return 0; return 1;
}while(*text++ != '\0');
return 0;
} }
// matchhere: search for re at beginning of text // matchhere: search for re at beginning of text
int matchhere(char *re, char *text) { int matchhere(char *re, char *text)
if (re[0] == '\0') {
return 1; if(re[0] == '\0')
if (re[1] == '*') return 1;
return matchstar(re[0], re + 2, text); if(re[1] == '*')
if (re[0] == '$' && re[1] == '\0') return matchstar(re[0], re+2, text);
return *text == '\0'; if(re[0] == '$' && re[1] == '\0')
if (*text != '\0' && (re[0] == '.' || re[0] == *text)) return *text == '\0';
return matchhere(re + 1, text + 1); if(*text!='\0' && (re[0]=='.' || re[0]==*text))
return 0; return matchhere(re+1, text+1);
return 0;
} }
// matchstar: search for c*re at beginning of text // matchstar: search for c*re at beginning of text
int matchstar(int c, char *re, char *text) { int matchstar(int c, char *re, char *text)
do { // a * matches zero or more instances {
if (matchhere(re, text)) do{ // a * matches zero or more instances
return 1; if(matchhere(re, text))
} while (*text != '\0' && (*text++ == c || c == '.')); return 1;
return 0; }while(*text!='\0' && (*text++==c || c=='.'));
return 0;
} }

View File

@ -13,328 +13,338 @@
#include "kernel/riscv.h" #include "kernel/riscv.h"
// from FreeBSD. // from FreeBSD.
int do_rand(unsigned long *ctx) { int
/* do_rand(unsigned long *ctx)
* Compute x = (7^5 * x) mod (2^31 - 1) {
* without overflowing 31 bits: /*
* (2^31 - 1) = 127773 * (7^5) + 2836 * Compute x = (7^5 * x) mod (2^31 - 1)
* From "Random number generators: good ones are hard to find", * without overflowing 31 bits:
* Park and Miller, Communications of the ACM, vol. 31, no. 10, * (2^31 - 1) = 127773 * (7^5) + 2836
* October 1988, p. 1195. * From "Random number generators: good ones are hard to find",
*/ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
long hi, lo, x; * October 1988, p. 1195.
*/
long hi, lo, x;
/* Transform to [1, 0x7ffffffe] range. */ /* Transform to [1, 0x7ffffffe] range. */
x = (*ctx % 0x7ffffffe) + 1; x = (*ctx % 0x7ffffffe) + 1;
hi = x / 127773; hi = x / 127773;
lo = x % 127773; lo = x % 127773;
x = 16807 * lo - 2836 * hi; x = 16807 * lo - 2836 * hi;
if (x < 0) if (x < 0)
x += 0x7fffffff; x += 0x7fffffff;
/* Transform to [0, 0x7ffffffd] range. */ /* Transform to [0, 0x7ffffffd] range. */
x--; x--;
*ctx = x; *ctx = x;
return (x); return (x);
} }
unsigned long rand_next = 1; unsigned long rand_next = 1;
int rand(void) { return (do_rand(&rand_next)); } int
rand(void)
void go(int which_child) { {
int fd = -1; return (do_rand(&rand_next));
static char buf[999];
char *break0 = sbrk(0);
uint64 iters = 0;
mkdir("grindir");
if (chdir("grindir") != 0) {
printf("grind: chdir grindir failed\n");
exit(1);
}
chdir("/");
while (1) {
iters++;
if ((iters % 500) == 0)
write(1, which_child ? "B" : "A", 1);
int what = rand() % 23;
if (what == 1) {
close(open("grindir/../a", O_CREATE | O_RDWR));
} else if (what == 2) {
close(open("grindir/../grindir/../b", O_CREATE | O_RDWR));
} else if (what == 3) {
unlink("grindir/../a");
} else if (what == 4) {
if (chdir("grindir") != 0) {
printf("grind: chdir grindir failed\n");
exit(1);
}
unlink("../b");
chdir("/");
} else if (what == 5) {
close(fd);
fd = open("/grindir/../a", O_CREATE | O_RDWR);
} else if (what == 6) {
close(fd);
fd = open("/./grindir/./../b", O_CREATE | O_RDWR);
} else if (what == 7) {
write(fd, buf, sizeof(buf));
} else if (what == 8) {
read(fd, buf, sizeof(buf));
} else if (what == 9) {
mkdir("grindir/../a");
close(open("a/../a/./a", O_CREATE | O_RDWR));
unlink("a/a");
} else if (what == 10) {
mkdir("/../b");
close(open("grindir/../b/b", O_CREATE | O_RDWR));
unlink("b/b");
} else if (what == 11) {
unlink("b");
link("../grindir/./../a", "../b");
} else if (what == 12) {
unlink("../grindir/../a");
link(".././b", "/grindir/../a");
} else if (what == 13) {
int pid = fork();
if (pid == 0) {
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if (what == 14) {
int pid = fork();
if (pid == 0) {
fork();
fork();
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if (what == 15) {
sbrk(6011);
} else if (what == 16) {
if (sbrk(0) > break0)
sbrk(-(sbrk(0) - break0));
} else if (what == 17) {
int pid = fork();
if (pid == 0) {
close(open("a", O_CREATE | O_RDWR));
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
if (chdir("../grindir/..") != 0) {
printf("grind: chdir failed\n");
exit(1);
}
kill(pid);
wait(0);
} else if (what == 18) {
int pid = fork();
if (pid == 0) {
kill(getpid());
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if (what == 19) {
int fds[2];
if (pipe(fds) < 0) {
printf("grind: pipe failed\n");
exit(1);
}
int pid = fork();
if (pid == 0) {
fork();
fork();
if (write(fds[1], "x", 1) != 1)
printf("grind: pipe write failed\n");
char c;
if (read(fds[0], &c, 1) != 1)
printf("grind: pipe read failed\n");
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
close(fds[0]);
close(fds[1]);
wait(0);
} else if (what == 20) {
int pid = fork();
if (pid == 0) {
unlink("a");
mkdir("a");
chdir("a");
unlink("../a");
fd = open("x", O_CREATE | O_RDWR);
unlink("x");
exit(0);
} else if (pid < 0) {
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if (what == 21) {
unlink("c");
// should always succeed. check that there are free i-nodes,
// file descriptors, blocks.
int fd1 = open("c", O_CREATE | O_RDWR);
if (fd1 < 0) {
printf("grind: create c failed\n");
exit(1);
}
if (write(fd1, "x", 1) != 1) {
printf("grind: write c failed\n");
exit(1);
}
struct stat st;
if (fstat(fd1, &st) != 0) {
printf("grind: fstat failed\n");
exit(1);
}
if (st.size != 1) {
printf("grind: fstat reports wrong size %d\n", (int)st.size);
exit(1);
}
if (st.ino > 200) {
printf("grind: fstat reports crazy i-number %d\n", st.ino);
exit(1);
}
close(fd1);
unlink("c");
} else if (what == 22) {
// echo hi | cat
int aa[2], bb[2];
if (pipe(aa) < 0) {
fprintf(2, "grind: pipe failed\n");
exit(1);
}
if (pipe(bb) < 0) {
fprintf(2, "grind: pipe failed\n");
exit(1);
}
int pid1 = fork();
if (pid1 == 0) {
close(bb[0]);
close(bb[1]);
close(aa[0]);
close(1);
if (dup(aa[1]) != 1) {
fprintf(2, "grind: dup failed\n");
exit(1);
}
close(aa[1]);
char *args[3] = {"echo", "hi", 0};
exec("grindir/../echo", args);
fprintf(2, "grind: echo: not found\n");
exit(2);
} else if (pid1 < 0) {
fprintf(2, "grind: fork failed\n");
exit(3);
}
int pid2 = fork();
if (pid2 == 0) {
close(aa[1]);
close(bb[0]);
close(0);
if (dup(aa[0]) != 0) {
fprintf(2, "grind: dup failed\n");
exit(4);
}
close(aa[0]);
close(1);
if (dup(bb[1]) != 1) {
fprintf(2, "grind: dup failed\n");
exit(5);
}
close(bb[1]);
char *args[2] = {"cat", 0};
exec("/cat", args);
fprintf(2, "grind: cat: not found\n");
exit(6);
} else if (pid2 < 0) {
fprintf(2, "grind: fork failed\n");
exit(7);
}
close(aa[0]);
close(aa[1]);
close(bb[1]);
char buf[4] = {0, 0, 0, 0};
read(bb[0], buf + 0, 1);
read(bb[0], buf + 1, 1);
read(bb[0], buf + 2, 1);
close(bb[0]);
int st1, st2;
wait(&st1);
wait(&st2);
if (st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0) {
printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2,
buf);
exit(1);
}
}
}
} }
void iter() { void
unlink("a"); go(int which_child)
unlink("b"); {
int fd = -1;
static char buf[999];
char *break0 = sbrk(0);
uint64 iters = 0;
int pid1 = fork(); mkdir("grindir");
if (pid1 < 0) { if(chdir("grindir") != 0){
printf("grind: fork failed\n"); printf("grind: chdir grindir failed\n");
exit(1); exit(1);
} }
if (pid1 == 0) { chdir("/");
rand_next ^= 31;
go(0); while(1){
exit(0); iters++;
} if((iters % 500) == 0)
write(1, which_child?"B":"A", 1);
int pid2 = fork(); int what = rand() % 23;
if (pid2 < 0) { if(what == 1){
printf("grind: fork failed\n"); close(open("grindir/../a", O_CREATE|O_RDWR));
exit(1); } else if(what == 2){
} close(open("grindir/../grindir/../b", O_CREATE|O_RDWR));
if (pid2 == 0) { } else if(what == 3){
rand_next ^= 7177; unlink("grindir/../a");
go(1); } else if(what == 4){
exit(0); if(chdir("grindir") != 0){
} printf("grind: chdir grindir failed\n");
exit(1);
int st1 = -1; }
wait(&st1); unlink("../b");
if (st1 != 0) { chdir("/");
kill(pid1); } else if(what == 5){
kill(pid2); close(fd);
} fd = open("/grindir/../a", O_CREATE|O_RDWR);
int st2 = -1; } else if(what == 6){
wait(&st2); close(fd);
fd = open("/./grindir/./../b", O_CREATE|O_RDWR);
exit(0); } else if(what == 7){
write(fd, buf, sizeof(buf));
} else if(what == 8){
read(fd, buf, sizeof(buf));
} else if(what == 9){
mkdir("grindir/../a");
close(open("a/../a/./a", O_CREATE|O_RDWR));
unlink("a/a");
} else if(what == 10){
mkdir("/../b");
close(open("grindir/../b/b", O_CREATE|O_RDWR));
unlink("b/b");
} else if(what == 11){
unlink("b");
link("../grindir/./../a", "../b");
} else if(what == 12){
unlink("../grindir/../a");
link(".././b", "/grindir/../a");
} else if(what == 13){
int pid = fork();
if(pid == 0){
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if(what == 14){
int pid = fork();
if(pid == 0){
fork();
fork();
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if(what == 15){
sbrk(6011);
} else if(what == 16){
if(sbrk(0) > break0)
sbrk(-(sbrk(0) - break0));
} else if(what == 17){
int pid = fork();
if(pid == 0){
close(open("a", O_CREATE|O_RDWR));
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
if(chdir("../grindir/..") != 0){
printf("grind: chdir failed\n");
exit(1);
}
kill(pid);
wait(0);
} else if(what == 18){
int pid = fork();
if(pid == 0){
kill(getpid());
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if(what == 19){
int fds[2];
if(pipe(fds) < 0){
printf("grind: pipe failed\n");
exit(1);
}
int pid = fork();
if(pid == 0){
fork();
fork();
if(write(fds[1], "x", 1) != 1)
printf("grind: pipe write failed\n");
char c;
if(read(fds[0], &c, 1) != 1)
printf("grind: pipe read failed\n");
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
close(fds[0]);
close(fds[1]);
wait(0);
} else if(what == 20){
int pid = fork();
if(pid == 0){
unlink("a");
mkdir("a");
chdir("a");
unlink("../a");
fd = open("x", O_CREATE|O_RDWR);
unlink("x");
exit(0);
} else if(pid < 0){
printf("grind: fork failed\n");
exit(1);
}
wait(0);
} else if(what == 21){
unlink("c");
// should always succeed. check that there are free i-nodes,
// file descriptors, blocks.
int fd1 = open("c", O_CREATE|O_RDWR);
if(fd1 < 0){
printf("grind: create c failed\n");
exit(1);
}
if(write(fd1, "x", 1) != 1){
printf("grind: write c failed\n");
exit(1);
}
struct stat st;
if(fstat(fd1, &st) != 0){
printf("grind: fstat failed\n");
exit(1);
}
if(st.size != 1){
printf("grind: fstat reports wrong size %d\n", (int)st.size);
exit(1);
}
if(st.ino > 200){
printf("grind: fstat reports crazy i-number %d\n", st.ino);
exit(1);
}
close(fd1);
unlink("c");
} else if(what == 22){
// echo hi | cat
int aa[2], bb[2];
if(pipe(aa) < 0){
fprintf(2, "grind: pipe failed\n");
exit(1);
}
if(pipe(bb) < 0){
fprintf(2, "grind: pipe failed\n");
exit(1);
}
int pid1 = fork();
if(pid1 == 0){
close(bb[0]);
close(bb[1]);
close(aa[0]);
close(1);
if(dup(aa[1]) != 1){
fprintf(2, "grind: dup failed\n");
exit(1);
}
close(aa[1]);
char *args[3] = { "echo", "hi", 0 };
exec("grindir/../echo", args);
fprintf(2, "grind: echo: not found\n");
exit(2);
} else if(pid1 < 0){
fprintf(2, "grind: fork failed\n");
exit(3);
}
int pid2 = fork();
if(pid2 == 0){
close(aa[1]);
close(bb[0]);
close(0);
if(dup(aa[0]) != 0){
fprintf(2, "grind: dup failed\n");
exit(4);
}
close(aa[0]);
close(1);
if(dup(bb[1]) != 1){
fprintf(2, "grind: dup failed\n");
exit(5);
}
close(bb[1]);
char *args[2] = { "cat", 0 };
exec("/cat", args);
fprintf(2, "grind: cat: not found\n");
exit(6);
} else if(pid2 < 0){
fprintf(2, "grind: fork failed\n");
exit(7);
}
close(aa[0]);
close(aa[1]);
close(bb[1]);
char buf[4] = { 0, 0, 0, 0 };
read(bb[0], buf+0, 1);
read(bb[0], buf+1, 1);
read(bb[0], buf+2, 1);
close(bb[0]);
int st1, st2;
wait(&st1);
wait(&st2);
if(st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0){
printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf);
exit(1);
}
}
}
} }
int main() { void
while (1) { iter()
int pid = fork(); {
if (pid == 0) { unlink("a");
iter(); unlink("b");
exit(0);
} int pid1 = fork();
if (pid > 0) { if(pid1 < 0){
wait(0); printf("grind: fork failed\n");
} exit(1);
sleep(20); }
rand_next += 1; if(pid1 == 0){
} rand_next = 31;
go(0);
exit(0);
}
int pid2 = fork();
if(pid2 < 0){
printf("grind: fork failed\n");
exit(1);
}
if(pid2 == 0){
rand_next = 7177;
go(1);
exit(0);
}
int st1 = -1;
wait(&st1);
if(st1 != 0){
kill(pid1);
kill(pid2);
}
int st2 = -1;
wait(&st2);
exit(0);
}
int
main()
{
while(1){
int pid = fork();
if(pid == 0){
iter();
exit(0);
}
if(pid > 0){
wait(0);
}
sleep(20);
}
} }

View File

@ -9,44 +9,46 @@
#include "user/user.h" #include "user/user.h"
#include "kernel/fcntl.h" #include "kernel/fcntl.h"
char *argv[] = {"sh", 0}; char *argv[] = { "sh", 0 };
int main(void) { int
int pid, wpid; main(void)
{
int pid, wpid;
if (open("console", O_RDWR) < 0) { if(open("console", O_RDWR) < 0){
mknod("console", CONSOLE, 0); mknod("console", CONSOLE, 0);
open("console", O_RDWR); open("console", O_RDWR);
} }
dup(0); // stdout dup(0); // stdout
dup(0); // stderr dup(0); // stderr
for (;;) { for(;;){
printf("init: starting sh\n"); printf("init: starting sh\n");
pid = fork(); pid = fork();
if (pid < 0) { if(pid < 0){
printf("init: fork failed\n"); printf("init: fork failed\n");
exit(1); exit(1);
} }
if (pid == 0) { if(pid == 0){
exec("sh", argv); exec("sh", argv);
printf("init: exec sh failed\n"); printf("init: exec sh failed\n");
exit(1); exit(1);
} }
for (;;) { for(;;){
// this call to wait() returns if the shell exits, // this call to wait() returns if the shell exits,
// or if a parentless process exits. // or if a parentless process exits.
wpid = wait((int *)0); wpid = wait((int *) 0);
if (wpid == pid) { if(wpid == pid){
// the shell exited; restart it. // the shell exited; restart it.
break; break;
} else if (wpid < 0) { } else if(wpid < 0){
printf("init: wait returned an error\n"); printf("init: wait returned an error\n");
exit(1); exit(1);
} else { } else {
// it was a parentless process; do nothing. // it was a parentless process; do nothing.
} }
} }
} }
} }

View File

@ -2,14 +2,16 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(int argc, char **argv) { int
int i; main(int argc, char **argv)
{
int i;
if (argc < 2) { if(argc < 2){
fprintf(2, "usage: kill pid...\n"); fprintf(2, "usage: kill pid...\n");
exit(1); exit(1);
} }
for (i = 1; i < argc; i++) for(i=1; i<argc; i++)
kill(atoi(argv[i])); kill(atoi(argv[i]));
exit(0); exit(0);
} }

View File

@ -2,12 +2,14 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(int argc, char *argv[]) { int
if (argc != 3) { main(int argc, char *argv[])
fprintf(2, "Usage: ln old new\n"); {
exit(1); if(argc != 3){
} fprintf(2, "Usage: ln old new\n");
if (link(argv[1], argv[2]) < 0) exit(1);
fprintf(2, "link %s %s: failed\n", argv[1], argv[2]); }
exit(0); if(link(argv[1], argv[2]) < 0)
fprintf(2, "link %s %s: failed\n", argv[1], argv[2]);
exit(0);
} }

132
user/ls.c
View File

@ -3,78 +3,84 @@
#include "user/user.h" #include "user/user.h"
#include "kernel/fs.h" #include "kernel/fs.h"
char *fmtname(char *path) { char*
static char buf[DIRSIZ + 1]; fmtname(char *path)
char *p; {
static char buf[DIRSIZ+1];
char *p;
// Find first character after last slash. // Find first character after last slash.
for (p = path + strlen(path); p >= path && *p != '/'; p--) for(p=path+strlen(path); p >= path && *p != '/'; p--)
; ;
p++; p++;
// Return blank-padded name. // Return blank-padded name.
if (strlen(p) >= DIRSIZ) if(strlen(p) >= DIRSIZ)
return p; return p;
memmove(buf, p, strlen(p)); memmove(buf, p, strlen(p));
memset(buf + strlen(p), ' ', DIRSIZ - strlen(p)); memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf; return buf;
} }
void ls(char *path) { void
char buf[512], *p; ls(char *path)
int fd; {
struct dirent de; char buf[512], *p;
struct stat st; int fd;
struct dirent de;
struct stat st;
if ((fd = open(path, 0)) < 0) { if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path); fprintf(2, "ls: cannot open %s\n", path);
return; return;
} }
if (fstat(fd, &st) < 0) { if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path); fprintf(2, "ls: cannot stat %s\n", path);
close(fd); close(fd);
return; return;
} }
switch (st.type) { switch(st.type){
case T_DEVICE: case T_DEVICE:
case T_FILE: case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size); printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break; break;
case T_DIR: case T_DIR:
if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) { if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: path too long\n"); printf("ls: path too long\n");
break; break;
} }
strcpy(buf, path); strcpy(buf, path);
p = buf + strlen(buf); p = buf+strlen(buf);
*p++ = '/'; *p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de)) { while(read(fd, &de, sizeof(de)) == sizeof(de)){
if (de.inum == 0) if(de.inum == 0)
continue; continue;
memmove(p, de.name, DIRSIZ); memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0; p[DIRSIZ] = 0;
if (stat(buf, &st) < 0) { if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf); printf("ls: cannot stat %s\n", buf);
continue; continue;
} }
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
} }
break; break;
} }
close(fd); close(fd);
} }
int main(int argc, char *argv[]) { int
int i; main(int argc, char *argv[])
{
int i;
if (argc < 2) { if(argc < 2){
ls("."); ls(".");
exit(0); exit(0);
} }
for (i = 1; i < argc; i++) for(i=1; i<argc; i++)
ls(argv[i]); ls(argv[i]);
exit(0); exit(0);
} }

View File

@ -2,20 +2,22 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(int argc, char *argv[]) { int
int i; main(int argc, char *argv[])
{
int i;
if (argc < 2) { if(argc < 2){
fprintf(2, "Usage: mkdir files...\n"); fprintf(2, "Usage: mkdir files...\n");
exit(1); exit(1);
} }
for (i = 1; i < argc; i++) { for(i = 1; i < argc; i++){
if (mkdir(argv[i]) < 0) { if(mkdir(argv[i]) < 0){
fprintf(2, "mkdir: %s failed to create\n", argv[i]); fprintf(2, "mkdir: %s failed to create\n", argv[i]);
break; break;
} }
} }
exit(0); exit(0);
} }

View File

@ -6,95 +6,108 @@
static char digits[] = "0123456789ABCDEF"; static char digits[] = "0123456789ABCDEF";
static void putc(int fd, char c) { write(fd, &c, 1); } static void
putc(int fd, char c)
static void printint(int fd, int xx, int base, int sgn) { {
char buf[16]; write(fd, &c, 1);
int i, neg;
uint x;
neg = 0;
if (sgn && xx < 0) {
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do {
buf[i++] = digits[x % base];
} while ((x /= base) != 0);
if (neg)
buf[i++] = '-';
while (--i >= 0)
putc(fd, buf[i]);
} }
static void printptr(int fd, uint64 x) { static void
int i; printint(int fd, int xx, int base, int sgn)
putc(fd, '0'); {
putc(fd, 'x'); char buf[16];
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) int i, neg;
putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]); uint x;
neg = 0;
if(sgn && xx < 0){
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do{
buf[i++] = digits[x % base];
}while((x /= base) != 0);
if(neg)
buf[i++] = '-';
while(--i >= 0)
putc(fd, buf[i]);
}
static void
printptr(int fd, uint64 x) {
int i;
putc(fd, '0');
putc(fd, 'x');
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
} }
// Print to the given fd. Only understands %d, %x, %p, %s. // Print to the given fd. Only understands %d, %x, %p, %s.
void vprintf(int fd, const char *fmt, va_list ap) { void
char *s; vprintf(int fd, const char *fmt, va_list ap)
int c, i, state; {
char *s;
int c, i, state;
state = 0; state = 0;
for (i = 0; fmt[i]; i++) { for(i = 0; fmt[i]; i++){
c = fmt[i] & 0xff; c = fmt[i] & 0xff;
if (state == 0) { if(state == 0){
if (c == '%') { if(c == '%'){
state = '%'; state = '%';
} else { } else {
putc(fd, c); putc(fd, c);
} }
} else if (state == '%') { } else if(state == '%'){
if (c == 'd') { if(c == 'd'){
printint(fd, va_arg(ap, int), 10, 1); printint(fd, va_arg(ap, int), 10, 1);
} else if (c == 'l') { } else if(c == 'l') {
printint(fd, va_arg(ap, uint64), 10, 0); printint(fd, va_arg(ap, uint64), 10, 0);
} else if (c == 'x') { } else if(c == 'x') {
printint(fd, va_arg(ap, int), 16, 0); printint(fd, va_arg(ap, int), 16, 0);
} else if (c == 'p') { } else if(c == 'p') {
printptr(fd, va_arg(ap, uint64)); printptr(fd, va_arg(ap, uint64));
} else if (c == 's') { } else if(c == 's'){
s = va_arg(ap, char *); s = va_arg(ap, char*);
if (s == 0) if(s == 0)
s = "(null)"; s = "(null)";
while (*s != 0) { while(*s != 0){
putc(fd, *s); putc(fd, *s);
s++; s++;
} }
} else if (c == 'c') { } else if(c == 'c'){
putc(fd, va_arg(ap, uint)); putc(fd, va_arg(ap, uint));
} else if (c == '%') { } else if(c == '%'){
putc(fd, c); putc(fd, c);
} else { } else {
// Unknown % sequence. Print it to draw attention. // Unknown % sequence. Print it to draw attention.
putc(fd, '%'); putc(fd, '%');
putc(fd, c); putc(fd, c);
} }
state = 0; state = 0;
} }
} }
} }
void fprintf(int fd, const char *fmt, ...) { void
va_list ap; fprintf(int fd, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vprintf(fd, fmt, ap); vprintf(fd, fmt, ap);
} }
void printf(const char *fmt, ...) { void
va_list ap; printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vprintf(1, fmt, ap); vprintf(1, fmt, ap);
} }

View File

@ -2,20 +2,22 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(int argc, char *argv[]) { int
int i; main(int argc, char *argv[])
{
int i;
if (argc < 2) { if(argc < 2){
fprintf(2, "Usage: rm files...\n"); fprintf(2, "Usage: rm files...\n");
exit(1); exit(1);
} }
for (i = 1; i < argc; i++) { for(i = 1; i < argc; i++){
if (unlink(argv[i]) < 0) { if(unlink(argv[i]) < 0){
fprintf(2, "rm: %s failed to delete\n", argv[i]); fprintf(2, "rm: %s failed to delete\n", argv[i]);
break; break;
} }
} }
exit(0); exit(0);
} }

739
user/sh.c
View File

@ -5,453 +5,490 @@
#include "kernel/fcntl.h" #include "kernel/fcntl.h"
// Parsed command representation // Parsed command representation
#define EXEC 1 #define EXEC 1
#define REDIR 2 #define REDIR 2
#define PIPE 3 #define PIPE 3
#define LIST 4 #define LIST 4
#define BACK 5 #define BACK 5
#define MAXARGS 10 #define MAXARGS 10
struct cmd { struct cmd {
int type; int type;
}; };
struct execcmd { struct execcmd {
int type; int type;
char *argv[MAXARGS]; char *argv[MAXARGS];
char *eargv[MAXARGS]; char *eargv[MAXARGS];
}; };
struct redircmd { struct redircmd {
int type; int type;
struct cmd *cmd; struct cmd *cmd;
char *file; char *file;
char *efile; char *efile;
int mode; int mode;
int fd; int fd;
}; };
struct pipecmd { struct pipecmd {
int type; int type;
struct cmd *left; struct cmd *left;
struct cmd *right; struct cmd *right;
}; };
struct listcmd { struct listcmd {
int type; int type;
struct cmd *left; struct cmd *left;
struct cmd *right; struct cmd *right;
}; };
struct backcmd { struct backcmd {
int type; int type;
struct cmd *cmd; struct cmd *cmd;
}; };
int fork1(void); // Fork but panics on failure. int fork1(void); // Fork but panics on failure.
void panic(char *); void panic(char*);
struct cmd *parsecmd(char *); struct cmd *parsecmd(char*);
void runcmd(struct cmd *) __attribute__((noreturn)); void runcmd(struct cmd*) __attribute__((noreturn));
// Execute cmd. Never returns. // Execute cmd. Never returns.
void runcmd(struct cmd *cmd) { void
int p[2]; runcmd(struct cmd *cmd)
struct backcmd *bcmd; {
struct execcmd *ecmd; int p[2];
struct listcmd *lcmd; struct backcmd *bcmd;
struct pipecmd *pcmd; struct execcmd *ecmd;
struct redircmd *rcmd; struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if (cmd == 0) if(cmd == 0)
exit(1); exit(1);
switch (cmd->type) { switch(cmd->type){
default: default:
panic("runcmd"); panic("runcmd");
case EXEC: case EXEC:
ecmd = (struct execcmd *)cmd; ecmd = (struct execcmd*)cmd;
if (ecmd->argv[0] == 0) if(ecmd->argv[0] == 0)
exit(1); exit(1);
exec(ecmd->argv[0], ecmd->argv); exec(ecmd->argv[0], ecmd->argv);
fprintf(2, "exec %s failed\n", ecmd->argv[0]); fprintf(2, "exec %s failed\n", ecmd->argv[0]);
break; break;
case REDIR: case REDIR:
rcmd = (struct redircmd *)cmd; rcmd = (struct redircmd*)cmd;
close(rcmd->fd); close(rcmd->fd);
if (open(rcmd->file, rcmd->mode) < 0) { if(open(rcmd->file, rcmd->mode) < 0){
fprintf(2, "open %s failed\n", rcmd->file); fprintf(2, "open %s failed\n", rcmd->file);
exit(1); exit(1);
} }
runcmd(rcmd->cmd); runcmd(rcmd->cmd);
break; break;
case LIST: case LIST:
lcmd = (struct listcmd *)cmd; lcmd = (struct listcmd*)cmd;
if (fork1() == 0) if(fork1() == 0)
runcmd(lcmd->left); runcmd(lcmd->left);
wait(0); wait(0);
runcmd(lcmd->right); runcmd(lcmd->right);
break; break;
case PIPE: case PIPE:
pcmd = (struct pipecmd *)cmd; pcmd = (struct pipecmd*)cmd;
if (pipe(p) < 0) if(pipe(p) < 0)
panic("pipe"); panic("pipe");
if (fork1() == 0) { if(fork1() == 0){
close(1); close(1);
dup(p[1]); dup(p[1]);
close(p[0]); close(p[0]);
close(p[1]); close(p[1]);
runcmd(pcmd->left); runcmd(pcmd->left);
} }
if (fork1() == 0) { if(fork1() == 0){
close(0); close(0);
dup(p[0]); dup(p[0]);
close(p[0]); close(p[0]);
close(p[1]); close(p[1]);
runcmd(pcmd->right); runcmd(pcmd->right);
} }
close(p[0]); close(p[0]);
close(p[1]); close(p[1]);
wait(0); wait(0);
wait(0); wait(0);
break; break;
case BACK: case BACK:
bcmd = (struct backcmd *)cmd; bcmd = (struct backcmd*)cmd;
if (fork1() == 0) if(fork1() == 0)
runcmd(bcmd->cmd); runcmd(bcmd->cmd);
break; break;
} }
exit(0); exit(0);
} }
int getcmd(char *buf, int nbuf) { int
write(2, "$ ", 2); getcmd(char *buf, int nbuf)
memset(buf, 0, nbuf); {
gets(buf, nbuf); write(2, "$ ", 2);
if (buf[0] == 0) // EOF memset(buf, 0, nbuf);
return -1; gets(buf, nbuf);
return 0; if(buf[0] == 0) // EOF
return -1;
return 0;
} }
int main(void) { int
static char buf[100]; main(void)
int fd; {
static char buf[100];
int fd;
// Ensure that three file descriptors are open. // Ensure that three file descriptors are open.
while ((fd = open("console", O_RDWR)) >= 0) { while((fd = open("console", O_RDWR)) >= 0){
if (fd >= 3) { if(fd >= 3){
close(fd); close(fd);
break; break;
} }
} }
// Read and run input commands. // Read and run input commands.
while (getcmd(buf, sizeof(buf)) >= 0) { while(getcmd(buf, sizeof(buf)) >= 0){
if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ') { if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
// Chdir must be called by the parent, not the child. // Chdir must be called by the parent, not the child.
buf[strlen(buf) - 1] = 0; // chop \n buf[strlen(buf)-1] = 0; // chop \n
if (chdir(buf + 3) < 0) if(chdir(buf+3) < 0)
fprintf(2, "cannot cd %s\n", buf + 3); fprintf(2, "cannot cd %s\n", buf+3);
continue; continue;
} }
if (fork1() == 0) if(fork1() == 0)
runcmd(parsecmd(buf)); runcmd(parsecmd(buf));
wait(0); wait(0);
} }
exit(0); exit(0);
} }
void panic(char *s) { void
fprintf(2, "%s\n", s); panic(char *s)
exit(1); {
fprintf(2, "%s\n", s);
exit(1);
} }
int fork1(void) { int
int pid; fork1(void)
{
int pid;
pid = fork(); pid = fork();
if (pid == -1) if(pid == -1)
panic("fork"); panic("fork");
return pid; return pid;
} }
// PAGEBREAK! //PAGEBREAK!
// Constructors // Constructors
struct cmd *execcmd(void) { struct cmd*
struct execcmd *cmd; execcmd(void)
{
struct execcmd *cmd;
cmd = malloc(sizeof(*cmd)); cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->type = EXEC; cmd->type = EXEC;
return (struct cmd *)cmd; return (struct cmd*)cmd;
} }
struct cmd *redircmd(struct cmd *subcmd, char *file, char *efile, int mode, struct cmd*
int fd) { redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
struct redircmd *cmd; {
struct redircmd *cmd;
cmd = malloc(sizeof(*cmd)); cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->type = REDIR; cmd->type = REDIR;
cmd->cmd = subcmd; cmd->cmd = subcmd;
cmd->file = file; cmd->file = file;
cmd->efile = efile; cmd->efile = efile;
cmd->mode = mode; cmd->mode = mode;
cmd->fd = fd; cmd->fd = fd;
return (struct cmd *)cmd; return (struct cmd*)cmd;
} }
struct cmd *pipecmd(struct cmd *left, struct cmd *right) { struct cmd*
struct pipecmd *cmd; pipecmd(struct cmd *left, struct cmd *right)
{
struct pipecmd *cmd;
cmd = malloc(sizeof(*cmd)); cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->type = PIPE; cmd->type = PIPE;
cmd->left = left; cmd->left = left;
cmd->right = right; cmd->right = right;
return (struct cmd *)cmd; return (struct cmd*)cmd;
} }
struct cmd *listcmd(struct cmd *left, struct cmd *right) { struct cmd*
struct listcmd *cmd; listcmd(struct cmd *left, struct cmd *right)
{
struct listcmd *cmd;
cmd = malloc(sizeof(*cmd)); cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->type = LIST; cmd->type = LIST;
cmd->left = left; cmd->left = left;
cmd->right = right; cmd->right = right;
return (struct cmd *)cmd; return (struct cmd*)cmd;
} }
struct cmd *backcmd(struct cmd *subcmd) { struct cmd*
struct backcmd *cmd; backcmd(struct cmd *subcmd)
{
struct backcmd *cmd;
cmd = malloc(sizeof(*cmd)); cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->type = BACK; cmd->type = BACK;
cmd->cmd = subcmd; cmd->cmd = subcmd;
return (struct cmd *)cmd; return (struct cmd*)cmd;
} }
// PAGEBREAK! //PAGEBREAK!
// Parsing // Parsing
char whitespace[] = " \t\r\n\v"; char whitespace[] = " \t\r\n\v";
char symbols[] = "<|>&;()"; char symbols[] = "<|>&;()";
int gettoken(char **ps, char *es, char **q, char **eq) { int
char *s; gettoken(char **ps, char *es, char **q, char **eq)
int ret; {
char *s;
int ret;
s = *ps; s = *ps;
while (s < es && strchr(whitespace, *s)) while(s < es && strchr(whitespace, *s))
s++; s++;
if (q) if(q)
*q = s; *q = s;
ret = *s; ret = *s;
switch (*s) { switch(*s){
case 0: case 0:
break; break;
case '|': case '|':
case '(': case '(':
case ')': case ')':
case ';': case ';':
case '&': case '&':
case '<': case '<':
s++; s++;
break; break;
case '>': case '>':
s++; s++;
if (*s == '>') { if(*s == '>'){
ret = '+'; ret = '+';
s++; s++;
} }
break; break;
default: default:
ret = 'a'; ret = 'a';
while (s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
s++; s++;
break; break;
} }
if (eq) if(eq)
*eq = s; *eq = s;
while (s < es && strchr(whitespace, *s)) while(s < es && strchr(whitespace, *s))
s++; s++;
*ps = s; *ps = s;
return ret; return ret;
} }
int peek(char **ps, char *es, char *toks) { int
char *s; peek(char **ps, char *es, char *toks)
{
char *s;
s = *ps; s = *ps;
while (s < es && strchr(whitespace, *s)) while(s < es && strchr(whitespace, *s))
s++; s++;
*ps = s; *ps = s;
return *s && strchr(toks, *s); return *s && strchr(toks, *s);
} }
struct cmd *parseline(char **, char *); struct cmd *parseline(char**, char*);
struct cmd *parsepipe(char **, char *); struct cmd *parsepipe(char**, char*);
struct cmd *parseexec(char **, char *); struct cmd *parseexec(char**, char*);
struct cmd *nulterminate(struct cmd *); struct cmd *nulterminate(struct cmd*);
struct cmd *parsecmd(char *s) { struct cmd*
char *es; parsecmd(char *s)
struct cmd *cmd; {
char *es;
struct cmd *cmd;
es = s + strlen(s); es = s + strlen(s);
cmd = parseline(&s, es); cmd = parseline(&s, es);
peek(&s, es, ""); peek(&s, es, "");
if (s != es) { if(s != es){
fprintf(2, "leftovers: %s\n", s); fprintf(2, "leftovers: %s\n", s);
panic("syntax"); panic("syntax");
} }
nulterminate(cmd); nulterminate(cmd);
return cmd; return cmd;
} }
struct cmd *parseline(char **ps, char *es) { struct cmd*
struct cmd *cmd; parseline(char **ps, char *es)
{
struct cmd *cmd;
cmd = parsepipe(ps, es); cmd = parsepipe(ps, es);
while (peek(ps, es, "&")) { while(peek(ps, es, "&")){
gettoken(ps, es, 0, 0); gettoken(ps, es, 0, 0);
cmd = backcmd(cmd); cmd = backcmd(cmd);
} }
if (peek(ps, es, ";")) { if(peek(ps, es, ";")){
gettoken(ps, es, 0, 0); gettoken(ps, es, 0, 0);
cmd = listcmd(cmd, parseline(ps, es)); cmd = listcmd(cmd, parseline(ps, es));
} }
return cmd; return cmd;
} }
struct cmd *parsepipe(char **ps, char *es) { struct cmd*
struct cmd *cmd; parsepipe(char **ps, char *es)
{
struct cmd *cmd;
cmd = parseexec(ps, es); cmd = parseexec(ps, es);
if (peek(ps, es, "|")) { if(peek(ps, es, "|")){
gettoken(ps, es, 0, 0); gettoken(ps, es, 0, 0);
cmd = pipecmd(cmd, parsepipe(ps, es)); cmd = pipecmd(cmd, parsepipe(ps, es));
} }
return cmd; return cmd;
} }
struct cmd *parseredirs(struct cmd *cmd, char **ps, char *es) { struct cmd*
int tok; parseredirs(struct cmd *cmd, char **ps, char *es)
char *q, *eq; {
int tok;
char *q, *eq;
while (peek(ps, es, "<>")) { while(peek(ps, es, "<>")){
tok = gettoken(ps, es, 0, 0); tok = gettoken(ps, es, 0, 0);
if (gettoken(ps, es, &q, &eq) != 'a') if(gettoken(ps, es, &q, &eq) != 'a')
panic("missing file for redirection"); panic("missing file for redirection");
switch (tok) { switch(tok){
case '<': case '<':
cmd = redircmd(cmd, q, eq, O_RDONLY, 0); cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
break; break;
case '>': case '>':
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREATE | O_TRUNC, 1); cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);
break; break;
case '+': // >> case '+': // >>
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREATE, 1); cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
break; break;
} }
} }
return cmd; return cmd;
} }
struct cmd *parseblock(char **ps, char *es) { struct cmd*
struct cmd *cmd; parseblock(char **ps, char *es)
{
struct cmd *cmd;
if (!peek(ps, es, "(")) if(!peek(ps, es, "("))
panic("parseblock"); panic("parseblock");
gettoken(ps, es, 0, 0); gettoken(ps, es, 0, 0);
cmd = parseline(ps, es); cmd = parseline(ps, es);
if (!peek(ps, es, ")")) if(!peek(ps, es, ")"))
panic("syntax - missing )"); panic("syntax - missing )");
gettoken(ps, es, 0, 0); gettoken(ps, es, 0, 0);
cmd = parseredirs(cmd, ps, es); cmd = parseredirs(cmd, ps, es);
return cmd; return cmd;
} }
struct cmd *parseexec(char **ps, char *es) { struct cmd*
char *q, *eq; parseexec(char **ps, char *es)
int tok, argc; {
struct execcmd *cmd; char *q, *eq;
struct cmd *ret; int tok, argc;
struct execcmd *cmd;
struct cmd *ret;
if (peek(ps, es, "(")) if(peek(ps, es, "("))
return parseblock(ps, es); return parseblock(ps, es);
ret = execcmd(); ret = execcmd();
cmd = (struct execcmd *)ret; cmd = (struct execcmd*)ret;
argc = 0; argc = 0;
ret = parseredirs(ret, ps, es); ret = parseredirs(ret, ps, es);
while (!peek(ps, es, "|)&;")) { while(!peek(ps, es, "|)&;")){
if ((tok = gettoken(ps, es, &q, &eq)) == 0) if((tok=gettoken(ps, es, &q, &eq)) == 0)
break; break;
if (tok != 'a') if(tok != 'a')
panic("syntax"); panic("syntax");
cmd->argv[argc] = q; cmd->argv[argc] = q;
cmd->eargv[argc] = eq; cmd->eargv[argc] = eq;
argc++; argc++;
if (argc >= MAXARGS) if(argc >= MAXARGS)
panic("too many args"); panic("too many args");
ret = parseredirs(ret, ps, es); ret = parseredirs(ret, ps, es);
} }
cmd->argv[argc] = 0; cmd->argv[argc] = 0;
cmd->eargv[argc] = 0; cmd->eargv[argc] = 0;
return ret; return ret;
} }
// NUL-terminate all the counted strings. // NUL-terminate all the counted strings.
struct cmd *nulterminate(struct cmd *cmd) { struct cmd*
int i; nulterminate(struct cmd *cmd)
struct backcmd *bcmd; {
struct execcmd *ecmd; int i;
struct listcmd *lcmd; struct backcmd *bcmd;
struct pipecmd *pcmd; struct execcmd *ecmd;
struct redircmd *rcmd; struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if (cmd == 0) if(cmd == 0)
return 0; return 0;
switch (cmd->type) { switch(cmd->type){
case EXEC: case EXEC:
ecmd = (struct execcmd *)cmd; ecmd = (struct execcmd*)cmd;
for (i = 0; ecmd->argv[i]; i++) for(i=0; ecmd->argv[i]; i++)
*ecmd->eargv[i] = 0; *ecmd->eargv[i] = 0;
break; break;
case REDIR: case REDIR:
rcmd = (struct redircmd *)cmd; rcmd = (struct redircmd*)cmd;
nulterminate(rcmd->cmd); nulterminate(rcmd->cmd);
*rcmd->efile = 0; *rcmd->efile = 0;
break; break;
case PIPE: case PIPE:
pcmd = (struct pipecmd *)cmd; pcmd = (struct pipecmd*)cmd;
nulterminate(pcmd->left); nulterminate(pcmd->left);
nulterminate(pcmd->right); nulterminate(pcmd->right);
break; break;
case LIST: case LIST:
lcmd = (struct listcmd *)cmd; lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left); nulterminate(lcmd->left);
nulterminate(lcmd->right); nulterminate(lcmd->right);
break; break;
case BACK: case BACK:
bcmd = (struct backcmd *)cmd; bcmd = (struct backcmd*)cmd;
nulterminate(bcmd->cmd); nulterminate(bcmd->cmd);
break; break;
} }
return cmd; return cmd;
} }

View File

@ -13,35 +13,37 @@
#include "kernel/fs.h" #include "kernel/fs.h"
#include "kernel/fcntl.h" #include "kernel/fcntl.h"
int main(int argc, char *argv[]) { int
int fd, i; main(int argc, char *argv[])
char path[] = "stressfs0"; {
char data[512]; int fd, i;
char path[] = "stressfs0";
char data[512];
printf("stressfs starting\n"); printf("stressfs starting\n");
memset(data, 'a', sizeof(data)); memset(data, 'a', sizeof(data));
for (i = 0; i < 4; i++) for(i = 0; i < 4; i++)
if (fork() > 0) if(fork() > 0)
break; break;
printf("write %d\n", i); printf("write %d\n", i);
path[8] += i; path[8] += i;
fd = open(path, O_CREATE | O_RDWR); fd = open(path, O_CREATE | O_RDWR);
for (i = 0; i < 20; i++) for(i = 0; i < 20; i++)
// printf(fd, "%d\n", i); // printf(fd, "%d\n", i);
write(fd, data, sizeof(data)); write(fd, data, sizeof(data));
close(fd); close(fd);
printf("read\n"); printf("read\n");
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
read(fd, data, sizeof(data)); read(fd, data, sizeof(data));
close(fd); close(fd);
wait(0); wait(0);
exit(0); exit(0);
} }

View File

@ -6,118 +6,142 @@
// //
// wrapper so that it's OK if main() does not call exit(). // wrapper so that it's OK if main() does not call exit().
// //
void _main() { void
extern int main(); _main()
main(); {
exit(0); extern int main();
main();
exit(0);
} }
char *strcpy(char *s, const char *t) { char*
char *os; strcpy(char *s, const char *t)
{
char *os;
os = s; os = s;
while ((*s++ = *t++) != 0) while((*s++ = *t++) != 0)
; ;
return os; return os;
} }
int strcmp(const char *p, const char *q) { int
while (*p && *p == *q) strcmp(const char *p, const char *q)
p++, q++; {
return (uchar)*p - (uchar)*q; while(*p && *p == *q)
p++, q++;
return (uchar)*p - (uchar)*q;
} }
uint strlen(const char *s) { uint
int n; strlen(const char *s)
{
int n;
for (n = 0; s[n]; n++) for(n = 0; s[n]; n++)
; ;
return n; return n;
} }
void *memset(void *dst, int c, uint n) { void*
char *cdst = (char *)dst; memset(void *dst, int c, uint n)
int i; {
for (i = 0; i < n; i++) { char *cdst = (char *) dst;
cdst[i] = c; int i;
} for(i = 0; i < n; i++){
return dst; cdst[i] = c;
}
return dst;
} }
char *strchr(const char *s, char c) { char*
for (; *s; s++) strchr(const char *s, char c)
if (*s == c) {
return (char *)s; for(; *s; s++)
return 0; if(*s == c)
return (char*)s;
return 0;
} }
char *gets(char *buf, int max) { char*
int i, cc; gets(char *buf, int max)
char c; {
int i, cc;
char c;
for (i = 0; i + 1 < max;) { for(i=0; i+1 < max; ){
cc = read(0, &c, 1); cc = read(0, &c, 1);
if (cc < 1) if(cc < 1)
break; break;
buf[i++] = c; buf[i++] = c;
if (c == '\n' || c == '\r') if(c == '\n' || c == '\r')
break; break;
} }
buf[i] = '\0'; buf[i] = '\0';
return buf; return buf;
} }
int stat(const char *n, struct stat *st) { int
int fd; stat(const char *n, struct stat *st)
int r; {
int fd;
int r;
fd = open(n, O_RDONLY); fd = open(n, O_RDONLY);
if (fd < 0) if(fd < 0)
return -1; return -1;
r = fstat(fd, st); r = fstat(fd, st);
close(fd); close(fd);
return r; return r;
} }
int atoi(const char *s) { int
int n; atoi(const char *s)
{
int n;
n = 0; n = 0;
while ('0' <= *s && *s <= '9') while('0' <= *s && *s <= '9')
n = n * 10 + *s++ - '0'; n = n*10 + *s++ - '0';
return n; return n;
} }
void *memmove(void *vdst, const void *vsrc, int n) { void*
char *dst; memmove(void *vdst, const void *vsrc, int n)
const char *src; {
char *dst;
const char *src;
dst = vdst; dst = vdst;
src = vsrc; src = vsrc;
if (src > dst) { if (src > dst) {
while (n-- > 0) while(n-- > 0)
*dst++ = *src++; *dst++ = *src++;
} else { } else {
dst += n; dst += n;
src += n; src += n;
while (n-- > 0) while(n-- > 0)
*--dst = *--src; *--dst = *--src;
} }
return vdst; return vdst;
} }
int memcmp(const void *s1, const void *s2, uint n) { int
const char *p1 = s1, *p2 = s2; memcmp(const void *s1, const void *s2, uint n)
while (n-- > 0) { {
if (*p1 != *p2) { const char *p1 = s1, *p2 = s2;
return *p1 - *p2; while (n-- > 0) {
} if (*p1 != *p2) {
p1++; return *p1 - *p2;
p2++; }
} p1++;
return 0; p2++;
}
return 0;
} }
void *memcpy(void *dst, const void *src, uint n) { void *
return memmove(dst, src, n); memcpy(void *dst, const void *src, uint n)
{
return memmove(dst, src, n);
} }

View File

@ -9,11 +9,11 @@
typedef long Align; typedef long Align;
union header { union header {
struct { struct {
union header *ptr; union header *ptr;
uint size; uint size;
} s; } s;
Align x; Align x;
}; };
typedef union header Header; typedef union header Header;
@ -21,64 +21,70 @@ typedef union header Header;
static Header base; static Header base;
static Header *freep; static Header *freep;
void free(void *ap) { void
Header *bp, *p; free(void *ap)
{
Header *bp, *p;
bp = (Header *)ap - 1; bp = (Header*)ap - 1;
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; break;
if (bp + bp->s.size == p->s.ptr) { if(bp + bp->s.size == p->s.ptr){
bp->s.size += p->s.ptr->s.size; bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr; bp->s.ptr = p->s.ptr->s.ptr;
} else } else
bp->s.ptr = p->s.ptr; bp->s.ptr = p->s.ptr;
if (p + p->s.size == bp) { if(p + p->s.size == bp){
p->s.size += bp->s.size; p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr; p->s.ptr = bp->s.ptr;
} else } else
p->s.ptr = bp; p->s.ptr = bp;
freep = p; freep = p;
} }
static Header *morecore(uint nu) { static Header*
char *p; morecore(uint nu)
Header *hp; {
char *p;
Header *hp;
if (nu < 4096) if(nu < 4096)
nu = 4096; nu = 4096;
p = sbrk(nu * sizeof(Header)); p = sbrk(nu * sizeof(Header));
if (p == (char *)-1) if(p == (char*)-1)
return 0; return 0;
hp = (Header *)p; hp = (Header*)p;
hp->s.size = nu; hp->s.size = nu;
free((void *)(hp + 1)); free((void*)(hp + 1));
return freep; return freep;
} }
void *malloc(uint nbytes) { void*
Header *p, *prevp; malloc(uint nbytes)
uint nunits; {
Header *p, *prevp;
uint nunits;
nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1; nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1;
if ((prevp = freep) == 0) { if((prevp = freep) == 0){
base.s.ptr = freep = prevp = &base; base.s.ptr = freep = prevp = &base;
base.s.size = 0; base.s.size = 0;
} }
for (p = prevp->s.ptr;; prevp = p, p = p->s.ptr) { for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){
if (p->s.size >= nunits) { if(p->s.size >= nunits){
if (p->s.size == nunits) if(p->s.size == nunits)
prevp->s.ptr = p->s.ptr; prevp->s.ptr = p->s.ptr;
else { else {
p->s.size -= nunits; p->s.size -= nunits;
p += p->s.size; p += p->s.size;
p->s.size = nunits; p->s.size = nunits;
} }
freep = prevp; freep = prevp;
return (void *)(p + 1); return (void*)(p + 1);
} }
if (p == freep) if(p == freep)
if ((p = morecore(nunits)) == 0) if((p = morecore(nunits)) == 0)
return 0; return 0;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,47 +4,51 @@
char buf[512]; char buf[512];
void wc(int fd, char *name) { void
int i, n; wc(int fd, char *name)
int l, w, c, inword; {
int i, n;
int l, w, c, inword;
l = w = c = 0; l = w = c = 0;
inword = 0; inword = 0;
while ((n = read(fd, buf, sizeof(buf))) > 0) { while((n = read(fd, buf, sizeof(buf))) > 0){
for (i = 0; i < n; i++) { for(i=0; i<n; i++){
c++; c++;
if (buf[i] == '\n') if(buf[i] == '\n')
l++; l++;
if (strchr(" \r\t\n\v", buf[i])) if(strchr(" \r\t\n\v", buf[i]))
inword = 0; inword = 0;
else if (!inword) { else if(!inword){
w++; w++;
inword = 1; inword = 1;
} }
} }
} }
if (n < 0) { if(n < 0){
printf("wc: read error\n"); printf("wc: read error\n");
exit(1); exit(1);
} }
printf("%d %d %d %s\n", l, w, c, name); printf("%d %d %d %s\n", l, w, c, name);
} }
int main(int argc, char *argv[]) { int
int fd, i; main(int argc, char *argv[])
{
int fd, i;
if (argc <= 1) { if(argc <= 1){
wc(0, ""); wc(0, "");
exit(0); exit(0);
} }
for (i = 1; i < argc; i++) { for(i = 1; i < argc; i++){
if ((fd = open(argv[i], 0)) < 0) { if((fd = open(argv[i], 0)) < 0){
printf("wc: cannot open %s\n", argv[i]); printf("wc: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }
wc(fd, argv[i]); wc(fd, argv[i]);
close(fd); close(fd);
} }
exit(0); exit(0);
} }

View File

@ -5,8 +5,10 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
int main(void) { int
if (fork() > 0) main(void)
sleep(5); // Let child exit before parent. {
exit(0); if(fork() > 0)
sleep(5); // Let child exit before parent.
exit(0);
} }