From 5753553213df8f9de851adb68377db43faecb91f Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 11 Jun 2019 09:57:14 -0400 Subject: separate source into kernel/ user/ mkfs/ --- .gdbinit.tmpl-i386 | 6 - .gdbinit.tmpl-riscv | 2 +- .gdbinit.tmpl-x64 | 18 - .gitignore | 1 - Makefile | 150 ++--- bio.c | 145 ----- buf.h | 14 - cat.c | 43 -- console.c | 271 -------- date.h | 8 - defs.h | 205 ------ echo.c | 13 - elf.h | 42 -- entry.S | 25 - exec.c | 149 ----- fcntl.h | 4 - file.c | 175 ----- file.h | 37 -- forktest.c | 56 -- fs.c | 681 -------------------- fs.h | 60 -- grep.c | 107 --- init.c | 37 -- initcode.S | 28 - ioapic.c | 76 --- kalloc.c | 80 --- kernel.ld | 31 - kernel/bio.c | 145 +++++ kernel/buf.h | 14 + kernel/console.c | 271 ++++++++ kernel/date.h | 8 + kernel/defs.h | 205 ++++++ kernel/elf.h | 42 ++ kernel/entry.S | 25 + kernel/exec.c | 149 +++++ kernel/fcntl.h | 4 + kernel/file.c | 175 +++++ kernel/file.h | 37 ++ kernel/fs.c | 681 ++++++++++++++++++++ kernel/fs.h | 60 ++ kernel/kalloc.c | 80 +++ kernel/kernel.ld | 31 + kernel/kernelvec.S | 112 ++++ kernel/log.c | 235 +++++++ kernel/main.c | 42 ++ kernel/memlayout.h | 50 ++ kernel/param.h | 13 + kernel/pipe.c | 129 ++++ kernel/plic.c | 63 ++ kernel/proc.c | 591 +++++++++++++++++ kernel/proc.h | 104 +++ kernel/ramdisk.c | 45 ++ kernel/riscv.h | 338 ++++++++++ kernel/sleeplock.c | 55 ++ kernel/sleeplock.h | 10 + kernel/spinlock.c | 110 ++++ kernel/spinlock.h | 9 + kernel/start.c | 56 ++ kernel/stat.h | 11 + kernel/string.c | 104 +++ kernel/swtch.S | 42 ++ kernel/syscall.c | 191 ++++++ kernel/syscall.h | 22 + kernel/sysfile.c | 465 +++++++++++++ kernel/sysproc.c | 90 +++ kernel/trampoline.S | 137 ++++ kernel/trap.c | 184 ++++++ kernel/types.h | 10 + kernel/uart.c | 75 +++ kernel/vm.c | 389 +++++++++++ kernelvec.S | 112 ---- kill.c | 17 - ln.c | 15 - log.c | 235 ------- ls.c | 85 --- main.c | 42 -- memlayout.h | 50 -- mkdir.c | 23 - mkfs.c | 298 --------- mkfs/mkfs.c | 305 +++++++++ param.h | 13 - pipe.c | 129 ---- plic.c | 63 -- printf.c | 95 --- proc.c | 591 ----------------- proc.h | 104 --- ramdisk.c | 45 -- riscv.h | 338 ---------- rm.c | 23 - sh.c | 493 -------------- sleeplock.c | 55 -- sleeplock.h | 10 - spinlock.c | 110 ---- spinlock.h | 9 - start.c | 56 -- stat.h | 11 - stressfs.c | 49 -- string.c | 104 --- swtch.S | 42 -- syscall.c | 191 ------ syscall.h | 22 - sysfile.c | 465 ------------- sysproc.c | 90 --- trampoline.S | 137 ---- trap.c | 184 ------ types.h | 10 - uart.c | 75 --- ulib.c | 109 ---- umalloc.c | 90 --- user.h | 39 -- user/cat.c | 43 ++ user/echo.c | 13 + user/forktest.c | 56 ++ user/grep.c | 107 +++ user/init.c | 37 ++ user/initcode.S | 28 + user/kill.c | 17 + user/ln.c | 15 + user/ls.c | 85 +++ user/mkdir.c | 23 + user/printf.c | 95 +++ user/rm.c | 23 + user/sh.c | 493 ++++++++++++++ user/stressfs.c | 49 ++ user/ulib.c | 109 ++++ user/umalloc.c | 90 +++ user/user.h | 39 ++ user/usertests.c | 1789 +++++++++++++++++++++++++++++++++++++++++++++++++++ user/usys.pl | 38 ++ user/wc.c | 54 ++ user/zombie.c | 14 + usertests.c | 1789 --------------------------------------------------- usys.pl | 38 -- vectors.pl | 47 -- vm.c | 389 ----------- wc.c | 54 -- zombie.c | 14 - 137 files changed, 9208 insertions(+), 9347 deletions(-) delete mode 100644 .gdbinit.tmpl-i386 delete mode 100644 .gdbinit.tmpl-x64 delete mode 100644 bio.c delete mode 100644 buf.h delete mode 100644 cat.c delete mode 100644 console.c delete mode 100644 date.h delete mode 100644 defs.h delete mode 100644 echo.c delete mode 100644 elf.h delete mode 100644 entry.S delete mode 100644 exec.c delete mode 100644 fcntl.h delete mode 100644 file.c delete mode 100644 file.h delete mode 100644 forktest.c delete mode 100644 fs.c delete mode 100644 fs.h delete mode 100644 grep.c delete mode 100644 init.c delete mode 100644 initcode.S delete mode 100644 ioapic.c delete mode 100644 kalloc.c delete mode 100644 kernel.ld create mode 100644 kernel/bio.c create mode 100644 kernel/buf.h create mode 100644 kernel/console.c create mode 100644 kernel/date.h create mode 100644 kernel/defs.h create mode 100644 kernel/elf.h create mode 100644 kernel/entry.S create mode 100644 kernel/exec.c create mode 100644 kernel/fcntl.h create mode 100644 kernel/file.c create mode 100644 kernel/file.h create mode 100644 kernel/fs.c create mode 100644 kernel/fs.h create mode 100644 kernel/kalloc.c create mode 100644 kernel/kernel.ld create mode 100644 kernel/kernelvec.S create mode 100644 kernel/log.c create mode 100644 kernel/main.c create mode 100644 kernel/memlayout.h create mode 100644 kernel/param.h create mode 100644 kernel/pipe.c create mode 100644 kernel/plic.c create mode 100644 kernel/proc.c create mode 100644 kernel/proc.h create mode 100644 kernel/ramdisk.c create mode 100644 kernel/riscv.h create mode 100644 kernel/sleeplock.c create mode 100644 kernel/sleeplock.h create mode 100644 kernel/spinlock.c create mode 100644 kernel/spinlock.h create mode 100644 kernel/start.c create mode 100644 kernel/stat.h create mode 100644 kernel/string.c create mode 100644 kernel/swtch.S create mode 100644 kernel/syscall.c create mode 100644 kernel/syscall.h create mode 100644 kernel/sysfile.c create mode 100644 kernel/sysproc.c create mode 100644 kernel/trampoline.S create mode 100644 kernel/trap.c create mode 100644 kernel/types.h create mode 100644 kernel/uart.c create mode 100644 kernel/vm.c delete mode 100644 kernelvec.S delete mode 100644 kill.c delete mode 100644 ln.c delete mode 100644 log.c delete mode 100644 ls.c delete mode 100644 main.c delete mode 100644 memlayout.h delete mode 100644 mkdir.c delete mode 100644 mkfs.c create mode 100644 mkfs/mkfs.c delete mode 100644 param.h delete mode 100644 pipe.c delete mode 100644 plic.c delete mode 100644 printf.c delete mode 100644 proc.c delete mode 100644 proc.h delete mode 100644 ramdisk.c delete mode 100644 riscv.h delete mode 100644 rm.c delete mode 100644 sh.c delete mode 100644 sleeplock.c delete mode 100644 sleeplock.h delete mode 100644 spinlock.c delete mode 100644 spinlock.h delete mode 100644 start.c delete mode 100644 stat.h delete mode 100644 stressfs.c delete mode 100644 string.c delete mode 100644 swtch.S delete mode 100644 syscall.c delete mode 100644 syscall.h delete mode 100644 sysfile.c delete mode 100644 sysproc.c delete mode 100644 trampoline.S delete mode 100644 trap.c delete mode 100644 types.h delete mode 100644 uart.c delete mode 100644 ulib.c delete mode 100644 umalloc.c delete mode 100644 user.h create mode 100644 user/cat.c create mode 100644 user/echo.c create mode 100644 user/forktest.c create mode 100644 user/grep.c create mode 100644 user/init.c create mode 100644 user/initcode.S create mode 100644 user/kill.c create mode 100644 user/ln.c create mode 100644 user/ls.c create mode 100644 user/mkdir.c create mode 100644 user/printf.c create mode 100644 user/rm.c create mode 100644 user/sh.c create mode 100644 user/stressfs.c create mode 100644 user/ulib.c create mode 100644 user/umalloc.c create mode 100644 user/user.h create mode 100644 user/usertests.c create mode 100755 user/usys.pl create mode 100644 user/wc.c create mode 100644 user/zombie.c delete mode 100644 usertests.c delete mode 100755 usys.pl delete mode 100755 vectors.pl delete mode 100644 vm.c delete mode 100644 wc.c delete mode 100644 zombie.c diff --git a/.gdbinit.tmpl-i386 b/.gdbinit.tmpl-i386 deleted file mode 100644 index a3a274b..0000000 --- a/.gdbinit.tmpl-i386 +++ /dev/null @@ -1,6 +0,0 @@ -set confirm off -python -gdb.execute("target remote localhost:26000") -gdb.execute("set architecture i386") -gdb.execute("symbol-file kernel") -gdb.execute("break *0x7c00") diff --git a/.gdbinit.tmpl-riscv b/.gdbinit.tmpl-riscv index c1616b6..8d461a1 100644 --- a/.gdbinit.tmpl-riscv +++ b/.gdbinit.tmpl-riscv @@ -1,4 +1,4 @@ set confirm off set architecture riscv target remote 127.0.0.1:1234 -symbol-file kernel +symbol-file kernel/kernel diff --git a/.gdbinit.tmpl-x64 b/.gdbinit.tmpl-x64 deleted file mode 100644 index 9c120ff..0000000 --- a/.gdbinit.tmpl-x64 +++ /dev/null @@ -1,18 +0,0 @@ -#if you would like to use gdb in 32bit mode, comment out lines 8 and 15, then uncomment -#the lines after. Note this will only work properly until 64bit mode is enabled in entry.S - -python -gdb.execute("set architecture i386:x86-64:intel") -gdb.execute("target remote localhost:26000") -gdb.execute("symbol-file kernel") -gdb.execute("break start64") -#gdb.execute("break *0x7c00") -try: - gdb.execute("continue") -except: - pass -gdb.execute("disconnect") -gdb.execute("set architecture i386:x86-64") -#gdb.execute("set architecture i386") -gdb.execute("target remote localhost:26000") -gdb.execute("delete break 1") diff --git a/.gitignore b/.gitignore index 3e2c9de..fe754f0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ bootblock entryother initcode initcode.out -kernel kernelmemfs mkfs .gdbinit diff --git a/Makefile b/Makefile index 731a157..545f28c 100644 --- a/Makefile +++ b/Makefile @@ -1,29 +1,33 @@ +K=kernel +U=user + OBJS = \ - start.o \ - console.o \ - uart.o \ - kalloc.o \ - spinlock.o \ - string.o \ - main.o \ - vm.o \ - proc.o \ - swtch.o \ - trampoline.o \ - trap.o \ - syscall.o \ - sysproc.o \ - bio.o \ - fs.o \ - log.o \ - sleeplock.o \ - file.o \ - pipe.o \ - ramdisk.o \ - exec.o \ - sysfile.o \ - kernelvec.o \ - plic.o + $K/entry.o \ + $K/start.o \ + $K/console.o \ + $K/uart.o \ + $K/kalloc.o \ + $K/spinlock.o \ + $K/string.o \ + $K/main.o \ + $K/vm.o \ + $K/proc.o \ + $K/swtch.o \ + $K/trampoline.o \ + $K/trap.o \ + $K/syscall.o \ + $K/sysproc.o \ + $K/bio.o \ + $K/fs.o \ + $K/log.o \ + $K/sleeplock.o \ + $K/file.o \ + $K/pipe.o \ + $K/ramdisk.o \ + $K/exec.o \ + $K/sysfile.o \ + $K/kernelvec.o \ + $K/plic.o # riscv64-unknown-elf- or riscv64-linux-gnu- # perhaps in /opt/riscv/bin @@ -53,6 +57,7 @@ OBJDUMP = $(TOOLPREFIX)objdump CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb CFLAGS += -mcmodel=medany CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax +CFLAGS += -I. CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) # Disable PIE when possible (for Ubuntu 16.10 toolchain) @@ -65,41 +70,41 @@ endif LDFLAGS = -z max-page-size=4096 -kernel: $(OBJS) entry.o kernel.ld initcode - $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) - $(OBJDUMP) -S kernel > kernel.asm - $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym +$K/kernel: $(OBJS) $K/kernel.ld $U/initcode + $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) + $(OBJDUMP) -S $K/kernel > $K/kernel.asm + $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym -initcode: initcode.S - $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S - $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o - $(OBJCOPY) -S -O binary initcode.out initcode - $(OBJDUMP) -S initcode.o > initcode.asm +$U/initcode: $U/initcode.S + $(CC) $(CFLAGS) -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o + $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o + $(OBJCOPY) -S -O binary $U/initcode.out $U/initcode + $(OBJDUMP) -S $U/initcode.o > $U/initcode.asm -tags: $(OBJS) entryother.S _init +tags: $(OBJS) _init etags *.S *.c -vectors.S: vectors.pl - ./vectors.pl > vectors.S - -ULIB = ulib.o usys.o printf.o umalloc.o +ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o _%: %.o $(ULIB) $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ $(OBJDUMP) -S $@ > $*.asm $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym -usys.S : usys.pl - perl ./usys.pl > usys.S +$U/usys.S : $U/usys.pl + perl $U/usys.pl > $U/usys.S + +$U/usys.o : $U/usys.S + $(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S -_forktest: forktest.o $(ULIB) +$U/_forktest: $U/forktest.o $(ULIB) # forktest has less library code linked in - needs to be small # in order to be able to max out the proc table. - $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o - $(OBJDUMP) -S _forktest > forktest.asm + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o + $(OBJDUMP) -S $U/_forktest > $U/forktest.asm -mkfs: mkfs.c fs.h - gcc -Werror -Wall -o mkfs mkfs.c +mkfs/mkfs: mkfs/mkfs.c $K/fs.h + gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c # Prevent deletion of intermediate files, e.g. cat.o, after first build, so # that disk image changes after first build are persistent until clean. More @@ -108,32 +113,33 @@ mkfs: mkfs.c fs.h .PRECIOUS: %.o UPROGS=\ - _cat\ - _echo\ - _forktest\ - _grep\ - _init\ - _kill\ - _ln\ - _ls\ - _mkdir\ - _rm\ - _sh\ - _stressfs\ - _usertests\ - _wc\ - _zombie\ - -fs.img: mkfs README $(UPROGS) - ./mkfs fs.img README $(UPROGS) + $U/_cat\ + $U/_echo\ + $U/_forktest\ + $U/_grep\ + $U/_init\ + $U/_kill\ + $U/_ln\ + $U/_ls\ + $U/_mkdir\ + $U/_rm\ + $U/_sh\ + $U/_stressfs\ + $U/_usertests\ + $U/_wc\ + $U/_zombie\ + +fs.img: mkfs/mkfs README $(UPROGS) + mkfs/mkfs fs.img README $(UPROGS) -include *.d clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ - *.o *.d *.asm *.sym vectors.S bootblock entryother \ - initcode initcode.out kernel fs.img kernelmemfs \ - mkfs .gdbinit \ + */*.o */*.d */*.asm */*.sym \ + $U/initcode $U/initcode.out $K/kernel fs.img \ + mkfs/mkfs .gdbinit \ + $U/usys.S \ $(UPROGS) # make a printout @@ -155,23 +161,19 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ ifndef CPUS CPUS := 3 endif -QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic +QEMUOPTS = -machine virt -kernel $K/kernel -m 3G -smp $(CPUS) -nographic QEMUOPTS += -initrd fs.img -qemu: kernel fs.img +qemu: $K/kernel fs.img $(QEMU) $(QEMUOPTS) .gdbinit: .gdbinit.tmpl-riscv sed "s/:1234/:$(GDBPORT)/" < $^ > $@ -qemu-gdb: kernel .gdbinit fs.img +qemu-gdb: $K/kernel .gdbinit fs.img @echo "*** Now run 'gdb'." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) -qemu-nox-gdb: fs.img kernel .gdbinit - @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) - # CUT HERE # prepare dist for students # after running make dist, probably want to diff --git a/bio.c b/bio.c deleted file mode 100644 index 90f9af9..0000000 --- a/bio.c +++ /dev/null @@ -1,145 +0,0 @@ -// Buffer cache. -// -// The buffer cache is a linked list of buf structures holding -// cached copies of disk block contents. Caching disk blocks -// in memory reduces the number of disk reads and also provides -// a synchronization point for disk blocks used by multiple processes. -// -// Interface: -// * To get a buffer for a particular disk block, call bread. -// * After changing buffer data, call bwrite to write it to disk. -// * When done with the buffer, call brelse. -// * Do not use the buffer after calling brelse. -// * Only one process at a time can use a buffer, -// so do not keep them longer than necessary. -// -// The implementation uses two state flags internally: -// * B_VALID: the buffer data has been read from the disk. -// * B_DIRTY: the buffer data has been modified -// and needs to be written to disk. - -#include "types.h" -#include "param.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "riscv.h" -#include "defs.h" -#include "fs.h" -#include "buf.h" - -struct { - struct spinlock lock; - struct buf buf[NBUF]; - - // Linked list of all buffers, through prev/next. - // head.next is most recently used. - struct buf head; -} bcache; - -void -binit(void) -{ - struct buf *b; - - initlock(&bcache.lock, "bcache"); - -//PAGEBREAK! - // Create linked list of buffers - bcache.head.prev = &bcache.head; - bcache.head.next = &bcache.head; - for(b = bcache.buf; b < bcache.buf+NBUF; b++){ - b->next = bcache.head.next; - b->prev = &bcache.head; - initsleeplock(&b->lock, "buffer"); - bcache.head.next->prev = b; - bcache.head.next = b; - } -} - -// Look through buffer cache for block on device dev. -// If not found, allocate a buffer. -// In either case, return locked buffer. -static struct buf* -bget(uint dev, uint blockno) -{ - struct buf *b; - - acquire(&bcache.lock); - - // Is the block already cached? - for(b = bcache.head.next; b != &bcache.head; b = b->next){ - if(b->dev == dev && b->blockno == blockno){ - b->refcnt++; - release(&bcache.lock); - acquiresleep(&b->lock); - return b; - } - } - - // Not cached; recycle an unused buffer. - // Even if refcnt==0, B_DIRTY indicates a buffer is in use - // because log.c has modified it but not yet committed it. - for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ - if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) { - b->dev = dev; - b->blockno = blockno; - b->flags = 0; - b->refcnt = 1; - release(&bcache.lock); - acquiresleep(&b->lock); - return b; - } - } - panic("bget: no buffers"); -} - -// Return a locked buf with the contents of the indicated block. -struct buf* -bread(uint dev, uint blockno) -{ - struct buf *b; - - b = bget(dev, blockno); - if((b->flags & B_VALID) == 0) { - ramdiskrw(b); - } - return b; -} - -// Write b's contents to disk. Must be locked. -void -bwrite(struct buf *b) -{ - if(!holdingsleep(&b->lock)) - panic("bwrite"); - b->flags |= B_DIRTY; - ramdiskrw(b); -} - -// Release a locked buffer. -// Move to the head of the MRU list. -void -brelse(struct buf *b) -{ - if(!holdingsleep(&b->lock)) - panic("brelse"); - - releasesleep(&b->lock); - - acquire(&bcache.lock); - b->refcnt--; - if (b->refcnt == 0) { - // no one is waiting for it. - b->next->prev = b->prev; - b->prev->next = b->next; - b->next = bcache.head.next; - b->prev = &bcache.head; - bcache.head.next->prev = b; - bcache.head.next = b; - } - - release(&bcache.lock); -} -//PAGEBREAK! -// Blank page. - diff --git a/buf.h b/buf.h deleted file mode 100644 index 3266495..0000000 --- a/buf.h +++ /dev/null @@ -1,14 +0,0 @@ -struct buf { - int flags; - uint dev; - uint blockno; - struct sleeplock lock; - uint refcnt; - struct buf *prev; // LRU cache list - struct buf *next; - struct buf *qnext; // disk queue - uchar data[BSIZE]; -}; -#define B_VALID 0x2 // buffer has been read from disk -#define B_DIRTY 0x4 // buffer needs to be written to disk - diff --git a/cat.c b/cat.c deleted file mode 100644 index 5ddc820..0000000 --- a/cat.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[512]; - -void -cat(int fd) -{ - int n; - - while((n = read(fd, buf, sizeof(buf))) > 0) { - if (write(1, buf, n) != n) { - printf(1, "cat: write error\n"); - exit(); - } - } - if(n < 0){ - printf(1, "cat: read error\n"); - exit(); - } -} - -int -main(int argc, char *argv[]) -{ - int fd, i; - - if(argc <= 1){ - cat(0); - exit(); - } - - for(i = 1; i < argc; i++){ - if((fd = open(argv[i], 0)) < 0){ - printf(1, "cat: cannot open %s\n", argv[i]); - exit(); - } - cat(fd); - close(fd); - } - exit(); -} diff --git a/console.c b/console.c deleted file mode 100644 index b20d4a9..0000000 --- a/console.c +++ /dev/null @@ -1,271 +0,0 @@ -// Console input and output. -// Input is from the keyboard or serial port. -// Output is written to the screen and serial port. - -#include - -#include "types.h" -#include "param.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "file.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" -#include "proc.h" - -static void consputc(int); - -static volatile int panicked = 0; - -static struct { - struct spinlock lock; - int locking; -} cons; - -static char digits[] = "0123456789abcdef"; - -static void -printint(int xx, int base, int sign) -{ - char buf[16]; - int i; - uint x; - - if(sign && (sign = xx < 0)) - x = -xx; - else - x = xx; - - i = 0; - do{ - buf[i++] = digits[x % base]; - }while((x /= base) != 0); - - if(sign) - buf[i++] = '-'; - - while(--i >= 0) - consputc(buf[i]); -} - -static void -printptr(uint64 x) { - int i; - consputc('0'); - consputc('x'); - for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) - consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); -} - - -//PAGEBREAK: 50 - -// Print to the console. only understands %d, %x, %p, %s. -void -printf(char *fmt, ...) -{ - va_list ap; - int i, c, locking; - char *s; - - locking = cons.locking; - if(locking) - acquire(&cons.lock); - - if (fmt == 0) - panic("null fmt"); - - va_start(ap, fmt); - for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ - if(c != '%'){ - consputc(c); - continue; - } - c = fmt[++i] & 0xff; - if(c == 0) - break; - switch(c){ - case 'd': - printint(va_arg(ap, int), 10, 1); - break; - case 'x': - printint(va_arg(ap, int), 16, 1); - break; - case 'p': - printptr(va_arg(ap, uint64)); - break; - case 's': - if((s = va_arg(ap, char*)) == 0) - s = "(null)"; - for(; *s; s++) - consputc(*s); - break; - case '%': - consputc('%'); - break; - default: - // Print unknown % sequence to draw attention. - consputc('%'); - consputc(c); - break; - } - } - - if(locking) - release(&cons.lock); -} - -void -panic(char *s) -{ - cons.locking = 0; - printf("panic: "); - printf(s); - printf("\n"); - panicked = 1; // freeze other CPU - for(;;) - ; -} - -#define BACKSPACE 0x100 - -void -consputc(int c) -{ - if(panicked){ - for(;;) - ; - } - - if(c == BACKSPACE){ - uartputc('\b'); uartputc(' '); uartputc('\b'); - } else - uartputc(c); -} - -#define INPUT_BUF 128 -struct { - char buf[INPUT_BUF]; - uint r; // Read index - uint w; // Write index - uint e; // Edit index -} input; - -#define C(x) ((x)-'@') // Contro - -int -consoleread(struct inode *ip, int user_dst, uint64 dst, int n) -{ - uint target; - int c; - char buf[1]; - - iunlock(ip); - target = n; - acquire(&cons.lock); - while(n > 0){ - while(input.r == input.w){ - if(myproc()->killed){ - release(&cons.lock); - ilock(ip); - return -1; - } - sleep(&input.r, &cons.lock); - } - c = input.buf[input.r++ % INPUT_BUF]; - if(c == C('D')){ // EOF - if(n < target){ - // Save ^D for next time, to make sure - // caller gets a 0-byte result. - input.r--; - } - break; - } - buf[0] = c; - if(either_copyout(user_dst, dst, &buf[0], 1) == -1) - break; - dst++; - --n; - if(c == '\n') - break; - } - release(&cons.lock); - ilock(ip); - - return target - n; -} - -int -consolewrite(struct inode *ip, int user_src, uint64 src, int n) -{ - int i; - - iunlock(ip); - acquire(&cons.lock); - for(i = 0; i < n; i++){ - char c; - if(either_copyin(&c, user_src, src+i, 1) == -1) - break; - consputc(c); - } - release(&cons.lock); - ilock(ip); - - return n; -} - -void -consoleintr(int c) -{ - int doprocdump = 0; - - acquire(&cons.lock); - - switch(c){ - case C('P'): // Process list. - // procdump() locks cons.lock indirectly; invoke later - doprocdump = 1; - break; - case C('U'): // Kill line. - while(input.e != input.w && - input.buf[(input.e-1) % INPUT_BUF] != '\n'){ - input.e--; - consputc(BACKSPACE); - } - break; - case C('H'): case '\x7f': // Backspace - if(input.e != input.w){ - input.e--; - consputc(BACKSPACE); - } - break; - default: - if(c != 0 && input.e-input.r < INPUT_BUF){ - c = (c == '\r') ? '\n' : c; - input.buf[input.e++ % INPUT_BUF] = c; - consputc(c); - if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ - input.w = input.e; - wakeup(&input.r); - } - } - break; - } - - release(&cons.lock); - - if(doprocdump) - procdump(); -} - -void -consoleinit(void) -{ - initlock(&cons.lock, "console"); - - devsw[CONSOLE].write = consolewrite; - devsw[CONSOLE].read = consoleread; - cons.locking = 1; -} diff --git a/date.h b/date.h deleted file mode 100644 index 94aec4b..0000000 --- a/date.h +++ /dev/null @@ -1,8 +0,0 @@ -struct rtcdate { - uint second; - uint minute; - uint hour; - uint day; - uint month; - uint year; -}; diff --git a/defs.h b/defs.h deleted file mode 100644 index 597e5b6..0000000 --- a/defs.h +++ /dev/null @@ -1,205 +0,0 @@ -struct buf; -struct context; -struct file; -struct inode; -struct pipe; -struct proc; -struct rtcdate; -struct spinlock; -struct sleeplock; -struct stat; -struct superblock; -struct sysframe; - -// bio.c -void binit(void); -struct buf* bread(uint, uint); -void brelse(struct buf*); -void bwrite(struct buf*); - -// console.c -void consoleinit(void); -void printf(char*, ...); -void consoleintr(int); -void panic(char*) __attribute__((noreturn)); - -// exec.c -int exec(char*, char**); - -// file.c -struct file* filealloc(void); -void fileclose(struct file*); -struct file* filedup(struct file*); -void fileinit(void); -int fileread(struct file*, uint64, int n); -int filestat(struct file*, uint64 addr); -int filewrite(struct file*, uint64, int n); - -// fs.c -void readsb(int dev, struct superblock *sb); -int dirlink(struct inode*, char*, uint); -struct inode* dirlookup(struct inode*, char*, uint*); -struct inode* ialloc(uint, short); -struct inode* idup(struct inode*); -void iinit(int dev); -void ilock(struct inode*); -void iput(struct inode*); -void iunlock(struct inode*); -void iunlockput(struct inode*); -void iupdate(struct inode*); -int namecmp(const char*, const char*); -struct inode* namei(char*); -struct inode* nameiparent(char*, char*); -int readi(struct inode*, int, uint64, uint, uint); -void stati(struct inode*, struct stat*); -int writei(struct inode*, int, uint64, uint, uint); - -// ramdisk.c -void ramdiskinit(void); -void ramdiskintr(void); -void ramdiskrw(struct buf*); - -// ioapic.c -void ioapicenable(int irq, int cpu); -extern uchar ioapicid; -void ioapicinit(void); - -// kalloc.c -void* kalloc(void); -void kfree(void *); -void kinit(); - -// kbd.c -void kbdintr(void); - -// lapic.c -void cmostime(struct rtcdate *r); -int lapicid(void); -extern volatile uint* lapic; -void lapiceoi(void); -void lapicinit(void); -void lapicstartap(uchar, uint); -void microdelay(int); - -// log.c -void initlog(int dev); -void log_write(struct buf*); -void begin_op(); -void end_op(); - -// mp.c -extern int ismp; -void mpinit(void); - -// picirq.c -void picenable(int); -void picinit(void); - -// pipe.c -int pipealloc(struct file**, struct file**); -void pipeclose(struct pipe*, int); -int piperead(struct pipe*, uint64, int); -int pipewrite(struct pipe*, uint64, int); - -//PAGEBREAK: 16 -// proc.c -int cpuid(void); -void exit(void); -int fork(void); -int growproc(int); -pagetable_t proc_pagetable(struct proc *); -void proc_freepagetable(pagetable_t, uint64); -int kill(int); -struct cpu* mycpu(void); -struct cpu* getmycpu(void); -struct proc* myproc(); -void procinit(void); -void scheduler(void) __attribute__((noreturn)); -void sched(void); -void setproc(struct proc*); -void sleep(void*, struct spinlock*); -void userinit(void); -int wait(void); -void wakeup(void*); -void yield(void); -int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); -int either_copyin(void *dst, int user_src, uint64 src, uint64 len); -void procdump(void); - -// swtch.S -void swtch(struct context*, struct context*); - -// spinlock.c -void acquire(struct spinlock*); -int holding(struct spinlock*); -void initlock(struct spinlock*, char*); -void release(struct spinlock*); -void push_off(void); -void pop_off(void); - -// sleeplock.c -void acquiresleep(struct sleeplock*); -void releasesleep(struct sleeplock*); -int holdingsleep(struct sleeplock*); -void initsleeplock(struct sleeplock*, char*); - -// string.c -int memcmp(const void*, const void*, uint); -void* memmove(void*, const void*, uint); -void* memset(void*, int, uint); -char* safestrcpy(char*, const char*, int); -int strlen(const char*); -int strncmp(const char*, const char*, uint); -char* strncpy(char*, const char*, int); - -// syscall.c -int argint(int, int*); -int argptr(int, uint64*, int); -int argstr(int, char*, int); -int argaddr(int, uint64 *); -int fetchint(uint64, int*); -int fetchstr(uint64, char*, int); -int fetchaddr(uint64, uint64*); -void syscall(); - -// timer.c -void timerinit(void); - -// trap.c -extern uint ticks; -void trapinit(void); -void trapinithart(void); -extern struct spinlock tickslock; -void usertrapret(void); - -// uart.c -void uartinit(void); -void uartintr(void); -void uartputc(int); -int uartgetc(void); - -// vm.c -void kvminit(void); -void kvminithart(void); -pagetable_t uvmcreate(void); -void uvminit(pagetable_t, uchar *, uint); -uint64 uvmalloc(pagetable_t, uint64, uint64); -uint64 uvmdealloc(pagetable_t, uint64, uint64); -void uvmcopy(pagetable_t, pagetable_t, uint64); -void uvmfree(pagetable_t, uint64); -void mappages(pagetable_t, uint64, uint64, uint64, int); -void unmappages(pagetable_t, uint64, uint64, int); -uint64 walkaddr(pagetable_t, uint64); -int copyout(pagetable_t, uint64, char *, uint64); -int copyin(pagetable_t, char *, uint64, uint64); -int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max); - -// plic.c -void plicinit(void); -void plicinithart(void); -uint64 plic_pending(void); -int plic_claim(void); -void plic_complete(int); - -// number of elements in fixed-size array -#define NELEM(x) (sizeof(x)/sizeof((x)[0])) diff --git a/echo.c b/echo.c deleted file mode 100644 index 806dee0..0000000 --- a/echo.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char *argv[]) -{ - int i; - - for(i = 1; i < argc; i++) - printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); - exit(); -} diff --git a/elf.h b/elf.h deleted file mode 100644 index 84555fa..0000000 --- a/elf.h +++ /dev/null @@ -1,42 +0,0 @@ -// Format of an ELF executable file - -#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian - -// File header -struct elfhdr { - uint magic; // must equal ELF_MAGIC - uchar elf[12]; - ushort type; - ushort machine; - uint version; - uint64 entry; - uint64 phoff; - uint64 shoff; - uint flags; - ushort ehsize; - ushort phentsize; - ushort phnum; - ushort shentsize; - ushort shnum; - ushort shstrndx; -}; - -// Program section header -struct proghdr { - uint32 type; - uint32 flags; - uint64 off; - uint64 vaddr; - uint64 paddr; - uint64 filesz; - uint64 memsz; - uint64 align; -}; - -// Values for Proghdr type -#define ELF_PROG_LOAD 1 - -// Flag bits for Proghdr flags -#define ELF_PROG_FLAG_EXEC 1 -#define ELF_PROG_FLAG_WRITE 2 -#define ELF_PROG_FLAG_READ 4 diff --git a/entry.S b/entry.S deleted file mode 100644 index b3d2c55..0000000 --- a/entry.S +++ /dev/null @@ -1,25 +0,0 @@ - # qemu -kernel starts at 0x1000. the instructions - # there seem to be provided by qemu, as if it - # were a ROM. the code at 0x1000 jumps to - # 0x8000000, the _start function here, - # in machine mode. -.section .data -.globl stack0 -.section .text -.globl mstart -.section .text -.globl _entry -_entry: - # set up a stack for C. - # stack0 is declared in start, - # with 4096 bytes per CPU. - la sp, stack0 - li a0, 1024*4 - csrr a1, mhartid - addi a1, a1, 1 - mul a0, a0, a1 - add sp, sp, a0 - # jump to mstart() in start.c - call mstart -junk: - j junk diff --git a/exec.c b/exec.c deleted file mode 100644 index c9af395..0000000 --- a/exec.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "defs.h" -#include "elf.h" - -static int loadseg(pde_t *pgdir, uint64 addr, struct inode *ip, uint offset, uint sz); - -int -exec(char *path, char **argv) -{ - char *s, *last; - int i, off; - uint64 argc, sz, sp, ustack[MAXARG+1], stackbase; - struct elfhdr elf; - struct inode *ip; - struct proghdr ph; - pagetable_t pagetable = 0, oldpagetable; - struct proc *p = myproc(); - uint64 oldsz = p->sz; - - begin_op(); - - if((ip = namei(path)) == 0){ - end_op(); - return -1; - } - ilock(ip); - - // Check ELF header - if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) - goto bad; - if(elf.magic != ELF_MAGIC) - goto bad; - - if((pagetable = proc_pagetable(p)) == 0) - goto bad; - - // Load program into memory. - sz = 0; - for(i=0, off=elf.phoff; i= MAXARG) - goto bad; - sp -= strlen(argv[argc]) + 1; - sp -= sp % 16; // riscv sp must be 16-byte aligned - if(sp < stackbase) - goto bad; - if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) - goto bad; - ustack[argc] = sp; - } - ustack[argc] = 0; - - // push the array of argv[] pointers. - sp -= (argc+1) * sizeof(uint64); - sp -= sp % 16; - if(sp < stackbase) - goto bad; - if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0) - goto bad; - - // arguments to user main(argc, argv) - // argc is returned via the system call return - // value, which goes in a0. - p->tf->a1 = sp; - - // Save program name for debugging. - for(last=s=path; *s; s++) - if(*s == '/') - last = s+1; - safestrcpy(p->name, last, sizeof(p->name)); - - // Commit to the user image. - oldpagetable = p->pagetable; - p->pagetable = pagetable; - p->sz = sz; - p->tf->epc = elf.entry; // initial program counter = main - p->tf->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) - proc_freepagetable(pagetable, sz); - if(ip){ - iunlockput(ip); - end_op(); - } - return -1; -} - -// Load a program segment into pagetable at virtual address va. -// va must be page-aligned -// and the pages from va to va+sz must already be mapped. -// Returns 0 on success, -1 on failure. -static int -loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz) -{ - uint i, n; - uint64 pa; - - if((va % PGSIZE) != 0) - panic("loadseg: va must be page aligned"); - - for(i = 0; i < sz; i += PGSIZE){ - pa = walkaddr(pagetable, va + i); - if(pa == 0) - panic("loadseg: address should exist"); - if(sz - i < PGSIZE) - n = sz - i; - else - n = PGSIZE; - if(readi(ip, 0, (uint64)pa, offset+i, n) != n) - return -1; - } - - return 0; -} diff --git a/fcntl.h b/fcntl.h deleted file mode 100644 index d565483..0000000 --- a/fcntl.h +++ /dev/null @@ -1,4 +0,0 @@ -#define O_RDONLY 0x000 -#define O_WRONLY 0x001 -#define O_RDWR 0x002 -#define O_CREATE 0x200 diff --git a/file.c b/file.c deleted file mode 100644 index 6f27f22..0000000 --- a/file.c +++ /dev/null @@ -1,175 +0,0 @@ -// -// Support functions for system calls that involve file descriptors. -// - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "fs.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "file.h" -#include "stat.h" -#include "proc.h" - -struct devsw devsw[NDEV]; -struct { - struct spinlock lock; - struct file file[NFILE]; -} ftable; - -void -fileinit(void) -{ - initlock(&ftable.lock, "ftable"); -} - -// Allocate a file structure. -struct file* -filealloc(void) -{ - struct file *f; - - acquire(&ftable.lock); - for(f = ftable.file; f < ftable.file + NFILE; f++){ - if(f->ref == 0){ - f->ref = 1; - release(&ftable.lock); - return f; - } - } - release(&ftable.lock); - return 0; -} - -// Increment ref count for file f. -struct file* -filedup(struct file *f) -{ - acquire(&ftable.lock); - if(f->ref < 1) - panic("filedup"); - f->ref++; - release(&ftable.lock); - return f; -} - -// Close file f. (Decrement ref count, close when reaches 0.) -void -fileclose(struct file *f) -{ - struct file ff; - - acquire(&ftable.lock); - if(f->ref < 1) - panic("fileclose"); - if(--f->ref > 0){ - release(&ftable.lock); - return; - } - ff = *f; - f->ref = 0; - f->type = FD_NONE; - release(&ftable.lock); - - if(ff.type == FD_PIPE) - pipeclose(ff.pipe, ff.writable); - else if(ff.type == FD_INODE){ - begin_op(); - iput(ff.ip); - end_op(); - } -} - -// Get metadata about file f. -// addr is a user virtual address, pointing to a struct stat. -int -filestat(struct file *f, uint64 addr) -{ - struct proc *p = myproc(); - struct stat st; - - if(f->type == FD_INODE){ - ilock(f->ip); - stati(f->ip, &st); - iunlock(f->ip); - if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) - return -1; - return 0; - } - return -1; -} - -// Read from file f. -// addr is a user virtual address. -int -fileread(struct file *f, uint64 addr, int n) -{ - int r = 0; - - if(f->readable == 0) - return -1; - - if(f->type == FD_PIPE){ - r = piperead(f->pipe, addr, n); - } else if(f->type == FD_INODE){ - ilock(f->ip); - if((r = readi(f->ip, 1, addr, f->off, n)) > 0) - f->off += r; - iunlock(f->ip); - } else { - panic("fileread"); - } - - return r; -} - -//PAGEBREAK! -// Write to file f. -// addr is a user virtual address. -int -filewrite(struct file *f, uint64 addr, int n) -{ - int r, ret = 0; - - if(f->writable == 0) - return -1; - - if(f->type == FD_PIPE){ - ret = pipewrite(f->pipe, addr, n); - } else if(f->type == FD_INODE){ - // write a few blocks at a time to avoid exceeding - // the maximum log transaction size, including - // i-node, indirect block, allocation blocks, - // and 2 blocks of slop for non-aligned writes. - // this really belongs lower down, since writei() - // might be writing a device like the console. - int max = ((MAXOPBLOCKS-1-1-2) / 2) * 512; - int i = 0; - while(i < n){ - int n1 = n - i; - if(n1 > max) - n1 = max; - - begin_op(); - ilock(f->ip); - if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0) - f->off += r; - iunlock(f->ip); - end_op(); - - if(r < 0) - break; - if(r != n1) - panic("short filewrite"); - i += r; - } - ret = (i == n ? n : -1); - } else { - panic("filewrite"); - } - - return ret; -} - diff --git a/file.h b/file.h deleted file mode 100644 index f28018f..0000000 --- a/file.h +++ /dev/null @@ -1,37 +0,0 @@ -struct file { - enum { FD_NONE, FD_PIPE, FD_INODE } type; - int ref; // reference count - char readable; - char writable; - struct pipe *pipe; - struct inode *ip; - uint off; -}; - - -// in-memory copy of an inode -struct inode { - uint dev; // Device number - uint inum; // Inode number - int ref; // Reference count - struct sleeplock lock; // protects everything below here - int valid; // inode has been read from disk? - - short type; // copy of disk inode - short major; - short minor; - short nlink; - uint size; - uint addrs[NDIRECT+1]; -}; - -// table mapping major device number to -// device functions -struct devsw { - int (*read)(struct inode*, int, uint64, int); - int (*write)(struct inode*, int, uint64, int); -}; - -extern struct devsw devsw[]; - -#define CONSOLE 1 diff --git a/forktest.c b/forktest.c deleted file mode 100644 index 8bc984d..0000000 --- a/forktest.c +++ /dev/null @@ -1,56 +0,0 @@ -// Test that fork fails gracefully. -// Tiny executable so that the limit can be filling the proc table. - -#include "types.h" -#include "stat.h" -#include "user.h" - -#define N 1000 - -void -printf(int fd, const char *s, ...) -{ - write(fd, s, strlen(s)); -} - -void -forktest(void) -{ - int n, pid; - - printf(1, "fork test\n"); - - for(n=0; n 0; n--){ - if(wait() < 0){ - printf(1, "wait stopped early\n"); - exit(); - } - } - - if(wait() != -1){ - printf(1, "wait got too many\n"); - exit(); - } - - printf(1, "fork test OK\n"); -} - -int -main(void) -{ - forktest(); - exit(); -} diff --git a/fs.c b/fs.c deleted file mode 100644 index ebe377a..0000000 --- a/fs.c +++ /dev/null @@ -1,681 +0,0 @@ -// File system implementation. Five layers: -// + Blocks: allocator for raw disk blocks. -// + Log: crash recovery for multi-step updates. -// + Files: inode allocator, reading, writing, metadata. -// + Directories: inode with special contents (list of other inodes!) -// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. -// -// This file contains the low-level file system manipulation -// routines. The (higher-level) system call implementations -// are in sysfile.c. - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "stat.h" -#include "proc.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "buf.h" -#include "file.h" - -#define min(a, b) ((a) < (b) ? (a) : (b)) -static void itrunc(struct inode*); -// there should be one superblock per disk device, but we run with -// only one device -struct superblock sb; - -// Read the super block. -void -readsb(int dev, struct superblock *sb) -{ - struct buf *bp; - - bp = bread(dev, 1); - memmove(sb, bp->data, sizeof(*sb)); - brelse(bp); -} - -// Zero a block. -static void -bzero(int dev, int bno) -{ - struct buf *bp; - - bp = bread(dev, bno); - memset(bp->data, 0, BSIZE); - log_write(bp); - brelse(bp); -} - -// Blocks. - -// Allocate a zeroed disk block. -static uint -balloc(uint dev) -{ - int b, bi, m; - struct buf *bp; - - bp = 0; - for(b = 0; b < sb.size; b += BPB){ - bp = bread(dev, BBLOCK(b, sb)); - for(bi = 0; bi < BPB && b + bi < sb.size; bi++){ - m = 1 << (bi % 8); - if((bp->data[bi/8] & m) == 0){ // Is block free? - bp->data[bi/8] |= m; // Mark block in use. - log_write(bp); - brelse(bp); - bzero(dev, b + bi); - return b + bi; - } - } - brelse(bp); - } - panic("balloc: out of blocks"); -} - -// Free a disk block. -static void -bfree(int dev, uint b) -{ - struct buf *bp; - int bi, m; - - readsb(dev, &sb); - bp = bread(dev, BBLOCK(b, sb)); - bi = b % BPB; - m = 1 << (bi % 8); - if((bp->data[bi/8] & m) == 0) - panic("freeing free block"); - bp->data[bi/8] &= ~m; - log_write(bp); - brelse(bp); -} - -// Inodes. -// -// An inode describes a single unnamed file. -// The inode disk structure holds metadata: the file's type, -// its size, the number of links referring to it, and the -// list of blocks holding the file's content. -// -// The inodes are laid out sequentially on disk at -// sb.startinode. Each inode has a number, indicating its -// position on the disk. -// -// The kernel keeps a cache of in-use inodes in memory -// to provide a place for synchronizing access -// to inodes used by multiple processes. The cached -// inodes include book-keeping information that is -// not stored on disk: ip->ref and ip->valid. -// -// An inode and its in-memory representation go through a -// sequence of states before they can be used by the -// rest of the file system code. -// -// * Allocation: an inode is allocated if its type (on disk) -// is non-zero. ialloc() allocates, and iput() frees if -// the reference and link counts have fallen to zero. -// -// * Referencing in cache: an entry in the inode cache -// is free if ip->ref is zero. Otherwise ip->ref tracks -// the number of in-memory pointers to the entry (open -// files and current directories). iget() finds or -// creates a cache entry and increments its ref; iput() -// decrements ref. -// -// * Valid: the information (type, size, &c) in an inode -// cache entry is only correct when ip->valid is 1. -// ilock() reads the inode from -// the disk and sets ip->valid, while iput() clears -// ip->valid if ip->ref has fallen to zero. -// -// * Locked: file system code may only examine and modify -// the information in an inode and its content if it -// has first locked the inode. -// -// Thus a typical sequence is: -// ip = iget(dev, inum) -// ilock(ip) -// ... examine and modify ip->xxx ... -// iunlock(ip) -// iput(ip) -// -// ilock() is separate from iget() so that system calls can -// get a long-term reference to an inode (as for an open file) -// and only lock it for short periods (e.g., in read()). -// The separation also helps avoid deadlock and races during -// pathname lookup. iget() increments ip->ref so that the inode -// stays cached and pointers to it remain valid. -// -// Many internal file system functions expect the caller to -// have locked the inodes involved; this lets callers create -// multi-step atomic operations. -// -// The icache.lock spin-lock protects the allocation of icache -// entries. Since ip->ref indicates whether an entry is free, -// and ip->dev and ip->inum indicate which i-node an entry -// holds, one must hold icache.lock while using any of those fields. -// -// An ip->lock sleep-lock protects all ip-> fields other than ref, -// dev, and inum. One must hold ip->lock in order to -// read or write that inode's ip->valid, ip->size, ip->type, &c. - -struct { - struct spinlock lock; - struct inode inode[NINODE]; -} icache; - -void -iinit(int dev) -{ - int i = 0; - - initlock(&icache.lock, "icache"); - for(i = 0; i < NINODE; i++) { - initsleeplock(&icache.inode[i].lock, "inode"); - } - - readsb(dev, &sb); - if(sb.magic != FSMAGIC) - panic("invalid file system"); -} - -static struct inode* iget(uint dev, uint inum); - -//PAGEBREAK! -// Allocate an inode on device dev. -// Mark it as allocated by giving it type type. -// Returns an unlocked but allocated and referenced inode. -struct inode* -ialloc(uint dev, short type) -{ - int inum; - struct buf *bp; - struct dinode *dip; - - for(inum = 1; inum < sb.ninodes; inum++){ - bp = bread(dev, IBLOCK(inum, sb)); - dip = (struct dinode*)bp->data + inum%IPB; - if(dip->type == 0){ // a free inode - memset(dip, 0, sizeof(*dip)); - dip->type = type; - log_write(bp); // mark it allocated on the disk - brelse(bp); - return iget(dev, inum); - } - brelse(bp); - } - panic("ialloc: no inodes"); -} - -// Copy a modified in-memory inode to disk. -// Must be called after every change to an ip->xxx field -// that lives on disk, since i-node cache is write-through. -// Caller must hold ip->lock. -void -iupdate(struct inode *ip) -{ - struct buf *bp; - struct dinode *dip; - - bp = bread(ip->dev, IBLOCK(ip->inum, sb)); - dip = (struct dinode*)bp->data + ip->inum%IPB; - dip->type = ip->type; - dip->major = ip->major; - dip->minor = ip->minor; - dip->nlink = ip->nlink; - dip->size = ip->size; - memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); - log_write(bp); - brelse(bp); -} - -// Find the inode with number inum on device dev -// and return the in-memory copy. Does not lock -// the inode and does not read it from disk. -static struct inode* -iget(uint dev, uint inum) -{ - struct inode *ip, *empty; - - acquire(&icache.lock); - - // Is the inode already cached? - empty = 0; - for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){ - if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){ - ip->ref++; - release(&icache.lock); - return ip; - } - if(empty == 0 && ip->ref == 0) // Remember empty slot. - empty = ip; - } - - // Recycle an inode cache entry. - if(empty == 0) - panic("iget: no inodes"); - - ip = empty; - ip->dev = dev; - ip->inum = inum; - ip->ref = 1; - ip->valid = 0; - release(&icache.lock); - - return ip; -} - -// Increment reference count for ip. -// Returns ip to enable ip = idup(ip1) idiom. -struct inode* -idup(struct inode *ip) -{ - acquire(&icache.lock); - ip->ref++; - release(&icache.lock); - return ip; -} - -// Lock the given inode. -// Reads the inode from disk if necessary. -void -ilock(struct inode *ip) -{ - struct buf *bp; - struct dinode *dip; - - if(ip == 0 || ip->ref < 1) - panic("ilock"); - - acquiresleep(&ip->lock); - - if(ip->valid == 0){ - bp = bread(ip->dev, IBLOCK(ip->inum, sb)); - dip = (struct dinode*)bp->data + ip->inum%IPB; - ip->type = dip->type; - ip->major = dip->major; - ip->minor = dip->minor; - ip->nlink = dip->nlink; - ip->size = dip->size; - memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); - brelse(bp); - ip->valid = 1; - if(ip->type == 0) - panic("ilock: no type"); - } -} - -// Unlock the given inode. -void -iunlock(struct inode *ip) -{ - if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1) - panic("iunlock"); - - releasesleep(&ip->lock); -} - -// Drop a reference to an in-memory inode. -// If that was the last reference, the inode cache entry can -// be recycled. -// If that was the last reference and the inode has no links -// to it, free the inode (and its content) on disk. -// All calls to iput() must be inside a transaction in -// case it has to free the inode. -void -iput(struct inode *ip) -{ - acquire(&icache.lock); - - if(ip->ref == 1 && ip->valid && ip->nlink == 0){ - // inode has no links and no other references: truncate and free. - - // ip->ref == 1 means no other process can have ip locked, - // so this acquiresleep() won't block (or deadlock). - acquiresleep(&ip->lock); - - release(&icache.lock); - - itrunc(ip); - ip->type = 0; - iupdate(ip); - ip->valid = 0; - - releasesleep(&ip->lock); - - acquire(&icache.lock); - } - - ip->ref--; - release(&icache.lock); -} - -// Common idiom: unlock, then put. -void -iunlockput(struct inode *ip) -{ - iunlock(ip); - iput(ip); -} - -//PAGEBREAK! -// Inode content -// -// The content (data) associated with each inode is stored -// in blocks on the disk. The first NDIRECT block numbers -// are listed in ip->addrs[]. The next NINDIRECT blocks are -// listed in block ip->addrs[NDIRECT]. - -// Return the disk block address of the nth block in inode ip. -// If there is no such block, bmap allocates one. -static uint -bmap(struct inode *ip, uint bn) -{ - uint addr, *a; - struct buf *bp; - - if(bn < NDIRECT){ - if((addr = ip->addrs[bn]) == 0) - ip->addrs[bn] = addr = balloc(ip->dev); - return addr; - } - bn -= NDIRECT; - - if(bn < NINDIRECT){ - // Load indirect block, allocating if necessary. - if((addr = ip->addrs[NDIRECT]) == 0) - ip->addrs[NDIRECT] = addr = balloc(ip->dev); - bp = bread(ip->dev, addr); - a = (uint*)bp->data; - if((addr = a[bn]) == 0){ - a[bn] = addr = balloc(ip->dev); - log_write(bp); - } - brelse(bp); - return addr; - } - - panic("bmap: out of range"); -} - -// Truncate inode (discard contents). -// Only called when the inode has no links -// to it (no directory entries referring to it) -// and has no in-memory reference to it (is -// not an open file or current directory). -static void -itrunc(struct inode *ip) -{ - int i, j; - struct buf *bp; - uint *a; - - for(i = 0; i < NDIRECT; i++){ - if(ip->addrs[i]){ - bfree(ip->dev, ip->addrs[i]); - ip->addrs[i] = 0; - } - } - - if(ip->addrs[NDIRECT]){ - bp = bread(ip->dev, ip->addrs[NDIRECT]); - a = (uint*)bp->data; - for(j = 0; j < NINDIRECT; j++){ - if(a[j]) - bfree(ip->dev, a[j]); - } - brelse(bp); - bfree(ip->dev, ip->addrs[NDIRECT]); - ip->addrs[NDIRECT] = 0; - } - - ip->size = 0; - iupdate(ip); -} - -// Copy stat information from inode. -// Caller must hold ip->lock. -void -stati(struct inode *ip, struct stat *st) -{ - st->dev = ip->dev; - st->ino = ip->inum; - st->type = ip->type; - st->nlink = ip->nlink; - st->size = ip->size; -} - -//PAGEBREAK! -// Read data from inode. -// Caller must hold ip->lock. -// If user_dst==1, then dst is a user virtual address; -// otherwise, dst is a kernel address. -int -readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) -{ - uint tot, m; - struct buf *bp; - - if(ip->type == T_DEV){ - if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) - return -1; - return devsw[ip->major].read(ip, user_dst, dst, n); - } - - if(off > ip->size || off + n < off) - return -1; - if(off + n > ip->size) - n = ip->size - off; - - for(tot=0; totdev, bmap(ip, off/BSIZE)); - m = min(n - tot, BSIZE - off%BSIZE); - if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) - break; - brelse(bp); - } - return n; -} - -// PAGEBREAK! -// Write data to inode. -// Caller must hold ip->lock. -// If user_src==1, then src is a user virtual address; -// otherwise, src is a kernel address. -int -writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) -{ - uint tot, m; - struct buf *bp; - - if(ip->type == T_DEV){ - if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write){ - return -1; - } - return devsw[ip->major].write(ip, user_src, src, n); - } - - if(off > ip->size || off + n < off) - return -1; - if(off + n > MAXFILE*BSIZE) - return -1; - - for(tot=0; totdev, bmap(ip, off/BSIZE)); - m = min(n - tot, BSIZE - off%BSIZE); - if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) - break; - log_write(bp); - brelse(bp); - } - - if(n > 0 && off > ip->size){ - ip->size = off; - iupdate(ip); - } - return n; -} - -//PAGEBREAK! -// Directories - -int -namecmp(const char *s, const char *t) -{ - return strncmp(s, t, DIRSIZ); -} - -// Look for a directory entry in a directory. -// If found, set *poff to byte offset of entry. -struct inode* -dirlookup(struct inode *dp, char *name, uint *poff) -{ - uint off, inum; - struct dirent de; - - if(dp->type != T_DIR) - panic("dirlookup not DIR"); - - for(off = 0; off < dp->size; off += sizeof(de)){ - if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - panic("dirlookup read"); - if(de.inum == 0) - continue; - if(namecmp(name, de.name) == 0){ - // entry matches path element - if(poff) - *poff = off; - inum = de.inum; - return iget(dp->dev, inum); - } - } - - return 0; -} - -// Write a new directory entry (name, inum) into the directory dp. -int -dirlink(struct inode *dp, char *name, uint inum) -{ - int off; - struct dirent de; - struct inode *ip; - - // Check that name is not present. - if((ip = dirlookup(dp, name, 0)) != 0){ - iput(ip); - return -1; - } - - // Look for an empty dirent. - for(off = 0; off < dp->size; off += sizeof(de)){ - if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - panic("dirlink read"); - if(de.inum == 0) - break; - } - - strncpy(de.name, name, DIRSIZ); - de.inum = inum; - if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - panic("dirlink"); - - return 0; -} - -//PAGEBREAK! -// Paths - -// Copy the next path element from path into name. -// Return a pointer to the element following the copied one. -// The returned path has no leading slashes, -// so the caller can check *path=='\0' to see if the name is the last one. -// If no name to remove, return 0. -// -// Examples: -// skipelem("a/bb/c", name) = "bb/c", setting name = "a" -// skipelem("///a//bb", name) = "bb", setting name = "a" -// skipelem("a", name) = "", setting name = "a" -// skipelem("", name) = skipelem("////", name) = 0 -// -static char* -skipelem(char *path, char *name) -{ - char *s; - int len; - - while(*path == '/') - path++; - if(*path == 0) - return 0; - s = path; - while(*path != '/' && *path != 0) - path++; - len = path - s; - if(len >= DIRSIZ) - memmove(name, s, DIRSIZ); - else { - memmove(name, s, len); - name[len] = 0; - } - while(*path == '/') - path++; - return path; -} - -// Look up and return the inode for a path name. -// If parent != 0, return the inode for the parent and copy the final -// path element into name, which must have room for DIRSIZ bytes. -// Must be called inside a transaction since it calls iput(). -static struct inode* -namex(char *path, int nameiparent, char *name) -{ - struct inode *ip, *next; - - if(*path == '/') - ip = iget(ROOTDEV, ROOTINO); - else - ip = idup(myproc()->cwd); - - while((path = skipelem(path, name)) != 0){ - ilock(ip); - if(ip->type != T_DIR){ - iunlockput(ip); - return 0; - } - if(nameiparent && *path == '\0'){ - // Stop one level early. - iunlock(ip); - return ip; - } - if((next = dirlookup(ip, name, 0)) == 0){ - iunlockput(ip); - return 0; - } - iunlockput(ip); - ip = next; - } - if(nameiparent){ - iput(ip); - return 0; - } - return ip; -} - -struct inode* -namei(char *path) -{ - char name[DIRSIZ]; - return namex(path, 0, name); -} - -struct inode* -nameiparent(char *path, char *name) -{ - return namex(path, 1, name); -} diff --git a/fs.h b/fs.h deleted file mode 100644 index bc0805f..0000000 --- a/fs.h +++ /dev/null @@ -1,60 +0,0 @@ -// On-disk file system format. -// Both the kernel and user programs use this header file. - - -#define ROOTINO 1 // root i-number -#define BSIZE 1024 // block size - -// Disk layout: -// [ boot block | super block | log | inode blocks | -// free bit map | data blocks] -// -// mkfs computes the super block and builds an initial file system. The -// super block describes the disk layout: -struct superblock { - uint magic; // Must be FSMAGIC - uint size; // Size of file system image (blocks) - uint nblocks; // Number of data blocks - uint ninodes; // Number of inodes. - uint nlog; // Number of log blocks - uint logstart; // Block number of first log block - uint inodestart; // Block number of first inode block - uint bmapstart; // Block number of first free map block -}; - -#define FSMAGIC 0x10203040 - -#define NDIRECT 12 -#define NINDIRECT (BSIZE / sizeof(uint)) -#define MAXFILE (NDIRECT + NINDIRECT) - -// On-disk inode structure -struct dinode { - short type; // File type - short major; // Major device number (T_DEV only) - short minor; // Minor device number (T_DEV only) - short nlink; // Number of links to inode in file system - uint size; // Size of file (bytes) - uint addrs[NDIRECT+1]; // Data block addresses -}; - -// Inodes per block. -#define IPB (BSIZE / sizeof(struct dinode)) - -// Block containing inode i -#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart) - -// Bitmap bits per block -#define BPB (BSIZE*8) - -// Block of free map containing bit for block b -#define BBLOCK(b, sb) ((b)/BPB + sb.bmapstart) - -// Directory is a file containing a sequence of dirent structures. -#define DIRSIZ 14 - -struct dirent { - ushort inum; - char name[DIRSIZ]; -}; - diff --git a/grep.c b/grep.c deleted file mode 100644 index adc4835..0000000 --- a/grep.c +++ /dev/null @@ -1,107 +0,0 @@ -// Simple grep. Only supports ^ . * $ operators. - -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[1024]; -int match(char*, char*); - -void -grep(char *pattern, int fd) -{ - int n, m; - char *p, *q; - - m = 0; - while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ - m += n; - buf[m] = '\0'; - p = buf; - while((q = strchr(p, '\n')) != 0){ - *q = 0; - if(match(pattern, p)){ - *q = '\n'; - write(1, p, q+1 - p); - } - p = q+1; - } - if(p == buf) - m = 0; - if(m > 0){ - m -= p - buf; - memmove(buf, p, m); - } - } -} - -int -main(int argc, char *argv[]) -{ - int fd, i; - char *pattern; - - if(argc <= 1){ - printf(2, "usage: grep pattern [file ...]\n"); - exit(); - } - pattern = argv[1]; - - if(argc <= 2){ - grep(pattern, 0); - exit(); - } - - for(i = 2; i < argc; i++){ - if((fd = open(argv[i], 0)) < 0){ - printf(1, "grep: cannot open %s\n", argv[i]); - exit(); - } - grep(pattern, fd); - close(fd); - } - exit(); -} - -// Regexp matcher from Kernighan & Pike, -// The Practice of Programming, Chapter 9. - -int matchhere(char*, char*); -int matchstar(int, char*, char*); - -int -match(char *re, char *text) -{ - if(re[0] == '^') - return matchhere(re+1, text); - do{ // must look at empty string - if(matchhere(re, text)) - return 1; - }while(*text++ != '\0'); - return 0; -} - -// matchhere: search for re at beginning of text -int matchhere(char *re, char *text) -{ - if(re[0] == '\0') - return 1; - if(re[1] == '*') - return matchstar(re[0], re+2, text); - if(re[0] == '$' && re[1] == '\0') - return *text == '\0'; - if(*text!='\0' && (re[0]=='.' || re[0]==*text)) - return matchhere(re+1, text+1); - return 0; -} - -// matchstar: search for c*re at beginning of text -int matchstar(int c, char *re, char *text) -{ - do{ // a * matches zero or more instances - if(matchhere(re, text)) - return 1; - }while(*text!='\0' && (*text++==c || c=='.')); - return 0; -} - diff --git a/init.c b/init.c deleted file mode 100644 index 046b551..0000000 --- a/init.c +++ /dev/null @@ -1,37 +0,0 @@ -// init: The initial user-level program - -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fcntl.h" - -char *argv[] = { "sh", 0 }; - -int -main(void) -{ - int pid, wpid; - - if(open("console", O_RDWR) < 0){ - mknod("console", 1, 1); - open("console", O_RDWR); - } - dup(0); // stdout - dup(0); // stderr - - for(;;){ - printf(1, "init: starting sh\n"); - pid = fork(); - if(pid < 0){ - printf(1, "init: fork failed\n"); - exit(); - } - if(pid == 0){ - exec("sh", argv); - printf(1, "init: exec sh failed\n"); - exit(); - } - while((wpid=wait()) >= 0 && wpid != pid) - printf(1, "zombie!\n"); - } -} diff --git a/initcode.S b/initcode.S deleted file mode 100644 index ca76972..0000000 --- a/initcode.S +++ /dev/null @@ -1,28 +0,0 @@ -# Initial process execs /init. -# This code runs in user space. - -#include "syscall.h" - -# exec(init, argv) -.globl start -start: - la a0, init - la a1, argv - li a7, SYS_exec - ecall - -# for(;;) exit(); -exit: - li a7, SYS_exit - ecall - jal exit - -# char init[] = "/init\0"; -init: - .string "/init\0" - -# char *argv[] = { init, 0 }; -.p2align 2 -argv: - .long init - .long 0 diff --git a/ioapic.c b/ioapic.c deleted file mode 100644 index bbe5f9b..0000000 --- a/ioapic.c +++ /dev/null @@ -1,76 +0,0 @@ -// The I/O APIC manages hardware interrupts for an SMP system. -// http://www.intel.com/design/chipsets/datashts/29056601.pdf -// See also picirq.c. - -#include "types.h" -#include "defs.h" -#include "memlayout.h" -#include "traps.h" - -#define IOAPIC 0xFEC00000 // Default physical address of IO APIC - -#define REG_ID 0x00 // Register index: ID -#define REG_VER 0x01 // Register index: version -#define REG_TABLE 0x10 // Redirection table base - -// The redirection table starts at REG_TABLE and uses -// two registers to configure each interrupt. -// The first (low) register in a pair contains configuration bits. -// The second (high) register contains a bitmask telling which -// CPUs can serve that interrupt. -#define INT_DISABLED 0x00010000 // Interrupt disabled -#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-) -#define INT_ACTIVELOW 0x00002000 // Active low (vs high) -#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID) - -volatile struct ioapic *ioapic; - -// IO APIC MMIO structure: write reg, then read or write data. -struct ioapic { - uint reg; - uint pad[3]; - uint data; -}; - -static uint -ioapicread(int reg) -{ - ioapic->reg = reg; - return ioapic->data; -} - -static void -ioapicwrite(int reg, uint data) -{ - ioapic->reg = reg; - ioapic->data = data; -} - -void -ioapicinit(void) -{ - int i, id, maxintr; - - ioapic = P2V((volatile struct ioapic*)IOAPIC); - maxintr = (ioapicread(REG_VER) >> 16) & 0xFF; - id = ioapicread(REG_ID) >> 24; - if(id != ioapicid) - cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n"); - - // Mark all interrupts edge-triggered, active high, disabled, - // and not routed to any CPUs. - for(i = 0; i <= maxintr; i++){ - ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); - ioapicwrite(REG_TABLE+2*i+1, 0); - } -} - -void -ioapicenable(int irq, int cpunum) -{ - // Mark interrupt edge-triggered, active high, - // enabled, and routed to the given cpunum, - // which happens to be that cpu's APIC ID. - ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq); - ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24); -} diff --git a/kalloc.c b/kalloc.c deleted file mode 100644 index 1ed1c49..0000000 --- a/kalloc.c +++ /dev/null @@ -1,80 +0,0 @@ -// Physical memory allocator, intended to allocate -// memory for user processes, kernel stacks, page table pages, -// and pipe buffers. Allocates 4096-byte pages. - -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "riscv.h" -#include "defs.h" - -void freerange(void *pa_start, void *pa_end); - -extern char end[]; // first address after kernel loaded from ELF file - // defined by the kernel linker script in kernel.ld - -struct run { - struct run *next; -}; - -struct { - struct spinlock lock; - struct run *freelist; -} kmem; - -void -kinit() -{ - initlock(&kmem.lock, "kmem"); - freerange(end, (void*)PHYSTOP); -} - -void -freerange(void *pa_start, void *pa_end) -{ - char *p; - p = (char*)PGROUNDUP((uint64)pa_start); - for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) - kfree(p); -} -//PAGEBREAK: 21 -// Free the page of physical memory pointed at by v, -// which normally should have been returned by a -// call to kalloc(). (The exception is when -// initializing the allocator; see kinit above.) -void -kfree(void *pa) -{ - struct run *r; - - if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) - panic("kfree"); - - // Fill with junk to catch dangling refs. - memset(pa, 1, PGSIZE); - - acquire(&kmem.lock); - r = (struct run*)pa; - r->next = kmem.freelist; - kmem.freelist = r; - release(&kmem.lock); -} - -// Allocate one 4096-byte page of physical memory. -// Returns a pointer that the kernel can use. -// Returns 0 if the memory cannot be allocated. -void * -kalloc(void) -{ - struct run *r; - - acquire(&kmem.lock); - r = kmem.freelist; - if(r) - kmem.freelist = r->next; - release(&kmem.lock); - if(r) - memset((char*)r, 5, PGSIZE); // fill with junk - return (void*)r; -} diff --git a/kernel.ld b/kernel.ld deleted file mode 100644 index 53c9b90..0000000 --- a/kernel.ld +++ /dev/null @@ -1,31 +0,0 @@ -OUTPUT_ARCH( "riscv" ) -ENTRY( _entry ) - -SECTIONS -{ - /* - * ensure that entry.S / _entry is at 0x80000000, - * where qemu's -kernel jumps. - */ - . = 0x80000000; - .text : - { - *(.text) - . = ALIGN(0x1000); - *(trampoline) - } - - . = ALIGN(0x1000); - PROVIDE(etext = .); - - /* - * make sure end is after data and bss. - */ - .data : { - *(.data) - } - bss : { - *(.bss) - PROVIDE(end = .); - } -} diff --git a/kernel/bio.c b/kernel/bio.c new file mode 100644 index 0000000..90f9af9 --- /dev/null +++ b/kernel/bio.c @@ -0,0 +1,145 @@ +// Buffer cache. +// +// The buffer cache is a linked list of buf structures holding +// cached copies of disk block contents. Caching disk blocks +// in memory reduces the number of disk reads and also provides +// a synchronization point for disk blocks used by multiple processes. +// +// Interface: +// * To get a buffer for a particular disk block, call bread. +// * After changing buffer data, call bwrite to write it to disk. +// * When done with the buffer, call brelse. +// * Do not use the buffer after calling brelse. +// * Only one process at a time can use a buffer, +// so do not keep them longer than necessary. +// +// The implementation uses two state flags internally: +// * B_VALID: the buffer data has been read from the disk. +// * B_DIRTY: the buffer data has been modified +// and needs to be written to disk. + +#include "types.h" +#include "param.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "riscv.h" +#include "defs.h" +#include "fs.h" +#include "buf.h" + +struct { + struct spinlock lock; + struct buf buf[NBUF]; + + // Linked list of all buffers, through prev/next. + // head.next is most recently used. + struct buf head; +} bcache; + +void +binit(void) +{ + struct buf *b; + + initlock(&bcache.lock, "bcache"); + +//PAGEBREAK! + // Create linked list of buffers + bcache.head.prev = &bcache.head; + bcache.head.next = &bcache.head; + for(b = bcache.buf; b < bcache.buf+NBUF; b++){ + b->next = bcache.head.next; + b->prev = &bcache.head; + initsleeplock(&b->lock, "buffer"); + bcache.head.next->prev = b; + bcache.head.next = b; + } +} + +// Look through buffer cache for block on device dev. +// If not found, allocate a buffer. +// In either case, return locked buffer. +static struct buf* +bget(uint dev, uint blockno) +{ + struct buf *b; + + acquire(&bcache.lock); + + // Is the block already cached? + for(b = bcache.head.next; b != &bcache.head; b = b->next){ + if(b->dev == dev && b->blockno == blockno){ + b->refcnt++; + release(&bcache.lock); + acquiresleep(&b->lock); + return b; + } + } + + // Not cached; recycle an unused buffer. + // Even if refcnt==0, B_DIRTY indicates a buffer is in use + // because log.c has modified it but not yet committed it. + for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ + if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) { + b->dev = dev; + b->blockno = blockno; + b->flags = 0; + b->refcnt = 1; + release(&bcache.lock); + acquiresleep(&b->lock); + return b; + } + } + panic("bget: no buffers"); +} + +// Return a locked buf with the contents of the indicated block. +struct buf* +bread(uint dev, uint blockno) +{ + struct buf *b; + + b = bget(dev, blockno); + if((b->flags & B_VALID) == 0) { + ramdiskrw(b); + } + return b; +} + +// Write b's contents to disk. Must be locked. +void +bwrite(struct buf *b) +{ + if(!holdingsleep(&b->lock)) + panic("bwrite"); + b->flags |= B_DIRTY; + ramdiskrw(b); +} + +// Release a locked buffer. +// Move to the head of the MRU list. +void +brelse(struct buf *b) +{ + if(!holdingsleep(&b->lock)) + panic("brelse"); + + releasesleep(&b->lock); + + acquire(&bcache.lock); + b->refcnt--; + if (b->refcnt == 0) { + // no one is waiting for it. + b->next->prev = b->prev; + b->prev->next = b->next; + b->next = bcache.head.next; + b->prev = &bcache.head; + bcache.head.next->prev = b; + bcache.head.next = b; + } + + release(&bcache.lock); +} +//PAGEBREAK! +// Blank page. + diff --git a/kernel/buf.h b/kernel/buf.h new file mode 100644 index 0000000..3266495 --- /dev/null +++ b/kernel/buf.h @@ -0,0 +1,14 @@ +struct buf { + int flags; + uint dev; + uint blockno; + struct sleeplock lock; + uint refcnt; + struct buf *prev; // LRU cache list + struct buf *next; + struct buf *qnext; // disk queue + uchar data[BSIZE]; +}; +#define B_VALID 0x2 // buffer has been read from disk +#define B_DIRTY 0x4 // buffer needs to be written to disk + diff --git a/kernel/console.c b/kernel/console.c new file mode 100644 index 0000000..b20d4a9 --- /dev/null +++ b/kernel/console.c @@ -0,0 +1,271 @@ +// Console input and output. +// Input is from the keyboard or serial port. +// Output is written to the screen and serial port. + +#include + +#include "types.h" +#include "param.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "riscv.h" +#include "defs.h" +#include "proc.h" + +static void consputc(int); + +static volatile int panicked = 0; + +static struct { + struct spinlock lock; + int locking; +} cons; + +static char digits[] = "0123456789abcdef"; + +static void +printint(int xx, int base, int sign) +{ + char buf[16]; + int i; + uint x; + + if(sign && (sign = xx < 0)) + x = -xx; + else + x = xx; + + i = 0; + do{ + buf[i++] = digits[x % base]; + }while((x /= base) != 0); + + if(sign) + buf[i++] = '-'; + + while(--i >= 0) + consputc(buf[i]); +} + +static void +printptr(uint64 x) { + int i; + consputc('0'); + consputc('x'); + for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) + consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); +} + + +//PAGEBREAK: 50 + +// Print to the console. only understands %d, %x, %p, %s. +void +printf(char *fmt, ...) +{ + va_list ap; + int i, c, locking; + char *s; + + locking = cons.locking; + if(locking) + acquire(&cons.lock); + + if (fmt == 0) + panic("null fmt"); + + va_start(ap, fmt); + for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ + if(c != '%'){ + consputc(c); + continue; + } + c = fmt[++i] & 0xff; + if(c == 0) + break; + switch(c){ + case 'd': + printint(va_arg(ap, int), 10, 1); + break; + case 'x': + printint(va_arg(ap, int), 16, 1); + break; + case 'p': + printptr(va_arg(ap, uint64)); + break; + case 's': + if((s = va_arg(ap, char*)) == 0) + s = "(null)"; + for(; *s; s++) + consputc(*s); + break; + case '%': + consputc('%'); + break; + default: + // Print unknown % sequence to draw attention. + consputc('%'); + consputc(c); + break; + } + } + + if(locking) + release(&cons.lock); +} + +void +panic(char *s) +{ + cons.locking = 0; + printf("panic: "); + printf(s); + printf("\n"); + panicked = 1; // freeze other CPU + for(;;) + ; +} + +#define BACKSPACE 0x100 + +void +consputc(int c) +{ + if(panicked){ + for(;;) + ; + } + + if(c == BACKSPACE){ + uartputc('\b'); uartputc(' '); uartputc('\b'); + } else + uartputc(c); +} + +#define INPUT_BUF 128 +struct { + char buf[INPUT_BUF]; + uint r; // Read index + uint w; // Write index + uint e; // Edit index +} input; + +#define C(x) ((x)-'@') // Contro + +int +consoleread(struct inode *ip, int user_dst, uint64 dst, int n) +{ + uint target; + int c; + char buf[1]; + + iunlock(ip); + target = n; + acquire(&cons.lock); + while(n > 0){ + while(input.r == input.w){ + if(myproc()->killed){ + release(&cons.lock); + ilock(ip); + return -1; + } + sleep(&input.r, &cons.lock); + } + c = input.buf[input.r++ % INPUT_BUF]; + if(c == C('D')){ // EOF + if(n < target){ + // Save ^D for next time, to make sure + // caller gets a 0-byte result. + input.r--; + } + break; + } + buf[0] = c; + if(either_copyout(user_dst, dst, &buf[0], 1) == -1) + break; + dst++; + --n; + if(c == '\n') + break; + } + release(&cons.lock); + ilock(ip); + + return target - n; +} + +int +consolewrite(struct inode *ip, int user_src, uint64 src, int n) +{ + int i; + + iunlock(ip); + acquire(&cons.lock); + for(i = 0; i < n; i++){ + char c; + if(either_copyin(&c, user_src, src+i, 1) == -1) + break; + consputc(c); + } + release(&cons.lock); + ilock(ip); + + return n; +} + +void +consoleintr(int c) +{ + int doprocdump = 0; + + acquire(&cons.lock); + + switch(c){ + case C('P'): // Process list. + // procdump() locks cons.lock indirectly; invoke later + doprocdump = 1; + break; + case C('U'): // Kill line. + while(input.e != input.w && + input.buf[(input.e-1) % INPUT_BUF] != '\n'){ + input.e--; + consputc(BACKSPACE); + } + break; + case C('H'): case '\x7f': // Backspace + if(input.e != input.w){ + input.e--; + consputc(BACKSPACE); + } + break; + default: + if(c != 0 && input.e-input.r < INPUT_BUF){ + c = (c == '\r') ? '\n' : c; + input.buf[input.e++ % INPUT_BUF] = c; + consputc(c); + if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ + input.w = input.e; + wakeup(&input.r); + } + } + break; + } + + release(&cons.lock); + + if(doprocdump) + procdump(); +} + +void +consoleinit(void) +{ + initlock(&cons.lock, "console"); + + devsw[CONSOLE].write = consolewrite; + devsw[CONSOLE].read = consoleread; + cons.locking = 1; +} diff --git a/kernel/date.h b/kernel/date.h new file mode 100644 index 0000000..94aec4b --- /dev/null +++ b/kernel/date.h @@ -0,0 +1,8 @@ +struct rtcdate { + uint second; + uint minute; + uint hour; + uint day; + uint month; + uint year; +}; diff --git a/kernel/defs.h b/kernel/defs.h new file mode 100644 index 0000000..597e5b6 --- /dev/null +++ b/kernel/defs.h @@ -0,0 +1,205 @@ +struct buf; +struct context; +struct file; +struct inode; +struct pipe; +struct proc; +struct rtcdate; +struct spinlock; +struct sleeplock; +struct stat; +struct superblock; +struct sysframe; + +// bio.c +void binit(void); +struct buf* bread(uint, uint); +void brelse(struct buf*); +void bwrite(struct buf*); + +// console.c +void consoleinit(void); +void printf(char*, ...); +void consoleintr(int); +void panic(char*) __attribute__((noreturn)); + +// exec.c +int exec(char*, char**); + +// file.c +struct file* filealloc(void); +void fileclose(struct file*); +struct file* filedup(struct file*); +void fileinit(void); +int fileread(struct file*, uint64, int n); +int filestat(struct file*, uint64 addr); +int filewrite(struct file*, uint64, int n); + +// fs.c +void readsb(int dev, struct superblock *sb); +int dirlink(struct inode*, char*, uint); +struct inode* dirlookup(struct inode*, char*, uint*); +struct inode* ialloc(uint, short); +struct inode* idup(struct inode*); +void iinit(int dev); +void ilock(struct inode*); +void iput(struct inode*); +void iunlock(struct inode*); +void iunlockput(struct inode*); +void iupdate(struct inode*); +int namecmp(const char*, const char*); +struct inode* namei(char*); +struct inode* nameiparent(char*, char*); +int readi(struct inode*, int, uint64, uint, uint); +void stati(struct inode*, struct stat*); +int writei(struct inode*, int, uint64, uint, uint); + +// ramdisk.c +void ramdiskinit(void); +void ramdiskintr(void); +void ramdiskrw(struct buf*); + +// ioapic.c +void ioapicenable(int irq, int cpu); +extern uchar ioapicid; +void ioapicinit(void); + +// kalloc.c +void* kalloc(void); +void kfree(void *); +void kinit(); + +// kbd.c +void kbdintr(void); + +// lapic.c +void cmostime(struct rtcdate *r); +int lapicid(void); +extern volatile uint* lapic; +void lapiceoi(void); +void lapicinit(void); +void lapicstartap(uchar, uint); +void microdelay(int); + +// log.c +void initlog(int dev); +void log_write(struct buf*); +void begin_op(); +void end_op(); + +// mp.c +extern int ismp; +void mpinit(void); + +// picirq.c +void picenable(int); +void picinit(void); + +// pipe.c +int pipealloc(struct file**, struct file**); +void pipeclose(struct pipe*, int); +int piperead(struct pipe*, uint64, int); +int pipewrite(struct pipe*, uint64, int); + +//PAGEBREAK: 16 +// proc.c +int cpuid(void); +void exit(void); +int fork(void); +int growproc(int); +pagetable_t proc_pagetable(struct proc *); +void proc_freepagetable(pagetable_t, uint64); +int kill(int); +struct cpu* mycpu(void); +struct cpu* getmycpu(void); +struct proc* myproc(); +void procinit(void); +void scheduler(void) __attribute__((noreturn)); +void sched(void); +void setproc(struct proc*); +void sleep(void*, struct spinlock*); +void userinit(void); +int wait(void); +void wakeup(void*); +void yield(void); +int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); +int either_copyin(void *dst, int user_src, uint64 src, uint64 len); +void procdump(void); + +// swtch.S +void swtch(struct context*, struct context*); + +// spinlock.c +void acquire(struct spinlock*); +int holding(struct spinlock*); +void initlock(struct spinlock*, char*); +void release(struct spinlock*); +void push_off(void); +void pop_off(void); + +// sleeplock.c +void acquiresleep(struct sleeplock*); +void releasesleep(struct sleeplock*); +int holdingsleep(struct sleeplock*); +void initsleeplock(struct sleeplock*, char*); + +// string.c +int memcmp(const void*, const void*, uint); +void* memmove(void*, const void*, uint); +void* memset(void*, int, uint); +char* safestrcpy(char*, const char*, int); +int strlen(const char*); +int strncmp(const char*, const char*, uint); +char* strncpy(char*, const char*, int); + +// syscall.c +int argint(int, int*); +int argptr(int, uint64*, int); +int argstr(int, char*, int); +int argaddr(int, uint64 *); +int fetchint(uint64, int*); +int fetchstr(uint64, char*, int); +int fetchaddr(uint64, uint64*); +void syscall(); + +// timer.c +void timerinit(void); + +// trap.c +extern uint ticks; +void trapinit(void); +void trapinithart(void); +extern struct spinlock tickslock; +void usertrapret(void); + +// uart.c +void uartinit(void); +void uartintr(void); +void uartputc(int); +int uartgetc(void); + +// vm.c +void kvminit(void); +void kvminithart(void); +pagetable_t uvmcreate(void); +void uvminit(pagetable_t, uchar *, uint); +uint64 uvmalloc(pagetable_t, uint64, uint64); +uint64 uvmdealloc(pagetable_t, uint64, uint64); +void uvmcopy(pagetable_t, pagetable_t, uint64); +void uvmfree(pagetable_t, uint64); +void mappages(pagetable_t, uint64, uint64, uint64, int); +void unmappages(pagetable_t, uint64, uint64, int); +uint64 walkaddr(pagetable_t, uint64); +int copyout(pagetable_t, uint64, char *, uint64); +int copyin(pagetable_t, char *, uint64, uint64); +int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max); + +// plic.c +void plicinit(void); +void plicinithart(void); +uint64 plic_pending(void); +int plic_claim(void); +void plic_complete(int); + +// number of elements in fixed-size array +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) diff --git a/kernel/elf.h b/kernel/elf.h new file mode 100644 index 0000000..84555fa --- /dev/null +++ b/kernel/elf.h @@ -0,0 +1,42 @@ +// Format of an ELF executable file + +#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian + +// File header +struct elfhdr { + uint magic; // must equal ELF_MAGIC + uchar elf[12]; + ushort type; + ushort machine; + uint version; + uint64 entry; + uint64 phoff; + uint64 shoff; + uint flags; + ushort ehsize; + ushort phentsize; + ushort phnum; + ushort shentsize; + ushort shnum; + ushort shstrndx; +}; + +// Program section header +struct proghdr { + uint32 type; + uint32 flags; + uint64 off; + uint64 vaddr; + uint64 paddr; + uint64 filesz; + uint64 memsz; + uint64 align; +}; + +// Values for Proghdr type +#define ELF_PROG_LOAD 1 + +// Flag bits for Proghdr flags +#define ELF_PROG_FLAG_EXEC 1 +#define ELF_PROG_FLAG_WRITE 2 +#define ELF_PROG_FLAG_READ 4 diff --git a/kernel/entry.S b/kernel/entry.S new file mode 100644 index 0000000..b3d2c55 --- /dev/null +++ b/kernel/entry.S @@ -0,0 +1,25 @@ + # qemu -kernel starts at 0x1000. the instructions + # there seem to be provided by qemu, as if it + # were a ROM. the code at 0x1000 jumps to + # 0x8000000, the _start function here, + # in machine mode. +.section .data +.globl stack0 +.section .text +.globl mstart +.section .text +.globl _entry +_entry: + # set up a stack for C. + # stack0 is declared in start, + # with 4096 bytes per CPU. + la sp, stack0 + li a0, 1024*4 + csrr a1, mhartid + addi a1, a1, 1 + mul a0, a0, a1 + add sp, sp, a0 + # jump to mstart() in start.c + call mstart +junk: + j junk diff --git a/kernel/exec.c b/kernel/exec.c new file mode 100644 index 0000000..c9af395 --- /dev/null +++ b/kernel/exec.c @@ -0,0 +1,149 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "defs.h" +#include "elf.h" + +static int loadseg(pde_t *pgdir, uint64 addr, struct inode *ip, uint offset, uint sz); + +int +exec(char *path, char **argv) +{ + char *s, *last; + int i, off; + uint64 argc, sz, sp, ustack[MAXARG+1], stackbase; + struct elfhdr elf; + struct inode *ip; + struct proghdr ph; + pagetable_t pagetable = 0, oldpagetable; + struct proc *p = myproc(); + uint64 oldsz = p->sz; + + begin_op(); + + if((ip = namei(path)) == 0){ + end_op(); + return -1; + } + ilock(ip); + + // Check ELF header + if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) + goto bad; + if(elf.magic != ELF_MAGIC) + goto bad; + + if((pagetable = proc_pagetable(p)) == 0) + goto bad; + + // Load program into memory. + sz = 0; + for(i=0, off=elf.phoff; i= MAXARG) + goto bad; + sp -= strlen(argv[argc]) + 1; + sp -= sp % 16; // riscv sp must be 16-byte aligned + if(sp < stackbase) + goto bad; + if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) + goto bad; + ustack[argc] = sp; + } + ustack[argc] = 0; + + // push the array of argv[] pointers. + sp -= (argc+1) * sizeof(uint64); + sp -= sp % 16; + if(sp < stackbase) + goto bad; + if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0) + goto bad; + + // arguments to user main(argc, argv) + // argc is returned via the system call return + // value, which goes in a0. + p->tf->a1 = sp; + + // Save program name for debugging. + for(last=s=path; *s; s++) + if(*s == '/') + last = s+1; + safestrcpy(p->name, last, sizeof(p->name)); + + // Commit to the user image. + oldpagetable = p->pagetable; + p->pagetable = pagetable; + p->sz = sz; + p->tf->epc = elf.entry; // initial program counter = main + p->tf->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) + proc_freepagetable(pagetable, sz); + if(ip){ + iunlockput(ip); + end_op(); + } + return -1; +} + +// Load a program segment into pagetable at virtual address va. +// va must be page-aligned +// and the pages from va to va+sz must already be mapped. +// Returns 0 on success, -1 on failure. +static int +loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz) +{ + uint i, n; + uint64 pa; + + if((va % PGSIZE) != 0) + panic("loadseg: va must be page aligned"); + + for(i = 0; i < sz; i += PGSIZE){ + pa = walkaddr(pagetable, va + i); + if(pa == 0) + panic("loadseg: address should exist"); + if(sz - i < PGSIZE) + n = sz - i; + else + n = PGSIZE; + if(readi(ip, 0, (uint64)pa, offset+i, n) != n) + return -1; + } + + return 0; +} diff --git a/kernel/fcntl.h b/kernel/fcntl.h new file mode 100644 index 0000000..d565483 --- /dev/null +++ b/kernel/fcntl.h @@ -0,0 +1,4 @@ +#define O_RDONLY 0x000 +#define O_WRONLY 0x001 +#define O_RDWR 0x002 +#define O_CREATE 0x200 diff --git a/kernel/file.c b/kernel/file.c new file mode 100644 index 0000000..6f27f22 --- /dev/null +++ b/kernel/file.c @@ -0,0 +1,175 @@ +// +// Support functions for system calls that involve file descriptors. +// + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "fs.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "file.h" +#include "stat.h" +#include "proc.h" + +struct devsw devsw[NDEV]; +struct { + struct spinlock lock; + struct file file[NFILE]; +} ftable; + +void +fileinit(void) +{ + initlock(&ftable.lock, "ftable"); +} + +// Allocate a file structure. +struct file* +filealloc(void) +{ + struct file *f; + + acquire(&ftable.lock); + for(f = ftable.file; f < ftable.file + NFILE; f++){ + if(f->ref == 0){ + f->ref = 1; + release(&ftable.lock); + return f; + } + } + release(&ftable.lock); + return 0; +} + +// Increment ref count for file f. +struct file* +filedup(struct file *f) +{ + acquire(&ftable.lock); + if(f->ref < 1) + panic("filedup"); + f->ref++; + release(&ftable.lock); + return f; +} + +// Close file f. (Decrement ref count, close when reaches 0.) +void +fileclose(struct file *f) +{ + struct file ff; + + acquire(&ftable.lock); + if(f->ref < 1) + panic("fileclose"); + if(--f->ref > 0){ + release(&ftable.lock); + return; + } + ff = *f; + f->ref = 0; + f->type = FD_NONE; + release(&ftable.lock); + + if(ff.type == FD_PIPE) + pipeclose(ff.pipe, ff.writable); + else if(ff.type == FD_INODE){ + begin_op(); + iput(ff.ip); + end_op(); + } +} + +// Get metadata about file f. +// addr is a user virtual address, pointing to a struct stat. +int +filestat(struct file *f, uint64 addr) +{ + struct proc *p = myproc(); + struct stat st; + + if(f->type == FD_INODE){ + ilock(f->ip); + stati(f->ip, &st); + iunlock(f->ip); + if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) + return -1; + return 0; + } + return -1; +} + +// Read from file f. +// addr is a user virtual address. +int +fileread(struct file *f, uint64 addr, int n) +{ + int r = 0; + + if(f->readable == 0) + return -1; + + if(f->type == FD_PIPE){ + r = piperead(f->pipe, addr, n); + } else if(f->type == FD_INODE){ + ilock(f->ip); + if((r = readi(f->ip, 1, addr, f->off, n)) > 0) + f->off += r; + iunlock(f->ip); + } else { + panic("fileread"); + } + + return r; +} + +//PAGEBREAK! +// Write to file f. +// addr is a user virtual address. +int +filewrite(struct file *f, uint64 addr, int n) +{ + int r, ret = 0; + + if(f->writable == 0) + return -1; + + if(f->type == FD_PIPE){ + ret = pipewrite(f->pipe, addr, n); + } else if(f->type == FD_INODE){ + // write a few blocks at a time to avoid exceeding + // the maximum log transaction size, including + // i-node, indirect block, allocation blocks, + // and 2 blocks of slop for non-aligned writes. + // this really belongs lower down, since writei() + // might be writing a device like the console. + int max = ((MAXOPBLOCKS-1-1-2) / 2) * 512; + int i = 0; + while(i < n){ + int n1 = n - i; + if(n1 > max) + n1 = max; + + begin_op(); + ilock(f->ip); + if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0) + f->off += r; + iunlock(f->ip); + end_op(); + + if(r < 0) + break; + if(r != n1) + panic("short filewrite"); + i += r; + } + ret = (i == n ? n : -1); + } else { + panic("filewrite"); + } + + return ret; +} + diff --git a/kernel/file.h b/kernel/file.h new file mode 100644 index 0000000..f28018f --- /dev/null +++ b/kernel/file.h @@ -0,0 +1,37 @@ +struct file { + enum { FD_NONE, FD_PIPE, FD_INODE } type; + int ref; // reference count + char readable; + char writable; + struct pipe *pipe; + struct inode *ip; + uint off; +}; + + +// in-memory copy of an inode +struct inode { + uint dev; // Device number + uint inum; // Inode number + int ref; // Reference count + struct sleeplock lock; // protects everything below here + int valid; // inode has been read from disk? + + short type; // copy of disk inode + short major; + short minor; + short nlink; + uint size; + uint addrs[NDIRECT+1]; +}; + +// table mapping major device number to +// device functions +struct devsw { + int (*read)(struct inode*, int, uint64, int); + int (*write)(struct inode*, int, uint64, int); +}; + +extern struct devsw devsw[]; + +#define CONSOLE 1 diff --git a/kernel/fs.c b/kernel/fs.c new file mode 100644 index 0000000..ebe377a --- /dev/null +++ b/kernel/fs.c @@ -0,0 +1,681 @@ +// File system implementation. Five layers: +// + Blocks: allocator for raw disk blocks. +// + Log: crash recovery for multi-step updates. +// + Files: inode allocator, reading, writing, metadata. +// + Directories: inode with special contents (list of other inodes!) +// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. +// +// This file contains the low-level file system manipulation +// routines. The (higher-level) system call implementations +// are in sysfile.c. + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "stat.h" +#include "proc.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "buf.h" +#include "file.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) +static void itrunc(struct inode*); +// there should be one superblock per disk device, but we run with +// only one device +struct superblock sb; + +// Read the super block. +void +readsb(int dev, struct superblock *sb) +{ + struct buf *bp; + + bp = bread(dev, 1); + memmove(sb, bp->data, sizeof(*sb)); + brelse(bp); +} + +// Zero a block. +static void +bzero(int dev, int bno) +{ + struct buf *bp; + + bp = bread(dev, bno); + memset(bp->data, 0, BSIZE); + log_write(bp); + brelse(bp); +} + +// Blocks. + +// Allocate a zeroed disk block. +static uint +balloc(uint dev) +{ + int b, bi, m; + struct buf *bp; + + bp = 0; + for(b = 0; b < sb.size; b += BPB){ + bp = bread(dev, BBLOCK(b, sb)); + for(bi = 0; bi < BPB && b + bi < sb.size; bi++){ + m = 1 << (bi % 8); + if((bp->data[bi/8] & m) == 0){ // Is block free? + bp->data[bi/8] |= m; // Mark block in use. + log_write(bp); + brelse(bp); + bzero(dev, b + bi); + return b + bi; + } + } + brelse(bp); + } + panic("balloc: out of blocks"); +} + +// Free a disk block. +static void +bfree(int dev, uint b) +{ + struct buf *bp; + int bi, m; + + readsb(dev, &sb); + bp = bread(dev, BBLOCK(b, sb)); + bi = b % BPB; + m = 1 << (bi % 8); + if((bp->data[bi/8] & m) == 0) + panic("freeing free block"); + bp->data[bi/8] &= ~m; + log_write(bp); + brelse(bp); +} + +// Inodes. +// +// An inode describes a single unnamed file. +// The inode disk structure holds metadata: the file's type, +// its size, the number of links referring to it, and the +// list of blocks holding the file's content. +// +// The inodes are laid out sequentially on disk at +// sb.startinode. Each inode has a number, indicating its +// position on the disk. +// +// The kernel keeps a cache of in-use inodes in memory +// to provide a place for synchronizing access +// to inodes used by multiple processes. The cached +// inodes include book-keeping information that is +// not stored on disk: ip->ref and ip->valid. +// +// An inode and its in-memory representation go through a +// sequence of states before they can be used by the +// rest of the file system code. +// +// * Allocation: an inode is allocated if its type (on disk) +// is non-zero. ialloc() allocates, and iput() frees if +// the reference and link counts have fallen to zero. +// +// * Referencing in cache: an entry in the inode cache +// is free if ip->ref is zero. Otherwise ip->ref tracks +// the number of in-memory pointers to the entry (open +// files and current directories). iget() finds or +// creates a cache entry and increments its ref; iput() +// decrements ref. +// +// * Valid: the information (type, size, &c) in an inode +// cache entry is only correct when ip->valid is 1. +// ilock() reads the inode from +// the disk and sets ip->valid, while iput() clears +// ip->valid if ip->ref has fallen to zero. +// +// * Locked: file system code may only examine and modify +// the information in an inode and its content if it +// has first locked the inode. +// +// Thus a typical sequence is: +// ip = iget(dev, inum) +// ilock(ip) +// ... examine and modify ip->xxx ... +// iunlock(ip) +// iput(ip) +// +// ilock() is separate from iget() so that system calls can +// get a long-term reference to an inode (as for an open file) +// and only lock it for short periods (e.g., in read()). +// The separation also helps avoid deadlock and races during +// pathname lookup. iget() increments ip->ref so that the inode +// stays cached and pointers to it remain valid. +// +// Many internal file system functions expect the caller to +// have locked the inodes involved; this lets callers create +// multi-step atomic operations. +// +// The icache.lock spin-lock protects the allocation of icache +// entries. Since ip->ref indicates whether an entry is free, +// and ip->dev and ip->inum indicate which i-node an entry +// holds, one must hold icache.lock while using any of those fields. +// +// An ip->lock sleep-lock protects all ip-> fields other than ref, +// dev, and inum. One must hold ip->lock in order to +// read or write that inode's ip->valid, ip->size, ip->type, &c. + +struct { + struct spinlock lock; + struct inode inode[NINODE]; +} icache; + +void +iinit(int dev) +{ + int i = 0; + + initlock(&icache.lock, "icache"); + for(i = 0; i < NINODE; i++) { + initsleeplock(&icache.inode[i].lock, "inode"); + } + + readsb(dev, &sb); + if(sb.magic != FSMAGIC) + panic("invalid file system"); +} + +static struct inode* iget(uint dev, uint inum); + +//PAGEBREAK! +// Allocate an inode on device dev. +// Mark it as allocated by giving it type type. +// Returns an unlocked but allocated and referenced inode. +struct inode* +ialloc(uint dev, short type) +{ + int inum; + struct buf *bp; + struct dinode *dip; + + for(inum = 1; inum < sb.ninodes; inum++){ + bp = bread(dev, IBLOCK(inum, sb)); + dip = (struct dinode*)bp->data + inum%IPB; + if(dip->type == 0){ // a free inode + memset(dip, 0, sizeof(*dip)); + dip->type = type; + log_write(bp); // mark it allocated on the disk + brelse(bp); + return iget(dev, inum); + } + brelse(bp); + } + panic("ialloc: no inodes"); +} + +// Copy a modified in-memory inode to disk. +// Must be called after every change to an ip->xxx field +// that lives on disk, since i-node cache is write-through. +// Caller must hold ip->lock. +void +iupdate(struct inode *ip) +{ + struct buf *bp; + struct dinode *dip; + + bp = bread(ip->dev, IBLOCK(ip->inum, sb)); + dip = (struct dinode*)bp->data + ip->inum%IPB; + dip->type = ip->type; + dip->major = ip->major; + dip->minor = ip->minor; + dip->nlink = ip->nlink; + dip->size = ip->size; + memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); + log_write(bp); + brelse(bp); +} + +// Find the inode with number inum on device dev +// and return the in-memory copy. Does not lock +// the inode and does not read it from disk. +static struct inode* +iget(uint dev, uint inum) +{ + struct inode *ip, *empty; + + acquire(&icache.lock); + + // Is the inode already cached? + empty = 0; + for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){ + if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){ + ip->ref++; + release(&icache.lock); + return ip; + } + if(empty == 0 && ip->ref == 0) // Remember empty slot. + empty = ip; + } + + // Recycle an inode cache entry. + if(empty == 0) + panic("iget: no inodes"); + + ip = empty; + ip->dev = dev; + ip->inum = inum; + ip->ref = 1; + ip->valid = 0; + release(&icache.lock); + + return ip; +} + +// Increment reference count for ip. +// Returns ip to enable ip = idup(ip1) idiom. +struct inode* +idup(struct inode *ip) +{ + acquire(&icache.lock); + ip->ref++; + release(&icache.lock); + return ip; +} + +// Lock the given inode. +// Reads the inode from disk if necessary. +void +ilock(struct inode *ip) +{ + struct buf *bp; + struct dinode *dip; + + if(ip == 0 || ip->ref < 1) + panic("ilock"); + + acquiresleep(&ip->lock); + + if(ip->valid == 0){ + bp = bread(ip->dev, IBLOCK(ip->inum, sb)); + dip = (struct dinode*)bp->data + ip->inum%IPB; + ip->type = dip->type; + ip->major = dip->major; + ip->minor = dip->minor; + ip->nlink = dip->nlink; + ip->size = dip->size; + memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); + brelse(bp); + ip->valid = 1; + if(ip->type == 0) + panic("ilock: no type"); + } +} + +// Unlock the given inode. +void +iunlock(struct inode *ip) +{ + if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1) + panic("iunlock"); + + releasesleep(&ip->lock); +} + +// Drop a reference to an in-memory inode. +// If that was the last reference, the inode cache entry can +// be recycled. +// If that was the last reference and the inode has no links +// to it, free the inode (and its content) on disk. +// All calls to iput() must be inside a transaction in +// case it has to free the inode. +void +iput(struct inode *ip) +{ + acquire(&icache.lock); + + if(ip->ref == 1 && ip->valid && ip->nlink == 0){ + // inode has no links and no other references: truncate and free. + + // ip->ref == 1 means no other process can have ip locked, + // so this acquiresleep() won't block (or deadlock). + acquiresleep(&ip->lock); + + release(&icache.lock); + + itrunc(ip); + ip->type = 0; + iupdate(ip); + ip->valid = 0; + + releasesleep(&ip->lock); + + acquire(&icache.lock); + } + + ip->ref--; + release(&icache.lock); +} + +// Common idiom: unlock, then put. +void +iunlockput(struct inode *ip) +{ + iunlock(ip); + iput(ip); +} + +//PAGEBREAK! +// Inode content +// +// The content (data) associated with each inode is stored +// in blocks on the disk. The first NDIRECT block numbers +// are listed in ip->addrs[]. The next NINDIRECT blocks are +// listed in block ip->addrs[NDIRECT]. + +// Return the disk block address of the nth block in inode ip. +// If there is no such block, bmap allocates one. +static uint +bmap(struct inode *ip, uint bn) +{ + uint addr, *a; + struct buf *bp; + + if(bn < NDIRECT){ + if((addr = ip->addrs[bn]) == 0) + ip->addrs[bn] = addr = balloc(ip->dev); + return addr; + } + bn -= NDIRECT; + + if(bn < NINDIRECT){ + // Load indirect block, allocating if necessary. + if((addr = ip->addrs[NDIRECT]) == 0) + ip->addrs[NDIRECT] = addr = balloc(ip->dev); + bp = bread(ip->dev, addr); + a = (uint*)bp->data; + if((addr = a[bn]) == 0){ + a[bn] = addr = balloc(ip->dev); + log_write(bp); + } + brelse(bp); + return addr; + } + + panic("bmap: out of range"); +} + +// Truncate inode (discard contents). +// Only called when the inode has no links +// to it (no directory entries referring to it) +// and has no in-memory reference to it (is +// not an open file or current directory). +static void +itrunc(struct inode *ip) +{ + int i, j; + struct buf *bp; + uint *a; + + for(i = 0; i < NDIRECT; i++){ + if(ip->addrs[i]){ + bfree(ip->dev, ip->addrs[i]); + ip->addrs[i] = 0; + } + } + + if(ip->addrs[NDIRECT]){ + bp = bread(ip->dev, ip->addrs[NDIRECT]); + a = (uint*)bp->data; + for(j = 0; j < NINDIRECT; j++){ + if(a[j]) + bfree(ip->dev, a[j]); + } + brelse(bp); + bfree(ip->dev, ip->addrs[NDIRECT]); + ip->addrs[NDIRECT] = 0; + } + + ip->size = 0; + iupdate(ip); +} + +// Copy stat information from inode. +// Caller must hold ip->lock. +void +stati(struct inode *ip, struct stat *st) +{ + st->dev = ip->dev; + st->ino = ip->inum; + st->type = ip->type; + st->nlink = ip->nlink; + st->size = ip->size; +} + +//PAGEBREAK! +// Read data from inode. +// Caller must hold ip->lock. +// If user_dst==1, then dst is a user virtual address; +// otherwise, dst is a kernel address. +int +readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) +{ + uint tot, m; + struct buf *bp; + + if(ip->type == T_DEV){ + if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) + return -1; + return devsw[ip->major].read(ip, user_dst, dst, n); + } + + if(off > ip->size || off + n < off) + return -1; + if(off + n > ip->size) + n = ip->size - off; + + for(tot=0; totdev, bmap(ip, off/BSIZE)); + m = min(n - tot, BSIZE - off%BSIZE); + if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) + break; + brelse(bp); + } + return n; +} + +// PAGEBREAK! +// Write data to inode. +// Caller must hold ip->lock. +// If user_src==1, then src is a user virtual address; +// otherwise, src is a kernel address. +int +writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) +{ + uint tot, m; + struct buf *bp; + + if(ip->type == T_DEV){ + if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write){ + return -1; + } + return devsw[ip->major].write(ip, user_src, src, n); + } + + if(off > ip->size || off + n < off) + return -1; + if(off + n > MAXFILE*BSIZE) + return -1; + + for(tot=0; totdev, bmap(ip, off/BSIZE)); + m = min(n - tot, BSIZE - off%BSIZE); + if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) + break; + log_write(bp); + brelse(bp); + } + + if(n > 0 && off > ip->size){ + ip->size = off; + iupdate(ip); + } + return n; +} + +//PAGEBREAK! +// Directories + +int +namecmp(const char *s, const char *t) +{ + return strncmp(s, t, DIRSIZ); +} + +// Look for a directory entry in a directory. +// If found, set *poff to byte offset of entry. +struct inode* +dirlookup(struct inode *dp, char *name, uint *poff) +{ + uint off, inum; + struct dirent de; + + if(dp->type != T_DIR) + panic("dirlookup not DIR"); + + for(off = 0; off < dp->size; off += sizeof(de)){ + if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) + panic("dirlookup read"); + if(de.inum == 0) + continue; + if(namecmp(name, de.name) == 0){ + // entry matches path element + if(poff) + *poff = off; + inum = de.inum; + return iget(dp->dev, inum); + } + } + + return 0; +} + +// Write a new directory entry (name, inum) into the directory dp. +int +dirlink(struct inode *dp, char *name, uint inum) +{ + int off; + struct dirent de; + struct inode *ip; + + // Check that name is not present. + if((ip = dirlookup(dp, name, 0)) != 0){ + iput(ip); + return -1; + } + + // Look for an empty dirent. + for(off = 0; off < dp->size; off += sizeof(de)){ + if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) + panic("dirlink read"); + if(de.inum == 0) + break; + } + + strncpy(de.name, name, DIRSIZ); + de.inum = inum; + if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) + panic("dirlink"); + + return 0; +} + +//PAGEBREAK! +// Paths + +// Copy the next path element from path into name. +// Return a pointer to the element following the copied one. +// The returned path has no leading slashes, +// so the caller can check *path=='\0' to see if the name is the last one. +// If no name to remove, return 0. +// +// Examples: +// skipelem("a/bb/c", name) = "bb/c", setting name = "a" +// skipelem("///a//bb", name) = "bb", setting name = "a" +// skipelem("a", name) = "", setting name = "a" +// skipelem("", name) = skipelem("////", name) = 0 +// +static char* +skipelem(char *path, char *name) +{ + char *s; + int len; + + while(*path == '/') + path++; + if(*path == 0) + return 0; + s = path; + while(*path != '/' && *path != 0) + path++; + len = path - s; + if(len >= DIRSIZ) + memmove(name, s, DIRSIZ); + else { + memmove(name, s, len); + name[len] = 0; + } + while(*path == '/') + path++; + return path; +} + +// Look up and return the inode for a path name. +// If parent != 0, return the inode for the parent and copy the final +// path element into name, which must have room for DIRSIZ bytes. +// Must be called inside a transaction since it calls iput(). +static struct inode* +namex(char *path, int nameiparent, char *name) +{ + struct inode *ip, *next; + + if(*path == '/') + ip = iget(ROOTDEV, ROOTINO); + else + ip = idup(myproc()->cwd); + + while((path = skipelem(path, name)) != 0){ + ilock(ip); + if(ip->type != T_DIR){ + iunlockput(ip); + return 0; + } + if(nameiparent && *path == '\0'){ + // Stop one level early. + iunlock(ip); + return ip; + } + if((next = dirlookup(ip, name, 0)) == 0){ + iunlockput(ip); + return 0; + } + iunlockput(ip); + ip = next; + } + if(nameiparent){ + iput(ip); + return 0; + } + return ip; +} + +struct inode* +namei(char *path) +{ + char name[DIRSIZ]; + return namex(path, 0, name); +} + +struct inode* +nameiparent(char *path, char *name) +{ + return namex(path, 1, name); +} diff --git a/kernel/fs.h b/kernel/fs.h new file mode 100644 index 0000000..bc0805f --- /dev/null +++ b/kernel/fs.h @@ -0,0 +1,60 @@ +// On-disk file system format. +// Both the kernel and user programs use this header file. + + +#define ROOTINO 1 // root i-number +#define BSIZE 1024 // block size + +// Disk layout: +// [ boot block | super block | log | inode blocks | +// free bit map | data blocks] +// +// mkfs computes the super block and builds an initial file system. The +// super block describes the disk layout: +struct superblock { + uint magic; // Must be FSMAGIC + uint size; // Size of file system image (blocks) + uint nblocks; // Number of data blocks + uint ninodes; // Number of inodes. + uint nlog; // Number of log blocks + uint logstart; // Block number of first log block + uint inodestart; // Block number of first inode block + uint bmapstart; // Block number of first free map block +}; + +#define FSMAGIC 0x10203040 + +#define NDIRECT 12 +#define NINDIRECT (BSIZE / sizeof(uint)) +#define MAXFILE (NDIRECT + NINDIRECT) + +// On-disk inode structure +struct dinode { + short type; // File type + short major; // Major device number (T_DEV only) + short minor; // Minor device number (T_DEV only) + short nlink; // Number of links to inode in file system + uint size; // Size of file (bytes) + uint addrs[NDIRECT+1]; // Data block addresses +}; + +// Inodes per block. +#define IPB (BSIZE / sizeof(struct dinode)) + +// Block containing inode i +#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart) + +// Bitmap bits per block +#define BPB (BSIZE*8) + +// Block of free map containing bit for block b +#define BBLOCK(b, sb) ((b)/BPB + sb.bmapstart) + +// Directory is a file containing a sequence of dirent structures. +#define DIRSIZ 14 + +struct dirent { + ushort inum; + char name[DIRSIZ]; +}; + diff --git a/kernel/kalloc.c b/kernel/kalloc.c new file mode 100644 index 0000000..1ed1c49 --- /dev/null +++ b/kernel/kalloc.c @@ -0,0 +1,80 @@ +// Physical memory allocator, intended to allocate +// memory for user processes, kernel stacks, page table pages, +// and pipe buffers. Allocates 4096-byte pages. + +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "riscv.h" +#include "defs.h" + +void freerange(void *pa_start, void *pa_end); + +extern char end[]; // first address after kernel loaded from ELF file + // defined by the kernel linker script in kernel.ld + +struct run { + struct run *next; +}; + +struct { + struct spinlock lock; + struct run *freelist; +} kmem; + +void +kinit() +{ + initlock(&kmem.lock, "kmem"); + freerange(end, (void*)PHYSTOP); +} + +void +freerange(void *pa_start, void *pa_end) +{ + char *p; + p = (char*)PGROUNDUP((uint64)pa_start); + for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) + kfree(p); +} +//PAGEBREAK: 21 +// Free the page of physical memory pointed at by v, +// which normally should have been returned by a +// call to kalloc(). (The exception is when +// initializing the allocator; see kinit above.) +void +kfree(void *pa) +{ + struct run *r; + + if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) + panic("kfree"); + + // Fill with junk to catch dangling refs. + memset(pa, 1, PGSIZE); + + acquire(&kmem.lock); + r = (struct run*)pa; + r->next = kmem.freelist; + kmem.freelist = r; + release(&kmem.lock); +} + +// Allocate one 4096-byte page of physical memory. +// Returns a pointer that the kernel can use. +// Returns 0 if the memory cannot be allocated. +void * +kalloc(void) +{ + struct run *r; + + acquire(&kmem.lock); + r = kmem.freelist; + if(r) + kmem.freelist = r->next; + release(&kmem.lock); + if(r) + memset((char*)r, 5, PGSIZE); // fill with junk + return (void*)r; +} diff --git a/kernel/kernel.ld b/kernel/kernel.ld new file mode 100644 index 0000000..53c9b90 --- /dev/null +++ b/kernel/kernel.ld @@ -0,0 +1,31 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY( _entry ) + +SECTIONS +{ + /* + * ensure that entry.S / _entry is at 0x80000000, + * where qemu's -kernel jumps. + */ + . = 0x80000000; + .text : + { + *(.text) + . = ALIGN(0x1000); + *(trampoline) + } + + . = ALIGN(0x1000); + PROVIDE(etext = .); + + /* + * make sure end is after data and bss. + */ + .data : { + *(.data) + } + bss : { + *(.bss) + PROVIDE(end = .); + } +} diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S new file mode 100644 index 0000000..4f52688 --- /dev/null +++ b/kernel/kernelvec.S @@ -0,0 +1,112 @@ + # + # interrupts and exceptions while in supervisor + # mode come here. + # + # push all registers, call kerneltrap(), restore, return. + # +.globl kerneltrap +.globl kernelvec +.align 4 +kernelvec: + addi sp, sp, -256 + + sd ra, 0(sp) + sd sp, 8(sp) + sd gp, 16(sp) + sd tp, 24(sp) + sd t0, 32(sp) + sd t1, 40(sp) + sd t2, 48(sp) + sd s0, 56(sp) + sd s1, 64(sp) + sd a0, 72(sp) + sd a1, 80(sp) + sd a2, 88(sp) + sd a3, 96(sp) + sd a4, 104(sp) + sd a5, 112(sp) + sd a6, 120(sp) + sd a7, 128(sp) + sd s2, 136(sp) + sd s3, 144(sp) + sd s4, 152(sp) + sd s5, 160(sp) + sd s6, 168(sp) + sd s7, 176(sp) + sd s8, 184(sp) + sd s9, 192(sp) + sd s10, 200(sp) + sd s11, 208(sp) + sd t3, 216(sp) + sd t4, 224(sp) + sd t5, 232(sp) + sd t6, 240(sp) + + call kerneltrap + + ld ra, 0(sp) + ld sp, 8(sp) + ld gp, 16(sp) + ld tp, 24(sp) + ld t0, 32(sp) + ld t1, 40(sp) + ld t2, 48(sp) + ld s0, 56(sp) + ld s1, 64(sp) + ld a0, 72(sp) + ld a1, 80(sp) + ld a2, 88(sp) + ld a3, 96(sp) + ld a4, 104(sp) + ld a5, 112(sp) + ld a6, 120(sp) + ld a7, 128(sp) + ld s2, 136(sp) + ld s3, 144(sp) + ld s4, 152(sp) + ld s5, 160(sp) + ld s6, 168(sp) + ld s7, 176(sp) + ld s8, 184(sp) + ld s9, 192(sp) + ld s10, 200(sp) + ld s11, 208(sp) + ld t3, 216(sp) + ld t4, 224(sp) + ld t5, 232(sp) + ld t6, 240(sp) + + addi sp, sp, 256 + + sret + + # + # machine-mode timer interrupt. + # +.globl machinevec +.align 4 +machinevec: + csrrw a0, mscratch, a0 + sd a1, 0(a0) + sd a2, 8(a0) + sd a3, 16(a0) + sd a4, 24(a0) + + # add another second to mtimecmp0. + ld a1, 32(a0) # CLINT_MTIMECMP(hart) + ld a2, 40(a0) # ticks per second + ld a3, 0(a1) + add a3, a3, a2 + sd a3, 0(a1) + + # raise a supervisor software interrupt. + li a1, 2 + csrw sip, a1 + + ld a4, 24(a0) + ld a3, 16(a0) + ld a2, 8(a0) + ld a1, 0(a0) + csrrw a0, mscratch, a0 + + mret diff --git a/kernel/log.c b/kernel/log.c new file mode 100644 index 0000000..c8f7e62 --- /dev/null +++ b/kernel/log.c @@ -0,0 +1,235 @@ +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "buf.h" + +// Simple logging that allows concurrent FS system calls. +// +// A log transaction contains the updates of multiple FS system +// calls. The logging system only commits when there are +// no FS system calls active. Thus there is never +// any reasoning required about whether a commit might +// write an uncommitted system call's updates to disk. +// +// A system call should call begin_op()/end_op() to mark +// its start and end. Usually begin_op() just increments +// the count of in-progress FS system calls and returns. +// But if it thinks the log is close to running out, it +// sleeps until the last outstanding end_op() commits. +// +// The log is a physical re-do log containing disk blocks. +// The on-disk log format: +// header block, containing block #s for block A, B, C, ... +// block A +// block B +// block C +// ... +// Log appends are synchronous. + +// Contents of the header block, used for both the on-disk header block +// and to keep track in memory of logged block# before commit. +struct logheader { + int n; + int block[LOGSIZE]; +}; + +struct log { + struct spinlock lock; + int start; + int size; + int outstanding; // how many FS sys calls are executing. + int committing; // in commit(), please wait. + int dev; + struct logheader lh; +}; +struct log log; + +static void recover_from_log(void); +static void commit(); + +void +initlog(int dev) +{ + if (sizeof(struct logheader) >= BSIZE) + panic("initlog: too big logheader"); + + struct superblock sb; + initlock(&log.lock, "log"); + readsb(dev, &sb); + log.start = sb.logstart; + log.size = sb.nlog; + log.dev = dev; + recover_from_log(); +} + +// Copy committed blocks from log to their home location +static void +install_trans(void) +{ + int tail; + + for (tail = 0; tail < log.lh.n; tail++) { + struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block + struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst + memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst + bwrite(dbuf); // write dst to disk + brelse(lbuf); + brelse(dbuf); + } +} + +// Read the log header from disk into the in-memory log header +static void +read_head(void) +{ + struct buf *buf = bread(log.dev, log.start); + struct logheader *lh = (struct logheader *) (buf->data); + int i; + log.lh.n = lh->n; + for (i = 0; i < log.lh.n; i++) { + log.lh.block[i] = lh->block[i]; + } + brelse(buf); +} + +// Write in-memory log header to disk. +// This is the true point at which the +// current transaction commits. +static void +write_head(void) +{ + struct buf *buf = bread(log.dev, log.start); + struct logheader *hb = (struct logheader *) (buf->data); + int i; + hb->n = log.lh.n; + for (i = 0; i < log.lh.n; i++) { + hb->block[i] = log.lh.block[i]; + } + bwrite(buf); + brelse(buf); +} + +static void +recover_from_log(void) +{ + read_head(); + install_trans(); // 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. +void +begin_op(void) +{ + acquire(&log.lock); + while(1){ + if(log.committing){ + sleep(&log, &log.lock); + } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){ + // this op might exhaust log space; wait for commit. + sleep(&log, &log.lock); + } else { + log.outstanding += 1; + release(&log.lock); + break; + } + } +} + +// called at the end of each FS system call. +// commits if this was the last outstanding operation. +void +end_op(void) +{ + int do_commit = 0; + + acquire(&log.lock); + log.outstanding -= 1; + if(log.committing) + panic("log.committing"); + if(log.outstanding == 0){ + do_commit = 1; + log.committing = 1; + } else { + // begin_op() may be waiting for log space, + // and decrementing log.outstanding has decreased + // the amount of reserved space. + wakeup(&log); + } + release(&log.lock); + + if(do_commit){ + // call commit w/o holding locks, since not allowed + // to sleep with locks. + commit(); + acquire(&log.lock); + log.committing = 0; + wakeup(&log); + release(&log.lock); + } +} + +// Copy modified blocks from cache to log. +static void +write_log(void) +{ + int tail; + + for (tail = 0; tail < log.lh.n; tail++) { + struct buf *to = bread(log.dev, log.start+tail+1); // log block + struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block + memmove(to->data, from->data, BSIZE); + bwrite(to); // write the log + brelse(from); + brelse(to); + } +} + +static void +commit() +{ + if (log.lh.n > 0) { + write_log(); // Write modified blocks from cache to log + write_head(); // Write header to disk -- the real commit + install_trans(); // 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. +// Record the block number and pin in the cache with B_DIRTY. +// commit()/write_log() will do the disk write. +// +// log_write() replaces bwrite(); a typical use is: +// bp = bread(...) +// modify bp->data[] +// log_write(bp) +// brelse(bp) +void +log_write(struct buf *b) +{ + int i; + + if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) + panic("too big a transaction"); + if (log.outstanding < 1) + panic("log_write outside of trans"); + + acquire(&log.lock); + for (i = 0; i < log.lh.n; i++) { + if (log.lh.block[i] == b->blockno) // log absorbtion + break; + } + log.lh.block[i] = b->blockno; + if (i == log.lh.n) + log.lh.n++; + b->flags |= B_DIRTY; // prevent eviction + release(&log.lock); +} + diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 0000000..2168b9f --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,42 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "defs.h" + +volatile static int started = 0; + +// Bootstrap processor starts running C code here. +// Allocate a real stack and switch to it, first +// doing some setup required for memory allocator to work. +void +main() +{ + if(cpuid() == 0){ + uartinit(); // serial port + consoleinit(); + printf("hart %d starting\n", cpuid()); + kinit(); // physical page allocator + kvminit(); // create kernel page table + kvminithart(); // turn on paging + procinit(); // process table + trapinit(); // trap vectors + trapinithart(); // install kernel trap vector + plicinit(); // set up interrupt controller + plicinithart(); // ask PLIC for device interrupts + binit(); // buffer cache + fileinit(); // file table + ramdiskinit(); // disk + userinit(); // first user process + started = 1; + } else { + while(started == 0) + ; + printf("hart %d starting\n", cpuid()); + kvminithart(); // turn on paging + trapinithart(); // install kernel trap vector + plicinithart(); // ask PLIC for device interrupts + } + + scheduler(); +} diff --git a/kernel/memlayout.h b/kernel/memlayout.h new file mode 100644 index 0000000..462986c --- /dev/null +++ b/kernel/memlayout.h @@ -0,0 +1,50 @@ +// Physical memory layout + +// qemu -machine virt is set up like this, +// based on qemu's hw/riscv/virt.c: +// +// 00001000 -- boot ROM, provided by qemu +// 02000000 -- CLINT +// 0C000000 -- PLIC +// 10000000 -- uart0 registers +// 80000000 -- boot ROM jumps here in machine mode +// -kernel loads the kernel here +// 88000000 -- -initrd fs.img ramdisk image. +// unused RAM after 80000000. + +// the kernel uses physical memory thus: +// 80000000 -- entry.S, then kernel text and data +// end -- start of kernel page allocation area +// PHYSTOP -- end RAM used by the kernel + +// qemu puts UART registers here in physical memory. +#define UART0 0x10000000L +#define UART0_IRQ 10 + +// local interrupt controller, which contains the timer. +#define CLINT 0x2000000L +#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) +#define CLINT_MTIME (CLINT + 0xBFF8) + +// qemu puts programmable interrupt controller here. +#define PLIC 0x0c000000L +#define PLIC_PRIORITY (PLIC + 0x0) +#define PLIC_PENDING (PLIC + 0x1000) +#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) +#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) +#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) +#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) +#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) +#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) + +#define RAMDISK 0x88000000L + +// the kernel expects there to be RAM +// for use by the kernel and user pages +// from physical address 0x80000000 to PHYSTOP. +#define KERNBASE 0x80000000L +#define PHYSTOP (KERNBASE + 128*1024*1024) + +// map the trampoline page to the highest address, +// in both user and kernel space. +#define TRAMPOLINE (MAXVA - PGSIZE) diff --git a/kernel/param.h b/kernel/param.h new file mode 100644 index 0000000..b5fdcb2 --- /dev/null +++ b/kernel/param.h @@ -0,0 +1,13 @@ +#define NPROC 64 // maximum number of processes +#define NCPU 8 // maximum number of CPUs +#define NOFILE 16 // open files per process +#define NFILE 100 // open files per system +#define NINODE 50 // maximum number of active i-nodes +#define NDEV 10 // maximum major device number +#define ROOTDEV 1 // device number of file system root disk +#define MAXARG 32 // max exec arguments +#define MAXOPBLOCKS 10 // max # of blocks any FS op writes +#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log +#define NBUF (MAXOPBLOCKS*3) // size of disk block cache +#define FSSIZE 1000 // size of file system in blocks +#define MAXPATH 128 // maximum file path name diff --git a/kernel/pipe.c b/kernel/pipe.c new file mode 100644 index 0000000..31bf0cc --- /dev/null +++ b/kernel/pipe.c @@ -0,0 +1,129 @@ +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "proc.h" +#include "fs.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "file.h" + +#define PIPESIZE 512 + +struct pipe { + struct spinlock lock; + char data[PIPESIZE]; + uint nread; // number of bytes read + uint nwrite; // number of bytes written + int readopen; // read fd is still open + int writeopen; // write fd is still open +}; + +int +pipealloc(struct file **f0, struct file **f1) +{ + struct pipe *p; + + p = 0; + *f0 = *f1 = 0; + if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) + goto bad; + if((p = (struct pipe*)kalloc()) == 0) + goto bad; + p->readopen = 1; + p->writeopen = 1; + p->nwrite = 0; + p->nread = 0; + initlock(&p->lock, "pipe"); + (*f0)->type = FD_PIPE; + (*f0)->readable = 1; + (*f0)->writable = 0; + (*f0)->pipe = p; + (*f1)->type = FD_PIPE; + (*f1)->readable = 0; + (*f1)->writable = 1; + (*f1)->pipe = p; + return 0; + +//PAGEBREAK: 20 + bad: + if(p) + kfree((char*)p); + if(*f0) + fileclose(*f0); + if(*f1) + fileclose(*f1); + return -1; +} + +void +pipeclose(struct pipe *p, int writable) +{ + acquire(&p->lock); + if(writable){ + p->writeopen = 0; + wakeup(&p->nread); + } else { + p->readopen = 0; + wakeup(&p->nwrite); + } + if(p->readopen == 0 && p->writeopen == 0){ + release(&p->lock); + kfree((char*)p); + } else + release(&p->lock); +} + +//PAGEBREAK: 40 +int +pipewrite(struct pipe *p, uint64 addr, int n) +{ + int i; + char ch; + struct proc *pr = myproc(); + + acquire(&p->lock); + for(i = 0; i < n; i++){ + while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full + if(p->readopen == 0 || myproc()->killed){ + release(&p->lock); + return -1; + } + wakeup(&p->nread); + sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep + } + if(copyin(pr->pagetable, &ch, addr + i, 1) == -1) + break; + p->data[p->nwrite++ % PIPESIZE] = ch; + } + wakeup(&p->nread); //DOC: pipewrite-wakeup1 + release(&p->lock); + return n; +} + +int +piperead(struct pipe *p, uint64 addr, int n) +{ + int i; + struct proc *pr = myproc(); + char ch; + + acquire(&p->lock); + while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty + if(myproc()->killed){ + release(&p->lock); + return -1; + } + sleep(&p->nread, &p->lock); //DOC: piperead-sleep + } + for(i = 0; i < n; i++){ //DOC: piperead-copy + if(p->nread == p->nwrite) + break; + ch = p->data[p->nread++ % PIPESIZE]; + if(copyout(pr->pagetable, addr + i, &ch, 1) == -1) + break; + } + wakeup(&p->nwrite); //DOC: piperead-wakeup + release(&p->lock); + return i; +} diff --git a/kernel/plic.c b/kernel/plic.c new file mode 100644 index 0000000..0f19ab0 --- /dev/null +++ b/kernel/plic.c @@ -0,0 +1,63 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "defs.h" + +// +// the riscv Platform Level Interrupt Controller (PLIC). +// + +void +plicinit(void) +{ + // set uart's priority to be non-zero (otherwise disabled). + *(uint32*)(PLIC + UART0_IRQ*4) = 1; +} + +void +plicinithart(void) +{ + int hart = cpuid(); + + // set uart's enable bit for this hart's S-mode. + //*(uint32*)(PLIC + 0x2080)= (1 << UART0_IRQ); + *(uint32*)PLIC_SENABLE(hart)= (1 << UART0_IRQ); + + // set this hart's S-mode priority threshold to 0. + //*(uint32*)(PLIC + 0x201000) = 0; + *(uint32*)PLIC_SPRIORITY(hart) = 0; +} + +// return a bitmap of which IRQs are waiting +// to be served. +uint64 +plic_pending(void) +{ + uint64 mask; + + //mask = *(uint32*)(PLIC + 0x1000); + //mask |= (uint64)*(uint32*)(PLIC + 0x1004) << 32; + mask = *(uint64*)PLIC_PENDING; + + return mask; +} + +// ask the PLIC what interrupt we should serve. +int +plic_claim(void) +{ + int hart = cpuid(); + //int irq = *(uint32*)(PLIC + 0x201004); + int irq = *(uint32*)PLIC_SCLAIM(hart); + return irq; +} + +// tell the PLIC we've served this IRQ. +void +plic_complete(int irq) +{ + int hart = cpuid(); + //*(uint32*)(PLIC + 0x201004) = irq; + *(uint32*)PLIC_SCLAIM(hart) = irq; +} diff --git a/kernel/proc.c b/kernel/proc.c new file mode 100644 index 0000000..4ae34c8 --- /dev/null +++ b/kernel/proc.c @@ -0,0 +1,591 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "spinlock.h" +#include "defs.h" + +struct { + struct spinlock lock; + struct proc proc[NPROC]; +} ptable; + +struct cpu cpus[NCPU]; + +struct proc *initproc; + +int nextpid = 1; +extern void forkret(void); + +// for returning out of the kernel +extern void sysexit(void); + +static void wakeup1(void *chan); + +extern char trampout[]; // trampoline.S + +void +procinit(void) +{ + initlock(&ptable.lock, "ptable"); +} + +// Must be called with interrupts disabled, +// to prevent race with process being moved +// to a different CPU. +int +cpuid() +{ + int id = r_tp(); + return id; +} + +// Return this core's cpu struct. +// Interrupts must be disabled. +struct cpu* +mycpu(void) { + int id = cpuid(); + struct cpu *c = &cpus[id]; + return c; +} + +// Return the current struct proc *. +struct proc* +myproc(void) { + push_off(); + struct cpu *c = mycpu(); + struct proc *p = c->proc; + pop_off(); + return p; +} + +//PAGEBREAK: 32 +// Look in the process table for an UNUSED proc. +// If found, change state to EMBRYO and initialize +// state required to run in the kernel. +// Otherwise return 0. +static struct proc* +allocproc(void) +{ + struct proc *p; + + acquire(&ptable.lock); + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) + if(p->state == UNUSED) + goto found; + + release(&ptable.lock); + return 0; + +found: + p->state = EMBRYO; + p->pid = nextpid++; + + release(&ptable.lock); + + // Allocate a page for the kernel stack. + if((p->kstack = kalloc()) == 0){ + p->state = UNUSED; + return 0; + } + + // Allocate a trapframe page. + if((p->tf = (struct trapframe *)kalloc()) == 0){ + p->state = UNUSED; + return 0; + } + + // An empty user page table. + p->pagetable = proc_pagetable(p); + + // Set up new context to start executing at forkret, + // which returns to user space. + memset(&p->context, 0, sizeof p->context); + p->context.ra = (uint64)forkret; + p->context.sp = (uint64)p->kstack + PGSIZE; + + return p; +} + +// Create a page table for a given process, +// with no users pages, but with trampoline pages. +// Called both when creating a process, and +// by exec() when building tentative new memory image, +// which might fail. +pagetable_t +proc_pagetable(struct proc *p) +{ + pagetable_t pagetable; + + // An empty user page table. + pagetable = uvmcreate(); + + // map the trampoline code (for system call return) + // at the highest user virtual address. + // only the supervisor uses it, on the way + // to/from user space, so not PTE_U. + mappages(pagetable, TRAMPOLINE, PGSIZE, + (uint64)trampout, PTE_R | PTE_X); + + // map the trapframe, for trampoline.S. + mappages(pagetable, (TRAMPOLINE - PGSIZE), PGSIZE, + (uint64)(p->tf), PTE_R | PTE_W); + + return pagetable; +} + +// Free a process's page table, and free the +// physical memory the page table refers to. +// Called both when a process exits and from +// exec() if it fails. +void +proc_freepagetable(pagetable_t pagetable, uint64 sz) +{ + unmappages(pagetable, TRAMPOLINE, PGSIZE, 0); + unmappages(pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0); + uvmfree(pagetable, sz); +} + +// a user program that calls exec("/init") +// od -t xC initcode +uchar initcode[] = { + 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, + 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, + 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 +}; + +//PAGEBREAK: 32 +// Set up first user process. +void +userinit(void) +{ + struct proc *p; + + p = allocproc(); + initproc = p; + + uvminit(p->pagetable, initcode, sizeof(initcode)); + p->sz = PGSIZE; + + // prepare for the very first kernel->user. + p->tf->epc = 0; + p->tf->sp = PGSIZE; + + safestrcpy(p->name, "initcode", sizeof(p->name)); + p->cwd = namei("/"); + + // this assignment to p->state lets other cores + // run this process. the acquire forces the above + // writes to be visible, and the lock is also needed + // because the assignment might not be atomic. + acquire(&ptable.lock); + + p->state = RUNNABLE; + + release(&ptable.lock); +} + +// Grow current process's memory by n bytes. +// Return 0 on success, -1 on failure. +int +growproc(int n) +{ + uint sz; + struct proc *p = myproc(); + + sz = p->sz; + if(n > 0){ + if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) + return -1; + } else if(n < 0){ + if((sz = uvmdealloc(p->pagetable, sz, sz + n)) == 0) + return -1; + } + p->sz = sz; + return 0; +} + +// Create a new process, copying p as the parent. +// Sets up child kernel stack to return as if from system call. +int +fork(void) +{ + int i, pid; + struct proc *np; + struct proc *p = myproc(); + + // Allocate process. + if((np = allocproc()) == 0){ + return -1; + } + + // Copy user memory from parent to child. + uvmcopy(p->pagetable, np->pagetable, p->sz); + np->sz = p->sz; + + np->parent = p; + + // copy saved user registers. + *(np->tf) = *(p->tf); + + // Cause fork to return 0 in the child. + np->tf->a0 = 0; + + // increment reference counts on open file descriptors. + for(i = 0; i < NOFILE; i++) + if(p->ofile[i]) + np->ofile[i] = filedup(p->ofile[i]); + np->cwd = idup(p->cwd); + + safestrcpy(np->name, p->name, sizeof(p->name)); + + pid = np->pid; + + acquire(&ptable.lock); + + np->state = RUNNABLE; + + release(&ptable.lock); + + return pid; +} + +// Exit the current process. Does not return. +// An exited process remains in the zombie state +// until its parent calls wait(). +void +exit(void) +{ + struct proc *p = myproc(); + struct proc *pp; + int fd; + + if(p == initproc) + panic("init exiting"); + + // Close all open files. + for(fd = 0; fd < NOFILE; fd++){ + if(p->ofile[fd]){ + fileclose(p->ofile[fd]); + p->ofile[fd] = 0; + } + } + + begin_op(); + iput(p->cwd); + end_op(); + p->cwd = 0; + + acquire(&ptable.lock); + + // Parent might be sleeping in wait(). + wakeup1(p->parent); + + // Pass abandoned children to init. + for(pp = ptable.proc; pp < &ptable.proc[NPROC]; pp++){ + if(pp->parent == p){ + pp->parent = initproc; + if(pp->state == ZOMBIE) + wakeup1(initproc); + } + } + + // Jump into the scheduler, never to return. + p->state = ZOMBIE; + sched(); + panic("zombie exit"); +} + +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +wait(void) +{ + struct proc *np; + int havekids, pid; + struct proc *p = myproc(); + + acquire(&ptable.lock); + for(;;){ + // Scan through table looking for exited children. + havekids = 0; + for(np = ptable.proc; np < &ptable.proc[NPROC]; np++){ + if(np->parent != p) + continue; + havekids = 1; + if(np->state == ZOMBIE){ + // Found one. + pid = np->pid; + kfree(np->kstack); + np->kstack = 0; + kfree((void*)np->tf); + np->tf = 0; + proc_freepagetable(np->pagetable, np->sz); + np->pagetable = 0; + np->pid = 0; + np->parent = 0; + np->name[0] = 0; + np->killed = 0; + np->state = UNUSED; + release(&ptable.lock); + return pid; + } + } + + // No point waiting if we don't have any children. + if(!havekids || p->killed){ + release(&ptable.lock); + return -1; + } + + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(p, &ptable.lock); //DOC: wait-sleep + } +} + +//PAGEBREAK: 42 +// Per-CPU process scheduler. +// Each CPU calls scheduler() after setting itself up. +// Scheduler never returns. It loops, doing: +// - choose a process to run +// - swtch to start running that process +// - eventually that process transfers control +// via swtch back to the scheduler. +void +scheduler(void) +{ + struct proc *p; + struct cpu *c = mycpu(); + + c->proc = 0; + for(;;){ + // Enable interrupts on this processor. + intr_on(); + + // Loop over process table looking for process to run. + acquire(&ptable.lock); + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state != RUNNABLE) + continue; + + // Switch to chosen process. It is the process's job + // to release ptable.lock and then reacquire it + // before jumping back to us. + c->proc = p; + p->state = RUNNING; + + swtch(&c->scheduler, &p->context); + + // Process is done running for now. + // It should have changed its p->state before coming back. + c->proc = 0; + } + release(&ptable.lock); + } +} + +// Enter scheduler. Must hold only ptable.lock +// and have changed proc->state. Saves and restores +// intena because intena is a property of this +// kernel thread, not this CPU. It should +// be proc->intena and proc->noff, but that would +// break in the few places where a lock is held but +// there's no process. +void +sched(void) +{ + int intena; + struct proc *p = myproc(); + + if(!holding(&ptable.lock)) + panic("sched ptable.lock"); + if(mycpu()->noff != 1) + panic("sched locks"); + if(p->state == RUNNING) + panic("sched running"); + if(intr_get()) + panic("sched interruptible"); + + intena = mycpu()->intena; + swtch(&p->context, &mycpu()->scheduler); + mycpu()->intena = intena; +} + +// Give up the CPU for one scheduling round. +void +yield(void) +{ + acquire(&ptable.lock); //DOC: yieldlock + myproc()->state = RUNNABLE; + sched(); + release(&ptable.lock); +} + +// A fork child's very first scheduling by scheduler() +// will swtch to forkret. +void +forkret(void) +{ + static int first = 1; + + // Still holding ptable.lock from scheduler. + release(&ptable.lock); + + if (first) { + // Some initialization functions must be run in the context + // of a regular process (e.g., they call sleep), and thus cannot + // be run from main(). + first = 0; + iinit(ROOTDEV); + initlog(ROOTDEV); + } + + usertrapret(); +} + +// Atomically release lock and sleep on chan. +// Reacquires lock when awakened. +void +sleep(void *chan, struct spinlock *lk) +{ + struct proc *p = myproc(); + + if(p == 0) + panic("sleep"); + + if(lk == 0) + panic("sleep without lk"); + + // Must acquire ptable.lock in order to + // change p->state and then call sched. + // Once we hold ptable.lock, we can be + // guaranteed that we won't miss any wakeup + // (wakeup runs with ptable.lock locked), + // so it's okay to release lk. + if(lk != &ptable.lock){ //DOC: sleeplock0 + acquire(&ptable.lock); //DOC: sleeplock1 + release(lk); + } + // Go to sleep. + p->chan = chan; + p->state = SLEEPING; + + sched(); + + // Tidy up. + p->chan = 0; + + // Reacquire original lock. + if(lk != &ptable.lock){ //DOC: sleeplock2 + release(&ptable.lock); + acquire(lk); + } +} + +//PAGEBREAK! +// Wake up all processes sleeping on chan. +// The ptable lock must be held. +static void +wakeup1(void *chan) +{ + struct proc *p; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) + if(p->state == SLEEPING && p->chan == chan) + p->state = RUNNABLE; +} + +// Wake up all processes sleeping on chan. +void +wakeup(void *chan) +{ + acquire(&ptable.lock); + wakeup1(chan); + release(&ptable.lock); +} + +// Kill the process with the given pid. +// Process won't exit until it returns +// to user space (see trap in trap.c). +int +kill(int pid) +{ + struct proc *p; + + acquire(&ptable.lock); + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->pid == pid){ + p->killed = 1; + // Wake process from sleep if necessary. + if(p->state == SLEEPING) + p->state = RUNNABLE; + release(&ptable.lock); + return 0; + } + } + release(&ptable.lock); + return -1; +} + +// Copy to either a user address, or kernel address, +// depending on usr_dst. +// Returns 0 on success, -1 on error. +int +either_copyout(int user_dst, uint64 dst, void *src, uint64 len) +{ + struct proc *p = myproc(); + if(user_dst){ + return copyout(p->pagetable, dst, src, len); + } else { + memmove((char *)dst, src, len); + return 0; + } +} + +// Copy from either a user address, or kernel address, +// depending on usr_src. +// Returns 0 on success, -1 on error. +int +either_copyin(void *dst, int user_src, uint64 src, uint64 len) +{ + struct proc *p = myproc(); + if(user_src){ + return copyin(p->pagetable, dst, src, len); + } else { + memmove(dst, (char*)src, len); + return 0; + } +} + +// Print a process listing to console. For debugging. +// Runs when user types ^P on console. +// No lock to avoid wedging a stuck machine further. +void +procdump(void) +{ + static char *states[] = { + [UNUSED] "unused", + [EMBRYO] "embryo", + [SLEEPING] "sleep ", + [RUNNABLE] "runble", + [RUNNING] "run ", + [ZOMBIE] "zombie" + }; + struct proc *p; + char *state; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state == UNUSED) + continue; + if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) + state = states[p->state]; + else + state = "???"; + printf("%d %s %s", p->pid, state, p->name); + printf("\n"); + } +} + diff --git a/kernel/proc.h b/kernel/proc.h new file mode 100644 index 0000000..278e4cd --- /dev/null +++ b/kernel/proc.h @@ -0,0 +1,104 @@ +// Saved registers for kernel context switches. +struct context { + uint64 ra; + uint64 sp; + + // callee-saved + uint64 s0; + uint64 s1; + uint64 s2; + uint64 s3; + uint64 s4; + uint64 s5; + uint64 s6; + uint64 s7; + uint64 s8; + uint64 s9; + uint64 s10; + uint64 s11; +}; + +// Per-CPU state +struct cpu { + struct proc *proc; // The process running on this cpu or null + struct context scheduler; // swtch() here to enter scheduler + int noff; // Depth of push_off() nesting. + int intena; // Were interrupts enabled before push_off()? +}; + +extern struct cpu cpus[NCPU]; + +//PAGEBREAK: 17 + +// per-process data for the early trap handling code in trampoline.S. +// sits in a page by itself just under the trampoline page in the +// user page table. not specially mapped in the kernel page table. +// the sscratch register points here. +// trampoline.S saves user registers, then restores kernel_sp and +// kernel_satp. +// includes callee-saved registers like s0-s11 because the +// return-to-user path via usertrapret() doesn't return through +// the entire kernel call stack. +struct trapframe { + /* 0 */ uint64 kernel_satp; + /* 8 */ uint64 kernel_sp; + /* 16 */ uint64 kernel_trap; // usertrap() + /* 24 */ uint64 epc; // saved user program counter + /* 32 */ uint64 hartid; + /* 40 */ uint64 ra; + /* 48 */ uint64 sp; + /* 56 */ uint64 gp; + /* 64 */ uint64 tp; + /* 72 */ uint64 t0; + /* 80 */ uint64 t1; + /* 88 */ uint64 t2; + /* 96 */ uint64 s0; + /* 104 */ uint64 s1; + /* 112 */ uint64 a0; + /* 120 */ uint64 a1; + /* 128 */ uint64 a2; + /* 136 */ uint64 a3; + /* 144 */ uint64 a4; + /* 152 */ uint64 a5; + /* 160 */ uint64 a6; + /* 168 */ uint64 a7; + /* 176 */ uint64 s2; + /* 184 */ uint64 s3; + /* 192 */ uint64 s4; + /* 200 */ uint64 s5; + /* 208 */ uint64 s6; + /* 216 */ uint64 s7; + /* 224 */ uint64 s8; + /* 232 */ uint64 s9; + /* 240 */ uint64 s10; + /* 248 */ uint64 s11; + /* 256 */ uint64 t3; + /* 264 */ uint64 t4; + /* 272 */ uint64 t5; + /* 280 */ uint64 t6; +}; + +enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; + +// Per-process state +struct proc { + char *kstack; // Bottom of kernel stack for this process + uint64 sz; // Size of process memory (bytes) + pagetable_t pagetable; // Page table + enum procstate state; // Process state + int pid; // Process ID + struct proc *parent; // Parent process + struct trapframe *tf; // data page for trampoline.S + struct context context; // swtch() here to run process + void *chan; // If non-zero, sleeping on chan + int killed; // If non-zero, have been killed + struct file *ofile[NOFILE]; // Open files + struct inode *cwd; // Current directory + char name[16]; // Process name (debugging) +}; + +// Process memory is laid out contiguously, low addresses first: +// text +// original data and bss +// fixed-size stack +// expandable heap diff --git a/kernel/ramdisk.c b/kernel/ramdisk.c new file mode 100644 index 0000000..9901294 --- /dev/null +++ b/kernel/ramdisk.c @@ -0,0 +1,45 @@ +// +// ramdisk that uses the disk image loaded by qemu -rdinit fs.img +// + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "buf.h" + +void +ramdiskinit(void) +{ +} + +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. +void +ramdiskrw(struct buf *b) +{ + if(!holdingsleep(&b->lock)) + panic("ramdiskrw: buf not locked"); + if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) + panic("ramdiskrw: nothing to do"); + + if(b->blockno >= FSSIZE) + panic("ramdiskrw: blockno too big"); + + uint64 diskaddr = b->blockno * BSIZE; + char *addr = (char *)RAMDISK + diskaddr; + + if(b->flags & B_DIRTY){ + // write + memmove(addr, b->data, BSIZE); + b->flags &= ~B_DIRTY; + } else { + // read + memmove(b->data, addr, BSIZE); + b->flags |= B_VALID; + } +} diff --git a/kernel/riscv.h b/kernel/riscv.h new file mode 100644 index 0000000..c3371a4 --- /dev/null +++ b/kernel/riscv.h @@ -0,0 +1,338 @@ +// which hart (core) is this? +static inline uint64 +r_mhartid() +{ + uint64 x; + asm volatile("csrr %0, mhartid" : "=r" (x) ); + return x; +} + +// Machine Status Register, mstatus + +#define MSTATUS_MPP_MASK (3L << 11) +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) + +static inline uint64 +r_mstatus() +{ + uint64 x; + asm volatile("csrr %0, mstatus" : "=r" (x) ); + return x; +} + +static inline void +w_mstatus(uint64 x) +{ + asm volatile("csrw mstatus, %0" : : "r" (x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void +w_mepc(uint64 x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +// Supervisor Status Register, sstatus + +#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +#define SSTATUS_UIE (1L << 0) // User Interrupt Enable + +static inline uint64 +r_sstatus() +{ + uint64 x; + asm volatile("csrr %0, sstatus" : "=r" (x) ); + return x; +} + +static inline void +w_sstatus(uint64 x) +{ + asm volatile("csrw sstatus, %0" : : "r" (x)); +} + +// Supervisor Interrupt Pending +static inline uint64 +r_sip() +{ + uint64 x; + asm volatile("csrr %0, sip" : "=r" (x) ); + return x; +} + +static inline void +w_sip(uint64 x) +{ + asm volatile("csrw sip, %0" : : "r" (x)); +} + +// Supervisor Interrupt Enable +#define SIE_SEIE (1L << 9) // external +#define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software +static inline uint64 +r_sie() +{ + uint64 x; + asm volatile("csrr %0, sie" : "=r" (x) ); + return x; +} + +static inline void +w_sie(uint64 x) +{ + asm volatile("csrw sie, %0" : : "r" (x)); +} + +// Machine-mode Interrupt Enable +#define MIE_MEIE (1L << 11) // external +#define MIE_MTIE (1L << 7) // timer +#define MIE_MSIE (1L << 3) // software +static inline uint64 +r_mie() +{ + uint64 x; + asm volatile("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void +w_mie(uint64 x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void +w_sepc(uint64 x) +{ + asm volatile("csrw sepc, %0" : : "r" (x)); +} + +static inline uint64 +r_sepc() +{ + uint64 x; + asm volatile("csrr %0, sepc" : "=r" (x) ); + return x; +} + +// Machine Exception Delegation +static inline uint64 +r_medeleg() +{ + uint64 x; + asm volatile("csrr %0, medeleg" : "=r" (x) ); + return x; +} + +static inline void +w_medeleg(uint64 x) +{ + asm volatile("csrw medeleg, %0" : : "r" (x)); +} + +// Machine Interrupt Delegation +static inline uint64 +r_mideleg() +{ + uint64 x; + asm volatile("csrr %0, mideleg" : "=r" (x) ); + return x; +} + +static inline void +w_mideleg(uint64 x) +{ + asm volatile("csrw mideleg, %0" : : "r" (x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void +w_stvec(uint64 x) +{ + asm volatile("csrw stvec, %0" : : "r" (x)); +} + +static inline uint64 +r_stvec() +{ + uint64 x; + asm volatile("csrr %0, stvec" : "=r" (x) ); + return x; +} + +// Machine-mode interrupt vector +static inline void +w_mtvec(uint64 x) +{ + asm volatile("csrw mtvec, %0" : : "r" (x)); +} + +// use riscv's sv39 page table scheme. +#define SATP_SV39 (8L << 60) + +#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void +w_satp(uint64 x) +{ + asm volatile("csrw satp, %0" : : "r" (x)); +} + +static inline uint64 +r_satp() +{ + uint64 x; + asm volatile("csrr %0, satp" : "=r" (x) ); + return x; +} + +// Supervisor Scratch register, for early trap handler in trampoline.S. +static inline void +w_sscratch(uint64 x) +{ + asm volatile("csrw sscratch, %0" : : "r" (x)); +} + +static inline void +w_mscratch(uint64 x) +{ + asm volatile("csrw mscratch, %0" : : "r" (x)); +} + +// Supervisor Trap Cause +static inline uint64 +r_scause() +{ + uint64 x; + asm volatile("csrr %0, scause" : "=r" (x) ); + return x; +} + +// Supervisor Trap Value +static inline uint64 +r_stval() +{ + uint64 x; + asm volatile("csrr %0, stval" : "=r" (x) ); + return x; +} + +// Machine-mode Counter-Enable +static inline void +w_mcounteren(uint64 x) +{ + asm volatile("csrw mcounteren, %0" : : "r" (x)); +} + +static inline uint64 +r_mcounteren() +{ + uint64 x; + asm volatile("csrr %0, mcounteren" : "=r" (x) ); + return x; +} + +// machine-mode cycle counter +static inline uint64 +r_time() +{ + uint64 x; + asm volatile("csrr %0, time" : "=r" (x) ); + return x; +} + +// enable interrupts +static inline void +intr_on() +{ + w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); + w_sstatus(r_sstatus() | SSTATUS_SIE); +} + +// disable interrupts +static inline void +intr_off() +{ + w_sstatus(r_sstatus() & ~SSTATUS_SIE); +} + +// are interrupts enabled? +static inline int +intr_get() +{ + uint64 x = r_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline uint64 +r_sp() +{ + uint64 x; + asm volatile("mv %0, sp" : "=r" (x) ); + return x; +} + +// read and write tp, the thread pointer, which holds +// this core's hartid (core number), the index into cpus[]. +static inline uint64 +r_tp() +{ + uint64 x; + asm volatile("mv %0, tp" : "=r" (x) ); + return x; +} + +static inline void +w_tp(uint64 x) +{ + asm volatile("mv tp, %0" : : "r" (x)); +} + +#define PGSIZE 4096 // bytes per page +#define PGSHIFT 12 // bits of offset within a page + +#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) + +#define PTE_V (1L << 0) // valid +#define PTE_R (1L << 1) +#define PTE_W (1L << 2) +#define PTE_X (1L << 3) +#define PTE_U (1L << 4) // 1 -> user can access + +// shift a physical address to the right place for a PTE. +#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) + +#define PTE2PA(pte) (((pte) >> 10) << 12) + +#define PTE_FLAGS(pte) ((pte) & (PTE_V|PTE_R|PTE_W|PTE_X|PTE_U)) + +// extract the three 9-bit page table indices from a virtual address. +#define PXMASK 0x1FF // 9 bits +#define PXSHIFT(level) (PGSHIFT+(9*(level))) +#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK) + +// one beyond the highest possible virtual address. +// MAXVA is actually one bit less than the max allowed by +// Sv39, to avoid having to sign-extend virtual addresses +// that have the high bit set. +#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) + +typedef uint64 pte_t; +typedef uint64 *pagetable_t; // 512 PTEs diff --git a/kernel/sleeplock.c b/kernel/sleeplock.c new file mode 100644 index 0000000..b490370 --- /dev/null +++ b/kernel/sleeplock.c @@ -0,0 +1,55 @@ +// Sleeping locks + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "proc.h" +#include "spinlock.h" +#include "sleeplock.h" + +void +initsleeplock(struct sleeplock *lk, char *name) +{ + initlock(&lk->lk, "sleep lock"); + lk->name = name; + lk->locked = 0; + lk->pid = 0; +} + +void +acquiresleep(struct sleeplock *lk) +{ + acquire(&lk->lk); + while (lk->locked) { + sleep(lk, &lk->lk); + } + lk->locked = 1; + lk->pid = myproc()->pid; + release(&lk->lk); +} + +void +releasesleep(struct sleeplock *lk) +{ + acquire(&lk->lk); + lk->locked = 0; + lk->pid = 0; + wakeup(lk); + release(&lk->lk); +} + +int +holdingsleep(struct sleeplock *lk) +{ + int r; + + acquire(&lk->lk); + r = lk->locked && (lk->pid == myproc()->pid); + release(&lk->lk); + return r; +} + + + diff --git a/kernel/sleeplock.h b/kernel/sleeplock.h new file mode 100644 index 0000000..110e6f3 --- /dev/null +++ b/kernel/sleeplock.h @@ -0,0 +1,10 @@ +// Long-term locks for processes +struct sleeplock { + uint locked; // Is the lock held? + struct spinlock lk; // spinlock protecting this sleep lock + + // For debugging: + char *name; // Name of lock. + int pid; // Process holding lock +}; + diff --git a/kernel/spinlock.c b/kernel/spinlock.c new file mode 100644 index 0000000..bbb7cb5 --- /dev/null +++ b/kernel/spinlock.c @@ -0,0 +1,110 @@ +// Mutual exclusion spin locks. + +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "riscv.h" +#include "proc.h" +#include "defs.h" + +void +initlock(struct spinlock *lk, char *name) +{ + lk->name = name; + lk->locked = 0; + lk->cpu = 0; +} + +// Acquire the lock. +// Loops (spins) until the lock is acquired. +// Holding a lock for a long time may cause +// other CPUs to waste time spinning to acquire it. +void +acquire(struct spinlock *lk) +{ + push_off(); // disable interrupts to avoid deadlock. + if(holding(lk)) + panic("acquire"); + + // The xchg is atomic. + //while(xchg(&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 + // past this point, to ensure that the critical section's memory + // references happen after the lock is acquired. + __sync_synchronize(); + + // Record info about lock acquisition for holding() and debugging. + lk->cpu = mycpu(); +} + +// Release the lock. +void +release(struct spinlock *lk) +{ + if(!holding(lk)) + panic("release"); + + lk->cpu = 0; + + // Tell the C compiler and the processor to not move loads or stores + // past this point, to ensure that all the stores in the critical + // section are visible to other cores before the lock is released. + // Both the C compiler and the hardware may re-order loads and + // stores; __sync_synchronize() tells them both not to. + // On RISC-V, this turns into a fence instruction. + __sync_synchronize(); + + // Release the lock, equivalent to lk->locked = 0. + // This code can't use a C assignment, since it might + // not be atomic. A real OS would use C atomics here. + // On RISC-V, use an amoswap instruction. + //asm volatile("movl $0, %0" : "+m" (lk->locked) : ); + __sync_lock_release(&lk->locked); + + pop_off(); +} + +// Check whether this cpu is holding the lock. +int +holding(struct spinlock *lk) +{ + int r; + push_off(); + r = (lk->locked && lk->cpu == mycpu()); + pop_off(); + return r; +} + +// push_off/pop_off are like intr_off()/intr_on() except that they are matched: +// it takes two pop_off to undo two push_off. Also, if interrupts +// are initially off, then push_off, pop_off leaves them off. + +void +push_off(void) +{ + struct cpu *c = mycpu(); + int old = intr_get(); + + intr_off(); + if(c->noff == 0) + c->intena = old; + c->noff += 1; +} + +void +pop_off(void) +{ + struct cpu *c = mycpu(); + if(intr_get()) + panic("pop_off - interruptible"); + c->noff -= 1; + if(c->noff < 0) + panic("pop_off"); + if(c->noff == 0 && c->intena) + intr_on(); +} diff --git a/kernel/spinlock.h b/kernel/spinlock.h new file mode 100644 index 0000000..4392820 --- /dev/null +++ b/kernel/spinlock.h @@ -0,0 +1,9 @@ +// Mutual exclusion lock. +struct spinlock { + uint locked; // Is the lock held? + + // For debugging: + char *name; // Name of lock. + struct cpu *cpu; // The cpu holding the lock. +}; + diff --git a/kernel/start.c b/kernel/start.c new file mode 100644 index 0000000..c4689dc --- /dev/null +++ b/kernel/start.c @@ -0,0 +1,56 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "defs.h" + +void main(); + +// entry.S needs one stack per CPU. +__attribute__ ((aligned (16))) char stack0[4096 * NCPU]; + +// scratch area for timer interrupt, one per CPU. +uint64 mscratch0[NCPU * 32]; + +// assembly code in kernelvec for machine-mode timer interrupt. +extern void machinevec(); + +// entry.S jumps here in machine mode on stack0. +void +mstart() +{ + // set M Previous Privilege mode to Supervisor, for mret. + unsigned long x = r_mstatus(); + x &= ~MSTATUS_MPP_MASK; + x |= MSTATUS_MPP_S; + w_mstatus(x); + + // set M Exception Program Counter to main, for mret. + // requires gcc -mcmodel=medany + w_mepc((uint64)main); + + // disable paging for now. + w_satp(0); + + // delegate all interrupts and exceptions to supervisor mode. + w_medeleg(0xffff); + w_mideleg(0xffff); + + // set up to receive timer interrupts in machine mode, + // for pre-emptive switching and (on hart 0) to drive time. + int id = r_mhartid(); + uint64 *scratch = &mscratch0[32 * id]; + *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + 10000; + scratch[4] = CLINT_MTIMECMP(id); + scratch[5] = 10000000; + w_mscratch((uint64)scratch); + w_mtvec((uint64)machinevec); + w_mstatus(r_mstatus() | MSTATUS_MIE); + w_mie(r_mie() | MIE_MTIE); + + // keep each CPU's hartid in its tp register, for cpuid(). + w_tp(id); + + // call main() in supervisor mode. + asm volatile("mret"); +} diff --git a/kernel/stat.h b/kernel/stat.h new file mode 100644 index 0000000..8a80933 --- /dev/null +++ b/kernel/stat.h @@ -0,0 +1,11 @@ +#define T_DIR 1 // Directory +#define T_FILE 2 // File +#define T_DEV 3 // Device + +struct stat { + short type; // Type of file + int dev; // File system's disk device + uint ino; // Inode number + short nlink; // Number of links to file + uint size; // Size of file in bytes +}; diff --git a/kernel/string.c b/kernel/string.c new file mode 100644 index 0000000..d99e612 --- /dev/null +++ b/kernel/string.c @@ -0,0 +1,104 @@ +#include "types.h" + +void* +memset(void *dst, int c, uint n) +{ + char *cdst = (char *) dst; + int i; + for(i = 0; i < n; i++){ + cdst[i] = c; + } + return dst; +} + +int +memcmp(const void *v1, const void *v2, uint n) +{ + const uchar *s1, *s2; + + s1 = v1; + s2 = v2; + while(n-- > 0){ + if(*s1 != *s2) + return *s1 - *s2; + s1++, s2++; + } + + return 0; +} + +void* +memmove(void *dst, const void *src, uint n) +{ + const char *s; + char *d; + + s = src; + d = dst; + if(s < d && s + n > d){ + s += n; + d += n; + while(n-- > 0) + *--d = *--s; + } else + while(n-- > 0) + *d++ = *s++; + + return dst; +} + +// memcpy exists to placate GCC. Use memmove. +void* +memcpy(void *dst, const void *src, uint n) +{ + return memmove(dst, src, n); +} + +int +strncmp(const char *p, const char *q, uint n) +{ + while(n > 0 && *p && *p == *q) + n--, p++, q++; + if(n == 0) + return 0; + return (uchar)*p - (uchar)*q; +} + +char* +strncpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + while(n-- > 0 && (*s++ = *t++) != 0) + ; + while(n-- > 0) + *s++ = 0; + return os; +} + +// Like strncpy but guaranteed to NUL-terminate. +char* +safestrcpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + if(n <= 0) + return os; + while(--n > 0 && (*s++ = *t++) != 0) + ; + *s = 0; + return os; +} + +int +strlen(const char *s) +{ + int n; + + for(n = 0; s[n]; n++) + ; + return n; +} + diff --git a/kernel/swtch.S b/kernel/swtch.S new file mode 100644 index 0000000..17a8663 --- /dev/null +++ b/kernel/swtch.S @@ -0,0 +1,42 @@ +# Context switch +# +# void swtch(struct context *old, struct context *new); +# +# Save current registers in old. Load from new. + + +.globl swtch +swtch: + sd ra, 0(a0) + sd sp, 8(a0) + sd s0, 16(a0) + sd s1, 24(a0) + sd s2, 32(a0) + sd s3, 40(a0) + sd s4, 48(a0) + sd s5, 56(a0) + sd s6, 64(a0) + sd s7, 72(a0) + sd s8, 80(a0) + sd s9, 88(a0) + sd s10, 96(a0) + sd s11, 104(a0) + + ld ra, 0(a1) + ld sp, 8(a1) + ld s0, 16(a1) + ld s1, 24(a1) + ld s2, 32(a1) + ld s3, 40(a1) + ld s4, 48(a1) + ld s5, 56(a1) + ld s6, 64(a1) + ld s7, 72(a1) + ld s8, 80(a1) + ld s9, 88(a1) + ld s10, 96(a1) + ld s11, 104(a1) + + ret + + diff --git a/kernel/syscall.c b/kernel/syscall.c new file mode 100644 index 0000000..ca34f2c --- /dev/null +++ b/kernel/syscall.c @@ -0,0 +1,191 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "syscall.h" +#include "defs.h" + +// User code makes a system call with INT T_SYSCALL. +// System call number in %eax. +// Arguments on the stack, from the user call to the C +// library system call function. The saved user %esp points +// to a saved program counter, and then the first argument. + +// Fetch the int at addr from the current process. +int +fetchint(uint64 addr, int *ip) +{ + struct proc *p = myproc(); + + if(addr >= p->sz || addr+4 > p->sz) + return -1; + if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) + return -1; + return 0; +} + +// Fetch the uint64 at addr from the current process. +int +fetchaddr(uint64 addr, uint64 *ip) +{ + struct proc *p = myproc(); + if(addr >= p->sz || addr+sizeof(uint64) > p->sz) + return -1; + if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) + return -1; + return 0; +} + +// Fetch the nul-terminated string at addr from the current process. +// Doesn't actually copy the string - just sets *pp to point at it. +// Returns length of string, not including nul, or -1 for error. +int +fetchstr(uint64 addr, char *buf, int max) +{ + struct proc *p = myproc(); + int err = copyinstr(p->pagetable, buf, addr, max); + if(err < 0) + return err; + return strlen(buf); +} + +static uint64 +fetcharg(int n) +{ + struct proc *p = myproc(); + switch (n) { + case 0: + return p->tf->a0; + case 1: + return p->tf->a1; + case 2: + return p->tf->a2; + case 3: + return p->tf->a3; + case 4: + return p->tf->a4; + case 5: + return p->tf->a5; + } + panic("fetcharg"); + return -1; +} + +// Fetch the nth 32-bit system call argument. +int +argint(int n, int *ip) +{ + *ip = fetcharg(n); + return 0; +} + +int +argaddr(int n, uint64 *ip) +{ + *ip = fetcharg(n); + return 0; +} + +// Fetch the nth word-sized system call argument as a pointer +// to a block of memory of size bytes. Check that the pointer +// lies within the process address space. +int +argptr(int n, uint64 *pp, int size) +{ + uint64 i; + struct proc *p = myproc(); + + if(argaddr(n, &i) < 0) + return -1; + if(size < 0 || (uint)i >= p->sz || (uint)i+size > p->sz) + return -1; + *pp = i; + return 0; +} + +// Fetch the nth word-sized system call argument as a null-terminated string. +// Copies into buf, at most max. +// Returns string length if OK (including nul), -1 if error. +int +argstr(int n, char *buf, int max) +{ + uint64 addr; + if(argaddr(n, &addr) < 0) + return -1; + return fetchstr(addr, buf, max); +} + +extern int sys_chdir(void); +extern int sys_close(void); +extern int sys_dup(void); +extern int sys_exec(void); +extern int sys_exit(void); +extern int sys_fork(void); +extern int sys_fstat(void); +extern int sys_getpid(void); +extern int sys_kill(void); +extern int sys_link(void); +extern int sys_mkdir(void); +extern int sys_mknod(void); +extern int sys_open(void); +extern int sys_pipe(void); +extern int sys_read(void); +extern int sys_sbrk(void); +extern int sys_sleep(void); +extern int sys_unlink(void); +extern int sys_wait(void); +extern int sys_write(void); +extern int sys_uptime(void); + +static int (*syscalls[])(void) = { +[SYS_fork] sys_fork, +[SYS_exit] sys_exit, +[SYS_wait] sys_wait, +[SYS_pipe] sys_pipe, +[SYS_read] sys_read, +[SYS_kill] sys_kill, +[SYS_exec] sys_exec, +[SYS_fstat] sys_fstat, +[SYS_chdir] sys_chdir, +[SYS_dup] sys_dup, +[SYS_getpid] sys_getpid, +[SYS_sbrk] sys_sbrk, +[SYS_sleep] sys_sleep, +[SYS_uptime] sys_uptime, +[SYS_open] sys_open, +[SYS_write] sys_write, +[SYS_mknod] sys_mknod, +[SYS_unlink] sys_unlink, +[SYS_link] sys_link, +[SYS_mkdir] sys_mkdir, +[SYS_close] sys_close, +}; + +static void +dosyscall(void) +{ + int num; + struct proc *p = myproc(); + + num = p->tf->a7; + if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { + p->tf->a0 = syscalls[num](); + } else { + printf("%d %s: unknown sys call %d\n", + p->pid, p->name, num); + p->tf->a0 = -1; + } +} + +void +syscall() +{ + if(myproc()->killed) + exit(); + dosyscall(); + if(myproc()->killed) + exit(); + return; +} + diff --git a/kernel/syscall.h b/kernel/syscall.h new file mode 100644 index 0000000..bc5f356 --- /dev/null +++ b/kernel/syscall.h @@ -0,0 +1,22 @@ +// System call numbers +#define SYS_fork 1 +#define SYS_exit 2 +#define SYS_wait 3 +#define SYS_pipe 4 +#define SYS_read 5 +#define SYS_kill 6 +#define SYS_exec 7 +#define SYS_fstat 8 +#define SYS_chdir 9 +#define SYS_dup 10 +#define SYS_getpid 11 +#define SYS_sbrk 12 +#define SYS_sleep 13 +#define SYS_uptime 14 +#define SYS_open 15 +#define SYS_write 16 +#define SYS_mknod 17 +#define SYS_unlink 18 +#define SYS_link 19 +#define SYS_mkdir 20 +#define SYS_close 21 diff --git a/kernel/sysfile.c b/kernel/sysfile.c new file mode 100644 index 0000000..83bb1ed --- /dev/null +++ b/kernel/sysfile.c @@ -0,0 +1,465 @@ +// +// File-system system calls. +// Mostly argument checking, since we don't trust +// user code, and calls into file.c and fs.c. +// + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "stat.h" +#include "proc.h" +#include "fs.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "file.h" +#include "fcntl.h" + +// Fetch the nth word-sized system call argument as a file descriptor +// and return both the descriptor and the corresponding struct file. +static int +argfd(int n, int *pfd, struct file **pf) +{ + int fd; + struct file *f; + + if(argint(n, &fd) < 0) + return -1; + if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0) + return -1; + if(pfd) + *pfd = fd; + if(pf) + *pf = f; + return 0; +} + +// Allocate a file descriptor for the given file. +// Takes over file reference from caller on success. +static int +fdalloc(struct file *f) +{ + int fd; + struct proc *p = myproc(); + + for(fd = 0; fd < NOFILE; fd++){ + if(p->ofile[fd] == 0){ + p->ofile[fd] = f; + return fd; + } + } + return -1; +} + +int +sys_dup(void) +{ + struct file *f; + int fd; + + if(argfd(0, 0, &f) < 0) + return -1; + if((fd=fdalloc(f)) < 0) + return -1; + filedup(f); + return fd; +} + +int +sys_read(void) +{ + struct file *f; + int n; + uint64 p; + + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) + return -1; + return fileread(f, p, n); +} + +int +sys_write(void) +{ + struct file *f; + int n; + uint64 p; + + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) + return -1; + + return filewrite(f, p, n); +} + +int +sys_close(void) +{ + int fd; + struct file *f; + + if(argfd(0, &fd, &f) < 0) + return -1; + myproc()->ofile[fd] = 0; + fileclose(f); + return 0; +} + +int +sys_fstat(void) +{ + struct file *f; + uint64 st; // user pointer to struct stat + + if(argfd(0, 0, &f) < 0 || argptr(1, &st, sizeof(struct stat)) < 0) + return -1; + return filestat(f, st); +} + +// Create the path new as a link to the same inode as old. +int +sys_link(void) +{ + char name[DIRSIZ], new[MAXPATH], old[MAXPATH]; + struct inode *dp, *ip; + + if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0) + return -1; + + begin_op(); + if((ip = namei(old)) == 0){ + end_op(); + return -1; + } + + ilock(ip); + if(ip->type == T_DIR){ + iunlockput(ip); + end_op(); + return -1; + } + + ip->nlink++; + iupdate(ip); + iunlock(ip); + + if((dp = nameiparent(new, name)) == 0) + goto bad; + ilock(dp); + if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ + iunlockput(dp); + goto bad; + } + iunlockput(dp); + iput(ip); + + end_op(); + + return 0; + +bad: + ilock(ip); + ip->nlink--; + iupdate(ip); + iunlockput(ip); + end_op(); + return -1; +} + +// Is the directory dp empty except for "." and ".." ? +static int +isdirempty(struct inode *dp) +{ + int off; + struct dirent de; + + for(off=2*sizeof(de); offsize; off+=sizeof(de)){ + if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) + panic("isdirempty: readi"); + if(de.inum != 0) + return 0; + } + return 1; +} + +//PAGEBREAK! +int +sys_unlink(void) +{ + struct inode *ip, *dp; + struct dirent de; + char name[DIRSIZ], path[MAXPATH]; + uint off; + + if(argstr(0, path, MAXPATH) < 0) + return -1; + + begin_op(); + if((dp = nameiparent(path, name)) == 0){ + end_op(); + return -1; + } + + ilock(dp); + + // Cannot unlink "." or "..". + if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0) + goto bad; + + if((ip = dirlookup(dp, name, &off)) == 0) + goto bad; + ilock(ip); + + if(ip->nlink < 1) + panic("unlink: nlink < 1"); + if(ip->type == T_DIR && !isdirempty(ip)){ + iunlockput(ip); + goto bad; + } + + memset(&de, 0, sizeof(de)); + if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) + panic("unlink: writei"); + if(ip->type == T_DIR){ + dp->nlink--; + iupdate(dp); + } + iunlockput(dp); + + ip->nlink--; + iupdate(ip); + iunlockput(ip); + + end_op(); + + return 0; + +bad: + iunlockput(dp); + end_op(); + return -1; +} + +static struct inode* +create(char *path, short type, short major, short minor) +{ + uint off; + struct inode *ip, *dp; + char name[DIRSIZ]; + + if((dp = nameiparent(path, name)) == 0) + return 0; + ilock(dp); + + if((ip = dirlookup(dp, name, &off)) != 0){ + iunlockput(dp); + ilock(ip); + if(type == T_FILE && ip->type == T_FILE) + return ip; + iunlockput(ip); + return 0; + } + + if((ip = ialloc(dp->dev, type)) == 0) + panic("create: ialloc"); + + ilock(ip); + ip->major = major; + ip->minor = minor; + ip->nlink = 1; + iupdate(ip); + + if(type == T_DIR){ // Create . and .. entries. + dp->nlink++; // for ".." + iupdate(dp); + // No ip->nlink++ for ".": avoid cyclic ref count. + if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) + panic("create dots"); + } + + if(dirlink(dp, name, ip->inum) < 0) + panic("create: dirlink"); + + iunlockput(dp); + + return ip; +} + +int +sys_open(void) +{ + char path[MAXPATH]; + int fd, omode; + struct file *f; + struct inode *ip; + + if(argstr(0, path, MAXPATH) < 0 || argint(1, &omode) < 0) + return -1; + + begin_op(); + + if(omode & O_CREATE){ + ip = create(path, T_FILE, 0, 0); + if(ip == 0){ + end_op(); + return -1; + } + } else { + if((ip = namei(path)) == 0){ + end_op(); + return -1; + } + ilock(ip); + if(ip->type == T_DIR && omode != O_RDONLY){ + iunlockput(ip); + end_op(); + return -1; + } + } + + if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ + if(f) + fileclose(f); + iunlockput(ip); + end_op(); + return -1; + } + iunlock(ip); + end_op(); + + f->type = FD_INODE; + f->ip = ip; + f->off = 0; + f->readable = !(omode & O_WRONLY); + f->writable = (omode & O_WRONLY) || (omode & O_RDWR); + return fd; +} + +int +sys_mkdir(void) +{ + char path[MAXPATH]; + struct inode *ip; + + begin_op(); + if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ + end_op(); + return -1; + } + iunlockput(ip); + end_op(); + return 0; +} + +int +sys_mknod(void) +{ + struct inode *ip; + char path[MAXPATH]; + int major, minor; + + begin_op(); + if((argstr(0, path, MAXPATH)) < 0 || + argint(1, &major) < 0 || + argint(2, &minor) < 0 || + (ip = create(path, T_DEV, major, minor)) == 0){ + end_op(); + return -1; + } + iunlockput(ip); + end_op(); + return 0; +} + +int +sys_chdir(void) +{ + char path[MAXPATH]; + struct inode *ip; + struct proc *p = myproc(); + + begin_op(); + if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){ + end_op(); + return -1; + } + ilock(ip); + if(ip->type != T_DIR){ + iunlockput(ip); + end_op(); + return -1; + } + iunlock(ip); + iput(p->cwd); + end_op(); + p->cwd = ip; + return 0; +} + +int +sys_exec(void) +{ + char path[MAXPATH], *argv[MAXARG]; + int i; + uint64 uargv, uarg; + + if(argstr(0, path, MAXPATH) < 0 || argaddr(1, &uargv) < 0){ + return -1; + } + memset(argv, 0, sizeof(argv)); + for(i=0;; i++){ + if(i >= NELEM(argv)){ + return -1; + } + if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){ + return -1; + } + if(uarg == 0){ + argv[i] = 0; + break; + } + argv[i] = kalloc(); + if(argv[i] == 0) + panic("sys_exec kalloc"); + if(fetchstr(uarg, argv[i], PGSIZE) < 0){ + return -1; + } + } + + int ret = exec(path, argv); + + for(i = 0; i < NELEM(argv) && argv[i] != 0; i++) + kfree(argv[i]); + + return ret; +} + +int +sys_pipe(void) +{ + uint64 fdarray; // user pointer to array of two integers + struct file *rf, *wf; + int fd0, fd1; + struct proc *p = myproc(); + + if(argptr(0, &fdarray, 2*sizeof(int)) < 0) + return -1; + if(pipealloc(&rf, &wf) < 0) + return -1; + fd0 = -1; + if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ + if(fd0 >= 0) + p->ofile[fd0] = 0; + fileclose(rf); + fileclose(wf); + return -1; + } + if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 || + copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){ + p->ofile[fd0] = 0; + p->ofile[fd1] = 0; + fileclose(rf); + fileclose(wf); + return -1; + } + return 0; +} diff --git a/kernel/sysproc.c b/kernel/sysproc.c new file mode 100644 index 0000000..e57e045 --- /dev/null +++ b/kernel/sysproc.c @@ -0,0 +1,90 @@ +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "date.h" +#include "param.h" +#include "memlayout.h" +#include "proc.h" + +int +sys_exit(void) +{ + exit(); + return 0; // not reached +} + +int +sys_getpid(void) +{ + return myproc()->pid; +} + +int +sys_fork(void) +{ + return fork(); +} + +int +sys_wait(void) +{ + return wait(); +} + +int +sys_sbrk(void) +{ + int addr; + int n; + + if(argint(0, &n) < 0) + return -1; + addr = myproc()->sz; + if(growproc(n) < 0) + return -1; + return addr; +} + +int +sys_sleep(void) +{ + int n; + uint ticks0; + + if(argint(0, &n) < 0) + return -1; + acquire(&tickslock); + ticks0 = ticks; + while(ticks - ticks0 < n){ + if(myproc()->killed){ + release(&tickslock); + return -1; + } + sleep(&ticks, &tickslock); + } + release(&tickslock); + return 0; +} + +int +sys_kill(void) +{ + int pid; + + if(argint(0, &pid) < 0) + return -1; + return kill(pid); +} + +// return how many clock tick interrupts have occurred +// since start. +int +sys_uptime(void) +{ + uint xticks; + + acquire(&tickslock); + xticks = ticks; + release(&tickslock); + return xticks; +} diff --git a/kernel/trampoline.S b/kernel/trampoline.S new file mode 100644 index 0000000..dd4eb02 --- /dev/null +++ b/kernel/trampoline.S @@ -0,0 +1,137 @@ + # + # code to switch between user and kernel space. + # + # this code is mapped at the same virtual address + # in user and kernel space so that it can switch + # page tables. + # + # kernel.ld causes trampout to be aligned + # to a page boundary. + # +.globl usertrap + .section trampoline +.globl trampout +trampout: + # switch from kernel to user. + # usertrapret() calls here. + # a0: p->tf in user page table + # a1: new value for satp, for user page table + + # switch to user page table + csrw satp, a1 + + # put the saved user a0 in sscratch, so we + # can swap it with our a0 (p->tf) in the last step. + ld t0, 112(a0) + csrw sscratch, t0 + + # restore all but a0 from p->tf + ld ra, 40(a0) + ld sp, 48(a0) + ld gp, 56(a0) + ld tp, 64(a0) + ld t0, 72(a0) + ld t1, 80(a0) + ld t2, 88(a0) + ld s0, 96(a0) + ld s1, 104(a0) + ld a1, 120(a0) + ld a2, 128(a0) + ld a3, 136(a0) + ld a4, 144(a0) + ld a5, 152(a0) + ld a6, 160(a0) + ld a7, 168(a0) + ld s2, 176(a0) + ld s3, 184(a0) + ld s4, 192(a0) + ld s5, 200(a0) + ld s6, 208(a0) + ld s7, 216(a0) + ld s8, 224(a0) + ld s9, 232(a0) + ld s10, 240(a0) + ld s11, 248(a0) + ld t3, 256(a0) + ld t4, 264(a0) + ld t5, 272(a0) + ld t6, 280(a0) + + # restore user a0, and save p->tf + csrrw a0, sscratch, a0 + + # return to user mode and user pc. + # caller has set up sstatus and sepc. + sret + +.align 4 +.globl trampin +trampin: + # + # trap.c set stvec to point here, so + # user interrupts and exceptions start here, + # in supervisor mode, but with a + # user page table. + # + # sscratch points to where the process's p->tf is + # mapped into user space (TRAMPOLINE - 4096). + # + + # swap a0 and sscratch + # so that a0 is p->tf + csrrw a0, sscratch, a0 + + # save the user registers in p->tf + sd ra, 40(a0) + sd sp, 48(a0) + sd gp, 56(a0) + sd tp, 64(a0) + sd t0, 72(a0) + sd t1, 80(a0) + sd t2, 88(a0) + sd s0, 96(a0) + sd s1, 104(a0) + sd a1, 120(a0) + sd a2, 128(a0) + sd a3, 136(a0) + sd a4, 144(a0) + sd a5, 152(a0) + sd a6, 160(a0) + sd a7, 168(a0) + sd s2, 176(a0) + sd s3, 184(a0) + sd s4, 192(a0) + sd s5, 200(a0) + sd s6, 208(a0) + sd s7, 216(a0) + sd s8, 224(a0) + sd s9, 232(a0) + sd s10, 240(a0) + sd s11, 248(a0) + sd t3, 256(a0) + sd t4, 264(a0) + sd t5, 272(a0) + sd t6, 280(a0) + + # save the user a0 in p->tf->a0 + csrr t0, sscratch + sd t0, 112(a0) + + # restore kernel stack pointer from p->tf->kernel_sp + ld sp, 8(a0) + + # make tp hold the current hartid, from p->tf->hartid + ld tp, 32(a0) + + # remember the address of usertrap(), p->tf->kernel_trap + ld t0, 16(a0) + + # restore kernel page table from p->tf->kernel_satp + ld t1, 0(a0) + csrw satp, t1 + + # a0 is no longer valid, since the kernel page + # table does not specially map p->td. + + # jump to usertrap(), which does not return + jr t0 diff --git a/kernel/trap.c b/kernel/trap.c new file mode 100644 index 0000000..050a94d --- /dev/null +++ b/kernel/trap.c @@ -0,0 +1,184 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "spinlock.h" +#include "defs.h" + +struct spinlock tickslock; +uint ticks; + +extern char trampout[], trampin[]; + +// in kernelvec.S, calls kerneltrap(). +void kernelvec(); + +extern int devintr(); + +void +trapinit(void) +{ + initlock(&tickslock, "time"); +} + +// set up to take exceptions and traps while in the kernel. +void +trapinithart(void) +{ + w_stvec((uint64)kernelvec); +} + +// +// handle an interrupt, exception, or system call from user space. +// called from trampoline.S +// +void +usertrap(void) +{ + int which_dev = 0; + + if((r_sstatus() & SSTATUS_SPP) != 0) + panic("usertrap: not from user mode"); + + // send interrupts and exceptions to kerneltrap(), + // since we're now in the kernel. + w_stvec((uint64)kernelvec); + + struct proc *p = myproc(); + + // save user program counter. + p->tf->epc = r_sepc(); + + intr_on(); + + if(r_scause() == 8){ + // system call + + // sepc points to the ecall instruction, + // but we want to return to the next instruction. + p->tf->epc += 4; + + syscall(); + } else if((which_dev = devintr()) != 0){ + // ok + } else { + printf("usertrap(): unexpected scause 0x%p pid=%d\n", r_scause(), p->pid); + printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); + p->killed = 1; + } + + if(p->killed) + exit(); + + // give up the CPU if this is a timer interrupt. + if(which_dev == 2) + yield(); + + usertrapret(); +} + +// +// return to user space +// +void +usertrapret(void) +{ + struct proc *p = myproc(); + + // turn off interrupts, since we're switching + // now from kerneltrap() to usertrap(). + intr_off(); + + // send interrupts and exceptions to trampoline.S + w_stvec(TRAMPOLINE + (trampin - trampout)); + + // set up values that trampoline.S will need when + // the process next re-enters the kernel. + p->tf->kernel_satp = r_satp(); + p->tf->kernel_sp = (uint64)p->kstack + PGSIZE; + p->tf->kernel_trap = (uint64)usertrap; + p->tf->hartid = r_tp(); + + // set up the registers that trampoline.S's sret will use + // 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 Exception Program Counter to the saved user pc. + w_sepc(p->tf->epc); + + // tell trampline.S the user page table to switch to. + uint64 satp = MAKE_SATP(p->pagetable); + + // jump to trampoline.S at the top of memory, which + // switches to the user page table, restores user registers, + // and switches to user mode with sret. + ((void (*)(uint64,uint64))TRAMPOLINE)(TRAMPOLINE - PGSIZE, satp); +} + +// interrupts and exceptions from kernel code go here, +// on whatever the current kernel stack is. +// must be 4-byte aligned to fit in stvec. +void +kerneltrap() +{ + 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(devintr() == 0){ + printf("scause 0x%p\n", scause); + printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); + panic("kerneltrap"); + } +} + +// check if it's an external interrupt or software interrupt, +// and handle it. +// returns 2 if timer interrupt, +// 1 if other device, +// 0 if not recognized. +int +devintr() +{ + uint64 scause = r_scause(); + + if((scause & 0x8000000000000000L) && + (scause & 0xff) == 9){ + // supervisor external interrupt, via PLIC. + int irq = plic_claim(); + + if(irq == UART0_IRQ){ + uartintr(); + } + + plic_complete(irq); + return 1; + } else if(scause == 0x8000000000000001){ + // software interrupt from a machine-mode timer interrupt. + + if(cpuid() == 0){ + acquire(&tickslock); + ticks++; + wakeup(&ticks); + release(&tickslock); + } + + // acknowledge. + w_sip(r_sip() & ~2); + + return 2; + } else { + return 0; + } +} + diff --git a/kernel/types.h b/kernel/types.h new file mode 100644 index 0000000..ee73164 --- /dev/null +++ b/kernel/types.h @@ -0,0 +1,10 @@ +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long uint64; + +typedef uint64 pde_t; diff --git a/kernel/uart.c b/kernel/uart.c new file mode 100644 index 0000000..35fac1b --- /dev/null +++ b/kernel/uart.c @@ -0,0 +1,75 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "spinlock.h" +#include "defs.h" + +// +// qemu -machine virt has a 16550a UART +// qemu/hw/riscv/virt.c +// http://byterunner.com/16550.html +// +// caller should lock. +// + +// address of one of the registers +#define R(reg) ((volatile unsigned char *)(UART0 + reg)) + +void +uartinit(void) +{ + // disable interrupts -- IER + *R(1) = 0x00; + + // special mode to set baud rate + *R(3) = 0x80; + + // LSB for baud rate of 38.4K + *R(0) = 0x03; + + // MSB for baud rate of 38.4K + *R(1) = 0x00; + + // leave set-baud mode, + // and set word length to 8 bits, no parity. + *R(3) = 0x03; + + // reset and enable FIFOs -- FCR. + *R(2) = 0x07; + + // enable receive interrupts -- IER. + *R(1) = 0x01; +} + +void +uartputc(int c) +{ + // wait for Transmit Holding Empty to be set in LSR. + while((*R(5) & (1 << 5)) == 0) + ; + *R(0) = c; +} + +int +uartgetc(void) +{ + if(*R(5) & 0x01){ + // input data is ready. + return *R(0); + } else { + return -1; + } +} + +void +uartintr(void) +{ + while(1){ + int c = uartgetc(); + if(c == -1) + break; + consoleintr(c); + } +} diff --git a/kernel/vm.c b/kernel/vm.c new file mode 100644 index 0000000..0ea6bca --- /dev/null +++ b/kernel/vm.c @@ -0,0 +1,389 @@ +#include "param.h" +#include "types.h" +#include "memlayout.h" +#include "elf.h" +#include "riscv.h" +#include "defs.h" +#include "fs.h" + +/* + * the kernel's page table. + */ +pagetable_t kernel_pagetable; + +extern char etext[]; // kernel.ld sets this to end of kernel code. + +extern char trampout[]; // trampoline.S + +/* + * create a direct-map page table for the kernel and + * turn on paging. called early, in supervisor mode. + * the page allocator is already initialized. + */ +void +kvminit() +{ + kernel_pagetable = (pagetable_t) kalloc(); + memset(kernel_pagetable, 0, PGSIZE); + + // uart registers + mappages(kernel_pagetable, UART0, PGSIZE, + UART0, PTE_R | PTE_W); + + // CLINT + mappages(kernel_pagetable, CLINT, 0x10000, + CLINT, PTE_R | PTE_W); + + // PLIC + mappages(kernel_pagetable, PLIC, 0x4000000, + PLIC, PTE_R | PTE_W); + + // map kernel text executable and read-only. + mappages(kernel_pagetable, KERNBASE, (uint64)etext-KERNBASE, + KERNBASE, PTE_R | PTE_X); + + // map kernel data and the physical RAM we'll make use of. + mappages(kernel_pagetable, (uint64)etext, PHYSTOP-(uint64)etext, + (uint64)etext, PTE_R | PTE_W); + + // map the qemu -initrd fs.img ramdisk + mappages(kernel_pagetable, RAMDISK, FSSIZE * BSIZE, + RAMDISK, PTE_R | PTE_W); + + // map the trampoline for trap entry/exit to + // the highest virtual address in the kernel. + mappages(kernel_pagetable, TRAMPOLINE, PGSIZE, + (uint64)trampout, PTE_R | PTE_X); +} + +// Switch h/w page table register to the kernel's page table, +// and enable paging. +void +kvminithart() +{ + w_satp(MAKE_SATP(kernel_pagetable)); +} + +// Return the address of the PTE in page table pagetable +// that corresponds to virtual address va. If alloc!=0, +// create any required page table pages. +// +// The risc-v Sv39 scheme has three levels of page table +// pages. A page table page contains 512 64-bit PTEs. +// A 64-bit virtual address is split into five fields: +// 39..63 -- must be zero. +// 30..38 -- 9 bits of level-2 index. +// 21..39 -- 9 bits of level-1 index. +// 12..20 -- 9 bits of level-0 index. +// 0..12 -- 12 bits of byte offset within the page. +static pte_t * +walk(pagetable_t pagetable, uint64 va, int alloc) +{ + if(va >= MAXVA) + panic("walk"); + + for(int level = 2; level > 0; level--) { + pte_t *pte = &pagetable[PX(level, va)]; + if(*pte & PTE_V) { + pagetable = (pagetable_t)PTE2PA(*pte); + } else { + if(!alloc || (pagetable = (pde_t*)kalloc()) == 0) + return 0; + memset(pagetable, 0, PGSIZE); + *pte = PA2PTE(pagetable) | PTE_V; + } + } + return &pagetable[PX(0, va)]; +} + +// Look up a virtual address, return the physical address, +// Can only be used to look up user pages. +// or 0 if not mapped. +uint64 +walkaddr(pagetable_t pagetable, uint64 va) +{ + pte_t *pte; + uint64 pa; + + pte = walk(pagetable, va, 0); + if(pte == 0) + return 0; + if((*pte & PTE_V) == 0) + return 0; + if((*pte & PTE_U) == 0) + return 0; + pa = PTE2PA(*pte); + return pa; +} + + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. +void +mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) +{ + uint64 a, last; + pte_t *pte; + + a = PGROUNDDOWN(va); + last = PGROUNDDOWN(va + size - 1); + for(;;){ + if((pte = walk(pagetable, a, 1)) == 0) + panic("mappages: walk"); + if(*pte & PTE_V) + panic("remap"); + *pte = PA2PTE(pa) | perm | PTE_V; + if(a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } +} + +// Remove mappings from a page table. The mappings in +// the given range must exist. Optionally free the +// physical memory. +void +unmappages(pagetable_t pagetable, uint64 va, uint64 size, int do_free) +{ + uint64 a, last; + pte_t *pte; + uint64 pa; + + a = PGROUNDDOWN(va); + last = PGROUNDDOWN(va + size - 1); + for(;;){ + if((pte = walk(pagetable, a, 0)) == 0) + panic("unmappages: walk"); + if((*pte & PTE_V) == 0){ + printf("va=%p pte=%p\n", a, *pte); + panic("unmappages: not mapped"); + } + if(PTE_FLAGS(*pte) == PTE_V) + panic("unmappages: not a leaf"); + if(do_free){ + pa = PTE2PA(*pte); + kfree((void*)pa); + } + *pte = 0; + if(a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } +} + +// create an empty user page table. +pagetable_t +uvmcreate() +{ + pagetable_t pagetable; + pagetable = (pagetable_t) kalloc(); + if(pagetable == 0) + panic("uvmcreate: out of memory"); + memset(pagetable, 0, PGSIZE); + return pagetable; +} + +// Load the user initcode into address 0 of pagetable, +// for the very first process. +// sz must be less than a page. +void +uvminit(pagetable_t pagetable, uchar *src, uint sz) +{ + char *mem; + + if(sz >= PGSIZE) + panic("inituvm: more than a page"); + mem = kalloc(); + memset(mem, 0, PGSIZE); + mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); + memmove(mem, src, sz); +} + +// 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. +uint64 +uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) +{ + char *mem; + uint64 a; + + if(newsz < oldsz) + return oldsz; + + oldsz = PGROUNDUP(oldsz); + a = oldsz; + for(; a < newsz; a += PGSIZE){ + mem = kalloc(); + if(mem == 0){ + uvmdealloc(pagetable, a, oldsz); + return 0; + } + memset(mem, 0, PGSIZE); + mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U); + } + return newsz; +} + +// Deallocate user pages to bring the process size from oldsz to +// 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 +// process size. Returns the new process size. +uint64 +uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) +{ + if(newsz >= oldsz) + return oldsz; + unmappages(pagetable, newsz, oldsz - newsz, 1); + return newsz; +} + +// Recursively free page table pages. +// All leaf mappings must already have been removed. +static void +freewalk(pagetable_t pagetable) +{ + // there are 2^9 = 512 PTEs in a page table. + for(int i = 0; i < 512; i++){ + pte_t pte = pagetable[i]; + if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ + // this PTE points to a lower-level page table. + uint64 child = PTE2PA(pte); + freewalk((pagetable_t)child); + pagetable[i] = 0; + } else if(pte & PTE_V){ + panic("freewalk: leaf"); + } + } + kfree((void*)pagetable); +} + +// Free user memory pages, +// then free page table pages. +void +uvmfree(pagetable_t pagetable, uint64 sz) +{ + unmappages(pagetable, 0, sz, 1); + freewalk(pagetable); +} + +// Given a parent process's page table, copy +// its memory into a child's page table. +// Copies both the page table and the +// physical memory. +void +uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) +{ + pte_t *pte; + uint64 pa, i; + uint flags; + char *mem; + + for(i = 0; i < sz; i += PGSIZE){ + if((pte = walk(old, i, 0)) == 0) + panic("copyuvm: pte should exist"); + if((*pte & PTE_V) == 0) + panic("copyuvm: page not present"); + pa = PTE2PA(*pte); + flags = PTE_FLAGS(*pte); + if((mem = kalloc()) == 0) + panic("uvmcopy: kalloc failed"); + memmove(mem, (char*)pa, PGSIZE); + mappages(new, i, PGSIZE, (uint64)mem, flags); + } +} + +// Copy from kernel to user. +// Copy len bytes from src to virtual address dstva in a given page table. +// Return 0 on success, -1 on error. +int +copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) +{ + uint64 n, va0, pa0; + + while(len > 0){ + va0 = (uint)PGROUNDDOWN(dstva); + pa0 = walkaddr(pagetable, va0); + if(pa0 == 0) + return -1; + n = PGSIZE - (dstva - va0); + if(n > len) + n = len; + memmove((void *)(pa0 + (dstva - va0)), src, n); + + len -= n; + src += n; + dstva = va0 + PGSIZE; + } + return 0; +} + +// Copy from user to kernel. +// Copy len bytes to dst from virtual address srcva in a given page table. +// Return 0 on success, -1 on error. +int +copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) +{ + uint64 n, va0, pa0; + + while(len > 0){ + va0 = (uint)PGROUNDDOWN(srcva); + pa0 = walkaddr(pagetable, va0); + if(pa0 == 0) + return -1; + n = PGSIZE - (srcva - va0); + if(n > len) + n = len; + memmove(dst, (void *)(pa0 + (srcva - va0)), n); + + len -= n; + dst += n; + srcva = va0 + PGSIZE; + } + return 0; +} + +// Copy a null-terminated string from user to kernel. +// Copy bytes to dst from virtual address srcva in a given page table, +// until a '\0', or max. +// Return 0 on success, -1 on error. +int +copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) +{ + uint64 n, va0, pa0; + int got_null = 0; + + while(got_null == 0 && max > 0){ + va0 = (uint)PGROUNDDOWN(srcva); + pa0 = walkaddr(pagetable, va0); + if(pa0 == 0) + return -1; + n = PGSIZE - (srcva - va0); + if(n > max) + n = max; + + char *p = (char *) (pa0 + (srcva - va0)); + while(n > 0){ + if(*p == '\0'){ + *dst = '\0'; + got_null = 1; + break; + } else { + *dst = *p; + } + --n; + --max; + p++; + dst++; + } + + srcva = va0 + PGSIZE; + } + if(got_null){ + return 0; + } else { + return -1; + } +} diff --git a/kernelvec.S b/kernelvec.S deleted file mode 100644 index 4f52688..0000000 --- a/kernelvec.S +++ /dev/null @@ -1,112 +0,0 @@ - # - # interrupts and exceptions while in supervisor - # mode come here. - # - # push all registers, call kerneltrap(), restore, return. - # -.globl kerneltrap -.globl kernelvec -.align 4 -kernelvec: - addi sp, sp, -256 - - sd ra, 0(sp) - sd sp, 8(sp) - sd gp, 16(sp) - sd tp, 24(sp) - sd t0, 32(sp) - sd t1, 40(sp) - sd t2, 48(sp) - sd s0, 56(sp) - sd s1, 64(sp) - sd a0, 72(sp) - sd a1, 80(sp) - sd a2, 88(sp) - sd a3, 96(sp) - sd a4, 104(sp) - sd a5, 112(sp) - sd a6, 120(sp) - sd a7, 128(sp) - sd s2, 136(sp) - sd s3, 144(sp) - sd s4, 152(sp) - sd s5, 160(sp) - sd s6, 168(sp) - sd s7, 176(sp) - sd s8, 184(sp) - sd s9, 192(sp) - sd s10, 200(sp) - sd s11, 208(sp) - sd t3, 216(sp) - sd t4, 224(sp) - sd t5, 232(sp) - sd t6, 240(sp) - - call kerneltrap - - ld ra, 0(sp) - ld sp, 8(sp) - ld gp, 16(sp) - ld tp, 24(sp) - ld t0, 32(sp) - ld t1, 40(sp) - ld t2, 48(sp) - ld s0, 56(sp) - ld s1, 64(sp) - ld a0, 72(sp) - ld a1, 80(sp) - ld a2, 88(sp) - ld a3, 96(sp) - ld a4, 104(sp) - ld a5, 112(sp) - ld a6, 120(sp) - ld a7, 128(sp) - ld s2, 136(sp) - ld s3, 144(sp) - ld s4, 152(sp) - ld s5, 160(sp) - ld s6, 168(sp) - ld s7, 176(sp) - ld s8, 184(sp) - ld s9, 192(sp) - ld s10, 200(sp) - ld s11, 208(sp) - ld t3, 216(sp) - ld t4, 224(sp) - ld t5, 232(sp) - ld t6, 240(sp) - - addi sp, sp, 256 - - sret - - # - # machine-mode timer interrupt. - # -.globl machinevec -.align 4 -machinevec: - csrrw a0, mscratch, a0 - sd a1, 0(a0) - sd a2, 8(a0) - sd a3, 16(a0) - sd a4, 24(a0) - - # add another second to mtimecmp0. - ld a1, 32(a0) # CLINT_MTIMECMP(hart) - ld a2, 40(a0) # ticks per second - ld a3, 0(a1) - add a3, a3, a2 - sd a3, 0(a1) - - # raise a supervisor software interrupt. - li a1, 2 - csrw sip, a1 - - ld a4, 24(a0) - ld a3, 16(a0) - ld a2, 8(a0) - ld a1, 0(a0) - csrrw a0, mscratch, a0 - - mret diff --git a/kill.c b/kill.c deleted file mode 100644 index 364f6af..0000000 --- a/kill.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char **argv) -{ - int i; - - if(argc < 2){ - printf(2, "usage: kill pid...\n"); - exit(); - } - for(i=1; i= BSIZE) - panic("initlog: too big logheader"); - - struct superblock sb; - initlock(&log.lock, "log"); - readsb(dev, &sb); - log.start = sb.logstart; - log.size = sb.nlog; - log.dev = dev; - recover_from_log(); -} - -// Copy committed blocks from log to their home location -static void -install_trans(void) -{ - int tail; - - for (tail = 0; tail < log.lh.n; tail++) { - struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block - struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst - memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst - bwrite(dbuf); // write dst to disk - brelse(lbuf); - brelse(dbuf); - } -} - -// Read the log header from disk into the in-memory log header -static void -read_head(void) -{ - struct buf *buf = bread(log.dev, log.start); - struct logheader *lh = (struct logheader *) (buf->data); - int i; - log.lh.n = lh->n; - for (i = 0; i < log.lh.n; i++) { - log.lh.block[i] = lh->block[i]; - } - brelse(buf); -} - -// Write in-memory log header to disk. -// This is the true point at which the -// current transaction commits. -static void -write_head(void) -{ - struct buf *buf = bread(log.dev, log.start); - struct logheader *hb = (struct logheader *) (buf->data); - int i; - hb->n = log.lh.n; - for (i = 0; i < log.lh.n; i++) { - hb->block[i] = log.lh.block[i]; - } - bwrite(buf); - brelse(buf); -} - -static void -recover_from_log(void) -{ - read_head(); - install_trans(); // 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. -void -begin_op(void) -{ - acquire(&log.lock); - while(1){ - if(log.committing){ - sleep(&log, &log.lock); - } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){ - // this op might exhaust log space; wait for commit. - sleep(&log, &log.lock); - } else { - log.outstanding += 1; - release(&log.lock); - break; - } - } -} - -// called at the end of each FS system call. -// commits if this was the last outstanding operation. -void -end_op(void) -{ - int do_commit = 0; - - acquire(&log.lock); - log.outstanding -= 1; - if(log.committing) - panic("log.committing"); - if(log.outstanding == 0){ - do_commit = 1; - log.committing = 1; - } else { - // begin_op() may be waiting for log space, - // and decrementing log.outstanding has decreased - // the amount of reserved space. - wakeup(&log); - } - release(&log.lock); - - if(do_commit){ - // call commit w/o holding locks, since not allowed - // to sleep with locks. - commit(); - acquire(&log.lock); - log.committing = 0; - wakeup(&log); - release(&log.lock); - } -} - -// Copy modified blocks from cache to log. -static void -write_log(void) -{ - int tail; - - for (tail = 0; tail < log.lh.n; tail++) { - struct buf *to = bread(log.dev, log.start+tail+1); // log block - struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block - memmove(to->data, from->data, BSIZE); - bwrite(to); // write the log - brelse(from); - brelse(to); - } -} - -static void -commit() -{ - if (log.lh.n > 0) { - write_log(); // Write modified blocks from cache to log - write_head(); // Write header to disk -- the real commit - install_trans(); // 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. -// Record the block number and pin in the cache with B_DIRTY. -// commit()/write_log() will do the disk write. -// -// log_write() replaces bwrite(); a typical use is: -// bp = bread(...) -// modify bp->data[] -// log_write(bp) -// brelse(bp) -void -log_write(struct buf *b) -{ - int i; - - if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) - panic("too big a transaction"); - if (log.outstanding < 1) - panic("log_write outside of trans"); - - acquire(&log.lock); - for (i = 0; i < log.lh.n; i++) { - if (log.lh.block[i] == b->blockno) // log absorbtion - break; - } - log.lh.block[i] = b->blockno; - if (i == log.lh.n) - log.lh.n++; - b->flags |= B_DIRTY; // prevent eviction - release(&log.lock); -} - diff --git a/ls.c b/ls.c deleted file mode 100644 index 2862913..0000000 --- a/ls.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fs.h" - -char* -fmtname(char *path) -{ - static char buf[DIRSIZ+1]; - char *p; - - // Find first character after last slash. - for(p=path+strlen(path); p >= path && *p != '/'; p--) - ; - p++; - - // Return blank-padded name. - if(strlen(p) >= DIRSIZ) - return p; - memmove(buf, p, strlen(p)); - memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); - return buf; -} - -void -ls(char *path) -{ - char buf[512], *p; - int fd; - struct dirent de; - struct stat st; - - if((fd = open(path, 0)) < 0){ - printf(2, "ls: cannot open %s\n", path); - return; - } - - if(fstat(fd, &st) < 0){ - printf(2, "ls: cannot stat %s\n", path); - close(fd); - return; - } - - switch(st.type){ - case T_FILE: - printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); - break; - - case T_DIR: - if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ - printf(1, "ls: path too long\n"); - break; - } - strcpy(buf, path); - p = buf+strlen(buf); - *p++ = '/'; - while(read(fd, &de, sizeof(de)) == sizeof(de)){ - if(de.inum == 0) - continue; - memmove(p, de.name, DIRSIZ); - p[DIRSIZ] = 0; - if(stat(buf, &st) < 0){ - printf(1, "ls: cannot stat %s\n", buf); - continue; - } - printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); - } - break; - } - close(fd); -} - -int -main(int argc, char *argv[]) -{ - int i; - - if(argc < 2){ - ls("."); - exit(); - } - for(i=1; i -#include -#include -#include -#include -#include - -#define stat xv6_stat // avoid clash with host struct stat -#include "types.h" -#include "fs.h" -#include "stat.h" -#include "param.h" - -#ifndef static_assert -#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) -#endif - -#define NINODES 200 - -// Disk layout: -// [ boot block | sb block | log | inode blocks | free bit map | data blocks ] - -int nbitmap = FSSIZE/(BSIZE*8) + 1; -int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGSIZE; -int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) -int nblocks; // Number of data blocks - -int fsfd; -struct superblock sb; -char zeroes[BSIZE]; -uint freeinode = 1; -uint freeblock; - - -void balloc(int); -void wsect(uint, void*); -void winode(uint, struct dinode*); -void rinode(uint inum, struct dinode *ip); -void rsect(uint sec, void *buf); -uint ialloc(ushort type); -void iappend(uint inum, void *p, int n); - -// convert to intel byte order -ushort -xshort(ushort x) -{ - ushort y; - uchar *a = (uchar*)&y; - a[0] = x; - a[1] = x >> 8; - return y; -} - -uint -xint(uint x) -{ - uint y; - uchar *a = (uchar*)&y; - a[0] = x; - a[1] = x >> 8; - a[2] = x >> 16; - a[3] = x >> 24; - return y; -} - -int -main(int argc, char *argv[]) -{ - int i, cc, fd; - uint rootino, inum, off; - struct dirent de; - char buf[BSIZE]; - struct dinode din; - - - static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); - - if(argc < 2){ - fprintf(stderr, "Usage: mkfs fs.img files...\n"); - exit(1); - } - - assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct dirent)) == 0); - - fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); - if(fsfd < 0){ - perror(argv[1]); - exit(1); - } - - // 1 fs block = 1 disk sector - nmeta = 2 + nlog + ninodeblocks + nbitmap; - nblocks = FSSIZE - nmeta; - - sb.magic = FSMAGIC; - sb.size = xint(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); - - 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); - - freeblock = nmeta; // the first free block that we can allocate - - for(i = 0; i < FSSIZE; i++) - wsect(i, zeroes); - - memset(buf, 0, sizeof(buf)); - memmove(buf, &sb, sizeof(sb)); - wsect(1, buf); - - rootino = ialloc(T_DIR); - assert(rootino == ROOTINO); - - bzero(&de, sizeof(de)); - de.inum = xshort(rootino); - strcpy(de.name, "."); - iappend(rootino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(rootino); - strcpy(de.name, ".."); - iappend(rootino, &de, sizeof(de)); - - for(i = 2; i < argc; i++){ - assert(index(argv[i], '/') == 0); - - if((fd = open(argv[i], 0)) < 0){ - perror(argv[i]); - exit(1); - } - - // Skip leading _ in name when writing to file system. - // The binaries are named _rm, _cat, etc. to keep the - // build operating system from trying to execute them - // in place of system binaries like rm and cat. - if(argv[i][0] == '_') - ++argv[i]; - - inum = ialloc(T_FILE); - - bzero(&de, sizeof(de)); - de.inum = xshort(inum); - strncpy(de.name, argv[i], DIRSIZ); - iappend(rootino, &de, sizeof(de)); - - while((cc = read(fd, buf, sizeof(buf))) > 0) - iappend(inum, buf, cc); - - close(fd); - } - - // fix size of root inode dir - rinode(rootino, &din); - off = xint(din.size); - off = ((off/BSIZE) + 1) * BSIZE; - din.size = xint(off); - winode(rootino, &din); - - balloc(freeblock); - - exit(0); -} - -void -wsect(uint sec, void *buf) -{ - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ - perror("lseek"); - exit(1); - } - if(write(fsfd, buf, BSIZE) != BSIZE){ - perror("write"); - exit(1); - } -} - -void -winode(uint inum, struct dinode *ip) -{ - char buf[BSIZE]; - uint bn; - struct dinode *dip; - - bn = IBLOCK(inum, sb); - rsect(bn, buf); - dip = ((struct dinode*)buf) + (inum % IPB); - *dip = *ip; - wsect(bn, buf); -} - -void -rinode(uint inum, struct dinode *ip) -{ - char buf[BSIZE]; - uint bn; - struct dinode *dip; - - bn = IBLOCK(inum, sb); - rsect(bn, buf); - dip = ((struct dinode*)buf) + (inum % IPB); - *ip = *dip; -} - -void -rsect(uint sec, void *buf) -{ - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ - perror("lseek"); - exit(1); - } - if(read(fsfd, buf, BSIZE) != BSIZE){ - perror("read"); - exit(1); - } -} - -uint -ialloc(ushort type) -{ - uint inum = freeinode++; - struct dinode din; - - bzero(&din, sizeof(din)); - din.type = xshort(type); - din.nlink = xshort(1); - din.size = xint(0); - winode(inum, &din); - return inum; -} - -void -balloc(int used) -{ - uchar buf[BSIZE]; - int i; - - printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BSIZE*8); - bzero(buf, BSIZE); - for(i = 0; i < used; i++){ - buf[i/8] = buf[i/8] | (0x1 << (i%8)); - } - printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); - wsect(sb.bmapstart, buf); -} - -#define min(a, b) ((a) < (b) ? (a) : (b)) - -void -iappend(uint inum, void *xp, int n) -{ - char *p = (char*)xp; - uint fbn, off, n1; - struct dinode din; - char buf[BSIZE]; - uint indirect[NINDIRECT]; - uint x; - - rinode(inum, &din); - off = xint(din.size); - // printf("append inum %d at off %d sz %d\n", inum, off, n); - while(n > 0){ - fbn = off / BSIZE; - assert(fbn < MAXFILE); - if(fbn < NDIRECT){ - if(xint(din.addrs[fbn]) == 0){ - din.addrs[fbn] = xint(freeblock++); - } - x = xint(din.addrs[fbn]); - } else { - if(xint(din.addrs[NDIRECT]) == 0){ - din.addrs[NDIRECT] = xint(freeblock++); - } - rsect(xint(din.addrs[NDIRECT]), (char*)indirect); - if(indirect[fbn - NDIRECT] == 0){ - indirect[fbn - NDIRECT] = xint(freeblock++); - wsect(xint(din.addrs[NDIRECT]), (char*)indirect); - } - x = xint(indirect[fbn-NDIRECT]); - } - n1 = min(n, (fbn + 1) * BSIZE - off); - rsect(x, buf); - bcopy(p, buf + off - (fbn * BSIZE), n1); - wsect(x, buf); - n -= n1; - off += n1; - p += n1; - } - din.size = xint(off); - winode(inum, &din); -} diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c new file mode 100644 index 0000000..246a4e2 --- /dev/null +++ b/mkfs/mkfs.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include + +#define stat xv6_stat // avoid clash with host struct stat +#include "kernel/types.h" +#include "kernel/fs.h" +#include "kernel/stat.h" +#include "kernel/param.h" + +#ifndef static_assert +#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) +#endif + +#define NINODES 200 + +// Disk layout: +// [ boot block | sb block | log | inode blocks | free bit map | data blocks ] + +int nbitmap = FSSIZE/(BSIZE*8) + 1; +int ninodeblocks = NINODES / IPB + 1; +int nlog = LOGSIZE; +int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) +int nblocks; // Number of data blocks + +int fsfd; +struct superblock sb; +char zeroes[BSIZE]; +uint freeinode = 1; +uint freeblock; + + +void balloc(int); +void wsect(uint, void*); +void winode(uint, struct dinode*); +void rinode(uint inum, struct dinode *ip); +void rsect(uint sec, void *buf); +uint ialloc(ushort type); +void iappend(uint inum, void *p, int n); + +// convert to intel byte order +ushort +xshort(ushort x) +{ + ushort y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + return y; +} + +uint +xint(uint x) +{ + uint y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + a[2] = x >> 16; + a[3] = x >> 24; + return y; +} + +int +main(int argc, char *argv[]) +{ + int i, cc, fd; + uint rootino, inum, off; + struct dirent de; + char buf[BSIZE]; + struct dinode din; + + + static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); + + if(argc < 2){ + fprintf(stderr, "Usage: mkfs fs.img files...\n"); + exit(1); + } + + assert((BSIZE % sizeof(struct dinode)) == 0); + assert((BSIZE % sizeof(struct dirent)) == 0); + + fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + if(fsfd < 0){ + perror(argv[1]); + exit(1); + } + + // 1 fs block = 1 disk sector + nmeta = 2 + nlog + ninodeblocks + nbitmap; + nblocks = FSSIZE - nmeta; + + sb.magic = FSMAGIC; + sb.size = xint(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); + + 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); + + freeblock = nmeta; // the first free block that we can allocate + + for(i = 0; i < FSSIZE; i++) + wsect(i, zeroes); + + memset(buf, 0, sizeof(buf)); + memmove(buf, &sb, sizeof(sb)); + wsect(1, buf); + + rootino = ialloc(T_DIR); + assert(rootino == ROOTINO); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, "."); + iappend(rootino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, ".."); + iappend(rootino, &de, sizeof(de)); + + 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){ + perror(argv[i]); + exit(1); + } + + // Skip leading _ in name when writing to file system. + // The binaries are named _rm, _cat, etc. to keep the + // build operating system from trying to execute them + // in place of system binaries like rm and cat. + if(shortname[0] == '_') + shortname += 1; + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, shortname, DIRSIZ); + iappend(rootino, &de, sizeof(de)); + + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); + } + + // fix size of root inode dir + rinode(rootino, &din); + off = xint(din.size); + off = ((off/BSIZE) + 1) * BSIZE; + din.size = xint(off); + winode(rootino, &din); + + balloc(freeblock); + + exit(0); +} + +void +wsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ + perror("lseek"); + exit(1); + } + if(write(fsfd, buf, BSIZE) != BSIZE){ + perror("write"); + exit(1); + } +} + +void +winode(uint inum, struct dinode *ip) +{ + char buf[BSIZE]; + uint bn; + struct dinode *dip; + + bn = IBLOCK(inum, sb); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *dip = *ip; + wsect(bn, buf); +} + +void +rinode(uint inum, struct dinode *ip) +{ + char buf[BSIZE]; + uint bn; + struct dinode *dip; + + bn = IBLOCK(inum, sb); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *ip = *dip; +} + +void +rsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ + perror("lseek"); + exit(1); + } + if(read(fsfd, buf, BSIZE) != BSIZE){ + perror("read"); + exit(1); + } +} + +uint +ialloc(ushort type) +{ + uint inum = freeinode++; + struct dinode din; + + bzero(&din, sizeof(din)); + din.type = xshort(type); + din.nlink = xshort(1); + din.size = xint(0); + winode(inum, &din); + return inum; +} + +void +balloc(int used) +{ + uchar buf[BSIZE]; + int i; + + printf("balloc: first %d blocks have been allocated\n", used); + assert(used < BSIZE*8); + bzero(buf, BSIZE); + for(i = 0; i < used; i++){ + buf[i/8] = buf[i/8] | (0x1 << (i%8)); + } + printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); + wsect(sb.bmapstart, buf); +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +void +iappend(uint inum, void *xp, int n) +{ + char *p = (char*)xp; + uint fbn, off, n1; + struct dinode din; + char buf[BSIZE]; + uint indirect[NINDIRECT]; + uint x; + + rinode(inum, &din); + off = xint(din.size); + // printf("append inum %d at off %d sz %d\n", inum, off, n); + while(n > 0){ + fbn = off / BSIZE; + assert(fbn < MAXFILE); + if(fbn < NDIRECT){ + if(xint(din.addrs[fbn]) == 0){ + din.addrs[fbn] = xint(freeblock++); + } + x = xint(din.addrs[fbn]); + } else { + if(xint(din.addrs[NDIRECT]) == 0){ + din.addrs[NDIRECT] = xint(freeblock++); + } + rsect(xint(din.addrs[NDIRECT]), (char*)indirect); + if(indirect[fbn - NDIRECT] == 0){ + indirect[fbn - NDIRECT] = xint(freeblock++); + wsect(xint(din.addrs[NDIRECT]), (char*)indirect); + } + x = xint(indirect[fbn-NDIRECT]); + } + n1 = min(n, (fbn + 1) * BSIZE - off); + rsect(x, buf); + bcopy(p, buf + off - (fbn * BSIZE), n1); + wsect(x, buf); + n -= n1; + off += n1; + p += n1; + } + din.size = xint(off); + winode(inum, &din); +} diff --git a/param.h b/param.h deleted file mode 100644 index b5fdcb2..0000000 --- a/param.h +++ /dev/null @@ -1,13 +0,0 @@ -#define NPROC 64 // maximum number of processes -#define NCPU 8 // maximum number of CPUs -#define NOFILE 16 // open files per process -#define NFILE 100 // open files per system -#define NINODE 50 // maximum number of active i-nodes -#define NDEV 10 // maximum major device number -#define ROOTDEV 1 // device number of file system root disk -#define MAXARG 32 // max exec arguments -#define MAXOPBLOCKS 10 // max # of blocks any FS op writes -#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log -#define NBUF (MAXOPBLOCKS*3) // size of disk block cache -#define FSSIZE 1000 // size of file system in blocks -#define MAXPATH 128 // maximum file path name diff --git a/pipe.c b/pipe.c deleted file mode 100644 index 31bf0cc..0000000 --- a/pipe.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "proc.h" -#include "fs.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "file.h" - -#define PIPESIZE 512 - -struct pipe { - struct spinlock lock; - char data[PIPESIZE]; - uint nread; // number of bytes read - uint nwrite; // number of bytes written - int readopen; // read fd is still open - int writeopen; // write fd is still open -}; - -int -pipealloc(struct file **f0, struct file **f1) -{ - struct pipe *p; - - p = 0; - *f0 = *f1 = 0; - if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) - goto bad; - if((p = (struct pipe*)kalloc()) == 0) - goto bad; - p->readopen = 1; - p->writeopen = 1; - p->nwrite = 0; - p->nread = 0; - initlock(&p->lock, "pipe"); - (*f0)->type = FD_PIPE; - (*f0)->readable = 1; - (*f0)->writable = 0; - (*f0)->pipe = p; - (*f1)->type = FD_PIPE; - (*f1)->readable = 0; - (*f1)->writable = 1; - (*f1)->pipe = p; - return 0; - -//PAGEBREAK: 20 - bad: - if(p) - kfree((char*)p); - if(*f0) - fileclose(*f0); - if(*f1) - fileclose(*f1); - return -1; -} - -void -pipeclose(struct pipe *p, int writable) -{ - acquire(&p->lock); - if(writable){ - p->writeopen = 0; - wakeup(&p->nread); - } else { - p->readopen = 0; - wakeup(&p->nwrite); - } - if(p->readopen == 0 && p->writeopen == 0){ - release(&p->lock); - kfree((char*)p); - } else - release(&p->lock); -} - -//PAGEBREAK: 40 -int -pipewrite(struct pipe *p, uint64 addr, int n) -{ - int i; - char ch; - struct proc *pr = myproc(); - - acquire(&p->lock); - for(i = 0; i < n; i++){ - while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full - if(p->readopen == 0 || myproc()->killed){ - release(&p->lock); - return -1; - } - wakeup(&p->nread); - sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep - } - if(copyin(pr->pagetable, &ch, addr + i, 1) == -1) - break; - p->data[p->nwrite++ % PIPESIZE] = ch; - } - wakeup(&p->nread); //DOC: pipewrite-wakeup1 - release(&p->lock); - return n; -} - -int -piperead(struct pipe *p, uint64 addr, int n) -{ - int i; - struct proc *pr = myproc(); - char ch; - - acquire(&p->lock); - while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty - if(myproc()->killed){ - release(&p->lock); - return -1; - } - sleep(&p->nread, &p->lock); //DOC: piperead-sleep - } - for(i = 0; i < n; i++){ //DOC: piperead-copy - if(p->nread == p->nwrite) - break; - ch = p->data[p->nread++ % PIPESIZE]; - if(copyout(pr->pagetable, addr + i, &ch, 1) == -1) - break; - } - wakeup(&p->nwrite); //DOC: piperead-wakeup - release(&p->lock); - return i; -} diff --git a/plic.c b/plic.c deleted file mode 100644 index 0f19ab0..0000000 --- a/plic.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -// -// the riscv Platform Level Interrupt Controller (PLIC). -// - -void -plicinit(void) -{ - // set uart's priority to be non-zero (otherwise disabled). - *(uint32*)(PLIC + UART0_IRQ*4) = 1; -} - -void -plicinithart(void) -{ - int hart = cpuid(); - - // set uart's enable bit for this hart's S-mode. - //*(uint32*)(PLIC + 0x2080)= (1 << UART0_IRQ); - *(uint32*)PLIC_SENABLE(hart)= (1 << UART0_IRQ); - - // set this hart's S-mode priority threshold to 0. - //*(uint32*)(PLIC + 0x201000) = 0; - *(uint32*)PLIC_SPRIORITY(hart) = 0; -} - -// return a bitmap of which IRQs are waiting -// to be served. -uint64 -plic_pending(void) -{ - uint64 mask; - - //mask = *(uint32*)(PLIC + 0x1000); - //mask |= (uint64)*(uint32*)(PLIC + 0x1004) << 32; - mask = *(uint64*)PLIC_PENDING; - - return mask; -} - -// ask the PLIC what interrupt we should serve. -int -plic_claim(void) -{ - int hart = cpuid(); - //int irq = *(uint32*)(PLIC + 0x201004); - int irq = *(uint32*)PLIC_SCLAIM(hart); - return irq; -} - -// tell the PLIC we've served this IRQ. -void -plic_complete(int irq) -{ - int hart = cpuid(); - //*(uint32*)(PLIC + 0x201004) = irq; - *(uint32*)PLIC_SCLAIM(hart) = irq; -} diff --git a/printf.c b/printf.c deleted file mode 100644 index c820305..0000000 --- a/printf.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -#include - -static char digits[] = "0123456789ABCDEF"; - -static void -putc(int fd, char c) -{ - write(fd, &c, 1); -} - -static void -printint(int fd, int xx, int base, int sgn) -{ - char buf[16]; - 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) { - 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. -void -printf(int fd, const char *fmt, ...) -{ - va_list ap; - char *s; - int c, i, state; - - va_start(ap, fmt); - state = 0; - for(i = 0; fmt[i]; i++){ - c = fmt[i] & 0xff; - if(state == 0){ - if(c == '%'){ - state = '%'; - } else { - putc(fd, c); - } - } else if(state == '%'){ - if(c == 'd'){ - printint(fd, va_arg(ap, int), 10, 1); - } else if(c == 'x') { - printint(fd, va_arg(ap, int), 16, 0); - } else if(c == 'p') { - printptr(fd, va_arg(ap, uint64)); - } else if(c == 's'){ - s = va_arg(ap, char*); - if(s == 0) - s = "(null)"; - while(*s != 0){ - putc(fd, *s); - s++; - } - } else if(c == 'c'){ - putc(fd, va_arg(ap, uint)); - } else if(c == '%'){ - putc(fd, c); - } else { - // Unknown % sequence. Print it to draw attention. - putc(fd, '%'); - putc(fd, c); - } - state = 0; - } - } -} diff --git a/proc.c b/proc.c deleted file mode 100644 index 4ae34c8..0000000 --- a/proc.c +++ /dev/null @@ -1,591 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "spinlock.h" -#include "defs.h" - -struct { - struct spinlock lock; - struct proc proc[NPROC]; -} ptable; - -struct cpu cpus[NCPU]; - -struct proc *initproc; - -int nextpid = 1; -extern void forkret(void); - -// for returning out of the kernel -extern void sysexit(void); - -static void wakeup1(void *chan); - -extern char trampout[]; // trampoline.S - -void -procinit(void) -{ - initlock(&ptable.lock, "ptable"); -} - -// Must be called with interrupts disabled, -// to prevent race with process being moved -// to a different CPU. -int -cpuid() -{ - int id = r_tp(); - return id; -} - -// Return this core's cpu struct. -// Interrupts must be disabled. -struct cpu* -mycpu(void) { - int id = cpuid(); - struct cpu *c = &cpus[id]; - return c; -} - -// Return the current struct proc *. -struct proc* -myproc(void) { - push_off(); - struct cpu *c = mycpu(); - struct proc *p = c->proc; - pop_off(); - return p; -} - -//PAGEBREAK: 32 -// Look in the process table for an UNUSED proc. -// If found, change state to EMBRYO and initialize -// state required to run in the kernel. -// Otherwise return 0. -static struct proc* -allocproc(void) -{ - struct proc *p; - - acquire(&ptable.lock); - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == UNUSED) - goto found; - - release(&ptable.lock); - return 0; - -found: - p->state = EMBRYO; - p->pid = nextpid++; - - release(&ptable.lock); - - // Allocate a page for the kernel stack. - if((p->kstack = kalloc()) == 0){ - p->state = UNUSED; - return 0; - } - - // Allocate a trapframe page. - if((p->tf = (struct trapframe *)kalloc()) == 0){ - p->state = UNUSED; - return 0; - } - - // An empty user page table. - p->pagetable = proc_pagetable(p); - - // Set up new context to start executing at forkret, - // which returns to user space. - memset(&p->context, 0, sizeof p->context); - p->context.ra = (uint64)forkret; - p->context.sp = (uint64)p->kstack + PGSIZE; - - return p; -} - -// Create a page table for a given process, -// with no users pages, but with trampoline pages. -// Called both when creating a process, and -// by exec() when building tentative new memory image, -// which might fail. -pagetable_t -proc_pagetable(struct proc *p) -{ - pagetable_t pagetable; - - // An empty user page table. - pagetable = uvmcreate(); - - // map the trampoline code (for system call return) - // at the highest user virtual address. - // only the supervisor uses it, on the way - // to/from user space, so not PTE_U. - mappages(pagetable, TRAMPOLINE, PGSIZE, - (uint64)trampout, PTE_R | PTE_X); - - // map the trapframe, for trampoline.S. - mappages(pagetable, (TRAMPOLINE - PGSIZE), PGSIZE, - (uint64)(p->tf), PTE_R | PTE_W); - - return pagetable; -} - -// Free a process's page table, and free the -// physical memory the page table refers to. -// Called both when a process exits and from -// exec() if it fails. -void -proc_freepagetable(pagetable_t pagetable, uint64 sz) -{ - unmappages(pagetable, TRAMPOLINE, PGSIZE, 0); - unmappages(pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0); - uvmfree(pagetable, sz); -} - -// a user program that calls exec("/init") -// od -t xC initcode -uchar initcode[] = { - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, - 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, - 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 -}; - -//PAGEBREAK: 32 -// Set up first user process. -void -userinit(void) -{ - struct proc *p; - - p = allocproc(); - initproc = p; - - uvminit(p->pagetable, initcode, sizeof(initcode)); - p->sz = PGSIZE; - - // prepare for the very first kernel->user. - p->tf->epc = 0; - p->tf->sp = PGSIZE; - - safestrcpy(p->name, "initcode", sizeof(p->name)); - p->cwd = namei("/"); - - // this assignment to p->state lets other cores - // run this process. the acquire forces the above - // writes to be visible, and the lock is also needed - // because the assignment might not be atomic. - acquire(&ptable.lock); - - p->state = RUNNABLE; - - release(&ptable.lock); -} - -// Grow current process's memory by n bytes. -// Return 0 on success, -1 on failure. -int -growproc(int n) -{ - uint sz; - struct proc *p = myproc(); - - sz = p->sz; - if(n > 0){ - if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) - return -1; - } else if(n < 0){ - if((sz = uvmdealloc(p->pagetable, sz, sz + n)) == 0) - return -1; - } - p->sz = sz; - return 0; -} - -// Create a new process, copying p as the parent. -// Sets up child kernel stack to return as if from system call. -int -fork(void) -{ - int i, pid; - struct proc *np; - struct proc *p = myproc(); - - // Allocate process. - if((np = allocproc()) == 0){ - return -1; - } - - // Copy user memory from parent to child. - uvmcopy(p->pagetable, np->pagetable, p->sz); - np->sz = p->sz; - - np->parent = p; - - // copy saved user registers. - *(np->tf) = *(p->tf); - - // Cause fork to return 0 in the child. - np->tf->a0 = 0; - - // increment reference counts on open file descriptors. - for(i = 0; i < NOFILE; i++) - if(p->ofile[i]) - np->ofile[i] = filedup(p->ofile[i]); - np->cwd = idup(p->cwd); - - safestrcpy(np->name, p->name, sizeof(p->name)); - - pid = np->pid; - - acquire(&ptable.lock); - - np->state = RUNNABLE; - - release(&ptable.lock); - - return pid; -} - -// Exit the current process. Does not return. -// An exited process remains in the zombie state -// until its parent calls wait(). -void -exit(void) -{ - struct proc *p = myproc(); - struct proc *pp; - int fd; - - if(p == initproc) - panic("init exiting"); - - // Close all open files. - for(fd = 0; fd < NOFILE; fd++){ - if(p->ofile[fd]){ - fileclose(p->ofile[fd]); - p->ofile[fd] = 0; - } - } - - begin_op(); - iput(p->cwd); - end_op(); - p->cwd = 0; - - acquire(&ptable.lock); - - // Parent might be sleeping in wait(). - wakeup1(p->parent); - - // Pass abandoned children to init. - for(pp = ptable.proc; pp < &ptable.proc[NPROC]; pp++){ - if(pp->parent == p){ - pp->parent = initproc; - if(pp->state == ZOMBIE) - wakeup1(initproc); - } - } - - // Jump into the scheduler, never to return. - p->state = ZOMBIE; - sched(); - panic("zombie exit"); -} - -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. -int -wait(void) -{ - struct proc *np; - int havekids, pid; - struct proc *p = myproc(); - - acquire(&ptable.lock); - for(;;){ - // Scan through table looking for exited children. - havekids = 0; - for(np = ptable.proc; np < &ptable.proc[NPROC]; np++){ - if(np->parent != p) - continue; - havekids = 1; - if(np->state == ZOMBIE){ - // Found one. - pid = np->pid; - kfree(np->kstack); - np->kstack = 0; - kfree((void*)np->tf); - np->tf = 0; - proc_freepagetable(np->pagetable, np->sz); - np->pagetable = 0; - np->pid = 0; - np->parent = 0; - np->name[0] = 0; - np->killed = 0; - np->state = UNUSED; - release(&ptable.lock); - return pid; - } - } - - // No point waiting if we don't have any children. - if(!havekids || p->killed){ - release(&ptable.lock); - return -1; - } - - // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(p, &ptable.lock); //DOC: wait-sleep - } -} - -//PAGEBREAK: 42 -// Per-CPU process scheduler. -// Each CPU calls scheduler() after setting itself up. -// Scheduler never returns. It loops, doing: -// - choose a process to run -// - swtch to start running that process -// - eventually that process transfers control -// via swtch back to the scheduler. -void -scheduler(void) -{ - struct proc *p; - struct cpu *c = mycpu(); - - c->proc = 0; - for(;;){ - // Enable interrupts on this processor. - intr_on(); - - // Loop over process table looking for process to run. - acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state != RUNNABLE) - continue; - - // Switch to chosen process. It is the process's job - // to release ptable.lock and then reacquire it - // before jumping back to us. - c->proc = p; - p->state = RUNNING; - - swtch(&c->scheduler, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - } - release(&ptable.lock); - } -} - -// Enter scheduler. Must hold only ptable.lock -// and have changed proc->state. Saves and restores -// intena because intena is a property of this -// kernel thread, not this CPU. It should -// be proc->intena and proc->noff, but that would -// break in the few places where a lock is held but -// there's no process. -void -sched(void) -{ - int intena; - struct proc *p = myproc(); - - if(!holding(&ptable.lock)) - panic("sched ptable.lock"); - if(mycpu()->noff != 1) - panic("sched locks"); - if(p->state == RUNNING) - panic("sched running"); - if(intr_get()) - panic("sched interruptible"); - - intena = mycpu()->intena; - swtch(&p->context, &mycpu()->scheduler); - mycpu()->intena = intena; -} - -// Give up the CPU for one scheduling round. -void -yield(void) -{ - acquire(&ptable.lock); //DOC: yieldlock - myproc()->state = RUNNABLE; - sched(); - release(&ptable.lock); -} - -// A fork child's very first scheduling by scheduler() -// will swtch to forkret. -void -forkret(void) -{ - static int first = 1; - - // Still holding ptable.lock from scheduler. - release(&ptable.lock); - - if (first) { - // Some initialization functions must be run in the context - // of a regular process (e.g., they call sleep), and thus cannot - // be run from main(). - first = 0; - iinit(ROOTDEV); - initlog(ROOTDEV); - } - - usertrapret(); -} - -// Atomically release lock and sleep on chan. -// Reacquires lock when awakened. -void -sleep(void *chan, struct spinlock *lk) -{ - struct proc *p = myproc(); - - if(p == 0) - panic("sleep"); - - if(lk == 0) - panic("sleep without lk"); - - // Must acquire ptable.lock in order to - // change p->state and then call sched. - // Once we hold ptable.lock, we can be - // guaranteed that we won't miss any wakeup - // (wakeup runs with ptable.lock locked), - // so it's okay to release lk. - if(lk != &ptable.lock){ //DOC: sleeplock0 - acquire(&ptable.lock); //DOC: sleeplock1 - release(lk); - } - // Go to sleep. - p->chan = chan; - p->state = SLEEPING; - - sched(); - - // Tidy up. - p->chan = 0; - - // Reacquire original lock. - if(lk != &ptable.lock){ //DOC: sleeplock2 - release(&ptable.lock); - acquire(lk); - } -} - -//PAGEBREAK! -// Wake up all processes sleeping on chan. -// The ptable lock must be held. -static void -wakeup1(void *chan) -{ - struct proc *p; - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == SLEEPING && p->chan == chan) - p->state = RUNNABLE; -} - -// Wake up all processes sleeping on chan. -void -wakeup(void *chan) -{ - acquire(&ptable.lock); - wakeup1(chan); - release(&ptable.lock); -} - -// Kill the process with the given pid. -// Process won't exit until it returns -// to user space (see trap in trap.c). -int -kill(int pid) -{ - struct proc *p; - - acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->pid == pid){ - p->killed = 1; - // Wake process from sleep if necessary. - if(p->state == SLEEPING) - p->state = RUNNABLE; - release(&ptable.lock); - return 0; - } - } - release(&ptable.lock); - return -1; -} - -// Copy to either a user address, or kernel address, -// depending on usr_dst. -// Returns 0 on success, -1 on error. -int -either_copyout(int user_dst, uint64 dst, void *src, uint64 len) -{ - struct proc *p = myproc(); - if(user_dst){ - return copyout(p->pagetable, dst, src, len); - } else { - memmove((char *)dst, src, len); - return 0; - } -} - -// Copy from either a user address, or kernel address, -// depending on usr_src. -// Returns 0 on success, -1 on error. -int -either_copyin(void *dst, int user_src, uint64 src, uint64 len) -{ - struct proc *p = myproc(); - if(user_src){ - return copyin(p->pagetable, dst, src, len); - } else { - memmove(dst, (char*)src, len); - return 0; - } -} - -// Print a process listing to console. For debugging. -// Runs when user types ^P on console. -// No lock to avoid wedging a stuck machine further. -void -procdump(void) -{ - static char *states[] = { - [UNUSED] "unused", - [EMBRYO] "embryo", - [SLEEPING] "sleep ", - [RUNNABLE] "runble", - [RUNNING] "run ", - [ZOMBIE] "zombie" - }; - struct proc *p; - char *state; - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state == UNUSED) - continue; - if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) - state = states[p->state]; - else - state = "???"; - printf("%d %s %s", p->pid, state, p->name); - printf("\n"); - } -} - diff --git a/proc.h b/proc.h deleted file mode 100644 index 278e4cd..0000000 --- a/proc.h +++ /dev/null @@ -1,104 +0,0 @@ -// Saved registers for kernel context switches. -struct context { - uint64 ra; - uint64 sp; - - // callee-saved - uint64 s0; - uint64 s1; - uint64 s2; - uint64 s3; - uint64 s4; - uint64 s5; - uint64 s6; - uint64 s7; - uint64 s8; - uint64 s9; - uint64 s10; - uint64 s11; -}; - -// Per-CPU state -struct cpu { - struct proc *proc; // The process running on this cpu or null - struct context scheduler; // swtch() here to enter scheduler - int noff; // Depth of push_off() nesting. - int intena; // Were interrupts enabled before push_off()? -}; - -extern struct cpu cpus[NCPU]; - -//PAGEBREAK: 17 - -// per-process data for the early trap handling code in trampoline.S. -// sits in a page by itself just under the trampoline page in the -// user page table. not specially mapped in the kernel page table. -// the sscratch register points here. -// trampoline.S saves user registers, then restores kernel_sp and -// kernel_satp. -// includes callee-saved registers like s0-s11 because the -// return-to-user path via usertrapret() doesn't return through -// the entire kernel call stack. -struct trapframe { - /* 0 */ uint64 kernel_satp; - /* 8 */ uint64 kernel_sp; - /* 16 */ uint64 kernel_trap; // usertrap() - /* 24 */ uint64 epc; // saved user program counter - /* 32 */ uint64 hartid; - /* 40 */ uint64 ra; - /* 48 */ uint64 sp; - /* 56 */ uint64 gp; - /* 64 */ uint64 tp; - /* 72 */ uint64 t0; - /* 80 */ uint64 t1; - /* 88 */ uint64 t2; - /* 96 */ uint64 s0; - /* 104 */ uint64 s1; - /* 112 */ uint64 a0; - /* 120 */ uint64 a1; - /* 128 */ uint64 a2; - /* 136 */ uint64 a3; - /* 144 */ uint64 a4; - /* 152 */ uint64 a5; - /* 160 */ uint64 a6; - /* 168 */ uint64 a7; - /* 176 */ uint64 s2; - /* 184 */ uint64 s3; - /* 192 */ uint64 s4; - /* 200 */ uint64 s5; - /* 208 */ uint64 s6; - /* 216 */ uint64 s7; - /* 224 */ uint64 s8; - /* 232 */ uint64 s9; - /* 240 */ uint64 s10; - /* 248 */ uint64 s11; - /* 256 */ uint64 t3; - /* 264 */ uint64 t4; - /* 272 */ uint64 t5; - /* 280 */ uint64 t6; -}; - -enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; - -// Per-process state -struct proc { - char *kstack; // Bottom of kernel stack for this process - uint64 sz; // Size of process memory (bytes) - pagetable_t pagetable; // Page table - enum procstate state; // Process state - int pid; // Process ID - struct proc *parent; // Parent process - struct trapframe *tf; // data page for trampoline.S - struct context context; // swtch() here to run process - void *chan; // If non-zero, sleeping on chan - int killed; // If non-zero, have been killed - struct file *ofile[NOFILE]; // Open files - struct inode *cwd; // Current directory - char name[16]; // Process name (debugging) -}; - -// Process memory is laid out contiguously, low addresses first: -// text -// original data and bss -// fixed-size stack -// expandable heap diff --git a/ramdisk.c b/ramdisk.c deleted file mode 100644 index 9901294..0000000 --- a/ramdisk.c +++ /dev/null @@ -1,45 +0,0 @@ -// -// ramdisk that uses the disk image loaded by qemu -rdinit fs.img -// - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "buf.h" - -void -ramdiskinit(void) -{ -} - -// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. -// Else if B_VALID is not set, read buf from disk, set B_VALID. -void -ramdiskrw(struct buf *b) -{ - if(!holdingsleep(&b->lock)) - panic("ramdiskrw: buf not locked"); - if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) - panic("ramdiskrw: nothing to do"); - - if(b->blockno >= FSSIZE) - panic("ramdiskrw: blockno too big"); - - uint64 diskaddr = b->blockno * BSIZE; - char *addr = (char *)RAMDISK + diskaddr; - - if(b->flags & B_DIRTY){ - // write - memmove(addr, b->data, BSIZE); - b->flags &= ~B_DIRTY; - } else { - // read - memmove(b->data, addr, BSIZE); - b->flags |= B_VALID; - } -} diff --git a/riscv.h b/riscv.h deleted file mode 100644 index c3371a4..0000000 --- a/riscv.h +++ /dev/null @@ -1,338 +0,0 @@ -// which hart (core) is this? -static inline uint64 -r_mhartid() -{ - uint64 x; - asm volatile("csrr %0, mhartid" : "=r" (x) ); - return x; -} - -// Machine Status Register, mstatus - -#define MSTATUS_MPP_MASK (3L << 11) -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) - -static inline uint64 -r_mstatus() -{ - uint64 x; - asm volatile("csrr %0, mstatus" : "=r" (x) ); - return x; -} - -static inline void -w_mstatus(uint64 x) -{ - asm volatile("csrw mstatus, %0" : : "r" (x)); -} - -// machine exception program counter, holds the -// instruction address to which a return from -// exception will go. -static inline void -w_mepc(uint64 x) -{ - asm volatile("csrw mepc, %0" : : "r" (x)); -} - -// Supervisor Status Register, sstatus - -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User -#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable -#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable -#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable -#define SSTATUS_UIE (1L << 0) // User Interrupt Enable - -static inline uint64 -r_sstatus() -{ - uint64 x; - asm volatile("csrr %0, sstatus" : "=r" (x) ); - return x; -} - -static inline void -w_sstatus(uint64 x) -{ - asm volatile("csrw sstatus, %0" : : "r" (x)); -} - -// Supervisor Interrupt Pending -static inline uint64 -r_sip() -{ - uint64 x; - asm volatile("csrr %0, sip" : "=r" (x) ); - return x; -} - -static inline void -w_sip(uint64 x) -{ - asm volatile("csrw sip, %0" : : "r" (x)); -} - -// Supervisor Interrupt Enable -#define SIE_SEIE (1L << 9) // external -#define SIE_STIE (1L << 5) // timer -#define SIE_SSIE (1L << 1) // software -static inline uint64 -r_sie() -{ - uint64 x; - asm volatile("csrr %0, sie" : "=r" (x) ); - return x; -} - -static inline void -w_sie(uint64 x) -{ - asm volatile("csrw sie, %0" : : "r" (x)); -} - -// Machine-mode Interrupt Enable -#define MIE_MEIE (1L << 11) // external -#define MIE_MTIE (1L << 7) // timer -#define MIE_MSIE (1L << 3) // software -static inline uint64 -r_mie() -{ - uint64 x; - asm volatile("csrr %0, mie" : "=r" (x) ); - return x; -} - -static inline void -w_mie(uint64 x) -{ - asm volatile("csrw mie, %0" : : "r" (x)); -} - -// machine exception program counter, holds the -// instruction address to which a return from -// exception will go. -static inline void -w_sepc(uint64 x) -{ - asm volatile("csrw sepc, %0" : : "r" (x)); -} - -static inline uint64 -r_sepc() -{ - uint64 x; - asm volatile("csrr %0, sepc" : "=r" (x) ); - return x; -} - -// Machine Exception Delegation -static inline uint64 -r_medeleg() -{ - uint64 x; - asm volatile("csrr %0, medeleg" : "=r" (x) ); - return x; -} - -static inline void -w_medeleg(uint64 x) -{ - asm volatile("csrw medeleg, %0" : : "r" (x)); -} - -// Machine Interrupt Delegation -static inline uint64 -r_mideleg() -{ - uint64 x; - asm volatile("csrr %0, mideleg" : "=r" (x) ); - return x; -} - -static inline void -w_mideleg(uint64 x) -{ - asm volatile("csrw mideleg, %0" : : "r" (x)); -} - -// Supervisor Trap-Vector Base Address -// low two bits are mode. -static inline void -w_stvec(uint64 x) -{ - asm volatile("csrw stvec, %0" : : "r" (x)); -} - -static inline uint64 -r_stvec() -{ - uint64 x; - asm volatile("csrr %0, stvec" : "=r" (x) ); - return x; -} - -// Machine-mode interrupt vector -static inline void -w_mtvec(uint64 x) -{ - asm volatile("csrw mtvec, %0" : : "r" (x)); -} - -// use riscv's sv39 page table scheme. -#define SATP_SV39 (8L << 60) - -#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) - -// supervisor address translation and protection; -// holds the address of the page table. -static inline void -w_satp(uint64 x) -{ - asm volatile("csrw satp, %0" : : "r" (x)); -} - -static inline uint64 -r_satp() -{ - uint64 x; - asm volatile("csrr %0, satp" : "=r" (x) ); - return x; -} - -// Supervisor Scratch register, for early trap handler in trampoline.S. -static inline void -w_sscratch(uint64 x) -{ - asm volatile("csrw sscratch, %0" : : "r" (x)); -} - -static inline void -w_mscratch(uint64 x) -{ - asm volatile("csrw mscratch, %0" : : "r" (x)); -} - -// Supervisor Trap Cause -static inline uint64 -r_scause() -{ - uint64 x; - asm volatile("csrr %0, scause" : "=r" (x) ); - return x; -} - -// Supervisor Trap Value -static inline uint64 -r_stval() -{ - uint64 x; - asm volatile("csrr %0, stval" : "=r" (x) ); - return x; -} - -// Machine-mode Counter-Enable -static inline void -w_mcounteren(uint64 x) -{ - asm volatile("csrw mcounteren, %0" : : "r" (x)); -} - -static inline uint64 -r_mcounteren() -{ - uint64 x; - asm volatile("csrr %0, mcounteren" : "=r" (x) ); - return x; -} - -// machine-mode cycle counter -static inline uint64 -r_time() -{ - uint64 x; - asm volatile("csrr %0, time" : "=r" (x) ); - return x; -} - -// enable interrupts -static inline void -intr_on() -{ - w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); - w_sstatus(r_sstatus() | SSTATUS_SIE); -} - -// disable interrupts -static inline void -intr_off() -{ - w_sstatus(r_sstatus() & ~SSTATUS_SIE); -} - -// are interrupts enabled? -static inline int -intr_get() -{ - uint64 x = r_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -static inline uint64 -r_sp() -{ - uint64 x; - asm volatile("mv %0, sp" : "=r" (x) ); - return x; -} - -// read and write tp, the thread pointer, which holds -// this core's hartid (core number), the index into cpus[]. -static inline uint64 -r_tp() -{ - uint64 x; - asm volatile("mv %0, tp" : "=r" (x) ); - return x; -} - -static inline void -w_tp(uint64 x) -{ - asm volatile("mv tp, %0" : : "r" (x)); -} - -#define PGSIZE 4096 // bytes per page -#define PGSHIFT 12 // bits of offset within a page - -#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) -#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) - -#define PTE_V (1L << 0) // valid -#define PTE_R (1L << 1) -#define PTE_W (1L << 2) -#define PTE_X (1L << 3) -#define PTE_U (1L << 4) // 1 -> user can access - -// shift a physical address to the right place for a PTE. -#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) - -#define PTE2PA(pte) (((pte) >> 10) << 12) - -#define PTE_FLAGS(pte) ((pte) & (PTE_V|PTE_R|PTE_W|PTE_X|PTE_U)) - -// extract the three 9-bit page table indices from a virtual address. -#define PXMASK 0x1FF // 9 bits -#define PXSHIFT(level) (PGSHIFT+(9*(level))) -#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK) - -// one beyond the highest possible virtual address. -// MAXVA is actually one bit less than the max allowed by -// Sv39, to avoid having to sign-extend virtual addresses -// that have the high bit set. -#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) - -typedef uint64 pte_t; -typedef uint64 *pagetable_t; // 512 PTEs diff --git a/rm.c b/rm.c deleted file mode 100644 index 4fd33c8..0000000 --- a/rm.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char *argv[]) -{ - int i; - - if(argc < 2){ - printf(2, "Usage: rm files...\n"); - exit(); - } - - for(i = 1; i < argc; i++){ - if(unlink(argv[i]) < 0){ - printf(2, "rm: %s failed to delete\n", argv[i]); - break; - } - } - - exit(); -} diff --git a/sh.c b/sh.c deleted file mode 100644 index 054bab9..0000000 --- a/sh.c +++ /dev/null @@ -1,493 +0,0 @@ -// Shell. - -#include "types.h" -#include "user.h" -#include "fcntl.h" - -// Parsed command representation -#define EXEC 1 -#define REDIR 2 -#define PIPE 3 -#define LIST 4 -#define BACK 5 - -#define MAXARGS 10 - -struct cmd { - int type; -}; - -struct execcmd { - int type; - char *argv[MAXARGS]; - char *eargv[MAXARGS]; -}; - -struct redircmd { - int type; - struct cmd *cmd; - char *file; - char *efile; - int mode; - int fd; -}; - -struct pipecmd { - int type; - struct cmd *left; - struct cmd *right; -}; - -struct listcmd { - int type; - struct cmd *left; - struct cmd *right; -}; - -struct backcmd { - int type; - struct cmd *cmd; -}; - -int fork1(void); // Fork but panics on failure. -void panic(char*); -struct cmd *parsecmd(char*); - -// Execute cmd. Never returns. -void -runcmd(struct cmd *cmd) -{ - int p[2]; - struct backcmd *bcmd; - struct execcmd *ecmd; - struct listcmd *lcmd; - struct pipecmd *pcmd; - struct redircmd *rcmd; - - if(cmd == 0) - exit(); - - switch(cmd->type){ - default: - panic("runcmd"); - - case EXEC: - ecmd = (struct execcmd*)cmd; - if(ecmd->argv[0] == 0) - exit(); - exec(ecmd->argv[0], ecmd->argv); - printf(2, "exec %s failed\n", ecmd->argv[0]); - break; - - case REDIR: - rcmd = (struct redircmd*)cmd; - close(rcmd->fd); - if(open(rcmd->file, rcmd->mode) < 0){ - printf(2, "open %s failed\n", rcmd->file); - exit(); - } - runcmd(rcmd->cmd); - break; - - case LIST: - lcmd = (struct listcmd*)cmd; - if(fork1() == 0) - runcmd(lcmd->left); - wait(); - runcmd(lcmd->right); - break; - - case PIPE: - pcmd = (struct pipecmd*)cmd; - if(pipe(p) < 0) - panic("pipe"); - if(fork1() == 0){ - close(1); - dup(p[1]); - close(p[0]); - close(p[1]); - runcmd(pcmd->left); - } - if(fork1() == 0){ - close(0); - dup(p[0]); - close(p[0]); - close(p[1]); - runcmd(pcmd->right); - } - close(p[0]); - close(p[1]); - wait(); - wait(); - break; - - case BACK: - bcmd = (struct backcmd*)cmd; - if(fork1() == 0) - runcmd(bcmd->cmd); - break; - } - exit(); -} - -int -getcmd(char *buf, int nbuf) -{ - printf(2, "$ "); - memset(buf, 0, nbuf); - gets(buf, nbuf); - if(buf[0] == 0) // EOF - return -1; - return 0; -} - -int -main(void) -{ - static char buf[100]; - int fd; - - // Ensure that three file descriptors are open. - while((fd = open("console", O_RDWR)) >= 0){ - if(fd >= 3){ - close(fd); - break; - } - } - - // Read and run input commands. - while(getcmd(buf, sizeof(buf)) >= 0){ - if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ - // Chdir must be called by the parent, not the child. - buf[strlen(buf)-1] = 0; // chop \n - if(chdir(buf+3) < 0) - printf(2, "cannot cd %s\n", buf+3); - continue; - } - if(fork1() == 0) - runcmd(parsecmd(buf)); - wait(); - } - exit(); -} - -void -panic(char *s) -{ - printf(2, "%s\n", s); - exit(); -} - -int -fork1(void) -{ - int pid; - - pid = fork(); - if(pid == -1) - panic("fork"); - return pid; -} - -//PAGEBREAK! -// Constructors - -struct cmd* -execcmd(void) -{ - struct execcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = EXEC; - return (struct cmd*)cmd; -} - -struct cmd* -redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) -{ - struct redircmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = REDIR; - cmd->cmd = subcmd; - cmd->file = file; - cmd->efile = efile; - cmd->mode = mode; - cmd->fd = fd; - return (struct cmd*)cmd; -} - -struct cmd* -pipecmd(struct cmd *left, struct cmd *right) -{ - struct pipecmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = PIPE; - cmd->left = left; - cmd->right = right; - return (struct cmd*)cmd; -} - -struct cmd* -listcmd(struct cmd *left, struct cmd *right) -{ - struct listcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = LIST; - cmd->left = left; - cmd->right = right; - return (struct cmd*)cmd; -} - -struct cmd* -backcmd(struct cmd *subcmd) -{ - struct backcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = BACK; - cmd->cmd = subcmd; - return (struct cmd*)cmd; -} -//PAGEBREAK! -// Parsing - -char whitespace[] = " \t\r\n\v"; -char symbols[] = "<|>&;()"; - -int -gettoken(char **ps, char *es, char **q, char **eq) -{ - char *s; - int ret; - - s = *ps; - while(s < es && strchr(whitespace, *s)) - s++; - if(q) - *q = s; - ret = *s; - switch(*s){ - case 0: - break; - case '|': - case '(': - case ')': - case ';': - case '&': - case '<': - s++; - break; - case '>': - s++; - if(*s == '>'){ - ret = '+'; - s++; - } - break; - default: - ret = 'a'; - while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) - s++; - break; - } - if(eq) - *eq = s; - - while(s < es && strchr(whitespace, *s)) - s++; - *ps = s; - return ret; -} - -int -peek(char **ps, char *es, char *toks) -{ - char *s; - - s = *ps; - while(s < es && strchr(whitespace, *s)) - s++; - *ps = s; - return *s && strchr(toks, *s); -} - -struct cmd *parseline(char**, char*); -struct cmd *parsepipe(char**, char*); -struct cmd *parseexec(char**, char*); -struct cmd *nulterminate(struct cmd*); - -struct cmd* -parsecmd(char *s) -{ - char *es; - struct cmd *cmd; - - es = s + strlen(s); - cmd = parseline(&s, es); - peek(&s, es, ""); - if(s != es){ - printf(2, "leftovers: %s\n", s); - panic("syntax"); - } - nulterminate(cmd); - return cmd; -} - -struct cmd* -parseline(char **ps, char *es) -{ - struct cmd *cmd; - - cmd = parsepipe(ps, es); - while(peek(ps, es, "&")){ - gettoken(ps, es, 0, 0); - cmd = backcmd(cmd); - } - if(peek(ps, es, ";")){ - gettoken(ps, es, 0, 0); - cmd = listcmd(cmd, parseline(ps, es)); - } - return cmd; -} - -struct cmd* -parsepipe(char **ps, char *es) -{ - struct cmd *cmd; - - cmd = parseexec(ps, es); - if(peek(ps, es, "|")){ - gettoken(ps, es, 0, 0); - cmd = pipecmd(cmd, parsepipe(ps, es)); - } - return cmd; -} - -struct cmd* -parseredirs(struct cmd *cmd, char **ps, char *es) -{ - int tok; - char *q, *eq; - - while(peek(ps, es, "<>")){ - tok = gettoken(ps, es, 0, 0); - if(gettoken(ps, es, &q, &eq) != 'a') - panic("missing file for redirection"); - switch(tok){ - case '<': - cmd = redircmd(cmd, q, eq, O_RDONLY, 0); - break; - case '>': - cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); - break; - case '+': // >> - cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); - break; - } - } - return cmd; -} - -struct cmd* -parseblock(char **ps, char *es) -{ - struct cmd *cmd; - - if(!peek(ps, es, "(")) - panic("parseblock"); - gettoken(ps, es, 0, 0); - cmd = parseline(ps, es); - if(!peek(ps, es, ")")) - panic("syntax - missing )"); - gettoken(ps, es, 0, 0); - cmd = parseredirs(cmd, ps, es); - return cmd; -} - -struct cmd* -parseexec(char **ps, char *es) -{ - char *q, *eq; - int tok, argc; - struct execcmd *cmd; - struct cmd *ret; - - if(peek(ps, es, "(")) - return parseblock(ps, es); - - ret = execcmd(); - cmd = (struct execcmd*)ret; - - argc = 0; - ret = parseredirs(ret, ps, es); - while(!peek(ps, es, "|)&;")){ - if((tok=gettoken(ps, es, &q, &eq)) == 0) - break; - if(tok != 'a') - panic("syntax"); - cmd->argv[argc] = q; - cmd->eargv[argc] = eq; - argc++; - if(argc >= MAXARGS) - panic("too many args"); - ret = parseredirs(ret, ps, es); - } - cmd->argv[argc] = 0; - cmd->eargv[argc] = 0; - return ret; -} - -// NUL-terminate all the counted strings. -struct cmd* -nulterminate(struct cmd *cmd) -{ - int i; - struct backcmd *bcmd; - struct execcmd *ecmd; - struct listcmd *lcmd; - struct pipecmd *pcmd; - struct redircmd *rcmd; - - if(cmd == 0) - return 0; - - switch(cmd->type){ - case EXEC: - ecmd = (struct execcmd*)cmd; - for(i=0; ecmd->argv[i]; i++) - *ecmd->eargv[i] = 0; - break; - - case REDIR: - rcmd = (struct redircmd*)cmd; - nulterminate(rcmd->cmd); - *rcmd->efile = 0; - break; - - case PIPE: - pcmd = (struct pipecmd*)cmd; - nulterminate(pcmd->left); - nulterminate(pcmd->right); - break; - - case LIST: - lcmd = (struct listcmd*)cmd; - nulterminate(lcmd->left); - nulterminate(lcmd->right); - break; - - case BACK: - bcmd = (struct backcmd*)cmd; - nulterminate(bcmd->cmd); - break; - } - return cmd; -} diff --git a/sleeplock.c b/sleeplock.c deleted file mode 100644 index b490370..0000000 --- a/sleeplock.c +++ /dev/null @@ -1,55 +0,0 @@ -// Sleeping locks - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "memlayout.h" -#include "proc.h" -#include "spinlock.h" -#include "sleeplock.h" - -void -initsleeplock(struct sleeplock *lk, char *name) -{ - initlock(&lk->lk, "sleep lock"); - lk->name = name; - lk->locked = 0; - lk->pid = 0; -} - -void -acquiresleep(struct sleeplock *lk) -{ - acquire(&lk->lk); - while (lk->locked) { - sleep(lk, &lk->lk); - } - lk->locked = 1; - lk->pid = myproc()->pid; - release(&lk->lk); -} - -void -releasesleep(struct sleeplock *lk) -{ - acquire(&lk->lk); - lk->locked = 0; - lk->pid = 0; - wakeup(lk); - release(&lk->lk); -} - -int -holdingsleep(struct sleeplock *lk) -{ - int r; - - acquire(&lk->lk); - r = lk->locked && (lk->pid == myproc()->pid); - release(&lk->lk); - return r; -} - - - diff --git a/sleeplock.h b/sleeplock.h deleted file mode 100644 index 110e6f3..0000000 --- a/sleeplock.h +++ /dev/null @@ -1,10 +0,0 @@ -// Long-term locks for processes -struct sleeplock { - uint locked; // Is the lock held? - struct spinlock lk; // spinlock protecting this sleep lock - - // For debugging: - char *name; // Name of lock. - int pid; // Process holding lock -}; - diff --git a/spinlock.c b/spinlock.c deleted file mode 100644 index bbb7cb5..0000000 --- a/spinlock.c +++ /dev/null @@ -1,110 +0,0 @@ -// Mutual exclusion spin locks. - -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "riscv.h" -#include "proc.h" -#include "defs.h" - -void -initlock(struct spinlock *lk, char *name) -{ - lk->name = name; - lk->locked = 0; - lk->cpu = 0; -} - -// Acquire the lock. -// Loops (spins) until the lock is acquired. -// Holding a lock for a long time may cause -// other CPUs to waste time spinning to acquire it. -void -acquire(struct spinlock *lk) -{ - push_off(); // disable interrupts to avoid deadlock. - if(holding(lk)) - panic("acquire"); - - // The xchg is atomic. - //while(xchg(&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 - // past this point, to ensure that the critical section's memory - // references happen after the lock is acquired. - __sync_synchronize(); - - // Record info about lock acquisition for holding() and debugging. - lk->cpu = mycpu(); -} - -// Release the lock. -void -release(struct spinlock *lk) -{ - if(!holding(lk)) - panic("release"); - - lk->cpu = 0; - - // Tell the C compiler and the processor to not move loads or stores - // past this point, to ensure that all the stores in the critical - // section are visible to other cores before the lock is released. - // Both the C compiler and the hardware may re-order loads and - // stores; __sync_synchronize() tells them both not to. - // On RISC-V, this turns into a fence instruction. - __sync_synchronize(); - - // Release the lock, equivalent to lk->locked = 0. - // This code can't use a C assignment, since it might - // not be atomic. A real OS would use C atomics here. - // On RISC-V, use an amoswap instruction. - //asm volatile("movl $0, %0" : "+m" (lk->locked) : ); - __sync_lock_release(&lk->locked); - - pop_off(); -} - -// Check whether this cpu is holding the lock. -int -holding(struct spinlock *lk) -{ - int r; - push_off(); - r = (lk->locked && lk->cpu == mycpu()); - pop_off(); - return r; -} - -// push_off/pop_off are like intr_off()/intr_on() except that they are matched: -// it takes two pop_off to undo two push_off. Also, if interrupts -// are initially off, then push_off, pop_off leaves them off. - -void -push_off(void) -{ - struct cpu *c = mycpu(); - int old = intr_get(); - - intr_off(); - if(c->noff == 0) - c->intena = old; - c->noff += 1; -} - -void -pop_off(void) -{ - struct cpu *c = mycpu(); - if(intr_get()) - panic("pop_off - interruptible"); - c->noff -= 1; - if(c->noff < 0) - panic("pop_off"); - if(c->noff == 0 && c->intena) - intr_on(); -} diff --git a/spinlock.h b/spinlock.h deleted file mode 100644 index 4392820..0000000 --- a/spinlock.h +++ /dev/null @@ -1,9 +0,0 @@ -// Mutual exclusion lock. -struct spinlock { - uint locked; // Is the lock held? - - // For debugging: - char *name; // Name of lock. - struct cpu *cpu; // The cpu holding the lock. -}; - diff --git a/start.c b/start.c deleted file mode 100644 index c4689dc..0000000 --- a/start.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -void main(); - -// entry.S needs one stack per CPU. -__attribute__ ((aligned (16))) char stack0[4096 * NCPU]; - -// scratch area for timer interrupt, one per CPU. -uint64 mscratch0[NCPU * 32]; - -// assembly code in kernelvec for machine-mode timer interrupt. -extern void machinevec(); - -// entry.S jumps here in machine mode on stack0. -void -mstart() -{ - // set M Previous Privilege mode to Supervisor, for mret. - unsigned long x = r_mstatus(); - x &= ~MSTATUS_MPP_MASK; - x |= MSTATUS_MPP_S; - w_mstatus(x); - - // set M Exception Program Counter to main, for mret. - // requires gcc -mcmodel=medany - w_mepc((uint64)main); - - // disable paging for now. - w_satp(0); - - // delegate all interrupts and exceptions to supervisor mode. - w_medeleg(0xffff); - w_mideleg(0xffff); - - // set up to receive timer interrupts in machine mode, - // for pre-emptive switching and (on hart 0) to drive time. - int id = r_mhartid(); - uint64 *scratch = &mscratch0[32 * id]; - *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + 10000; - scratch[4] = CLINT_MTIMECMP(id); - scratch[5] = 10000000; - w_mscratch((uint64)scratch); - w_mtvec((uint64)machinevec); - w_mstatus(r_mstatus() | MSTATUS_MIE); - w_mie(r_mie() | MIE_MTIE); - - // keep each CPU's hartid in its tp register, for cpuid(). - w_tp(id); - - // call main() in supervisor mode. - asm volatile("mret"); -} diff --git a/stat.h b/stat.h deleted file mode 100644 index 8a80933..0000000 --- a/stat.h +++ /dev/null @@ -1,11 +0,0 @@ -#define T_DIR 1 // Directory -#define T_FILE 2 // File -#define T_DEV 3 // Device - -struct stat { - short type; // Type of file - int dev; // File system's disk device - uint ino; // Inode number - short nlink; // Number of links to file - uint size; // Size of file in bytes -}; diff --git a/stressfs.c b/stressfs.c deleted file mode 100644 index c0a4743..0000000 --- a/stressfs.c +++ /dev/null @@ -1,49 +0,0 @@ -// Demonstrate that moving the "acquire" in iderw after the loop that -// appends to the idequeue results in a race. - -// For this to work, you should also add a spin within iderw's -// idequeue traversal loop. Adding the following demonstrated a panic -// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: -// for (i = 0; i < 40000; i++) -// asm volatile(""); - -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fs.h" -#include "fcntl.h" - -int -main(int argc, char *argv[]) -{ - int fd, i; - char path[] = "stressfs0"; - char data[512]; - - printf(1, "stressfs starting\n"); - memset(data, 'a', sizeof(data)); - - for(i = 0; i < 4; i++) - if(fork() > 0) - break; - - printf(1, "write %d\n", i); - - path[8] += i; - fd = open(path, O_CREATE | O_RDWR); - for(i = 0; i < 20; i++) -// printf(fd, "%d\n", i); - write(fd, data, sizeof(data)); - close(fd); - - printf(1, "read\n"); - - fd = open(path, O_RDONLY); - for (i = 0; i < 20; i++) - read(fd, data, sizeof(data)); - close(fd); - - wait(); - - exit(); -} diff --git a/string.c b/string.c deleted file mode 100644 index d99e612..0000000 --- a/string.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "types.h" - -void* -memset(void *dst, int c, uint n) -{ - char *cdst = (char *) dst; - int i; - for(i = 0; i < n; i++){ - cdst[i] = c; - } - return dst; -} - -int -memcmp(const void *v1, const void *v2, uint n) -{ - const uchar *s1, *s2; - - s1 = v1; - s2 = v2; - while(n-- > 0){ - if(*s1 != *s2) - return *s1 - *s2; - s1++, s2++; - } - - return 0; -} - -void* -memmove(void *dst, const void *src, uint n) -{ - const char *s; - char *d; - - s = src; - d = dst; - if(s < d && s + n > d){ - s += n; - d += n; - while(n-- > 0) - *--d = *--s; - } else - while(n-- > 0) - *d++ = *s++; - - return dst; -} - -// memcpy exists to placate GCC. Use memmove. -void* -memcpy(void *dst, const void *src, uint n) -{ - return memmove(dst, src, n); -} - -int -strncmp(const char *p, const char *q, uint n) -{ - while(n > 0 && *p && *p == *q) - n--, p++, q++; - if(n == 0) - return 0; - return (uchar)*p - (uchar)*q; -} - -char* -strncpy(char *s, const char *t, int n) -{ - char *os; - - os = s; - while(n-- > 0 && (*s++ = *t++) != 0) - ; - while(n-- > 0) - *s++ = 0; - return os; -} - -// Like strncpy but guaranteed to NUL-terminate. -char* -safestrcpy(char *s, const char *t, int n) -{ - char *os; - - os = s; - if(n <= 0) - return os; - while(--n > 0 && (*s++ = *t++) != 0) - ; - *s = 0; - return os; -} - -int -strlen(const char *s) -{ - int n; - - for(n = 0; s[n]; n++) - ; - return n; -} - diff --git a/swtch.S b/swtch.S deleted file mode 100644 index 17a8663..0000000 --- a/swtch.S +++ /dev/null @@ -1,42 +0,0 @@ -# Context switch -# -# void swtch(struct context *old, struct context *new); -# -# Save current registers in old. Load from new. - - -.globl swtch -swtch: - sd ra, 0(a0) - sd sp, 8(a0) - sd s0, 16(a0) - sd s1, 24(a0) - sd s2, 32(a0) - sd s3, 40(a0) - sd s4, 48(a0) - sd s5, 56(a0) - sd s6, 64(a0) - sd s7, 72(a0) - sd s8, 80(a0) - sd s9, 88(a0) - sd s10, 96(a0) - sd s11, 104(a0) - - ld ra, 0(a1) - ld sp, 8(a1) - ld s0, 16(a1) - ld s1, 24(a1) - ld s2, 32(a1) - ld s3, 40(a1) - ld s4, 48(a1) - ld s5, 56(a1) - ld s6, 64(a1) - ld s7, 72(a1) - ld s8, 80(a1) - ld s9, 88(a1) - ld s10, 96(a1) - ld s11, 104(a1) - - ret - - diff --git a/syscall.c b/syscall.c deleted file mode 100644 index ca34f2c..0000000 --- a/syscall.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "syscall.h" -#include "defs.h" - -// User code makes a system call with INT T_SYSCALL. -// System call number in %eax. -// Arguments on the stack, from the user call to the C -// library system call function. The saved user %esp points -// to a saved program counter, and then the first argument. - -// Fetch the int at addr from the current process. -int -fetchint(uint64 addr, int *ip) -{ - struct proc *p = myproc(); - - if(addr >= p->sz || addr+4 > p->sz) - return -1; - if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) - return -1; - return 0; -} - -// Fetch the uint64 at addr from the current process. -int -fetchaddr(uint64 addr, uint64 *ip) -{ - struct proc *p = myproc(); - if(addr >= p->sz || addr+sizeof(uint64) > p->sz) - return -1; - if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) - return -1; - return 0; -} - -// Fetch the nul-terminated string at addr from the current process. -// Doesn't actually copy the string - just sets *pp to point at it. -// Returns length of string, not including nul, or -1 for error. -int -fetchstr(uint64 addr, char *buf, int max) -{ - struct proc *p = myproc(); - int err = copyinstr(p->pagetable, buf, addr, max); - if(err < 0) - return err; - return strlen(buf); -} - -static uint64 -fetcharg(int n) -{ - struct proc *p = myproc(); - switch (n) { - case 0: - return p->tf->a0; - case 1: - return p->tf->a1; - case 2: - return p->tf->a2; - case 3: - return p->tf->a3; - case 4: - return p->tf->a4; - case 5: - return p->tf->a5; - } - panic("fetcharg"); - return -1; -} - -// Fetch the nth 32-bit system call argument. -int -argint(int n, int *ip) -{ - *ip = fetcharg(n); - return 0; -} - -int -argaddr(int n, uint64 *ip) -{ - *ip = fetcharg(n); - return 0; -} - -// Fetch the nth word-sized system call argument as a pointer -// to a block of memory of size bytes. Check that the pointer -// lies within the process address space. -int -argptr(int n, uint64 *pp, int size) -{ - uint64 i; - struct proc *p = myproc(); - - if(argaddr(n, &i) < 0) - return -1; - if(size < 0 || (uint)i >= p->sz || (uint)i+size > p->sz) - return -1; - *pp = i; - return 0; -} - -// Fetch the nth word-sized system call argument as a null-terminated string. -// Copies into buf, at most max. -// Returns string length if OK (including nul), -1 if error. -int -argstr(int n, char *buf, int max) -{ - uint64 addr; - if(argaddr(n, &addr) < 0) - return -1; - return fetchstr(addr, buf, max); -} - -extern int sys_chdir(void); -extern int sys_close(void); -extern int sys_dup(void); -extern int sys_exec(void); -extern int sys_exit(void); -extern int sys_fork(void); -extern int sys_fstat(void); -extern int sys_getpid(void); -extern int sys_kill(void); -extern int sys_link(void); -extern int sys_mkdir(void); -extern int sys_mknod(void); -extern int sys_open(void); -extern int sys_pipe(void); -extern int sys_read(void); -extern int sys_sbrk(void); -extern int sys_sleep(void); -extern int sys_unlink(void); -extern int sys_wait(void); -extern int sys_write(void); -extern int sys_uptime(void); - -static int (*syscalls[])(void) = { -[SYS_fork] sys_fork, -[SYS_exit] sys_exit, -[SYS_wait] sys_wait, -[SYS_pipe] sys_pipe, -[SYS_read] sys_read, -[SYS_kill] sys_kill, -[SYS_exec] sys_exec, -[SYS_fstat] sys_fstat, -[SYS_chdir] sys_chdir, -[SYS_dup] sys_dup, -[SYS_getpid] sys_getpid, -[SYS_sbrk] sys_sbrk, -[SYS_sleep] sys_sleep, -[SYS_uptime] sys_uptime, -[SYS_open] sys_open, -[SYS_write] sys_write, -[SYS_mknod] sys_mknod, -[SYS_unlink] sys_unlink, -[SYS_link] sys_link, -[SYS_mkdir] sys_mkdir, -[SYS_close] sys_close, -}; - -static void -dosyscall(void) -{ - int num; - struct proc *p = myproc(); - - num = p->tf->a7; - if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { - p->tf->a0 = syscalls[num](); - } else { - printf("%d %s: unknown sys call %d\n", - p->pid, p->name, num); - p->tf->a0 = -1; - } -} - -void -syscall() -{ - if(myproc()->killed) - exit(); - dosyscall(); - if(myproc()->killed) - exit(); - return; -} - diff --git a/syscall.h b/syscall.h deleted file mode 100644 index bc5f356..0000000 --- a/syscall.h +++ /dev/null @@ -1,22 +0,0 @@ -// System call numbers -#define SYS_fork 1 -#define SYS_exit 2 -#define SYS_wait 3 -#define SYS_pipe 4 -#define SYS_read 5 -#define SYS_kill 6 -#define SYS_exec 7 -#define SYS_fstat 8 -#define SYS_chdir 9 -#define SYS_dup 10 -#define SYS_getpid 11 -#define SYS_sbrk 12 -#define SYS_sleep 13 -#define SYS_uptime 14 -#define SYS_open 15 -#define SYS_write 16 -#define SYS_mknod 17 -#define SYS_unlink 18 -#define SYS_link 19 -#define SYS_mkdir 20 -#define SYS_close 21 diff --git a/sysfile.c b/sysfile.c deleted file mode 100644 index 83bb1ed..0000000 --- a/sysfile.c +++ /dev/null @@ -1,465 +0,0 @@ -// -// File-system system calls. -// Mostly argument checking, since we don't trust -// user code, and calls into file.c and fs.c. -// - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "stat.h" -#include "proc.h" -#include "fs.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "file.h" -#include "fcntl.h" - -// Fetch the nth word-sized system call argument as a file descriptor -// and return both the descriptor and the corresponding struct file. -static int -argfd(int n, int *pfd, struct file **pf) -{ - int fd; - struct file *f; - - if(argint(n, &fd) < 0) - return -1; - if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0) - return -1; - if(pfd) - *pfd = fd; - if(pf) - *pf = f; - return 0; -} - -// Allocate a file descriptor for the given file. -// Takes over file reference from caller on success. -static int -fdalloc(struct file *f) -{ - int fd; - struct proc *p = myproc(); - - for(fd = 0; fd < NOFILE; fd++){ - if(p->ofile[fd] == 0){ - p->ofile[fd] = f; - return fd; - } - } - return -1; -} - -int -sys_dup(void) -{ - struct file *f; - int fd; - - if(argfd(0, 0, &f) < 0) - return -1; - if((fd=fdalloc(f)) < 0) - return -1; - filedup(f); - return fd; -} - -int -sys_read(void) -{ - struct file *f; - int n; - uint64 p; - - if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) - return -1; - return fileread(f, p, n); -} - -int -sys_write(void) -{ - struct file *f; - int n; - uint64 p; - - if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) - return -1; - - return filewrite(f, p, n); -} - -int -sys_close(void) -{ - int fd; - struct file *f; - - if(argfd(0, &fd, &f) < 0) - return -1; - myproc()->ofile[fd] = 0; - fileclose(f); - return 0; -} - -int -sys_fstat(void) -{ - struct file *f; - uint64 st; // user pointer to struct stat - - if(argfd(0, 0, &f) < 0 || argptr(1, &st, sizeof(struct stat)) < 0) - return -1; - return filestat(f, st); -} - -// Create the path new as a link to the same inode as old. -int -sys_link(void) -{ - char name[DIRSIZ], new[MAXPATH], old[MAXPATH]; - struct inode *dp, *ip; - - if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0) - return -1; - - begin_op(); - if((ip = namei(old)) == 0){ - end_op(); - return -1; - } - - ilock(ip); - if(ip->type == T_DIR){ - iunlockput(ip); - end_op(); - return -1; - } - - ip->nlink++; - iupdate(ip); - iunlock(ip); - - if((dp = nameiparent(new, name)) == 0) - goto bad; - ilock(dp); - if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ - iunlockput(dp); - goto bad; - } - iunlockput(dp); - iput(ip); - - end_op(); - - return 0; - -bad: - ilock(ip); - ip->nlink--; - iupdate(ip); - iunlockput(ip); - end_op(); - return -1; -} - -// Is the directory dp empty except for "." and ".." ? -static int -isdirempty(struct inode *dp) -{ - int off; - struct dirent de; - - for(off=2*sizeof(de); offsize; off+=sizeof(de)){ - if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - panic("isdirempty: readi"); - if(de.inum != 0) - return 0; - } - return 1; -} - -//PAGEBREAK! -int -sys_unlink(void) -{ - struct inode *ip, *dp; - struct dirent de; - char name[DIRSIZ], path[MAXPATH]; - uint off; - - if(argstr(0, path, MAXPATH) < 0) - return -1; - - begin_op(); - if((dp = nameiparent(path, name)) == 0){ - end_op(); - return -1; - } - - ilock(dp); - - // Cannot unlink "." or "..". - if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0) - goto bad; - - if((ip = dirlookup(dp, name, &off)) == 0) - goto bad; - ilock(ip); - - if(ip->nlink < 1) - panic("unlink: nlink < 1"); - if(ip->type == T_DIR && !isdirempty(ip)){ - iunlockput(ip); - goto bad; - } - - memset(&de, 0, sizeof(de)); - if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - panic("unlink: writei"); - if(ip->type == T_DIR){ - dp->nlink--; - iupdate(dp); - } - iunlockput(dp); - - ip->nlink--; - iupdate(ip); - iunlockput(ip); - - end_op(); - - return 0; - -bad: - iunlockput(dp); - end_op(); - return -1; -} - -static struct inode* -create(char *path, short type, short major, short minor) -{ - uint off; - struct inode *ip, *dp; - char name[DIRSIZ]; - - if((dp = nameiparent(path, name)) == 0) - return 0; - ilock(dp); - - if((ip = dirlookup(dp, name, &off)) != 0){ - iunlockput(dp); - ilock(ip); - if(type == T_FILE && ip->type == T_FILE) - return ip; - iunlockput(ip); - return 0; - } - - if((ip = ialloc(dp->dev, type)) == 0) - panic("create: ialloc"); - - ilock(ip); - ip->major = major; - ip->minor = minor; - ip->nlink = 1; - iupdate(ip); - - if(type == T_DIR){ // Create . and .. entries. - dp->nlink++; // for ".." - iupdate(dp); - // No ip->nlink++ for ".": avoid cyclic ref count. - if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) - panic("create dots"); - } - - if(dirlink(dp, name, ip->inum) < 0) - panic("create: dirlink"); - - iunlockput(dp); - - return ip; -} - -int -sys_open(void) -{ - char path[MAXPATH]; - int fd, omode; - struct file *f; - struct inode *ip; - - if(argstr(0, path, MAXPATH) < 0 || argint(1, &omode) < 0) - return -1; - - begin_op(); - - if(omode & O_CREATE){ - ip = create(path, T_FILE, 0, 0); - if(ip == 0){ - end_op(); - return -1; - } - } else { - if((ip = namei(path)) == 0){ - end_op(); - return -1; - } - ilock(ip); - if(ip->type == T_DIR && omode != O_RDONLY){ - iunlockput(ip); - end_op(); - return -1; - } - } - - if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ - if(f) - fileclose(f); - iunlockput(ip); - end_op(); - return -1; - } - iunlock(ip); - end_op(); - - f->type = FD_INODE; - f->ip = ip; - f->off = 0; - f->readable = !(omode & O_WRONLY); - f->writable = (omode & O_WRONLY) || (omode & O_RDWR); - return fd; -} - -int -sys_mkdir(void) -{ - char path[MAXPATH]; - struct inode *ip; - - begin_op(); - if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ - end_op(); - return -1; - } - iunlockput(ip); - end_op(); - return 0; -} - -int -sys_mknod(void) -{ - struct inode *ip; - char path[MAXPATH]; - int major, minor; - - begin_op(); - if((argstr(0, path, MAXPATH)) < 0 || - argint(1, &major) < 0 || - argint(2, &minor) < 0 || - (ip = create(path, T_DEV, major, minor)) == 0){ - end_op(); - return -1; - } - iunlockput(ip); - end_op(); - return 0; -} - -int -sys_chdir(void) -{ - char path[MAXPATH]; - struct inode *ip; - struct proc *p = myproc(); - - begin_op(); - if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){ - end_op(); - return -1; - } - ilock(ip); - if(ip->type != T_DIR){ - iunlockput(ip); - end_op(); - return -1; - } - iunlock(ip); - iput(p->cwd); - end_op(); - p->cwd = ip; - return 0; -} - -int -sys_exec(void) -{ - char path[MAXPATH], *argv[MAXARG]; - int i; - uint64 uargv, uarg; - - if(argstr(0, path, MAXPATH) < 0 || argaddr(1, &uargv) < 0){ - return -1; - } - memset(argv, 0, sizeof(argv)); - for(i=0;; i++){ - if(i >= NELEM(argv)){ - return -1; - } - if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){ - return -1; - } - if(uarg == 0){ - argv[i] = 0; - break; - } - argv[i] = kalloc(); - if(argv[i] == 0) - panic("sys_exec kalloc"); - if(fetchstr(uarg, argv[i], PGSIZE) < 0){ - return -1; - } - } - - int ret = exec(path, argv); - - for(i = 0; i < NELEM(argv) && argv[i] != 0; i++) - kfree(argv[i]); - - return ret; -} - -int -sys_pipe(void) -{ - uint64 fdarray; // user pointer to array of two integers - struct file *rf, *wf; - int fd0, fd1; - struct proc *p = myproc(); - - if(argptr(0, &fdarray, 2*sizeof(int)) < 0) - return -1; - if(pipealloc(&rf, &wf) < 0) - return -1; - fd0 = -1; - if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ - if(fd0 >= 0) - p->ofile[fd0] = 0; - fileclose(rf); - fileclose(wf); - return -1; - } - if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 || - copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){ - p->ofile[fd0] = 0; - p->ofile[fd1] = 0; - fileclose(rf); - fileclose(wf); - return -1; - } - return 0; -} diff --git a/sysproc.c b/sysproc.c deleted file mode 100644 index e57e045..0000000 --- a/sysproc.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "date.h" -#include "param.h" -#include "memlayout.h" -#include "proc.h" - -int -sys_exit(void) -{ - exit(); - return 0; // not reached -} - -int -sys_getpid(void) -{ - return myproc()->pid; -} - -int -sys_fork(void) -{ - return fork(); -} - -int -sys_wait(void) -{ - return wait(); -} - -int -sys_sbrk(void) -{ - int addr; - int n; - - if(argint(0, &n) < 0) - return -1; - addr = myproc()->sz; - if(growproc(n) < 0) - return -1; - return addr; -} - -int -sys_sleep(void) -{ - int n; - uint ticks0; - - if(argint(0, &n) < 0) - return -1; - acquire(&tickslock); - ticks0 = ticks; - while(ticks - ticks0 < n){ - if(myproc()->killed){ - release(&tickslock); - return -1; - } - sleep(&ticks, &tickslock); - } - release(&tickslock); - return 0; -} - -int -sys_kill(void) -{ - int pid; - - if(argint(0, &pid) < 0) - return -1; - return kill(pid); -} - -// return how many clock tick interrupts have occurred -// since start. -int -sys_uptime(void) -{ - uint xticks; - - acquire(&tickslock); - xticks = ticks; - release(&tickslock); - return xticks; -} diff --git a/trampoline.S b/trampoline.S deleted file mode 100644 index dd4eb02..0000000 --- a/trampoline.S +++ /dev/null @@ -1,137 +0,0 @@ - # - # code to switch between user and kernel space. - # - # this code is mapped at the same virtual address - # in user and kernel space so that it can switch - # page tables. - # - # kernel.ld causes trampout to be aligned - # to a page boundary. - # -.globl usertrap - .section trampoline -.globl trampout -trampout: - # switch from kernel to user. - # usertrapret() calls here. - # a0: p->tf in user page table - # a1: new value for satp, for user page table - - # switch to user page table - csrw satp, a1 - - # put the saved user a0 in sscratch, so we - # can swap it with our a0 (p->tf) in the last step. - ld t0, 112(a0) - csrw sscratch, t0 - - # restore all but a0 from p->tf - ld ra, 40(a0) - ld sp, 48(a0) - ld gp, 56(a0) - ld tp, 64(a0) - ld t0, 72(a0) - ld t1, 80(a0) - ld t2, 88(a0) - ld s0, 96(a0) - ld s1, 104(a0) - ld a1, 120(a0) - ld a2, 128(a0) - ld a3, 136(a0) - ld a4, 144(a0) - ld a5, 152(a0) - ld a6, 160(a0) - ld a7, 168(a0) - ld s2, 176(a0) - ld s3, 184(a0) - ld s4, 192(a0) - ld s5, 200(a0) - ld s6, 208(a0) - ld s7, 216(a0) - ld s8, 224(a0) - ld s9, 232(a0) - ld s10, 240(a0) - ld s11, 248(a0) - ld t3, 256(a0) - ld t4, 264(a0) - ld t5, 272(a0) - ld t6, 280(a0) - - # restore user a0, and save p->tf - csrrw a0, sscratch, a0 - - # return to user mode and user pc. - # caller has set up sstatus and sepc. - sret - -.align 4 -.globl trampin -trampin: - # - # trap.c set stvec to point here, so - # user interrupts and exceptions start here, - # in supervisor mode, but with a - # user page table. - # - # sscratch points to where the process's p->tf is - # mapped into user space (TRAMPOLINE - 4096). - # - - # swap a0 and sscratch - # so that a0 is p->tf - csrrw a0, sscratch, a0 - - # save the user registers in p->tf - sd ra, 40(a0) - sd sp, 48(a0) - sd gp, 56(a0) - sd tp, 64(a0) - sd t0, 72(a0) - sd t1, 80(a0) - sd t2, 88(a0) - sd s0, 96(a0) - sd s1, 104(a0) - sd a1, 120(a0) - sd a2, 128(a0) - sd a3, 136(a0) - sd a4, 144(a0) - sd a5, 152(a0) - sd a6, 160(a0) - sd a7, 168(a0) - sd s2, 176(a0) - sd s3, 184(a0) - sd s4, 192(a0) - sd s5, 200(a0) - sd s6, 208(a0) - sd s7, 216(a0) - sd s8, 224(a0) - sd s9, 232(a0) - sd s10, 240(a0) - sd s11, 248(a0) - sd t3, 256(a0) - sd t4, 264(a0) - sd t5, 272(a0) - sd t6, 280(a0) - - # save the user a0 in p->tf->a0 - csrr t0, sscratch - sd t0, 112(a0) - - # restore kernel stack pointer from p->tf->kernel_sp - ld sp, 8(a0) - - # make tp hold the current hartid, from p->tf->hartid - ld tp, 32(a0) - - # remember the address of usertrap(), p->tf->kernel_trap - ld t0, 16(a0) - - # restore kernel page table from p->tf->kernel_satp - ld t1, 0(a0) - csrw satp, t1 - - # a0 is no longer valid, since the kernel page - # table does not specially map p->td. - - # jump to usertrap(), which does not return - jr t0 diff --git a/trap.c b/trap.c deleted file mode 100644 index 050a94d..0000000 --- a/trap.c +++ /dev/null @@ -1,184 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "spinlock.h" -#include "defs.h" - -struct spinlock tickslock; -uint ticks; - -extern char trampout[], trampin[]; - -// in kernelvec.S, calls kerneltrap(). -void kernelvec(); - -extern int devintr(); - -void -trapinit(void) -{ - initlock(&tickslock, "time"); -} - -// set up to take exceptions and traps while in the kernel. -void -trapinithart(void) -{ - w_stvec((uint64)kernelvec); -} - -// -// handle an interrupt, exception, or system call from user space. -// called from trampoline.S -// -void -usertrap(void) -{ - int which_dev = 0; - - if((r_sstatus() & SSTATUS_SPP) != 0) - panic("usertrap: not from user mode"); - - // send interrupts and exceptions to kerneltrap(), - // since we're now in the kernel. - w_stvec((uint64)kernelvec); - - struct proc *p = myproc(); - - // save user program counter. - p->tf->epc = r_sepc(); - - intr_on(); - - if(r_scause() == 8){ - // system call - - // sepc points to the ecall instruction, - // but we want to return to the next instruction. - p->tf->epc += 4; - - syscall(); - } else if((which_dev = devintr()) != 0){ - // ok - } else { - printf("usertrap(): unexpected scause 0x%p pid=%d\n", r_scause(), p->pid); - printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); - p->killed = 1; - } - - if(p->killed) - exit(); - - // give up the CPU if this is a timer interrupt. - if(which_dev == 2) - yield(); - - usertrapret(); -} - -// -// return to user space -// -void -usertrapret(void) -{ - struct proc *p = myproc(); - - // turn off interrupts, since we're switching - // now from kerneltrap() to usertrap(). - intr_off(); - - // send interrupts and exceptions to trampoline.S - w_stvec(TRAMPOLINE + (trampin - trampout)); - - // set up values that trampoline.S will need when - // the process next re-enters the kernel. - p->tf->kernel_satp = r_satp(); - p->tf->kernel_sp = (uint64)p->kstack + PGSIZE; - p->tf->kernel_trap = (uint64)usertrap; - p->tf->hartid = r_tp(); - - // set up the registers that trampoline.S's sret will use - // 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 Exception Program Counter to the saved user pc. - w_sepc(p->tf->epc); - - // tell trampline.S the user page table to switch to. - uint64 satp = MAKE_SATP(p->pagetable); - - // jump to trampoline.S at the top of memory, which - // switches to the user page table, restores user registers, - // and switches to user mode with sret. - ((void (*)(uint64,uint64))TRAMPOLINE)(TRAMPOLINE - PGSIZE, satp); -} - -// interrupts and exceptions from kernel code go here, -// on whatever the current kernel stack is. -// must be 4-byte aligned to fit in stvec. -void -kerneltrap() -{ - 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(devintr() == 0){ - printf("scause 0x%p\n", scause); - printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); - panic("kerneltrap"); - } -} - -// check if it's an external interrupt or software interrupt, -// and handle it. -// returns 2 if timer interrupt, -// 1 if other device, -// 0 if not recognized. -int -devintr() -{ - uint64 scause = r_scause(); - - if((scause & 0x8000000000000000L) && - (scause & 0xff) == 9){ - // supervisor external interrupt, via PLIC. - int irq = plic_claim(); - - if(irq == UART0_IRQ){ - uartintr(); - } - - plic_complete(irq); - return 1; - } else if(scause == 0x8000000000000001){ - // software interrupt from a machine-mode timer interrupt. - - if(cpuid() == 0){ - acquire(&tickslock); - ticks++; - wakeup(&ticks); - release(&tickslock); - } - - // acknowledge. - w_sip(r_sip() & ~2); - - return 2; - } else { - return 0; - } -} - diff --git a/types.h b/types.h deleted file mode 100644 index ee73164..0000000 --- a/types.h +++ /dev/null @@ -1,10 +0,0 @@ -typedef unsigned int uint; -typedef unsigned short ushort; -typedef unsigned char uchar; - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef unsigned long uint64; - -typedef uint64 pde_t; diff --git a/uart.c b/uart.c deleted file mode 100644 index 35fac1b..0000000 --- a/uart.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "spinlock.h" -#include "defs.h" - -// -// qemu -machine virt has a 16550a UART -// qemu/hw/riscv/virt.c -// http://byterunner.com/16550.html -// -// caller should lock. -// - -// address of one of the registers -#define R(reg) ((volatile unsigned char *)(UART0 + reg)) - -void -uartinit(void) -{ - // disable interrupts -- IER - *R(1) = 0x00; - - // special mode to set baud rate - *R(3) = 0x80; - - // LSB for baud rate of 38.4K - *R(0) = 0x03; - - // MSB for baud rate of 38.4K - *R(1) = 0x00; - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - *R(3) = 0x03; - - // reset and enable FIFOs -- FCR. - *R(2) = 0x07; - - // enable receive interrupts -- IER. - *R(1) = 0x01; -} - -void -uartputc(int c) -{ - // wait for Transmit Holding Empty to be set in LSR. - while((*R(5) & (1 << 5)) == 0) - ; - *R(0) = c; -} - -int -uartgetc(void) -{ - if(*R(5) & 0x01){ - // input data is ready. - return *R(0); - } else { - return -1; - } -} - -void -uartintr(void) -{ - while(1){ - int c = uartgetc(); - if(c == -1) - break; - consoleintr(c); - } -} diff --git a/ulib.c b/ulib.c deleted file mode 100644 index 532fe42..0000000 --- a/ulib.c +++ /dev/null @@ -1,109 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "fcntl.h" -#include "user.h" - -char* -strcpy(char *s, const char *t) -{ - char *os; - - os = s; - while((*s++ = *t++) != 0) - ; - return os; -} - -int -strcmp(const char *p, const char *q) -{ - while(*p && *p == *q) - p++, q++; - return (uchar)*p - (uchar)*q; -} - -uint -strlen(const char *s) -{ - int n; - - for(n = 0; s[n]; n++) - ; - return n; -} - -void* -memset(void *dst, int c, uint n) -{ - char *cdst = (char *) dst; - int i; - for(i = 0; i < n; i++){ - cdst[i] = c; - } - return dst; -} - -char* -strchr(const char *s, char c) -{ - for(; *s; s++) - if(*s == c) - return (char*)s; - return 0; -} - -char* -gets(char *buf, int max) -{ - int i, cc; - char c; - - for(i=0; i+1 < max; ){ - cc = read(0, &c, 1); - if(cc < 1) - break; - buf[i++] = c; - if(c == '\n' || c == '\r') - break; - } - buf[i] = '\0'; - return buf; -} - -int -stat(const char *n, struct stat *st) -{ - int fd; - int r; - - fd = open(n, O_RDONLY); - if(fd < 0) - return -1; - r = fstat(fd, st); - close(fd); - return r; -} - -int -atoi(const char *s) -{ - int n; - - n = 0; - while('0' <= *s && *s <= '9') - n = n*10 + *s++ - '0'; - return n; -} - -void* -memmove(void *vdst, const void *vsrc, int n) -{ - char *dst; - const char *src; - - dst = vdst; - src = vsrc; - while(n-- > 0) - *dst++ = *src++; - return vdst; -} diff --git a/umalloc.c b/umalloc.c deleted file mode 100644 index a7e7d2c..0000000 --- a/umalloc.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" -#include "param.h" - -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. - -typedef long Align; - -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; -}; - -typedef union header Header; - -static Header base; -static Header *freep; - -void -free(void *ap) -{ - Header *bp, *p; - - bp = (Header*)ap - 1; - for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) - if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) - break; - if(bp + bp->s.size == p->s.ptr){ - bp->s.size += p->s.ptr->s.size; - bp->s.ptr = p->s.ptr->s.ptr; - } else - bp->s.ptr = p->s.ptr; - if(p + p->s.size == bp){ - p->s.size += bp->s.size; - p->s.ptr = bp->s.ptr; - } else - p->s.ptr = bp; - freep = p; -} - -static Header* -morecore(uint nu) -{ - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == (char*)-1) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; -} - -void* -malloc(uint nbytes) -{ - Header *p, *prevp; - uint nunits; - - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; - } - for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ - if(p->s.size >= nunits){ - if(p->s.size == nunits) - prevp->s.ptr = p->s.ptr; - else { - p->s.size -= nunits; - p += p->s.size; - p->s.size = nunits; - } - freep = prevp; - return (void*)(p + 1); - } - if(p == freep) - if((p = morecore(nunits)) == 0) - return 0; - } -} diff --git a/user.h b/user.h deleted file mode 100644 index 4f99c52..0000000 --- a/user.h +++ /dev/null @@ -1,39 +0,0 @@ -struct stat; -struct rtcdate; - -// system calls -int fork(void); -int exit(void) __attribute__((noreturn)); -int wait(void); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); -int close(int); -int kill(int); -int exec(char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); -int dup(int); -int getpid(void); -char* sbrk(int); -int sleep(int); -int uptime(void); - -// ulib.c -int stat(const char*, struct stat*); -char* strcpy(char*, const char*); -void *memmove(void*, const void*, int); -char* strchr(const char*, char c); -int strcmp(const char*, const char*); -void printf(int, const char*, ...); -char* gets(char*, int max); -uint strlen(const char*); -void* memset(void*, int, uint); -void* malloc(uint); -void free(void*); -int atoi(const char*); diff --git a/user/cat.c b/user/cat.c new file mode 100644 index 0000000..d3d16c4 --- /dev/null +++ b/user/cat.c @@ -0,0 +1,43 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[512]; + +void +cat(int fd) +{ + int n; + + while((n = read(fd, buf, sizeof(buf))) > 0) { + if (write(1, buf, n) != n) { + printf(1, "cat: write error\n"); + exit(); + } + } + if(n < 0){ + printf(1, "cat: read error\n"); + exit(); + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + + if(argc <= 1){ + cat(0); + exit(); + } + + for(i = 1; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "cat: cannot open %s\n", argv[i]); + exit(); + } + cat(fd); + close(fd); + } + exit(); +} diff --git a/user/echo.c b/user/echo.c new file mode 100644 index 0000000..ef744ab --- /dev/null +++ b/user/echo.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + for(i = 1; i < argc; i++) + printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); + exit(); +} diff --git a/user/forktest.c b/user/forktest.c new file mode 100644 index 0000000..be4915e --- /dev/null +++ b/user/forktest.c @@ -0,0 +1,56 @@ +// Test that fork fails gracefully. +// Tiny executable so that the limit can be filling the proc table. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +#define N 1000 + +void +printf(int fd, const char *s, ...) +{ + write(fd, s, strlen(s)); +} + +void +forktest(void) +{ + int n, pid; + + printf(1, "fork test\n"); + + for(n=0; n 0; n--){ + if(wait() < 0){ + printf(1, "wait stopped early\n"); + exit(); + } + } + + if(wait() != -1){ + printf(1, "wait got too many\n"); + exit(); + } + + printf(1, "fork test OK\n"); +} + +int +main(void) +{ + forktest(); + exit(); +} diff --git a/user/grep.c b/user/grep.c new file mode 100644 index 0000000..b5fdfc2 --- /dev/null +++ b/user/grep.c @@ -0,0 +1,107 @@ +// Simple grep. Only supports ^ . * $ operators. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[1024]; +int match(char*, char*); + +void +grep(char *pattern, int fd) +{ + int n, m; + char *p, *q; + + m = 0; + while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ + m += n; + buf[m] = '\0'; + p = buf; + while((q = strchr(p, '\n')) != 0){ + *q = 0; + if(match(pattern, p)){ + *q = '\n'; + write(1, p, q+1 - p); + } + p = q+1; + } + if(p == buf) + m = 0; + if(m > 0){ + m -= p - buf; + memmove(buf, p, m); + } + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + char *pattern; + + if(argc <= 1){ + printf(2, "usage: grep pattern [file ...]\n"); + exit(); + } + pattern = argv[1]; + + if(argc <= 2){ + grep(pattern, 0); + exit(); + } + + for(i = 2; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "grep: cannot open %s\n", argv[i]); + exit(); + } + grep(pattern, fd); + close(fd); + } + exit(); +} + +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9. + +int matchhere(char*, char*); +int matchstar(int, char*, char*); + +int +match(char *re, char *text) +{ + if(re[0] == '^') + return matchhere(re+1, text); + do{ // must look at empty string + if(matchhere(re, text)) + return 1; + }while(*text++ != '\0'); + return 0; +} + +// matchhere: search for re at beginning of text +int matchhere(char *re, char *text) +{ + if(re[0] == '\0') + return 1; + if(re[1] == '*') + return matchstar(re[0], re+2, text); + if(re[0] == '$' && re[1] == '\0') + return *text == '\0'; + if(*text!='\0' && (re[0]=='.' || re[0]==*text)) + return matchhere(re+1, text+1); + return 0; +} + +// matchstar: search for c*re at beginning of text +int matchstar(int c, char *re, char *text) +{ + do{ // a * matches zero or more instances + if(matchhere(re, text)) + return 1; + }while(*text!='\0' && (*text++==c || c=='.')); + return 0; +} + diff --git a/user/init.c b/user/init.c new file mode 100644 index 0000000..f36ba31 --- /dev/null +++ b/user/init.c @@ -0,0 +1,37 @@ +// init: The initial user-level program + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +char *argv[] = { "sh", 0 }; + +int +main(void) +{ + int pid, wpid; + + if(open("console", O_RDWR) < 0){ + mknod("console", 1, 1); + open("console", O_RDWR); + } + dup(0); // stdout + dup(0); // stderr + + for(;;){ + printf(1, "init: starting sh\n"); + pid = fork(); + if(pid < 0){ + printf(1, "init: fork failed\n"); + exit(); + } + if(pid == 0){ + exec("sh", argv); + printf(1, "init: exec sh failed\n"); + exit(); + } + while((wpid=wait()) >= 0 && wpid != pid) + printf(1, "zombie!\n"); + } +} diff --git a/user/initcode.S b/user/initcode.S new file mode 100644 index 0000000..ca76972 --- /dev/null +++ b/user/initcode.S @@ -0,0 +1,28 @@ +# Initial process execs /init. +# This code runs in user space. + +#include "syscall.h" + +# exec(init, argv) +.globl start +start: + la a0, init + la a1, argv + li a7, SYS_exec + ecall + +# for(;;) exit(); +exit: + li a7, SYS_exit + ecall + jal exit + +# char init[] = "/init\0"; +init: + .string "/init\0" + +# char *argv[] = { init, 0 }; +.p2align 2 +argv: + .long init + .long 0 diff --git a/user/kill.c b/user/kill.c new file mode 100644 index 0000000..4b19d3c --- /dev/null +++ b/user/kill.c @@ -0,0 +1,17 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + int i; + + if(argc < 2){ + printf(2, "usage: kill pid...\n"); + exit(); + } + for(i=1; i= path && *p != '/'; p--) + ; + p++; + + // Return blank-padded name. + if(strlen(p) >= DIRSIZ) + return p; + memmove(buf, p, strlen(p)); + memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); + return buf; +} + +void +ls(char *path) +{ + char buf[512], *p; + int fd; + struct dirent de; + struct stat st; + + if((fd = open(path, 0)) < 0){ + printf(2, "ls: cannot open %s\n", path); + return; + } + + if(fstat(fd, &st) < 0){ + printf(2, "ls: cannot stat %s\n", path); + close(fd); + return; + } + + switch(st.type){ + case T_FILE: + printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); + break; + + case T_DIR: + if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ + printf(1, "ls: path too long\n"); + break; + } + strcpy(buf, path); + p = buf+strlen(buf); + *p++ = '/'; + while(read(fd, &de, sizeof(de)) == sizeof(de)){ + if(de.inum == 0) + continue; + memmove(p, de.name, DIRSIZ); + p[DIRSIZ] = 0; + if(stat(buf, &st) < 0){ + printf(1, "ls: cannot stat %s\n", buf); + continue; + } + printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); + } + break; + } + close(fd); +} + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + ls("."); + exit(); + } + for(i=1; i + +static char digits[] = "0123456789ABCDEF"; + +static void +putc(int fd, char c) +{ + write(fd, &c, 1); +} + +static void +printint(int fd, int xx, int base, int sgn) +{ + char buf[16]; + 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) { + 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. +void +printf(int fd, const char *fmt, ...) +{ + va_list ap; + char *s; + int c, i, state; + + va_start(ap, fmt); + state = 0; + for(i = 0; fmt[i]; i++){ + c = fmt[i] & 0xff; + if(state == 0){ + if(c == '%'){ + state = '%'; + } else { + putc(fd, c); + } + } else if(state == '%'){ + if(c == 'd'){ + printint(fd, va_arg(ap, int), 10, 1); + } else if(c == 'x') { + printint(fd, va_arg(ap, int), 16, 0); + } else if(c == 'p') { + printptr(fd, va_arg(ap, uint64)); + } else if(c == 's'){ + s = va_arg(ap, char*); + if(s == 0) + s = "(null)"; + while(*s != 0){ + putc(fd, *s); + s++; + } + } else if(c == 'c'){ + putc(fd, va_arg(ap, uint)); + } else if(c == '%'){ + putc(fd, c); + } else { + // Unknown % sequence. Print it to draw attention. + putc(fd, '%'); + putc(fd, c); + } + state = 0; + } + } +} diff --git a/user/rm.c b/user/rm.c new file mode 100644 index 0000000..3076d83 --- /dev/null +++ b/user/rm.c @@ -0,0 +1,23 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + printf(2, "Usage: rm files...\n"); + exit(); + } + + for(i = 1; i < argc; i++){ + if(unlink(argv[i]) < 0){ + printf(2, "rm: %s failed to delete\n", argv[i]); + break; + } + } + + exit(); +} diff --git a/user/sh.c b/user/sh.c new file mode 100644 index 0000000..13c7883 --- /dev/null +++ b/user/sh.c @@ -0,0 +1,493 @@ +// Shell. + +#include "kernel/types.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +// Parsed command representation +#define EXEC 1 +#define REDIR 2 +#define PIPE 3 +#define LIST 4 +#define BACK 5 + +#define MAXARGS 10 + +struct cmd { + int type; +}; + +struct execcmd { + int type; + char *argv[MAXARGS]; + char *eargv[MAXARGS]; +}; + +struct redircmd { + int type; + struct cmd *cmd; + char *file; + char *efile; + int mode; + int fd; +}; + +struct pipecmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct listcmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct backcmd { + int type; + struct cmd *cmd; +}; + +int fork1(void); // Fork but panics on failure. +void panic(char*); +struct cmd *parsecmd(char*); + +// Execute cmd. Never returns. +void +runcmd(struct cmd *cmd) +{ + int p[2]; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + exit(); + + switch(cmd->type){ + default: + panic("runcmd"); + + case EXEC: + ecmd = (struct execcmd*)cmd; + if(ecmd->argv[0] == 0) + exit(); + exec(ecmd->argv[0], ecmd->argv); + printf(2, "exec %s failed\n", ecmd->argv[0]); + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + close(rcmd->fd); + if(open(rcmd->file, rcmd->mode) < 0){ + printf(2, "open %s failed\n", rcmd->file); + exit(); + } + runcmd(rcmd->cmd); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + if(fork1() == 0) + runcmd(lcmd->left); + wait(); + runcmd(lcmd->right); + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + if(pipe(p) < 0) + panic("pipe"); + if(fork1() == 0){ + close(1); + dup(p[1]); + close(p[0]); + close(p[1]); + runcmd(pcmd->left); + } + if(fork1() == 0){ + close(0); + dup(p[0]); + close(p[0]); + close(p[1]); + runcmd(pcmd->right); + } + close(p[0]); + close(p[1]); + wait(); + wait(); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + if(fork1() == 0) + runcmd(bcmd->cmd); + break; + } + exit(); +} + +int +getcmd(char *buf, int nbuf) +{ + printf(2, "$ "); + memset(buf, 0, nbuf); + gets(buf, nbuf); + if(buf[0] == 0) // EOF + return -1; + return 0; +} + +int +main(void) +{ + static char buf[100]; + int fd; + + // Ensure that three file descriptors are open. + while((fd = open("console", O_RDWR)) >= 0){ + if(fd >= 3){ + close(fd); + break; + } + } + + // Read and run input commands. + while(getcmd(buf, sizeof(buf)) >= 0){ + if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ + // Chdir must be called by the parent, not the child. + buf[strlen(buf)-1] = 0; // chop \n + if(chdir(buf+3) < 0) + printf(2, "cannot cd %s\n", buf+3); + continue; + } + if(fork1() == 0) + runcmd(parsecmd(buf)); + wait(); + } + exit(); +} + +void +panic(char *s) +{ + printf(2, "%s\n", s); + exit(); +} + +int +fork1(void) +{ + int pid; + + pid = fork(); + if(pid == -1) + panic("fork"); + return pid; +} + +//PAGEBREAK! +// Constructors + +struct cmd* +execcmd(void) +{ + struct execcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = EXEC; + return (struct cmd*)cmd; +} + +struct cmd* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ + struct redircmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = REDIR; + cmd->cmd = subcmd; + cmd->file = file; + cmd->efile = efile; + cmd->mode = mode; + cmd->fd = fd; + return (struct cmd*)cmd; +} + +struct cmd* +pipecmd(struct cmd *left, struct cmd *right) +{ + struct pipecmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = PIPE; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +listcmd(struct cmd *left, struct cmd *right) +{ + struct listcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = LIST; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +backcmd(struct cmd *subcmd) +{ + struct backcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = BACK; + cmd->cmd = subcmd; + return (struct cmd*)cmd; +} +//PAGEBREAK! +// Parsing + +char whitespace[] = " \t\r\n\v"; +char symbols[] = "<|>&;()"; + +int +gettoken(char **ps, char *es, char **q, char **eq) +{ + char *s; + int ret; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + if(q) + *q = s; + ret = *s; + switch(*s){ + case 0: + break; + case '|': + case '(': + case ')': + case ';': + case '&': + case '<': + s++; + break; + case '>': + s++; + if(*s == '>'){ + ret = '+'; + s++; + } + break; + default: + ret = 'a'; + while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) + s++; + break; + } + if(eq) + *eq = s; + + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return ret; +} + +int +peek(char **ps, char *es, char *toks) +{ + char *s; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return *s && strchr(toks, *s); +} + +struct cmd *parseline(char**, char*); +struct cmd *parsepipe(char**, char*); +struct cmd *parseexec(char**, char*); +struct cmd *nulterminate(struct cmd*); + +struct cmd* +parsecmd(char *s) +{ + char *es; + struct cmd *cmd; + + es = s + strlen(s); + cmd = parseline(&s, es); + peek(&s, es, ""); + if(s != es){ + printf(2, "leftovers: %s\n", s); + panic("syntax"); + } + nulterminate(cmd); + return cmd; +} + +struct cmd* +parseline(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parsepipe(ps, es); + while(peek(ps, es, "&")){ + gettoken(ps, es, 0, 0); + cmd = backcmd(cmd); + } + if(peek(ps, es, ";")){ + gettoken(ps, es, 0, 0); + cmd = listcmd(cmd, parseline(ps, es)); + } + return cmd; +} + +struct cmd* +parsepipe(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parseexec(ps, es); + if(peek(ps, es, "|")){ + gettoken(ps, es, 0, 0); + cmd = pipecmd(cmd, parsepipe(ps, es)); + } + return cmd; +} + +struct cmd* +parseredirs(struct cmd *cmd, char **ps, char *es) +{ + int tok; + char *q, *eq; + + while(peek(ps, es, "<>")){ + tok = gettoken(ps, es, 0, 0); + if(gettoken(ps, es, &q, &eq) != 'a') + panic("missing file for redirection"); + switch(tok){ + case '<': + cmd = redircmd(cmd, q, eq, O_RDONLY, 0); + break; + case '>': + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + case '+': // >> + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + } + } + return cmd; +} + +struct cmd* +parseblock(char **ps, char *es) +{ + struct cmd *cmd; + + if(!peek(ps, es, "(")) + panic("parseblock"); + gettoken(ps, es, 0, 0); + cmd = parseline(ps, es); + if(!peek(ps, es, ")")) + panic("syntax - missing )"); + gettoken(ps, es, 0, 0); + cmd = parseredirs(cmd, ps, es); + return cmd; +} + +struct cmd* +parseexec(char **ps, char *es) +{ + char *q, *eq; + int tok, argc; + struct execcmd *cmd; + struct cmd *ret; + + if(peek(ps, es, "(")) + return parseblock(ps, es); + + ret = execcmd(); + cmd = (struct execcmd*)ret; + + argc = 0; + ret = parseredirs(ret, ps, es); + while(!peek(ps, es, "|)&;")){ + if((tok=gettoken(ps, es, &q, &eq)) == 0) + break; + if(tok != 'a') + panic("syntax"); + cmd->argv[argc] = q; + cmd->eargv[argc] = eq; + argc++; + if(argc >= MAXARGS) + panic("too many args"); + ret = parseredirs(ret, ps, es); + } + cmd->argv[argc] = 0; + cmd->eargv[argc] = 0; + return ret; +} + +// NUL-terminate all the counted strings. +struct cmd* +nulterminate(struct cmd *cmd) +{ + int i; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + return 0; + + switch(cmd->type){ + case EXEC: + ecmd = (struct execcmd*)cmd; + for(i=0; ecmd->argv[i]; i++) + *ecmd->eargv[i] = 0; + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + nulterminate(rcmd->cmd); + *rcmd->efile = 0; + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + nulterminate(pcmd->left); + nulterminate(pcmd->right); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + nulterminate(lcmd->left); + nulterminate(lcmd->right); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + nulterminate(bcmd->cmd); + break; + } + return cmd; +} diff --git a/user/stressfs.c b/user/stressfs.c new file mode 100644 index 0000000..ef8f1cd --- /dev/null +++ b/user/stressfs.c @@ -0,0 +1,49 @@ +// Demonstrate that moving the "acquire" in iderw after the loop that +// appends to the idequeue results in a race. + +// For this to work, you should also add a spin within iderw's +// idequeue traversal loop. Adding the following demonstrated a panic +// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: +// for (i = 0; i < 40000; i++) +// asm volatile(""); + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" + +int +main(int argc, char *argv[]) +{ + int fd, i; + char path[] = "stressfs0"; + char data[512]; + + printf(1, "stressfs starting\n"); + memset(data, 'a', sizeof(data)); + + for(i = 0; i < 4; i++) + if(fork() > 0) + break; + + printf(1, "write %d\n", i); + + path[8] += i; + fd = open(path, O_CREATE | O_RDWR); + for(i = 0; i < 20; i++) +// printf(fd, "%d\n", i); + write(fd, data, sizeof(data)); + close(fd); + + printf(1, "read\n"); + + fd = open(path, O_RDONLY); + for (i = 0; i < 20; i++) + read(fd, data, sizeof(data)); + close(fd); + + wait(); + + exit(); +} diff --git a/user/ulib.c b/user/ulib.c new file mode 100644 index 0000000..ddda0f5 --- /dev/null +++ b/user/ulib.c @@ -0,0 +1,109 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +char* +strcpy(char *s, const char *t) +{ + char *os; + + os = s; + while((*s++ = *t++) != 0) + ; + return os; +} + +int +strcmp(const char *p, const char *q) +{ + while(*p && *p == *q) + p++, q++; + return (uchar)*p - (uchar)*q; +} + +uint +strlen(const char *s) +{ + int n; + + for(n = 0; s[n]; n++) + ; + return n; +} + +void* +memset(void *dst, int c, uint n) +{ + char *cdst = (char *) dst; + int i; + for(i = 0; i < n; i++){ + cdst[i] = c; + } + return dst; +} + +char* +strchr(const char *s, char c) +{ + for(; *s; s++) + if(*s == c) + return (char*)s; + return 0; +} + +char* +gets(char *buf, int max) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(0, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return buf; +} + +int +stat(const char *n, struct stat *st) +{ + int fd; + int r; + + fd = open(n, O_RDONLY); + if(fd < 0) + return -1; + r = fstat(fd, st); + close(fd); + return r; +} + +int +atoi(const char *s) +{ + int n; + + n = 0; + while('0' <= *s && *s <= '9') + n = n*10 + *s++ - '0'; + return n; +} + +void* +memmove(void *vdst, const void *vsrc, int n) +{ + char *dst; + const char *src; + + dst = vdst; + src = vsrc; + while(n-- > 0) + *dst++ = *src++; + return vdst; +} diff --git a/user/umalloc.c b/user/umalloc.c new file mode 100644 index 0000000..2092a32 --- /dev/null +++ b/user/umalloc.c @@ -0,0 +1,90 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/param.h" + +// Memory allocator by Kernighan and Ritchie, +// The C programming Language, 2nd ed. Section 8.7. + +typedef long Align; + +union header { + struct { + union header *ptr; + uint size; + } s; + Align x; +}; + +typedef union header Header; + +static Header base; +static Header *freep; + +void +free(void *ap) +{ + Header *bp, *p; + + bp = (Header*)ap - 1; + for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) + if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) + break; + if(bp + bp->s.size == p->s.ptr){ + bp->s.size += p->s.ptr->s.size; + bp->s.ptr = p->s.ptr->s.ptr; + } else + bp->s.ptr = p->s.ptr; + if(p + p->s.size == bp){ + p->s.size += bp->s.size; + p->s.ptr = bp->s.ptr; + } else + p->s.ptr = bp; + freep = p; +} + +static Header* +morecore(uint nu) +{ + char *p; + Header *hp; + + if(nu < 4096) + nu = 4096; + p = sbrk(nu * sizeof(Header)); + if(p == (char*)-1) + return 0; + hp = (Header*)p; + hp->s.size = nu; + free((void*)(hp + 1)); + return freep; +} + +void* +malloc(uint nbytes) +{ + Header *p, *prevp; + uint nunits; + + nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; + if((prevp = freep) == 0){ + base.s.ptr = freep = prevp = &base; + base.s.size = 0; + } + for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ + if(p->s.size >= nunits){ + if(p->s.size == nunits) + prevp->s.ptr = p->s.ptr; + else { + p->s.size -= nunits; + p += p->s.size; + p->s.size = nunits; + } + freep = prevp; + return (void*)(p + 1); + } + if(p == freep) + if((p = morecore(nunits)) == 0) + return 0; + } +} diff --git a/user/user.h b/user/user.h new file mode 100644 index 0000000..4f99c52 --- /dev/null +++ b/user/user.h @@ -0,0 +1,39 @@ +struct stat; +struct rtcdate; + +// system calls +int fork(void); +int exit(void) __attribute__((noreturn)); +int wait(void); +int pipe(int*); +int write(int, const void*, int); +int read(int, void*, int); +int close(int); +int kill(int); +int exec(char*, char**); +int open(const char*, int); +int mknod(const char*, short, short); +int unlink(const char*); +int fstat(int fd, struct stat*); +int link(const char*, const char*); +int mkdir(const char*); +int chdir(const char*); +int dup(int); +int getpid(void); +char* sbrk(int); +int sleep(int); +int uptime(void); + +// ulib.c +int stat(const char*, struct stat*); +char* strcpy(char*, const char*); +void *memmove(void*, const void*, int); +char* strchr(const char*, char c); +int strcmp(const char*, const char*); +void printf(int, const char*, ...); +char* gets(char*, int max); +uint strlen(const char*); +void* memset(void*, int, uint); +void* malloc(uint); +void free(void*); +int atoi(const char*); diff --git a/user/usertests.c b/user/usertests.c new file mode 100644 index 0000000..beca8f9 --- /dev/null +++ b/user/usertests.c @@ -0,0 +1,1789 @@ +#include "kernel/param.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" +#include "kernel/syscall.h" +#include "kernel/memlayout.h" + +char buf[8192]; +char name[3]; +char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 }; +int stdout = 1; + +// does chdir() call iput(p->cwd) in a transaction? +void +iputtest(void) +{ + printf(stdout, "iput test\n"); + + if(mkdir("iputdir") < 0){ + printf(stdout, "mkdir failed\n"); + exit(); + } + if(chdir("iputdir") < 0){ + printf(stdout, "chdir iputdir failed\n"); + exit(); + } + if(unlink("../iputdir") < 0){ + printf(stdout, "unlink ../iputdir failed\n"); + exit(); + } + if(chdir("/") < 0){ + printf(stdout, "chdir / failed\n"); + exit(); + } + printf(stdout, "iput test ok\n"); +} + +// does exit() call iput(p->cwd) in a transaction? +void +exitiputtest(void) +{ + int pid; + + printf(stdout, "exitiput test\n"); + + pid = fork(); + if(pid < 0){ + printf(stdout, "fork failed\n"); + exit(); + } + if(pid == 0){ + if(mkdir("iputdir") < 0){ + printf(stdout, "mkdir failed\n"); + exit(); + } + if(chdir("iputdir") < 0){ + printf(stdout, "child chdir failed\n"); + exit(); + } + if(unlink("../iputdir") < 0){ + printf(stdout, "unlink ../iputdir failed\n"); + exit(); + } + exit(); + } + wait(); + printf(stdout, "exitiput test ok\n"); +} + +// does the error path in open() for attempt to write a +// directory call iput() in a transaction? +// needs a hacked kernel that pauses just after the namei() +// call in sys_open(): +// if((ip = namei(path)) == 0) +// return -1; +// { +// int i; +// for(i = 0; i < 10000; i++) +// yield(); +// } +void +openiputtest(void) +{ + int pid; + + printf(stdout, "openiput test\n"); + if(mkdir("oidir") < 0){ + printf(stdout, "mkdir oidir failed\n"); + exit(); + } + pid = fork(); + if(pid < 0){ + printf(stdout, "fork failed\n"); + exit(); + } + if(pid == 0){ + int fd = open("oidir", O_RDWR); + if(fd >= 0){ + printf(stdout, "open directory for write succeeded\n"); + exit(); + } + exit(); + } + sleep(1); + if(unlink("oidir") != 0){ + printf(stdout, "unlink failed\n"); + exit(); + } + wait(); + printf(stdout, "openiput test ok\n"); +} + +// simple file system tests + +void +opentest(void) +{ + int fd; + + printf(stdout, "open test\n"); + fd = open("echo", 0); + if(fd < 0){ + printf(stdout, "open echo failed!\n"); + exit(); + } + close(fd); + fd = open("doesnotexist", 0); + if(fd >= 0){ + printf(stdout, "open doesnotexist succeeded!\n"); + exit(); + } + printf(stdout, "open test ok\n"); +} + +void +writetest(void) +{ + int fd; + int i; + + printf(stdout, "small file test\n"); + fd = open("small", O_CREATE|O_RDWR); + if(fd >= 0){ + printf(stdout, "creat small succeeded; ok\n"); + } else { + printf(stdout, "error: creat small failed!\n"); + exit(); + } + for(i = 0; i < 100; i++){ + if(write(fd, "aaaaaaaaaa", 10) != 10){ + printf(stdout, "error: write aa %d new file failed\n", i); + exit(); + } + if(write(fd, "bbbbbbbbbb", 10) != 10){ + printf(stdout, "error: write bb %d new file failed\n", i); + exit(); + } + } + printf(stdout, "writes ok\n"); + close(fd); + fd = open("small", O_RDONLY); + if(fd >= 0){ + printf(stdout, "open small succeeded ok\n"); + } else { + printf(stdout, "error: open small failed!\n"); + exit(); + } + i = read(fd, buf, 2000); + if(i == 2000){ + printf(stdout, "read succeeded ok\n"); + } else { + printf(stdout, "read failed\n"); + exit(); + } + close(fd); + + if(unlink("small") < 0){ + printf(stdout, "unlink small failed\n"); + exit(); + } + printf(stdout, "small file test ok\n"); +} + +void +writetest1(void) +{ + int i, fd, n; + + printf(stdout, "big files test\n"); + + fd = open("big", O_CREATE|O_RDWR); + if(fd < 0){ + printf(stdout, "error: creat big failed!\n"); + exit(); + } + + for(i = 0; i < MAXFILE; i++){ + ((int*)buf)[0] = i; + if(write(fd, buf, 512) != 512){ + printf(stdout, "error: write big file failed\n", i); + exit(); + } + } + + close(fd); + + fd = open("big", O_RDONLY); + if(fd < 0){ + printf(stdout, "error: open big failed!\n"); + exit(); + } + + n = 0; + for(;;){ + i = read(fd, buf, 512); + if(i == 0){ + if(n == MAXFILE - 1){ + printf(stdout, "read only %d blocks from big", n); + exit(); + } + break; + } else if(i != 512){ + printf(stdout, "read failed %d\n", i); + exit(); + } + if(((int*)buf)[0] != n){ + printf(stdout, "read content of block %d is %d\n", + n, ((int*)buf)[0]); + exit(); + } + n++; + } + close(fd); + if(unlink("big") < 0){ + printf(stdout, "unlink big failed\n"); + exit(); + } + printf(stdout, "big files ok\n"); +} + +void +createtest(void) +{ + int i, fd; + + printf(stdout, "many creates, followed by unlink test\n"); + + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE|O_RDWR); + close(fd); + } + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + unlink(name); + } + printf(stdout, "many creates, followed by unlink; ok\n"); +} + +void dirtest(void) +{ + printf(stdout, "mkdir test\n"); + + if(mkdir("dir0") < 0){ + printf(stdout, "mkdir failed\n"); + exit(); + } + + if(chdir("dir0") < 0){ + printf(stdout, "chdir dir0 failed\n"); + exit(); + } + + if(chdir("..") < 0){ + printf(stdout, "chdir .. failed\n"); + exit(); + } + + if(unlink("dir0") < 0){ + printf(stdout, "unlink dir0 failed\n"); + exit(); + } + printf(stdout, "mkdir test ok\n"); +} + +void +exectest(void) +{ + printf(stdout, "exec test\n"); + if(exec("echo", echoargv) < 0){ + printf(stdout, "exec echo failed\n"); + exit(); + } +} + +// simple fork and pipe read/write + +void +pipe1(void) +{ + int fds[2], pid; + int seq, i, n, cc, total; + + if(pipe(fds) != 0){ + printf(1, "pipe() failed\n"); + exit(); + } + pid = fork(); + seq = 0; + if(pid == 0){ + close(fds[0]); + for(n = 0; n < 5; n++){ + for(i = 0; i < 1033; i++) + buf[i] = seq++; + if(write(fds[1], buf, 1033) != 1033){ + printf(1, "pipe1 oops 1\n"); + exit(); + } + } + exit(); + } else if(pid > 0){ + close(fds[1]); + total = 0; + cc = 1; + while((n = read(fds[0], buf, cc)) > 0){ + for(i = 0; i < n; i++){ + if((buf[i] & 0xff) != (seq++ & 0xff)){ + printf(1, "pipe1 oops 2\n"); + return; + } + } + total += n; + cc = cc * 2; + if(cc > sizeof(buf)) + cc = sizeof(buf); + } + if(total != 5 * 1033){ + printf(1, "pipe1 oops 3 total %d\n", total); + exit(); + } + close(fds[0]); + wait(); + } else { + printf(1, "fork() failed\n"); + exit(); + } + printf(1, "pipe1 ok\n"); +} + +// meant to be run w/ at most two CPUs +void +preempt(void) +{ + int pid1, pid2, pid3; + int pfds[2]; + + printf(1, "preempt: "); + pid1 = fork(); + if(pid1 < 0) { + printf(1, "fork failed"); + exit(); + } + if(pid1 == 0) + for(;;) + ; + + pid2 = fork(); + if(pid2 < 0) { + printf(1, "fork failed\n"); + exit(); + } + if(pid2 == 0) + for(;;) + ; + + pipe(pfds); + pid3 = fork(); + if(pid3 < 0) { + printf(1, "fork failed\n"); + exit(); + } + if(pid3 == 0){ + close(pfds[0]); + if(write(pfds[1], "x", 1) != 1) + printf(1, "preempt write error"); + close(pfds[1]); + for(;;) + ; + } + + close(pfds[1]); + if(read(pfds[0], buf, sizeof(buf)) != 1){ + printf(1, "preempt read error"); + return; + } + close(pfds[0]); + printf(1, "kill... "); + kill(pid1); + kill(pid2); + kill(pid3); + printf(1, "wait... "); + wait(); + wait(); + wait(); + printf(1, "preempt ok\n"); +} + +// try to find any races between exit and wait +void +exitwait(void) +{ + int i, pid; + + for(i = 0; i < 100; i++){ + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + return; + } + if(pid){ + if(wait() != pid){ + printf(1, "wait wrong pid\n"); + return; + } + } else { + exit(); + } + } + printf(1, "exitwait ok\n"); +} + +void +mem(void) +{ + void *m1, *m2; + int pid, ppid; + + printf(1, "mem test\n"); + ppid = getpid(); + if((pid = fork()) == 0){ + m1 = 0; + while((m2 = malloc(10001)) != 0){ + *(char**)m2 = m1; + m1 = m2; + } + while(m1){ + m2 = *(char**)m1; + free(m1); + m1 = m2; + } + m1 = malloc(1024*20); + if(m1 == 0){ + printf(1, "couldn't allocate mem?!!\n"); + kill(ppid); + exit(); + } + free(m1); + printf(1, "mem ok\n"); + exit(); + } else { + wait(); + } +} + +// More file system tests + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void +sharedfd(void) +{ + int fd, pid, i, n, nc, np; + char buf[10]; + + printf(1, "sharedfd test\n"); + + unlink("sharedfd"); + fd = open("sharedfd", O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for writing"); + return; + } + pid = fork(); + memset(buf, pid==0?'c':'p', sizeof(buf)); + for(i = 0; i < 1000; i++){ + if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ + printf(1, "fstests: write sharedfd failed\n"); + break; + } + } + if(pid == 0) + exit(); + else + wait(); + close(fd); + fd = open("sharedfd", 0); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for reading\n"); + return; + } + nc = np = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i = 0; i < sizeof(buf); i++){ + if(buf[i] == 'c') + nc++; + if(buf[i] == 'p') + np++; + } + } + close(fd); + unlink("sharedfd"); + if(nc == 10000 && np == 10000){ + printf(1, "sharedfd ok\n"); + } else { + printf(1, "sharedfd oops %d %d\n", nc, np); + exit(); + } +} + +// four processes write different files at the same +// time, to test block allocation. +void +fourfiles(void) +{ + int fd, pid, i, j, n, total, pi; + char *names[] = { "f0", "f1", "f2", "f3" }; + char *fname; + + printf(1, "fourfiles test\n"); + + for(pi = 0; pi < 4; pi++){ + fname = names[pi]; + unlink(fname); + + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + if(pid == 0){ + fd = open(fname, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create failed\n"); + exit(); + } + + memset(buf, '0'+pi, 512); + for(i = 0; i < 12; i++){ + if((n = write(fd, buf, 500)) != 500){ + printf(1, "write failed %d\n", n); + exit(); + } + } + exit(); + } + } + + for(pi = 0; pi < 4; pi++){ + wait(); + } + + for(i = 0; i < 2; i++){ + fname = names[i]; + fd = open(fname, 0); + total = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(j = 0; j < n; j++){ + if(buf[j] != '0'+i){ + printf(1, "wrong char\n"); + exit(); + } + } + total += n; + } + close(fd); + if(total != 12*500){ + printf(1, "wrong length %d\n", total); + exit(); + } + unlink(fname); + } + + printf(1, "fourfiles ok\n"); +} + +// four processes create and delete different files in same directory +void +createdelete(void) +{ + enum { N = 20, NCHILD=4 }; + int pid, i, fd, pi; + char name[32]; + + printf(1, "createdelete test\n"); + + for(pi = 0; pi < NCHILD; pi++){ + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + if(pid == 0){ + name[0] = 'p' + pi; + name[2] = '\0'; + for(i = 0; i < N; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create failed\n"); + exit(); + } + close(fd); + if(i > 0 && (i % 2 ) == 0){ + name[1] = '0' + (i / 2); + if(unlink(name) < 0){ + printf(1, "unlink failed\n"); + exit(); + } + } + } + exit(); + } + } + + for(pi = 0; pi < NCHILD; pi++){ + wait(); + } + + name[0] = name[1] = name[2] = 0; + for(i = 0; i < N; i++){ + for(pi = 0; pi < NCHILD; pi++){ + name[0] = 'p' + pi; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= N/2) && fd < 0){ + printf(1, "oops createdelete %s didn't exist\n", name); + exit(); + } else if((i >= 1 && i < N/2) && fd >= 0){ + printf(1, "oops createdelete %s did exist\n", name); + exit(); + } + if(fd >= 0) + close(fd); + } + } + + for(i = 0; i < N; i++){ + for(pi = 0; pi < NCHILD; pi++){ + name[0] = 'p' + i; + name[1] = '0' + i; + unlink(name); + } + } + + printf(1, "createdelete ok\n"); +} + +// can I unlink a file and still read it? +void +unlinkread(void) +{ + int fd, fd1; + + printf(1, "unlinkread test\n"); + fd = open("unlinkread", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create unlinkread failed\n"); + exit(); + } + write(fd, "hello", 5); + close(fd); + + fd = open("unlinkread", O_RDWR); + if(fd < 0){ + printf(1, "open unlinkread failed\n"); + exit(); + } + if(unlink("unlinkread") != 0){ + printf(1, "unlink unlinkread failed\n"); + exit(); + } + + fd1 = open("unlinkread", O_CREATE | O_RDWR); + write(fd1, "yyy", 3); + close(fd1); + + if(read(fd, buf, sizeof(buf)) != 5){ + printf(1, "unlinkread read failed"); + exit(); + } + if(buf[0] != 'h'){ + printf(1, "unlinkread wrong data\n"); + exit(); + } + if(write(fd, buf, 10) != 10){ + printf(1, "unlinkread write failed\n"); + exit(); + } + close(fd); + unlink("unlinkread"); + printf(1, "unlinkread ok\n"); +} + +void +linktest(void) +{ + int fd; + + printf(1, "linktest\n"); + + unlink("lf1"); + unlink("lf2"); + + fd = open("lf1", O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "create lf1 failed\n"); + exit(); + } + if(write(fd, "hello", 5) != 5){ + printf(1, "write lf1 failed\n"); + exit(); + } + close(fd); + + if(link("lf1", "lf2") < 0){ + printf(1, "link lf1 lf2 failed\n"); + exit(); + } + unlink("lf1"); + + if(open("lf1", 0) >= 0){ + printf(1, "unlinked lf1 but it is still there!\n"); + exit(); + } + + fd = open("lf2", 0); + if(fd < 0){ + printf(1, "open lf2 failed\n"); + exit(); + } + if(read(fd, buf, sizeof(buf)) != 5){ + printf(1, "read lf2 failed\n"); + exit(); + } + close(fd); + + if(link("lf2", "lf2") >= 0){ + printf(1, "link lf2 lf2 succeeded! oops\n"); + exit(); + } + + unlink("lf2"); + if(link("lf2", "lf1") >= 0){ + printf(1, "link non-existant succeeded! oops\n"); + exit(); + } + + if(link(".", "lf1") >= 0){ + printf(1, "link . lf1 succeeded! oops\n"); + exit(); + } + + printf(1, "linktest ok\n"); +} + +// test concurrent create/link/unlink of the same file +void +concreate(void) +{ + char file[3]; + int i, pid, n, fd; + char fa[40]; + struct { + ushort inum; + char name[14]; + } de; + + printf(1, "concreate test\n"); + file[0] = 'C'; + file[2] = '\0'; + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + unlink(file); + pid = fork(); + if(pid && (i % 3) == 1){ + link("C0", file); + } else if(pid == 0 && (i % 5) == 1){ + link("C0", file); + } else { + fd = open(file, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "concreate create %s failed\n", file); + exit(); + } + close(fd); + } + if(pid == 0) + exit(); + else + wait(); + } + + memset(fa, 0, sizeof(fa)); + fd = open(".", 0); + n = 0; + while(read(fd, &de, sizeof(de)) > 0){ + if(de.inum == 0) + continue; + if(de.name[0] == 'C' && de.name[2] == '\0'){ + i = de.name[1] - '0'; + if(i < 0 || i >= sizeof(fa)){ + printf(1, "concreate weird file %s\n", de.name); + exit(); + } + if(fa[i]){ + printf(1, "concreate duplicate file %s\n", de.name); + exit(); + } + fa[i] = 1; + n++; + } + } + close(fd); + + if(n != 40){ + printf(1, "concreate not enough files in directory listing\n"); + exit(); + } + + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + if(((i % 3) == 0 && pid == 0) || + ((i % 3) == 1 && pid != 0)){ + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + } else { + unlink(file); + unlink(file); + unlink(file); + unlink(file); + } + if(pid == 0) + exit(); + else + wait(); + } + + printf(1, "concreate ok\n"); +} + +// another concurrent link/unlink/create test, +// to look for deadlocks. +void +linkunlink() +{ + int pid, i; + + printf(1, "linkunlink test\n"); + + unlink("x"); + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + unsigned int x = (pid ? 1 : 97); + for(i = 0; i < 100; i++){ + x = x * 1103515245 + 12345; + if((x % 3) == 0){ + close(open("x", O_RDWR | O_CREATE)); + } else if((x % 3) == 1){ + link("cat", "x"); + } else { + unlink("x"); + } + } + + if(pid) + wait(); + else + exit(); + + printf(1, "linkunlink ok\n"); +} + +// directory that uses indirect blocks +void +bigdir(void) +{ + int i, fd; + char name[10]; + + printf(1, "bigdir test\n"); + unlink("bd"); + + fd = open("bd", O_CREATE); + if(fd < 0){ + printf(1, "bigdir create failed\n"); + exit(); + } + close(fd); + + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(link("bd", name) != 0){ + printf(1, "bigdir link failed\n"); + exit(); + } + } + + unlink("bd"); + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(unlink(name) != 0){ + printf(1, "bigdir unlink failed"); + exit(); + } + } + + printf(1, "bigdir ok\n"); +} + +void +subdir(void) +{ + int fd, cc; + + printf(1, "subdir test\n"); + + unlink("ff"); + if(mkdir("dd") != 0){ + printf(1, "subdir mkdir dd failed\n"); + exit(); + } + + fd = open("dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create dd/ff failed\n"); + exit(); + } + write(fd, "ff", 2); + close(fd); + + if(unlink("dd") >= 0){ + printf(1, "unlink dd (non-empty dir) succeeded!\n"); + exit(); + } + + if(mkdir("/dd/dd") != 0){ + printf(1, "subdir mkdir dd/dd failed\n"); + exit(); + } + + fd = open("dd/dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create dd/dd/ff failed\n"); + exit(); + } + write(fd, "FF", 2); + close(fd); + + fd = open("dd/dd/../ff", 0); + if(fd < 0){ + printf(1, "open dd/dd/../ff failed\n"); + exit(); + } + cc = read(fd, buf, sizeof(buf)); + if(cc != 2 || buf[0] != 'f'){ + printf(1, "dd/dd/../ff wrong content\n"); + exit(); + } + close(fd); + + if(link("dd/dd/ff", "dd/dd/ffff") != 0){ + printf(1, "link dd/dd/ff dd/dd/ffff failed\n"); + exit(); + } + + if(unlink("dd/dd/ff") != 0){ + printf(1, "unlink dd/dd/ff failed\n"); + exit(); + } + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf(1, "open (unlinked) dd/dd/ff succeeded\n"); + exit(); + } + + if(chdir("dd") != 0){ + printf(1, "chdir dd failed\n"); + exit(); + } + if(chdir("dd/../../dd") != 0){ + printf(1, "chdir dd/../../dd failed\n"); + exit(); + } + if(chdir("dd/../../../dd") != 0){ + printf(1, "chdir dd/../../dd failed\n"); + exit(); + } + if(chdir("./..") != 0){ + printf(1, "chdir ./.. failed\n"); + exit(); + } + + fd = open("dd/dd/ffff", 0); + if(fd < 0){ + printf(1, "open dd/dd/ffff failed\n"); + exit(); + } + if(read(fd, buf, sizeof(buf)) != 2){ + printf(1, "read dd/dd/ffff wrong len\n"); + exit(); + } + close(fd); + + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf(1, "open (unlinked) dd/dd/ff succeeded!\n"); + exit(); + } + + if(open("dd/ff/ff", O_CREATE|O_RDWR) >= 0){ + printf(1, "create dd/ff/ff succeeded!\n"); + exit(); + } + if(open("dd/xx/ff", O_CREATE|O_RDWR) >= 0){ + printf(1, "create dd/xx/ff succeeded!\n"); + exit(); + } + if(open("dd", O_CREATE) >= 0){ + printf(1, "create dd succeeded!\n"); + exit(); + } + if(open("dd", O_RDWR) >= 0){ + printf(1, "open dd rdwr succeeded!\n"); + exit(); + } + if(open("dd", O_WRONLY) >= 0){ + printf(1, "open dd wronly succeeded!\n"); + exit(); + } + if(link("dd/ff/ff", "dd/dd/xx") == 0){ + printf(1, "link dd/ff/ff dd/dd/xx succeeded!\n"); + exit(); + } + if(link("dd/xx/ff", "dd/dd/xx") == 0){ + printf(1, "link dd/xx/ff dd/dd/xx succeeded!\n"); + exit(); + } + if(link("dd/ff", "dd/dd/ffff") == 0){ + printf(1, "link dd/ff dd/dd/ffff succeeded!\n"); + exit(); + } + if(mkdir("dd/ff/ff") == 0){ + printf(1, "mkdir dd/ff/ff succeeded!\n"); + exit(); + } + if(mkdir("dd/xx/ff") == 0){ + printf(1, "mkdir dd/xx/ff succeeded!\n"); + exit(); + } + if(mkdir("dd/dd/ffff") == 0){ + printf(1, "mkdir dd/dd/ffff succeeded!\n"); + exit(); + } + if(unlink("dd/xx/ff") == 0){ + printf(1, "unlink dd/xx/ff succeeded!\n"); + exit(); + } + if(unlink("dd/ff/ff") == 0){ + printf(1, "unlink dd/ff/ff succeeded!\n"); + exit(); + } + if(chdir("dd/ff") == 0){ + printf(1, "chdir dd/ff succeeded!\n"); + exit(); + } + if(chdir("dd/xx") == 0){ + printf(1, "chdir dd/xx succeeded!\n"); + exit(); + } + + if(unlink("dd/dd/ffff") != 0){ + printf(1, "unlink dd/dd/ff failed\n"); + exit(); + } + if(unlink("dd/ff") != 0){ + printf(1, "unlink dd/ff failed\n"); + exit(); + } + if(unlink("dd") == 0){ + printf(1, "unlink non-empty dd succeeded!\n"); + exit(); + } + if(unlink("dd/dd") < 0){ + printf(1, "unlink dd/dd failed\n"); + exit(); + } + if(unlink("dd") < 0){ + printf(1, "unlink dd failed\n"); + exit(); + } + + printf(1, "subdir ok\n"); +} + +// test writes that are larger than the log. +void +bigwrite(void) +{ + int fd, sz; + + printf(1, "bigwrite test\n"); + + unlink("bigwrite"); + for(sz = 499; sz < 12*512; sz += 471){ + fd = open("bigwrite", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "cannot create bigwrite\n"); + exit(); + } + int i; + for(i = 0; i < 2; i++){ + int cc = write(fd, buf, sz); + if(cc != sz){ + printf(1, "write(%d) ret %d\n", sz, cc); + exit(); + } + } + close(fd); + unlink("bigwrite"); + } + + printf(1, "bigwrite ok\n"); +} + +void +bigfile(void) +{ + int fd, i, total, cc; + + printf(1, "bigfile test\n"); + + unlink("bigfile"); + fd = open("bigfile", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "cannot create bigfile"); + exit(); + } + for(i = 0; i < 20; i++){ + memset(buf, i, 600); + if(write(fd, buf, 600) != 600){ + printf(1, "write bigfile failed\n"); + exit(); + } + } + close(fd); + + fd = open("bigfile", 0); + if(fd < 0){ + printf(1, "cannot open bigfile\n"); + exit(); + } + total = 0; + for(i = 0; ; i++){ + cc = read(fd, buf, 300); + if(cc < 0){ + printf(1, "read bigfile failed\n"); + exit(); + } + if(cc == 0) + break; + if(cc != 300){ + printf(1, "short read bigfile\n"); + exit(); + } + if(buf[0] != i/2 || buf[299] != i/2){ + printf(1, "read bigfile wrong data\n"); + exit(); + } + total += cc; + } + close(fd); + if(total != 20*600){ + printf(1, "read bigfile wrong total\n"); + exit(); + } + unlink("bigfile"); + + printf(1, "bigfile test ok\n"); +} + +void +fourteen(void) +{ + int fd; + + // DIRSIZ is 14. + printf(1, "fourteen test\n"); + + if(mkdir("12345678901234") != 0){ + printf(1, "mkdir 12345678901234 failed\n"); + exit(); + } + if(mkdir("12345678901234/123456789012345") != 0){ + printf(1, "mkdir 12345678901234/123456789012345 failed\n"); + exit(); + } + fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); + if(fd < 0){ + printf(1, "create 123456789012345/123456789012345/123456789012345 failed\n"); + exit(); + } + close(fd); + fd = open("12345678901234/12345678901234/12345678901234", 0); + if(fd < 0){ + printf(1, "open 12345678901234/12345678901234/12345678901234 failed\n"); + exit(); + } + close(fd); + + if(mkdir("12345678901234/12345678901234") == 0){ + printf(1, "mkdir 12345678901234/12345678901234 succeeded!\n"); + exit(); + } + if(mkdir("123456789012345/12345678901234") == 0){ + printf(1, "mkdir 12345678901234/123456789012345 succeeded!\n"); + exit(); + } + + printf(1, "fourteen ok\n"); +} + +void +rmdot(void) +{ + printf(1, "rmdot test\n"); + if(mkdir("dots") != 0){ + printf(1, "mkdir dots failed\n"); + exit(); + } + if(chdir("dots") != 0){ + printf(1, "chdir dots failed\n"); + exit(); + } + if(unlink(".") == 0){ + printf(1, "rm . worked!\n"); + exit(); + } + if(unlink("..") == 0){ + printf(1, "rm .. worked!\n"); + exit(); + } + if(chdir("/") != 0){ + printf(1, "chdir / failed\n"); + exit(); + } + if(unlink("dots/.") == 0){ + printf(1, "unlink dots/. worked!\n"); + exit(); + } + if(unlink("dots/..") == 0){ + printf(1, "unlink dots/.. worked!\n"); + exit(); + } + if(unlink("dots") != 0){ + printf(1, "unlink dots failed!\n"); + exit(); + } + printf(1, "rmdot ok\n"); +} + +void +dirfile(void) +{ + int fd; + + printf(1, "dir vs file\n"); + + fd = open("dirfile", O_CREATE); + if(fd < 0){ + printf(1, "create dirfile failed\n"); + exit(); + } + close(fd); + if(chdir("dirfile") == 0){ + printf(1, "chdir dirfile succeeded!\n"); + exit(); + } + fd = open("dirfile/xx", 0); + if(fd >= 0){ + printf(1, "create dirfile/xx succeeded!\n"); + exit(); + } + fd = open("dirfile/xx", O_CREATE); + if(fd >= 0){ + printf(1, "create dirfile/xx succeeded!\n"); + exit(); + } + if(mkdir("dirfile/xx") == 0){ + printf(1, "mkdir dirfile/xx succeeded!\n"); + exit(); + } + if(unlink("dirfile/xx") == 0){ + printf(1, "unlink dirfile/xx succeeded!\n"); + exit(); + } + if(link("README", "dirfile/xx") == 0){ + printf(1, "link to dirfile/xx succeeded!\n"); + exit(); + } + if(unlink("dirfile") != 0){ + printf(1, "unlink dirfile failed!\n"); + exit(); + } + + fd = open(".", O_RDWR); + if(fd >= 0){ + printf(1, "open . for writing succeeded!\n"); + exit(); + } + fd = open(".", 0); + if(write(fd, "x", 1) > 0){ + printf(1, "write . succeeded!\n"); + exit(); + } + close(fd); + + printf(1, "dir vs file OK\n"); +} + +// test that iput() is called at the end of _namei() +void +iref(void) +{ + int i, fd; + + printf(1, "empty file name\n"); + + // the 50 is NINODE + for(i = 0; i < 50 + 1; i++){ + if(mkdir("irefd") != 0){ + printf(1, "mkdir irefd failed\n"); + exit(); + } + if(chdir("irefd") != 0){ + printf(1, "chdir irefd failed\n"); + exit(); + } + + mkdir(""); + link("README", ""); + fd = open("", O_CREATE); + if(fd >= 0) + close(fd); + fd = open("xx", O_CREATE); + if(fd >= 0) + close(fd); + unlink("xx"); + } + + chdir("/"); + printf(1, "empty file name OK\n"); +} + +// test that fork fails gracefully +// the forktest binary also does this, but it runs out of proc entries first. +// inside the bigger usertests binary, we run out of memory first. +void +forktest(void) +{ + int n, pid; + + printf(1, "fork test\n"); + + for(n=0; n<1000; n++){ + pid = fork(); + if(pid < 0) + break; + if(pid == 0) + exit(); + } + + if (n == 0) { + printf(1, "no fork at all!\n"); + exit(); + } + + if(n == 1000){ + printf(1, "fork claimed to work 1000 times!\n"); + exit(); + } + + for(; n > 0; n--){ + if(wait() < 0){ + printf(1, "wait stopped early\n"); + exit(); + } + } + + if(wait() != -1){ + printf(1, "wait got too many\n"); + exit(); + } + + printf(1, "fork test OK\n"); +} + +void +sbrktest(void) +{ + int i, fds[2], pids[10], pid, ppid; + char *c, *oldbrk, scratch, *a, *b, *lastaddr, *p; + uint64 amt; + #define BIG (100*1024*1024) + + printf(stdout, "sbrk test\n"); + oldbrk = sbrk(0); + + // can one sbrk() less than a page? + a = sbrk(0); + for(i = 0; i < 5000; i++){ + b = sbrk(1); + if(b != a){ + printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); + exit(); + } + *b = 1; + a = b + 1; + } + pid = fork(); + if(pid < 0){ + printf(stdout, "sbrk test fork failed\n"); + exit(); + } + c = sbrk(1); + c = sbrk(1); + if(c != a + 1){ + printf(stdout, "sbrk test failed post-fork\n"); + exit(); + } + if(pid == 0) + exit(); + wait(); + + // can one grow address space to something big? + a = sbrk(0); + amt = (BIG) - (uint64)a; + p = sbrk(amt); + if (p != a) { + printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n"); + exit(); + } + lastaddr = (char*) (BIG-1); + *lastaddr = 99; + + // can one de-allocate? + a = sbrk(0); + c = sbrk(-4096); + if(c == (char*)0xffffffff){ + printf(stdout, "sbrk could not deallocate\n"); + exit(); + } + c = sbrk(0); + if(c != a - 4096){ + printf(stdout, "sbrk deallocation produced wrong address, a %x c %x\n", a, c); + exit(); + } + + // can one re-allocate that page? + a = sbrk(0); + c = sbrk(4096); + if(c != a || sbrk(0) != a + 4096){ + printf(stdout, "sbrk re-allocation failed, a %x c %x\n", a, c); + exit(); + } + if(*lastaddr == 99){ + // should be zero + printf(stdout, "sbrk de-allocation didn't really deallocate\n"); + exit(); + } + + a = sbrk(0); + c = sbrk(-(sbrk(0) - oldbrk)); + if(c != a){ + printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c); + exit(); + } + + // can we read the kernel's memory? + for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){ + ppid = getpid(); + pid = fork(); + if(pid < 0){ + printf(stdout, "fork failed\n"); + exit(); + } + if(pid == 0){ + printf(stdout, "oops could read %x = %x\n", a, *a); + kill(ppid); + exit(); + } + wait(); + } + + // if we run the system out of memory, does it clean up the last + // failed allocation? + if(pipe(fds) != 0){ + printf(1, "pipe() failed\n"); + exit(); + } + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if((pids[i] = fork()) == 0){ + // allocate a lot of memory + sbrk(BIG - (uint64)sbrk(0)); + write(fds[1], "x", 1); + // sit around until killed + for(;;) sleep(1000); + } + if(pids[i] != -1) + read(fds[0], &scratch, 1); + } + + // if those failed allocations freed up the pages they did allocate, + // we'll be able to allocate here + c = sbrk(4096); + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if(pids[i] == -1) + continue; + kill(pids[i]); + wait(); + } + if(c == (char*)0xffffffff){ + printf(stdout, "failed sbrk leaked memory\n"); + exit(); + } + + if(sbrk(0) > oldbrk) + sbrk(-(sbrk(0) - oldbrk)); + + printf(stdout, "sbrk test OK\n"); +} + +void +validateint(int *p) +{ + /* XXX int res; + asm("mov %%esp, %%ebx\n\t" + "mov %3, %%esp\n\t" + "int %2\n\t" + "mov %%ebx, %%esp" : + "=a" (res) : + "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) : + "ebx"); + */ +} + +void +validatetest(void) +{ + int hi, pid; + uint64 p; + + printf(stdout, "validate test\n"); + hi = 1100*1024; + + for(p = 0; p <= (uint)hi; p += 4096){ + if((pid = fork()) == 0){ + // try to crash the kernel by passing in a badly placed integer + validateint((int*)p); + exit(); + } + sleep(0); + sleep(0); + kill(pid); + wait(); + + // try to crash the kernel by passing in a bad string pointer + if(link("nosuchfile", (char*)p) != -1){ + printf(stdout, "link should not succeed\n"); + exit(); + } + } + + printf(stdout, "validate ok\n"); +} + +// does unintialized data start out zero? +char uninit[10000]; +void +bsstest(void) +{ + int i; + + printf(stdout, "bss test\n"); + for(i = 0; i < sizeof(uninit); i++){ + if(uninit[i] != '\0'){ + printf(stdout, "bss test failed\n"); + exit(); + } + } + printf(stdout, "bss test ok\n"); +} + +// does exec return an error if the arguments +// are larger than a page? or does it write +// below the stack and wreck the instructions/data? +void +bigargtest(void) +{ + int pid, fd; + + unlink("bigarg-ok"); + pid = fork(); + if(pid == 0){ + static char *args[MAXARG]; + int i; + for(i = 0; i < MAXARG-1; i++) + args[i] = "bigargs test: failed\n "; + args[MAXARG-1] = 0; + printf(stdout, "bigarg test\n"); + exec("echo", args); + printf(stdout, "bigarg test ok\n"); + fd = open("bigarg-ok", O_CREATE); + close(fd); + exit(); + } else if(pid < 0){ + printf(stdout, "bigargtest: fork failed\n"); + exit(); + } + wait(); + fd = open("bigarg-ok", 0); + if(fd < 0){ + printf(stdout, "bigarg test failed!\n"); + exit(); + } + close(fd); + unlink("bigarg-ok"); +} + +// what happens when the file system runs out of blocks? +// answer: balloc panics, so this test is not useful. +void +fsfull() +{ + int nfiles; + int fsblocks = 0; + + printf(1, "fsfull test\n"); + + for(nfiles = 0; ; nfiles++){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + printf(1, "writing %s\n", name); + int fd = open(name, O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "open %s failed\n", name); + break; + } + int total = 0; + while(1){ + int cc = write(fd, buf, 512); + if(cc < 512) + break; + total += cc; + fsblocks++; + } + printf(1, "wrote %d bytes\n", total); + close(fd); + if(total == 0) + break; + } + + while(nfiles >= 0){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + unlink(name); + nfiles--; + } + + printf(1, "fsfull test finished\n"); +} + +void argptest() +{ + int fd; + fd = open("init", O_RDONLY); + if (fd < 0) { + printf(2, "open failed\n"); + exit(); + } + read(fd, sbrk(0) - 1, -1); + close(fd); + printf(1, "arg test passed\n"); +} + +unsigned long randstate = 1; +unsigned int +rand() +{ + randstate = randstate * 1664525 + 1013904223; + return randstate; +} + +int +main(int argc, char *argv[]) +{ + printf(1, "usertests starting\n"); + + if(open("usertests.ran", 0) >= 0){ + printf(1, "already ran user tests -- rebuild fs.img\n"); + exit(); + } + close(open("usertests.ran", O_CREATE)); + + argptest(); + createdelete(); + linkunlink(); + concreate(); + fourfiles(); + sharedfd(); + + bigargtest(); + bigwrite(); + bigargtest(); + bsstest(); + sbrktest(); + validatetest(); + + opentest(); + writetest(); + writetest1(); + createtest(); + + openiputtest(); + exitiputtest(); + iputtest(); + + mem(); + pipe1(); + preempt(); + exitwait(); + + rmdot(); + fourteen(); + bigfile(); + subdir(); + linktest(); + unlinkread(); + dirfile(); + iref(); + forktest(); + bigdir(); // slow + + exectest(); + + exit(); +} diff --git a/user/usys.pl b/user/usys.pl new file mode 100755 index 0000000..01e426e --- /dev/null +++ b/user/usys.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w + +# Generate usys.S, the stubs for syscalls. + +print "# generated by usys.pl - do not edit\n"; + +print "#include \"kernel/syscall.h\"\n"; + +sub entry { + my $name = shift; + print ".global $name\n"; + print "${name}:\n"; + print " li a7, SYS_${name}\n"; + print " ecall\n"; + print " ret\n"; +} + +entry("fork"); +entry("exit"); +entry("wait"); +entry("pipe"); +entry("read"); +entry("write"); +entry("close"); +entry("kill"); +entry("exec"); +entry("open"); +entry("mknod"); +entry("unlink"); +entry("fstat"); +entry("link"); +entry("mkdir"); +entry("chdir"); +entry("dup"); +entry("getpid"); +entry("sbrk"); +entry("sleep"); +entry("uptime"); diff --git a/user/wc.c b/user/wc.c new file mode 100644 index 0000000..e2543a6 --- /dev/null +++ b/user/wc.c @@ -0,0 +1,54 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[512]; + +void +wc(int fd, char *name) +{ + int i, n; + int l, w, c, inword; + + l = w = c = 0; + inword = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i=0; i 0) + sleep(5); // Let child exit before parent. + exit(); +} diff --git a/usertests.c b/usertests.c deleted file mode 100644 index 292319e..0000000 --- a/usertests.c +++ /dev/null @@ -1,1789 +0,0 @@ -#include "param.h" -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fs.h" -#include "fcntl.h" -#include "syscall.h" -#include "memlayout.h" - -char buf[8192]; -char name[3]; -char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 }; -int stdout = 1; - -// does chdir() call iput(p->cwd) in a transaction? -void -iputtest(void) -{ - printf(stdout, "iput test\n"); - - if(mkdir("iputdir") < 0){ - printf(stdout, "mkdir failed\n"); - exit(); - } - if(chdir("iputdir") < 0){ - printf(stdout, "chdir iputdir failed\n"); - exit(); - } - if(unlink("../iputdir") < 0){ - printf(stdout, "unlink ../iputdir failed\n"); - exit(); - } - if(chdir("/") < 0){ - printf(stdout, "chdir / failed\n"); - exit(); - } - printf(stdout, "iput test ok\n"); -} - -// does exit() call iput(p->cwd) in a transaction? -void -exitiputtest(void) -{ - int pid; - - printf(stdout, "exitiput test\n"); - - pid = fork(); - if(pid < 0){ - printf(stdout, "fork failed\n"); - exit(); - } - if(pid == 0){ - if(mkdir("iputdir") < 0){ - printf(stdout, "mkdir failed\n"); - exit(); - } - if(chdir("iputdir") < 0){ - printf(stdout, "child chdir failed\n"); - exit(); - } - if(unlink("../iputdir") < 0){ - printf(stdout, "unlink ../iputdir failed\n"); - exit(); - } - exit(); - } - wait(); - printf(stdout, "exitiput test ok\n"); -} - -// does the error path in open() for attempt to write a -// directory call iput() in a transaction? -// needs a hacked kernel that pauses just after the namei() -// call in sys_open(): -// if((ip = namei(path)) == 0) -// return -1; -// { -// int i; -// for(i = 0; i < 10000; i++) -// yield(); -// } -void -openiputtest(void) -{ - int pid; - - printf(stdout, "openiput test\n"); - if(mkdir("oidir") < 0){ - printf(stdout, "mkdir oidir failed\n"); - exit(); - } - pid = fork(); - if(pid < 0){ - printf(stdout, "fork failed\n"); - exit(); - } - if(pid == 0){ - int fd = open("oidir", O_RDWR); - if(fd >= 0){ - printf(stdout, "open directory for write succeeded\n"); - exit(); - } - exit(); - } - sleep(1); - if(unlink("oidir") != 0){ - printf(stdout, "unlink failed\n"); - exit(); - } - wait(); - printf(stdout, "openiput test ok\n"); -} - -// simple file system tests - -void -opentest(void) -{ - int fd; - - printf(stdout, "open test\n"); - fd = open("echo", 0); - if(fd < 0){ - printf(stdout, "open echo failed!\n"); - exit(); - } - close(fd); - fd = open("doesnotexist", 0); - if(fd >= 0){ - printf(stdout, "open doesnotexist succeeded!\n"); - exit(); - } - printf(stdout, "open test ok\n"); -} - -void -writetest(void) -{ - int fd; - int i; - - printf(stdout, "small file test\n"); - fd = open("small", O_CREATE|O_RDWR); - if(fd >= 0){ - printf(stdout, "creat small succeeded; ok\n"); - } else { - printf(stdout, "error: creat small failed!\n"); - exit(); - } - for(i = 0; i < 100; i++){ - if(write(fd, "aaaaaaaaaa", 10) != 10){ - printf(stdout, "error: write aa %d new file failed\n", i); - exit(); - } - if(write(fd, "bbbbbbbbbb", 10) != 10){ - printf(stdout, "error: write bb %d new file failed\n", i); - exit(); - } - } - printf(stdout, "writes ok\n"); - close(fd); - fd = open("small", O_RDONLY); - if(fd >= 0){ - printf(stdout, "open small succeeded ok\n"); - } else { - printf(stdout, "error: open small failed!\n"); - exit(); - } - i = read(fd, buf, 2000); - if(i == 2000){ - printf(stdout, "read succeeded ok\n"); - } else { - printf(stdout, "read failed\n"); - exit(); - } - close(fd); - - if(unlink("small") < 0){ - printf(stdout, "unlink small failed\n"); - exit(); - } - printf(stdout, "small file test ok\n"); -} - -void -writetest1(void) -{ - int i, fd, n; - - printf(stdout, "big files test\n"); - - fd = open("big", O_CREATE|O_RDWR); - if(fd < 0){ - printf(stdout, "error: creat big failed!\n"); - exit(); - } - - for(i = 0; i < MAXFILE; i++){ - ((int*)buf)[0] = i; - if(write(fd, buf, 512) != 512){ - printf(stdout, "error: write big file failed\n", i); - exit(); - } - } - - close(fd); - - fd = open("big", O_RDONLY); - if(fd < 0){ - printf(stdout, "error: open big failed!\n"); - exit(); - } - - n = 0; - for(;;){ - i = read(fd, buf, 512); - if(i == 0){ - if(n == MAXFILE - 1){ - printf(stdout, "read only %d blocks from big", n); - exit(); - } - break; - } else if(i != 512){ - printf(stdout, "read failed %d\n", i); - exit(); - } - if(((int*)buf)[0] != n){ - printf(stdout, "read content of block %d is %d\n", - n, ((int*)buf)[0]); - exit(); - } - n++; - } - close(fd); - if(unlink("big") < 0){ - printf(stdout, "unlink big failed\n"); - exit(); - } - printf(stdout, "big files ok\n"); -} - -void -createtest(void) -{ - int i, fd; - - printf(stdout, "many creates, followed by unlink test\n"); - - name[0] = 'a'; - name[2] = '\0'; - for(i = 0; i < 52; i++){ - name[1] = '0' + i; - fd = open(name, O_CREATE|O_RDWR); - close(fd); - } - name[0] = 'a'; - name[2] = '\0'; - for(i = 0; i < 52; i++){ - name[1] = '0' + i; - unlink(name); - } - printf(stdout, "many creates, followed by unlink; ok\n"); -} - -void dirtest(void) -{ - printf(stdout, "mkdir test\n"); - - if(mkdir("dir0") < 0){ - printf(stdout, "mkdir failed\n"); - exit(); - } - - if(chdir("dir0") < 0){ - printf(stdout, "chdir dir0 failed\n"); - exit(); - } - - if(chdir("..") < 0){ - printf(stdout, "chdir .. failed\n"); - exit(); - } - - if(unlink("dir0") < 0){ - printf(stdout, "unlink dir0 failed\n"); - exit(); - } - printf(stdout, "mkdir test ok\n"); -} - -void -exectest(void) -{ - printf(stdout, "exec test\n"); - if(exec("echo", echoargv) < 0){ - printf(stdout, "exec echo failed\n"); - exit(); - } -} - -// simple fork and pipe read/write - -void -pipe1(void) -{ - int fds[2], pid; - int seq, i, n, cc, total; - - if(pipe(fds) != 0){ - printf(1, "pipe() failed\n"); - exit(); - } - pid = fork(); - seq = 0; - if(pid == 0){ - close(fds[0]); - for(n = 0; n < 5; n++){ - for(i = 0; i < 1033; i++) - buf[i] = seq++; - if(write(fds[1], buf, 1033) != 1033){ - printf(1, "pipe1 oops 1\n"); - exit(); - } - } - exit(); - } else if(pid > 0){ - close(fds[1]); - total = 0; - cc = 1; - while((n = read(fds[0], buf, cc)) > 0){ - for(i = 0; i < n; i++){ - if((buf[i] & 0xff) != (seq++ & 0xff)){ - printf(1, "pipe1 oops 2\n"); - return; - } - } - total += n; - cc = cc * 2; - if(cc > sizeof(buf)) - cc = sizeof(buf); - } - if(total != 5 * 1033){ - printf(1, "pipe1 oops 3 total %d\n", total); - exit(); - } - close(fds[0]); - wait(); - } else { - printf(1, "fork() failed\n"); - exit(); - } - printf(1, "pipe1 ok\n"); -} - -// meant to be run w/ at most two CPUs -void -preempt(void) -{ - int pid1, pid2, pid3; - int pfds[2]; - - printf(1, "preempt: "); - pid1 = fork(); - if(pid1 < 0) { - printf(1, "fork failed"); - exit(); - } - if(pid1 == 0) - for(;;) - ; - - pid2 = fork(); - if(pid2 < 0) { - printf(1, "fork failed\n"); - exit(); - } - if(pid2 == 0) - for(;;) - ; - - pipe(pfds); - pid3 = fork(); - if(pid3 < 0) { - printf(1, "fork failed\n"); - exit(); - } - if(pid3 == 0){ - close(pfds[0]); - if(write(pfds[1], "x", 1) != 1) - printf(1, "preempt write error"); - close(pfds[1]); - for(;;) - ; - } - - close(pfds[1]); - if(read(pfds[0], buf, sizeof(buf)) != 1){ - printf(1, "preempt read error"); - return; - } - close(pfds[0]); - printf(1, "kill... "); - kill(pid1); - kill(pid2); - kill(pid3); - printf(1, "wait... "); - wait(); - wait(); - wait(); - printf(1, "preempt ok\n"); -} - -// try to find any races between exit and wait -void -exitwait(void) -{ - int i, pid; - - for(i = 0; i < 100; i++){ - pid = fork(); - if(pid < 0){ - printf(1, "fork failed\n"); - return; - } - if(pid){ - if(wait() != pid){ - printf(1, "wait wrong pid\n"); - return; - } - } else { - exit(); - } - } - printf(1, "exitwait ok\n"); -} - -void -mem(void) -{ - void *m1, *m2; - int pid, ppid; - - printf(1, "mem test\n"); - ppid = getpid(); - if((pid = fork()) == 0){ - m1 = 0; - while((m2 = malloc(10001)) != 0){ - *(char**)m2 = m1; - m1 = m2; - } - while(m1){ - m2 = *(char**)m1; - free(m1); - m1 = m2; - } - m1 = malloc(1024*20); - if(m1 == 0){ - printf(1, "couldn't allocate mem?!!\n"); - kill(ppid); - exit(); - } - free(m1); - printf(1, "mem ok\n"); - exit(); - } else { - wait(); - } -} - -// More file system tests - -// two processes write to the same file descriptor -// is the offset shared? does inode locking work? -void -sharedfd(void) -{ - int fd, pid, i, n, nc, np; - char buf[10]; - - printf(1, "sharedfd test\n"); - - unlink("sharedfd"); - fd = open("sharedfd", O_CREATE|O_RDWR); - if(fd < 0){ - printf(1, "fstests: cannot open sharedfd for writing"); - return; - } - pid = fork(); - memset(buf, pid==0?'c':'p', sizeof(buf)); - for(i = 0; i < 1000; i++){ - if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ - printf(1, "fstests: write sharedfd failed\n"); - break; - } - } - if(pid == 0) - exit(); - else - wait(); - close(fd); - fd = open("sharedfd", 0); - if(fd < 0){ - printf(1, "fstests: cannot open sharedfd for reading\n"); - return; - } - nc = np = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(i = 0; i < sizeof(buf); i++){ - if(buf[i] == 'c') - nc++; - if(buf[i] == 'p') - np++; - } - } - close(fd); - unlink("sharedfd"); - if(nc == 10000 && np == 10000){ - printf(1, "sharedfd ok\n"); - } else { - printf(1, "sharedfd oops %d %d\n", nc, np); - exit(); - } -} - -// four processes write different files at the same -// time, to test block allocation. -void -fourfiles(void) -{ - int fd, pid, i, j, n, total, pi; - char *names[] = { "f0", "f1", "f2", "f3" }; - char *fname; - - printf(1, "fourfiles test\n"); - - for(pi = 0; pi < 4; pi++){ - fname = names[pi]; - unlink(fname); - - pid = fork(); - if(pid < 0){ - printf(1, "fork failed\n"); - exit(); - } - - if(pid == 0){ - fd = open(fname, O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "create failed\n"); - exit(); - } - - memset(buf, '0'+pi, 512); - for(i = 0; i < 12; i++){ - if((n = write(fd, buf, 500)) != 500){ - printf(1, "write failed %d\n", n); - exit(); - } - } - exit(); - } - } - - for(pi = 0; pi < 4; pi++){ - wait(); - } - - for(i = 0; i < 2; i++){ - fname = names[i]; - fd = open(fname, 0); - total = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(j = 0; j < n; j++){ - if(buf[j] != '0'+i){ - printf(1, "wrong char\n"); - exit(); - } - } - total += n; - } - close(fd); - if(total != 12*500){ - printf(1, "wrong length %d\n", total); - exit(); - } - unlink(fname); - } - - printf(1, "fourfiles ok\n"); -} - -// four processes create and delete different files in same directory -void -createdelete(void) -{ - enum { N = 20, NCHILD=4 }; - int pid, i, fd, pi; - char name[32]; - - printf(1, "createdelete test\n"); - - for(pi = 0; pi < NCHILD; pi++){ - pid = fork(); - if(pid < 0){ - printf(1, "fork failed\n"); - exit(); - } - - if(pid == 0){ - name[0] = 'p' + pi; - name[2] = '\0'; - for(i = 0; i < N; i++){ - name[1] = '0' + i; - fd = open(name, O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "create failed\n"); - exit(); - } - close(fd); - if(i > 0 && (i % 2 ) == 0){ - name[1] = '0' + (i / 2); - if(unlink(name) < 0){ - printf(1, "unlink failed\n"); - exit(); - } - } - } - exit(); - } - } - - for(pi = 0; pi < NCHILD; pi++){ - wait(); - } - - name[0] = name[1] = name[2] = 0; - for(i = 0; i < N; i++){ - for(pi = 0; pi < NCHILD; pi++){ - name[0] = 'p' + pi; - name[1] = '0' + i; - fd = open(name, 0); - if((i == 0 || i >= N/2) && fd < 0){ - printf(1, "oops createdelete %s didn't exist\n", name); - exit(); - } else if((i >= 1 && i < N/2) && fd >= 0){ - printf(1, "oops createdelete %s did exist\n", name); - exit(); - } - if(fd >= 0) - close(fd); - } - } - - for(i = 0; i < N; i++){ - for(pi = 0; pi < NCHILD; pi++){ - name[0] = 'p' + i; - name[1] = '0' + i; - unlink(name); - } - } - - printf(1, "createdelete ok\n"); -} - -// can I unlink a file and still read it? -void -unlinkread(void) -{ - int fd, fd1; - - printf(1, "unlinkread test\n"); - fd = open("unlinkread", O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "create unlinkread failed\n"); - exit(); - } - write(fd, "hello", 5); - close(fd); - - fd = open("unlinkread", O_RDWR); - if(fd < 0){ - printf(1, "open unlinkread failed\n"); - exit(); - } - if(unlink("unlinkread") != 0){ - printf(1, "unlink unlinkread failed\n"); - exit(); - } - - fd1 = open("unlinkread", O_CREATE | O_RDWR); - write(fd1, "yyy", 3); - close(fd1); - - if(read(fd, buf, sizeof(buf)) != 5){ - printf(1, "unlinkread read failed"); - exit(); - } - if(buf[0] != 'h'){ - printf(1, "unlinkread wrong data\n"); - exit(); - } - if(write(fd, buf, 10) != 10){ - printf(1, "unlinkread write failed\n"); - exit(); - } - close(fd); - unlink("unlinkread"); - printf(1, "unlinkread ok\n"); -} - -void -linktest(void) -{ - int fd; - - printf(1, "linktest\n"); - - unlink("lf1"); - unlink("lf2"); - - fd = open("lf1", O_CREATE|O_RDWR); - if(fd < 0){ - printf(1, "create lf1 failed\n"); - exit(); - } - if(write(fd, "hello", 5) != 5){ - printf(1, "write lf1 failed\n"); - exit(); - } - close(fd); - - if(link("lf1", "lf2") < 0){ - printf(1, "link lf1 lf2 failed\n"); - exit(); - } - unlink("lf1"); - - if(open("lf1", 0) >= 0){ - printf(1, "unlinked lf1 but it is still there!\n"); - exit(); - } - - fd = open("lf2", 0); - if(fd < 0){ - printf(1, "open lf2 failed\n"); - exit(); - } - if(read(fd, buf, sizeof(buf)) != 5){ - printf(1, "read lf2 failed\n"); - exit(); - } - close(fd); - - if(link("lf2", "lf2") >= 0){ - printf(1, "link lf2 lf2 succeeded! oops\n"); - exit(); - } - - unlink("lf2"); - if(link("lf2", "lf1") >= 0){ - printf(1, "link non-existant succeeded! oops\n"); - exit(); - } - - if(link(".", "lf1") >= 0){ - printf(1, "link . lf1 succeeded! oops\n"); - exit(); - } - - printf(1, "linktest ok\n"); -} - -// test concurrent create/link/unlink of the same file -void -concreate(void) -{ - char file[3]; - int i, pid, n, fd; - char fa[40]; - struct { - ushort inum; - char name[14]; - } de; - - printf(1, "concreate test\n"); - file[0] = 'C'; - file[2] = '\0'; - for(i = 0; i < 40; i++){ - file[1] = '0' + i; - unlink(file); - pid = fork(); - if(pid && (i % 3) == 1){ - link("C0", file); - } else if(pid == 0 && (i % 5) == 1){ - link("C0", file); - } else { - fd = open(file, O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "concreate create %s failed\n", file); - exit(); - } - close(fd); - } - if(pid == 0) - exit(); - else - wait(); - } - - memset(fa, 0, sizeof(fa)); - fd = open(".", 0); - n = 0; - while(read(fd, &de, sizeof(de)) > 0){ - if(de.inum == 0) - continue; - if(de.name[0] == 'C' && de.name[2] == '\0'){ - i = de.name[1] - '0'; - if(i < 0 || i >= sizeof(fa)){ - printf(1, "concreate weird file %s\n", de.name); - exit(); - } - if(fa[i]){ - printf(1, "concreate duplicate file %s\n", de.name); - exit(); - } - fa[i] = 1; - n++; - } - } - close(fd); - - if(n != 40){ - printf(1, "concreate not enough files in directory listing\n"); - exit(); - } - - for(i = 0; i < 40; i++){ - file[1] = '0' + i; - pid = fork(); - if(pid < 0){ - printf(1, "fork failed\n"); - exit(); - } - if(((i % 3) == 0 && pid == 0) || - ((i % 3) == 1 && pid != 0)){ - close(open(file, 0)); - close(open(file, 0)); - close(open(file, 0)); - close(open(file, 0)); - } else { - unlink(file); - unlink(file); - unlink(file); - unlink(file); - } - if(pid == 0) - exit(); - else - wait(); - } - - printf(1, "concreate ok\n"); -} - -// another concurrent link/unlink/create test, -// to look for deadlocks. -void -linkunlink() -{ - int pid, i; - - printf(1, "linkunlink test\n"); - - unlink("x"); - pid = fork(); - if(pid < 0){ - printf(1, "fork failed\n"); - exit(); - } - - unsigned int x = (pid ? 1 : 97); - for(i = 0; i < 100; i++){ - x = x * 1103515245 + 12345; - if((x % 3) == 0){ - close(open("x", O_RDWR | O_CREATE)); - } else if((x % 3) == 1){ - link("cat", "x"); - } else { - unlink("x"); - } - } - - if(pid) - wait(); - else - exit(); - - printf(1, "linkunlink ok\n"); -} - -// directory that uses indirect blocks -void -bigdir(void) -{ - int i, fd; - char name[10]; - - printf(1, "bigdir test\n"); - unlink("bd"); - - fd = open("bd", O_CREATE); - if(fd < 0){ - printf(1, "bigdir create failed\n"); - exit(); - } - close(fd); - - for(i = 0; i < 500; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(link("bd", name) != 0){ - printf(1, "bigdir link failed\n"); - exit(); - } - } - - unlink("bd"); - for(i = 0; i < 500; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(unlink(name) != 0){ - printf(1, "bigdir unlink failed"); - exit(); - } - } - - printf(1, "bigdir ok\n"); -} - -void -subdir(void) -{ - int fd, cc; - - printf(1, "subdir test\n"); - - unlink("ff"); - if(mkdir("dd") != 0){ - printf(1, "subdir mkdir dd failed\n"); - exit(); - } - - fd = open("dd/ff", O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "create dd/ff failed\n"); - exit(); - } - write(fd, "ff", 2); - close(fd); - - if(unlink("dd") >= 0){ - printf(1, "unlink dd (non-empty dir) succeeded!\n"); - exit(); - } - - if(mkdir("/dd/dd") != 0){ - printf(1, "subdir mkdir dd/dd failed\n"); - exit(); - } - - fd = open("dd/dd/ff", O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "create dd/dd/ff failed\n"); - exit(); - } - write(fd, "FF", 2); - close(fd); - - fd = open("dd/dd/../ff", 0); - if(fd < 0){ - printf(1, "open dd/dd/../ff failed\n"); - exit(); - } - cc = read(fd, buf, sizeof(buf)); - if(cc != 2 || buf[0] != 'f'){ - printf(1, "dd/dd/../ff wrong content\n"); - exit(); - } - close(fd); - - if(link("dd/dd/ff", "dd/dd/ffff") != 0){ - printf(1, "link dd/dd/ff dd/dd/ffff failed\n"); - exit(); - } - - if(unlink("dd/dd/ff") != 0){ - printf(1, "unlink dd/dd/ff failed\n"); - exit(); - } - if(open("dd/dd/ff", O_RDONLY) >= 0){ - printf(1, "open (unlinked) dd/dd/ff succeeded\n"); - exit(); - } - - if(chdir("dd") != 0){ - printf(1, "chdir dd failed\n"); - exit(); - } - if(chdir("dd/../../dd") != 0){ - printf(1, "chdir dd/../../dd failed\n"); - exit(); - } - if(chdir("dd/../../../dd") != 0){ - printf(1, "chdir dd/../../dd failed\n"); - exit(); - } - if(chdir("./..") != 0){ - printf(1, "chdir ./.. failed\n"); - exit(); - } - - fd = open("dd/dd/ffff", 0); - if(fd < 0){ - printf(1, "open dd/dd/ffff failed\n"); - exit(); - } - if(read(fd, buf, sizeof(buf)) != 2){ - printf(1, "read dd/dd/ffff wrong len\n"); - exit(); - } - close(fd); - - if(open("dd/dd/ff", O_RDONLY) >= 0){ - printf(1, "open (unlinked) dd/dd/ff succeeded!\n"); - exit(); - } - - if(open("dd/ff/ff", O_CREATE|O_RDWR) >= 0){ - printf(1, "create dd/ff/ff succeeded!\n"); - exit(); - } - if(open("dd/xx/ff", O_CREATE|O_RDWR) >= 0){ - printf(1, "create dd/xx/ff succeeded!\n"); - exit(); - } - if(open("dd", O_CREATE) >= 0){ - printf(1, "create dd succeeded!\n"); - exit(); - } - if(open("dd", O_RDWR) >= 0){ - printf(1, "open dd rdwr succeeded!\n"); - exit(); - } - if(open("dd", O_WRONLY) >= 0){ - printf(1, "open dd wronly succeeded!\n"); - exit(); - } - if(link("dd/ff/ff", "dd/dd/xx") == 0){ - printf(1, "link dd/ff/ff dd/dd/xx succeeded!\n"); - exit(); - } - if(link("dd/xx/ff", "dd/dd/xx") == 0){ - printf(1, "link dd/xx/ff dd/dd/xx succeeded!\n"); - exit(); - } - if(link("dd/ff", "dd/dd/ffff") == 0){ - printf(1, "link dd/ff dd/dd/ffff succeeded!\n"); - exit(); - } - if(mkdir("dd/ff/ff") == 0){ - printf(1, "mkdir dd/ff/ff succeeded!\n"); - exit(); - } - if(mkdir("dd/xx/ff") == 0){ - printf(1, "mkdir dd/xx/ff succeeded!\n"); - exit(); - } - if(mkdir("dd/dd/ffff") == 0){ - printf(1, "mkdir dd/dd/ffff succeeded!\n"); - exit(); - } - if(unlink("dd/xx/ff") == 0){ - printf(1, "unlink dd/xx/ff succeeded!\n"); - exit(); - } - if(unlink("dd/ff/ff") == 0){ - printf(1, "unlink dd/ff/ff succeeded!\n"); - exit(); - } - if(chdir("dd/ff") == 0){ - printf(1, "chdir dd/ff succeeded!\n"); - exit(); - } - if(chdir("dd/xx") == 0){ - printf(1, "chdir dd/xx succeeded!\n"); - exit(); - } - - if(unlink("dd/dd/ffff") != 0){ - printf(1, "unlink dd/dd/ff failed\n"); - exit(); - } - if(unlink("dd/ff") != 0){ - printf(1, "unlink dd/ff failed\n"); - exit(); - } - if(unlink("dd") == 0){ - printf(1, "unlink non-empty dd succeeded!\n"); - exit(); - } - if(unlink("dd/dd") < 0){ - printf(1, "unlink dd/dd failed\n"); - exit(); - } - if(unlink("dd") < 0){ - printf(1, "unlink dd failed\n"); - exit(); - } - - printf(1, "subdir ok\n"); -} - -// test writes that are larger than the log. -void -bigwrite(void) -{ - int fd, sz; - - printf(1, "bigwrite test\n"); - - unlink("bigwrite"); - for(sz = 499; sz < 12*512; sz += 471){ - fd = open("bigwrite", O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "cannot create bigwrite\n"); - exit(); - } - int i; - for(i = 0; i < 2; i++){ - int cc = write(fd, buf, sz); - if(cc != sz){ - printf(1, "write(%d) ret %d\n", sz, cc); - exit(); - } - } - close(fd); - unlink("bigwrite"); - } - - printf(1, "bigwrite ok\n"); -} - -void -bigfile(void) -{ - int fd, i, total, cc; - - printf(1, "bigfile test\n"); - - unlink("bigfile"); - fd = open("bigfile", O_CREATE | O_RDWR); - if(fd < 0){ - printf(1, "cannot create bigfile"); - exit(); - } - for(i = 0; i < 20; i++){ - memset(buf, i, 600); - if(write(fd, buf, 600) != 600){ - printf(1, "write bigfile failed\n"); - exit(); - } - } - close(fd); - - fd = open("bigfile", 0); - if(fd < 0){ - printf(1, "cannot open bigfile\n"); - exit(); - } - total = 0; - for(i = 0; ; i++){ - cc = read(fd, buf, 300); - if(cc < 0){ - printf(1, "read bigfile failed\n"); - exit(); - } - if(cc == 0) - break; - if(cc != 300){ - printf(1, "short read bigfile\n"); - exit(); - } - if(buf[0] != i/2 || buf[299] != i/2){ - printf(1, "read bigfile wrong data\n"); - exit(); - } - total += cc; - } - close(fd); - if(total != 20*600){ - printf(1, "read bigfile wrong total\n"); - exit(); - } - unlink("bigfile"); - - printf(1, "bigfile test ok\n"); -} - -void -fourteen(void) -{ - int fd; - - // DIRSIZ is 14. - printf(1, "fourteen test\n"); - - if(mkdir("12345678901234") != 0){ - printf(1, "mkdir 12345678901234 failed\n"); - exit(); - } - if(mkdir("12345678901234/123456789012345") != 0){ - printf(1, "mkdir 12345678901234/123456789012345 failed\n"); - exit(); - } - fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); - if(fd < 0){ - printf(1, "create 123456789012345/123456789012345/123456789012345 failed\n"); - exit(); - } - close(fd); - fd = open("12345678901234/12345678901234/12345678901234", 0); - if(fd < 0){ - printf(1, "open 12345678901234/12345678901234/12345678901234 failed\n"); - exit(); - } - close(fd); - - if(mkdir("12345678901234/12345678901234") == 0){ - printf(1, "mkdir 12345678901234/12345678901234 succeeded!\n"); - exit(); - } - if(mkdir("123456789012345/12345678901234") == 0){ - printf(1, "mkdir 12345678901234/123456789012345 succeeded!\n"); - exit(); - } - - printf(1, "fourteen ok\n"); -} - -void -rmdot(void) -{ - printf(1, "rmdot test\n"); - if(mkdir("dots") != 0){ - printf(1, "mkdir dots failed\n"); - exit(); - } - if(chdir("dots") != 0){ - printf(1, "chdir dots failed\n"); - exit(); - } - if(unlink(".") == 0){ - printf(1, "rm . worked!\n"); - exit(); - } - if(unlink("..") == 0){ - printf(1, "rm .. worked!\n"); - exit(); - } - if(chdir("/") != 0){ - printf(1, "chdir / failed\n"); - exit(); - } - if(unlink("dots/.") == 0){ - printf(1, "unlink dots/. worked!\n"); - exit(); - } - if(unlink("dots/..") == 0){ - printf(1, "unlink dots/.. worked!\n"); - exit(); - } - if(unlink("dots") != 0){ - printf(1, "unlink dots failed!\n"); - exit(); - } - printf(1, "rmdot ok\n"); -} - -void -dirfile(void) -{ - int fd; - - printf(1, "dir vs file\n"); - - fd = open("dirfile", O_CREATE); - if(fd < 0){ - printf(1, "create dirfile failed\n"); - exit(); - } - close(fd); - if(chdir("dirfile") == 0){ - printf(1, "chdir dirfile succeeded!\n"); - exit(); - } - fd = open("dirfile/xx", 0); - if(fd >= 0){ - printf(1, "create dirfile/xx succeeded!\n"); - exit(); - } - fd = open("dirfile/xx", O_CREATE); - if(fd >= 0){ - printf(1, "create dirfile/xx succeeded!\n"); - exit(); - } - if(mkdir("dirfile/xx") == 0){ - printf(1, "mkdir dirfile/xx succeeded!\n"); - exit(); - } - if(unlink("dirfile/xx") == 0){ - printf(1, "unlink dirfile/xx succeeded!\n"); - exit(); - } - if(link("README", "dirfile/xx") == 0){ - printf(1, "link to dirfile/xx succeeded!\n"); - exit(); - } - if(unlink("dirfile") != 0){ - printf(1, "unlink dirfile failed!\n"); - exit(); - } - - fd = open(".", O_RDWR); - if(fd >= 0){ - printf(1, "open . for writing succeeded!\n"); - exit(); - } - fd = open(".", 0); - if(write(fd, "x", 1) > 0){ - printf(1, "write . succeeded!\n"); - exit(); - } - close(fd); - - printf(1, "dir vs file OK\n"); -} - -// test that iput() is called at the end of _namei() -void -iref(void) -{ - int i, fd; - - printf(1, "empty file name\n"); - - // the 50 is NINODE - for(i = 0; i < 50 + 1; i++){ - if(mkdir("irefd") != 0){ - printf(1, "mkdir irefd failed\n"); - exit(); - } - if(chdir("irefd") != 0){ - printf(1, "chdir irefd failed\n"); - exit(); - } - - mkdir(""); - link("README", ""); - fd = open("", O_CREATE); - if(fd >= 0) - close(fd); - fd = open("xx", O_CREATE); - if(fd >= 0) - close(fd); - unlink("xx"); - } - - chdir("/"); - printf(1, "empty file name OK\n"); -} - -// test that fork fails gracefully -// the forktest binary also does this, but it runs out of proc entries first. -// inside the bigger usertests binary, we run out of memory first. -void -forktest(void) -{ - int n, pid; - - printf(1, "fork test\n"); - - for(n=0; n<1000; n++){ - pid = fork(); - if(pid < 0) - break; - if(pid == 0) - exit(); - } - - if (n == 0) { - printf(1, "no fork at all!\n"); - exit(); - } - - if(n == 1000){ - printf(1, "fork claimed to work 1000 times!\n"); - exit(); - } - - for(; n > 0; n--){ - if(wait() < 0){ - printf(1, "wait stopped early\n"); - exit(); - } - } - - if(wait() != -1){ - printf(1, "wait got too many\n"); - exit(); - } - - printf(1, "fork test OK\n"); -} - -void -sbrktest(void) -{ - int i, fds[2], pids[10], pid, ppid; - char *c, *oldbrk, scratch, *a, *b, *lastaddr, *p; - uint64 amt; - #define BIG (100*1024*1024) - - printf(stdout, "sbrk test\n"); - oldbrk = sbrk(0); - - // can one sbrk() less than a page? - a = sbrk(0); - for(i = 0; i < 5000; i++){ - b = sbrk(1); - if(b != a){ - printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); - exit(); - } - *b = 1; - a = b + 1; - } - pid = fork(); - if(pid < 0){ - printf(stdout, "sbrk test fork failed\n"); - exit(); - } - c = sbrk(1); - c = sbrk(1); - if(c != a + 1){ - printf(stdout, "sbrk test failed post-fork\n"); - exit(); - } - if(pid == 0) - exit(); - wait(); - - // can one grow address space to something big? - a = sbrk(0); - amt = (BIG) - (uint64)a; - p = sbrk(amt); - if (p != a) { - printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n"); - exit(); - } - lastaddr = (char*) (BIG-1); - *lastaddr = 99; - - // can one de-allocate? - a = sbrk(0); - c = sbrk(-4096); - if(c == (char*)0xffffffff){ - printf(stdout, "sbrk could not deallocate\n"); - exit(); - } - c = sbrk(0); - if(c != a - 4096){ - printf(stdout, "sbrk deallocation produced wrong address, a %x c %x\n", a, c); - exit(); - } - - // can one re-allocate that page? - a = sbrk(0); - c = sbrk(4096); - if(c != a || sbrk(0) != a + 4096){ - printf(stdout, "sbrk re-allocation failed, a %x c %x\n", a, c); - exit(); - } - if(*lastaddr == 99){ - // should be zero - printf(stdout, "sbrk de-allocation didn't really deallocate\n"); - exit(); - } - - a = sbrk(0); - c = sbrk(-(sbrk(0) - oldbrk)); - if(c != a){ - printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c); - exit(); - } - - // can we read the kernel's memory? - for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){ - ppid = getpid(); - pid = fork(); - if(pid < 0){ - printf(stdout, "fork failed\n"); - exit(); - } - if(pid == 0){ - printf(stdout, "oops could read %x = %x\n", a, *a); - kill(ppid); - exit(); - } - wait(); - } - - // if we run the system out of memory, does it clean up the last - // failed allocation? - if(pipe(fds) != 0){ - printf(1, "pipe() failed\n"); - exit(); - } - for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ - if((pids[i] = fork()) == 0){ - // allocate a lot of memory - sbrk(BIG - (uint64)sbrk(0)); - write(fds[1], "x", 1); - // sit around until killed - for(;;) sleep(1000); - } - if(pids[i] != -1) - read(fds[0], &scratch, 1); - } - - // if those failed allocations freed up the pages they did allocate, - // we'll be able to allocate here - c = sbrk(4096); - for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ - if(pids[i] == -1) - continue; - kill(pids[i]); - wait(); - } - if(c == (char*)0xffffffff){ - printf(stdout, "failed sbrk leaked memory\n"); - exit(); - } - - if(sbrk(0) > oldbrk) - sbrk(-(sbrk(0) - oldbrk)); - - printf(stdout, "sbrk test OK\n"); -} - -void -validateint(int *p) -{ - /* XXX int res; - asm("mov %%esp, %%ebx\n\t" - "mov %3, %%esp\n\t" - "int %2\n\t" - "mov %%ebx, %%esp" : - "=a" (res) : - "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) : - "ebx"); - */ -} - -void -validatetest(void) -{ - int hi, pid; - uint64 p; - - printf(stdout, "validate test\n"); - hi = 1100*1024; - - for(p = 0; p <= (uint)hi; p += 4096){ - if((pid = fork()) == 0){ - // try to crash the kernel by passing in a badly placed integer - validateint((int*)p); - exit(); - } - sleep(0); - sleep(0); - kill(pid); - wait(); - - // try to crash the kernel by passing in a bad string pointer - if(link("nosuchfile", (char*)p) != -1){ - printf(stdout, "link should not succeed\n"); - exit(); - } - } - - printf(stdout, "validate ok\n"); -} - -// does unintialized data start out zero? -char uninit[10000]; -void -bsstest(void) -{ - int i; - - printf(stdout, "bss test\n"); - for(i = 0; i < sizeof(uninit); i++){ - if(uninit[i] != '\0'){ - printf(stdout, "bss test failed\n"); - exit(); - } - } - printf(stdout, "bss test ok\n"); -} - -// does exec return an error if the arguments -// are larger than a page? or does it write -// below the stack and wreck the instructions/data? -void -bigargtest(void) -{ - int pid, fd; - - unlink("bigarg-ok"); - pid = fork(); - if(pid == 0){ - static char *args[MAXARG]; - int i; - for(i = 0; i < MAXARG-1; i++) - args[i] = "bigargs test: failed\n "; - args[MAXARG-1] = 0; - printf(stdout, "bigarg test\n"); - exec("echo", args); - printf(stdout, "bigarg test ok\n"); - fd = open("bigarg-ok", O_CREATE); - close(fd); - exit(); - } else if(pid < 0){ - printf(stdout, "bigargtest: fork failed\n"); - exit(); - } - wait(); - fd = open("bigarg-ok", 0); - if(fd < 0){ - printf(stdout, "bigarg test failed!\n"); - exit(); - } - close(fd); - unlink("bigarg-ok"); -} - -// what happens when the file system runs out of blocks? -// answer: balloc panics, so this test is not useful. -void -fsfull() -{ - int nfiles; - int fsblocks = 0; - - printf(1, "fsfull test\n"); - - for(nfiles = 0; ; nfiles++){ - char name[64]; - name[0] = 'f'; - name[1] = '0' + nfiles / 1000; - name[2] = '0' + (nfiles % 1000) / 100; - name[3] = '0' + (nfiles % 100) / 10; - name[4] = '0' + (nfiles % 10); - name[5] = '\0'; - printf(1, "writing %s\n", name); - int fd = open(name, O_CREATE|O_RDWR); - if(fd < 0){ - printf(1, "open %s failed\n", name); - break; - } - int total = 0; - while(1){ - int cc = write(fd, buf, 512); - if(cc < 512) - break; - total += cc; - fsblocks++; - } - printf(1, "wrote %d bytes\n", total); - close(fd); - if(total == 0) - break; - } - - while(nfiles >= 0){ - char name[64]; - name[0] = 'f'; - name[1] = '0' + nfiles / 1000; - name[2] = '0' + (nfiles % 1000) / 100; - name[3] = '0' + (nfiles % 100) / 10; - name[4] = '0' + (nfiles % 10); - name[5] = '\0'; - unlink(name); - nfiles--; - } - - printf(1, "fsfull test finished\n"); -} - -void argptest() -{ - int fd; - fd = open("init", O_RDONLY); - if (fd < 0) { - printf(2, "open failed\n"); - exit(); - } - read(fd, sbrk(0) - 1, -1); - close(fd); - printf(1, "arg test passed\n"); -} - -unsigned long randstate = 1; -unsigned int -rand() -{ - randstate = randstate * 1664525 + 1013904223; - return randstate; -} - -int -main(int argc, char *argv[]) -{ - printf(1, "usertests starting\n"); - - if(open("usertests.ran", 0) >= 0){ - printf(1, "already ran user tests -- rebuild fs.img\n"); - exit(); - } - close(open("usertests.ran", O_CREATE)); - - argptest(); - createdelete(); - linkunlink(); - concreate(); - fourfiles(); - sharedfd(); - - bigargtest(); - bigwrite(); - bigargtest(); - bsstest(); - sbrktest(); - validatetest(); - - opentest(); - writetest(); - writetest1(); - createtest(); - - openiputtest(); - exitiputtest(); - iputtest(); - - mem(); - pipe1(); - preempt(); - exitwait(); - - rmdot(); - fourteen(); - bigfile(); - subdir(); - linktest(); - unlinkread(); - dirfile(); - iref(); - forktest(); - bigdir(); // slow - - exectest(); - - exit(); -} diff --git a/usys.pl b/usys.pl deleted file mode 100755 index f8d47d5..0000000 --- a/usys.pl +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/perl -w - -# Generate usys.S, the stubs for syscalls. - -print "# generated by usys.pl - do not edit\n"; - -print "#include \"syscall.h\"\n"; - -sub entry { - my $name = shift; - print ".global $name\n"; - print "${name}:\n"; - print " li a7, SYS_${name}\n"; - print " ecall\n"; - print " ret\n"; -} - -entry("fork"); -entry("exit"); -entry("wait"); -entry("pipe"); -entry("read"); -entry("write"); -entry("close"); -entry("kill"); -entry("exec"); -entry("open"); -entry("mknod"); -entry("unlink"); -entry("fstat"); -entry("link"); -entry("mkdir"); -entry("chdir"); -entry("dup"); -entry("getpid"); -entry("sbrk"); -entry("sleep"); -entry("uptime"); diff --git a/vectors.pl b/vectors.pl deleted file mode 100755 index d746d6b..0000000 --- a/vectors.pl +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/perl -w - -# Generate vectors.S, the trap/interrupt entry points. -# There has to be one entry point per interrupt number -# since otherwise there's no way for trap() to discover -# the interrupt number. - -print "# generated by vectors.pl - do not edit\n"; -print "# handlers\n"; -print ".globl alltraps\n"; -for(my $i = 0; $i < 256; $i++){ - print ".globl vector$i\n"; - print "vector$i:\n"; - if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ - print " push \$0\n"; - } - print " push \$$i\n"; - print " jmp alltraps\n"; -} - -print "\n# vector table\n"; -print ".data\n"; -print ".globl vectors\n"; -print "vectors:\n"; -for(my $i = 0; $i < 256; $i++){ - print " .quad vector$i\n"; -} - -# sample output: -# # handlers -# .globl alltraps -# .globl vector0 -# vector0: -# push $0 -# push $0 -# jmp alltraps -# ... -# -# # vector table -# .data -# .globl vectors -# vectors: -# .quad vector0 -# .quad vector1 -# .quad vector2 -# ... - diff --git a/vm.c b/vm.c deleted file mode 100644 index 0ea6bca..0000000 --- a/vm.c +++ /dev/null @@ -1,389 +0,0 @@ -#include "param.h" -#include "types.h" -#include "memlayout.h" -#include "elf.h" -#include "riscv.h" -#include "defs.h" -#include "fs.h" - -/* - * the kernel's page table. - */ -pagetable_t kernel_pagetable; - -extern char etext[]; // kernel.ld sets this to end of kernel code. - -extern char trampout[]; // trampoline.S - -/* - * create a direct-map page table for the kernel and - * turn on paging. called early, in supervisor mode. - * the page allocator is already initialized. - */ -void -kvminit() -{ - kernel_pagetable = (pagetable_t) kalloc(); - memset(kernel_pagetable, 0, PGSIZE); - - // uart registers - mappages(kernel_pagetable, UART0, PGSIZE, - UART0, PTE_R | PTE_W); - - // CLINT - mappages(kernel_pagetable, CLINT, 0x10000, - CLINT, PTE_R | PTE_W); - - // PLIC - mappages(kernel_pagetable, PLIC, 0x4000000, - PLIC, PTE_R | PTE_W); - - // map kernel text executable and read-only. - mappages(kernel_pagetable, KERNBASE, (uint64)etext-KERNBASE, - KERNBASE, PTE_R | PTE_X); - - // map kernel data and the physical RAM we'll make use of. - mappages(kernel_pagetable, (uint64)etext, PHYSTOP-(uint64)etext, - (uint64)etext, PTE_R | PTE_W); - - // map the qemu -initrd fs.img ramdisk - mappages(kernel_pagetable, RAMDISK, FSSIZE * BSIZE, - RAMDISK, PTE_R | PTE_W); - - // map the trampoline for trap entry/exit to - // the highest virtual address in the kernel. - mappages(kernel_pagetable, TRAMPOLINE, PGSIZE, - (uint64)trampout, PTE_R | PTE_X); -} - -// Switch h/w page table register to the kernel's page table, -// and enable paging. -void -kvminithart() -{ - w_satp(MAKE_SATP(kernel_pagetable)); -} - -// Return the address of the PTE in page table pagetable -// that corresponds to virtual address va. If alloc!=0, -// create any required page table pages. -// -// The risc-v Sv39 scheme has three levels of page table -// pages. A page table page contains 512 64-bit PTEs. -// A 64-bit virtual address is split into five fields: -// 39..63 -- must be zero. -// 30..38 -- 9 bits of level-2 index. -// 21..39 -- 9 bits of level-1 index. -// 12..20 -- 9 bits of level-0 index. -// 0..12 -- 12 bits of byte offset within the page. -static pte_t * -walk(pagetable_t pagetable, uint64 va, int alloc) -{ - if(va >= MAXVA) - panic("walk"); - - for(int level = 2; level > 0; level--) { - pte_t *pte = &pagetable[PX(level, va)]; - if(*pte & PTE_V) { - pagetable = (pagetable_t)PTE2PA(*pte); - } else { - if(!alloc || (pagetable = (pde_t*)kalloc()) == 0) - return 0; - memset(pagetable, 0, PGSIZE); - *pte = PA2PTE(pagetable) | PTE_V; - } - } - return &pagetable[PX(0, va)]; -} - -// Look up a virtual address, return the physical address, -// Can only be used to look up user pages. -// or 0 if not mapped. -uint64 -walkaddr(pagetable_t pagetable, uint64 va) -{ - pte_t *pte; - uint64 pa; - - pte = walk(pagetable, va, 0); - if(pte == 0) - return 0; - if((*pte & PTE_V) == 0) - return 0; - if((*pte & PTE_U) == 0) - return 0; - pa = PTE2PA(*pte); - return pa; -} - - -// Create PTEs for virtual addresses starting at va that refer to -// physical addresses starting at pa. va and size might not -// be page-aligned. -void -mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) -{ - uint64 a, last; - pte_t *pte; - - a = PGROUNDDOWN(va); - last = PGROUNDDOWN(va + size - 1); - for(;;){ - if((pte = walk(pagetable, a, 1)) == 0) - panic("mappages: walk"); - if(*pte & PTE_V) - panic("remap"); - *pte = PA2PTE(pa) | perm | PTE_V; - if(a == last) - break; - a += PGSIZE; - pa += PGSIZE; - } -} - -// Remove mappings from a page table. The mappings in -// the given range must exist. Optionally free the -// physical memory. -void -unmappages(pagetable_t pagetable, uint64 va, uint64 size, int do_free) -{ - uint64 a, last; - pte_t *pte; - uint64 pa; - - a = PGROUNDDOWN(va); - last = PGROUNDDOWN(va + size - 1); - for(;;){ - if((pte = walk(pagetable, a, 0)) == 0) - panic("unmappages: walk"); - if((*pte & PTE_V) == 0){ - printf("va=%p pte=%p\n", a, *pte); - panic("unmappages: not mapped"); - } - if(PTE_FLAGS(*pte) == PTE_V) - panic("unmappages: not a leaf"); - if(do_free){ - pa = PTE2PA(*pte); - kfree((void*)pa); - } - *pte = 0; - if(a == last) - break; - a += PGSIZE; - pa += PGSIZE; - } -} - -// create an empty user page table. -pagetable_t -uvmcreate() -{ - pagetable_t pagetable; - pagetable = (pagetable_t) kalloc(); - if(pagetable == 0) - panic("uvmcreate: out of memory"); - memset(pagetable, 0, PGSIZE); - return pagetable; -} - -// Load the user initcode into address 0 of pagetable, -// for the very first process. -// sz must be less than a page. -void -uvminit(pagetable_t pagetable, uchar *src, uint sz) -{ - char *mem; - - if(sz >= PGSIZE) - panic("inituvm: more than a page"); - mem = kalloc(); - memset(mem, 0, PGSIZE); - mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); - memmove(mem, src, sz); -} - -// 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. -uint64 -uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) -{ - char *mem; - uint64 a; - - if(newsz < oldsz) - return oldsz; - - oldsz = PGROUNDUP(oldsz); - a = oldsz; - for(; a < newsz; a += PGSIZE){ - mem = kalloc(); - if(mem == 0){ - uvmdealloc(pagetable, a, oldsz); - return 0; - } - memset(mem, 0, PGSIZE); - mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U); - } - return newsz; -} - -// Deallocate user pages to bring the process size from oldsz to -// 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 -// process size. Returns the new process size. -uint64 -uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) -{ - if(newsz >= oldsz) - return oldsz; - unmappages(pagetable, newsz, oldsz - newsz, 1); - return newsz; -} - -// Recursively free page table pages. -// All leaf mappings must already have been removed. -static void -freewalk(pagetable_t pagetable) -{ - // there are 2^9 = 512 PTEs in a page table. - for(int i = 0; i < 512; i++){ - pte_t pte = pagetable[i]; - if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ - // this PTE points to a lower-level page table. - uint64 child = PTE2PA(pte); - freewalk((pagetable_t)child); - pagetable[i] = 0; - } else if(pte & PTE_V){ - panic("freewalk: leaf"); - } - } - kfree((void*)pagetable); -} - -// Free user memory pages, -// then free page table pages. -void -uvmfree(pagetable_t pagetable, uint64 sz) -{ - unmappages(pagetable, 0, sz, 1); - freewalk(pagetable); -} - -// Given a parent process's page table, copy -// its memory into a child's page table. -// Copies both the page table and the -// physical memory. -void -uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) -{ - pte_t *pte; - uint64 pa, i; - uint flags; - char *mem; - - for(i = 0; i < sz; i += PGSIZE){ - if((pte = walk(old, i, 0)) == 0) - panic("copyuvm: pte should exist"); - if((*pte & PTE_V) == 0) - panic("copyuvm: page not present"); - pa = PTE2PA(*pte); - flags = PTE_FLAGS(*pte); - if((mem = kalloc()) == 0) - panic("uvmcopy: kalloc failed"); - memmove(mem, (char*)pa, PGSIZE); - mappages(new, i, PGSIZE, (uint64)mem, flags); - } -} - -// Copy from kernel to user. -// Copy len bytes from src to virtual address dstva in a given page table. -// Return 0 on success, -1 on error. -int -copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) -{ - uint64 n, va0, pa0; - - while(len > 0){ - va0 = (uint)PGROUNDDOWN(dstva); - pa0 = walkaddr(pagetable, va0); - if(pa0 == 0) - return -1; - n = PGSIZE - (dstva - va0); - if(n > len) - n = len; - memmove((void *)(pa0 + (dstva - va0)), src, n); - - len -= n; - src += n; - dstva = va0 + PGSIZE; - } - return 0; -} - -// Copy from user to kernel. -// Copy len bytes to dst from virtual address srcva in a given page table. -// Return 0 on success, -1 on error. -int -copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) -{ - uint64 n, va0, pa0; - - while(len > 0){ - va0 = (uint)PGROUNDDOWN(srcva); - pa0 = walkaddr(pagetable, va0); - if(pa0 == 0) - return -1; - n = PGSIZE - (srcva - va0); - if(n > len) - n = len; - memmove(dst, (void *)(pa0 + (srcva - va0)), n); - - len -= n; - dst += n; - srcva = va0 + PGSIZE; - } - return 0; -} - -// Copy a null-terminated string from user to kernel. -// Copy bytes to dst from virtual address srcva in a given page table, -// until a '\0', or max. -// Return 0 on success, -1 on error. -int -copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) -{ - uint64 n, va0, pa0; - int got_null = 0; - - while(got_null == 0 && max > 0){ - va0 = (uint)PGROUNDDOWN(srcva); - pa0 = walkaddr(pagetable, va0); - if(pa0 == 0) - return -1; - n = PGSIZE - (srcva - va0); - if(n > max) - n = max; - - char *p = (char *) (pa0 + (srcva - va0)); - while(n > 0){ - if(*p == '\0'){ - *dst = '\0'; - got_null = 1; - break; - } else { - *dst = *p; - } - --n; - --max; - p++; - dst++; - } - - srcva = va0 + PGSIZE; - } - if(got_null){ - return 0; - } else { - return -1; - } -} diff --git a/wc.c b/wc.c deleted file mode 100644 index d6a54df..0000000 --- a/wc.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[512]; - -void -wc(int fd, char *name) -{ - int i, n; - int l, w, c, inword; - - l = w = c = 0; - inword = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(i=0; i 0) - sleep(5); // Let child exit before parent. - exit(); -} -- cgit v1.2.3