--- /dev/null
+cmake_minimum_required(VERSION 3.10)
+
+if (NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+project(xv6)
+
+#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -MD -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing -Wall -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector")
+
+set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
+set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb3")
+
+set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}")
+
+function(add_prefix_suffix var prefix suffix)
+ set(listVar "")
+ foreach(f ${ARGN})
+ list(APPEND listVar "${prefix}${f}${suffix}")
+ endforeach(f)
+ set(${var} "${listVar}" PARENT_SCOPE)
+endfunction(add_prefix_suffix)
+
+# Build the tools!
+add_subdirectory(tools)
+
+include_directories(include)
+
+# Kernel and bootblock
+add_subdirectory(bootblock)
+add_subdirectory(kernel)
+
+# User-space applications
+add_subdirectory(user)
+
+# Whoo boy, lets build the actual boot image
+add_custom_command(
+ OUTPUT xv6.img
+ COMMAND dd if=/dev/zero of=xv6.img count=10000
+ COMMAND dd if=${CMAKE_CURRENT_BINARY_DIR}/bootblock/bootblock of=xv6.img conv=notrunc
+ COMMAND dd if=${CMAKE_CURRENT_BINARY_DIR}/kernel/kernel of=xv6.img seek=1 conv=notrunc
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bootblock/bootblock ${CMAKE_CURRENT_BINARY_DIR}/kernel/kernel
+)
+
+add_custom_target(
+ image ALL
+ DEPENDS buildkern buildboot xv6.img makeuserfs
+ #DEPENDS buildkern buildboot xv6.img
+)
+
+++ /dev/null
-//
-// assembler macros to create x86 segments
-//
-
-#define SEG_NULLASM \
- .word 0, 0; \
- .byte 0, 0, 0, 0
-
-// The 0xC0 means the limit is in 4096-byte units
-// and (for executable segments) 32-bit mode.
-#define SEG_ASM(type,base,lim) \
- .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
- .byte (((base) >> 16) & 0xff), (0x90 | (type)), \
- (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
-
-#define STA_X 0x8 // Executable segment
-#define STA_W 0x2 // Writeable (non-executable segments)
-#define STA_R 0x2 // Readable (executable segments)
+++ /dev/null
-// 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 "defs.h"
-#include "param.h"
-#include "spinlock.h"
-#include "sleeplock.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) {
- iderw(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;
- iderw(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.
-
+++ /dev/null
-#include "asm.h"
-#include "memlayout.h"
-#include "mmu.h"
-
-# Start the first CPU: switch to 32-bit protected mode, jump into C.
-# The BIOS loads this code from the first sector of the hard disk into
-# memory at physical address 0x7c00 and starts executing in real mode
-# with %cs=0 %ip=7c00.
-
-.code16 # Assemble for 16-bit mode
-.globl start
-start:
- cli # BIOS enabled interrupts; disable
-
- # Zero data segment registers DS, ES, and SS.
- xorw %ax,%ax # Set %ax to zero
- movw %ax,%ds # -> Data Segment
- movw %ax,%es # -> Extra Segment
- movw %ax,%ss # -> Stack Segment
-
- # Physical address line A20 is tied to zero so that the first PCs
- # with 2 MB would run software that assumed 1 MB. Undo that.
-seta20.1:
- inb $0x64,%al # Wait for not busy
- testb $0x2,%al
- jnz seta20.1
-
- movb $0xd1,%al # 0xd1 -> port 0x64
- outb %al,$0x64
-
-seta20.2:
- inb $0x64,%al # Wait for not busy
- testb $0x2,%al
- jnz seta20.2
-
- movb $0xdf,%al # 0xdf -> port 0x60
- outb %al,$0x60
-
- # Switch from real to protected mode. Use a bootstrap GDT that makes
- # virtual addresses map directly to physical addresses so that the
- # effective memory map doesn't change during the transition.
- lgdt gdtdesc
- movl %cr0, %eax
- orl $CR0_PE, %eax
- movl %eax, %cr0
-
-//PAGEBREAK!
- # Complete the transition to 32-bit protected mode by using a long jmp
- # to reload %cs and %eip. The segment descriptors are set up with no
- # translation, so that the mapping is still the identity mapping.
- ljmp $(SEG_KCODE<<3), $start32
-
-.code32 # Tell assembler to generate 32-bit code now.
-start32:
- # Set up the protected-mode data segment registers
- movw $(SEG_KDATA<<3), %ax # Our data segment selector
- movw %ax, %ds # -> DS: Data Segment
- movw %ax, %es # -> ES: Extra Segment
- movw %ax, %ss # -> SS: Stack Segment
- movw $0, %ax # Zero segments not ready for use
- movw %ax, %fs # -> FS
- movw %ax, %gs # -> GS
-
- # Set up the stack pointer and call into C.
- movl $start, %esp
- call bootmain
-
- # If bootmain returns (it shouldn't), trigger a Bochs
- # breakpoint if running under Bochs, then loop.
- movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
- movw %ax, %dx
- outw %ax, %dx
- movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
- outw %ax, %dx
-spin:
- jmp spin
-
-# Bootstrap GDT
-.p2align 2 # force 4 byte alignment
-gdt:
- SEG_NULLASM # null seg
- SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
- SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
-
-gdtdesc:
- .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
- .long gdt # address gdt
-
+++ /dev/null
-// Boot loader.
-//
-// Part of the boot block, along with bootasm.S, which calls bootmain().
-// bootasm.S has put the processor into protected 32-bit mode.
-// bootmain() loads an ELF kernel image from the disk starting at
-// sector 1 and then jumps to the kernel entry routine.
-
-#include "types.h"
-#include "elf.h"
-#include "x86.h"
-#include "memlayout.h"
-
-#define SECTSIZE 512
-
-void readseg(uchar*, uint, uint);
-
-void
-bootmain(void)
-{
- struct elfhdr *elf;
- struct proghdr *ph, *eph;
- void (*entry)(void);
- uchar* pa;
-
- elf = (struct elfhdr*)0x10000; // scratch space
-
- // Read 1st page off disk
- readseg((uchar*)elf, 4096, 0);
-
- // Is this an ELF executable?
- if(elf->magic != ELF_MAGIC)
- return; // let bootasm.S handle error
-
- // Load each program segment (ignores ph flags).
- ph = (struct proghdr*)((uchar*)elf + elf->phoff);
- eph = ph + elf->phnum;
- for(; ph < eph; ph++){
- pa = (uchar*)ph->paddr;
- readseg(pa, ph->filesz, ph->off);
- if(ph->memsz > ph->filesz)
- stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
- }
-
- // Call the entry point from the ELF header.
- // Does not return!
- entry = (void(*)(void))(elf->entry);
- entry();
-}
-
-void
-waitdisk(void)
-{
- // Wait for disk ready.
- while((inb(0x1F7) & 0xC0) != 0x40)
- ;
-}
-
-// Read a single sector at offset into dst.
-void
-readsect(void *dst, uint offset)
-{
- // Issue command.
- waitdisk();
- outb(0x1F2, 1); // count = 1
- outb(0x1F3, offset);
- outb(0x1F4, offset >> 8);
- outb(0x1F5, offset >> 16);
- outb(0x1F6, (offset >> 24) | 0xE0);
- outb(0x1F7, 0x20); // cmd 0x20 - read sectors
-
- // Read data.
- waitdisk();
- insl(0x1F0, dst, SECTSIZE/4);
-}
-
-// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
-// Might copy more than asked.
-void
-readseg(uchar* pa, uint count, uint offset)
-{
- uchar* epa;
-
- epa = pa + count;
-
- // Round down to sector boundary.
- pa -= offset % SECTSIZE;
-
- // Translate from bytes to sectors; kernel starts at sector 1.
- offset = (offset / SECTSIZE) + 1;
-
- // If this is too slow, we could read lots of sectors at a time.
- // We'd write more to memory than asked, but it doesn't matter --
- // we load in increasing order.
- for(; pa < epa; pa += SECTSIZE, offset++)
- readsect(pa, offset);
-}
+++ /dev/null
-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
-
+++ /dev/null
-#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();
-}
+++ /dev/null
-// Console input and output.
-// Input is from the keyboard or serial port.
-// Output is written to the screen and serial port.
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "traps.h"
-#include "spinlock.h"
-#include "sleeplock.h"
-#include "fs.h"
-#include "file.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-
-static void consputc(int);
-
-static int panicked = 0;
-
-static struct {
- struct spinlock lock;
- int locking;
-} cons;
-
-static void
-printint(int xx, int base, int sign)
-{
- static char digits[] = "0123456789abcdef";
- 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]);
-}
-//PAGEBREAK: 50
-
-// Print to the console. only understands %d, %x, %p, %s.
-void
-cprintf(char *fmt, ...)
-{
- int i, c, locking;
- uint *argp;
- char *s;
-
- locking = cons.locking;
- if(locking)
- acquire(&cons.lock);
-
- if (fmt == 0)
- panic("null fmt");
-
- argp = (uint*)(void*)(&fmt + 1);
- 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(*argp++, 10, 1);
- break;
- case 'x':
- case 'p':
- printint(*argp++, 16, 0);
- break;
- case 's':
- if((s = (char*)*argp++) == 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)
-{
- int i;
- uint pcs[10];
-
- cli();
- cons.locking = 0;
- // use lapiccpunum so that we can call panic from mycpu()
- cprintf("lapicid %d: panic: ", lapicid());
- cprintf(s);
- cprintf("\n");
- getcallerpcs(&s, pcs);
- for(i=0; i<10; i++)
- cprintf(" %p", pcs[i]);
- panicked = 1; // freeze other CPU
- for(;;)
- ;
-}
-
-//PAGEBREAK: 50
-#define BACKSPACE 0x100
-#define CRTPORT 0x3d4
-static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory
-
-static void
-cgaputc(int c)
-{
- int pos;
-
- // Cursor position: col + 80*row.
- outb(CRTPORT, 14);
- pos = inb(CRTPORT+1) << 8;
- outb(CRTPORT, 15);
- pos |= inb(CRTPORT+1);
-
- if(c == '\n')
- pos += 80 - pos%80;
- else if(c == BACKSPACE){
- if(pos > 0) --pos;
- } else
- crt[pos++] = (c&0xff) | 0x0700; // black on white
-
- if(pos < 0 || pos > 25*80)
- panic("pos under/overflow");
-
- if((pos/80) >= 24){ // Scroll up.
- memmove(crt, crt+80, sizeof(crt[0])*23*80);
- pos -= 80;
- memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos));
- }
-
- outb(CRTPORT, 14);
- outb(CRTPORT+1, pos>>8);
- outb(CRTPORT, 15);
- outb(CRTPORT+1, pos);
- crt[pos] = ' ' | 0x0700;
-}
-
-void
-consputc(int c)
-{
- if(panicked){
- cli();
- for(;;)
- ;
- }
-
- if(c == BACKSPACE){
- uartputc('\b'); uartputc(' '); uartputc('\b');
- } else
- uartputc(c);
- cgaputc(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)-'@') // Control-x
-
-void
-consoleintr(int (*getc)(void))
-{
- int c, doprocdump = 0;
-
- acquire(&cons.lock);
- while((c = getc()) >= 0){
- switch(c){
- case C('P'): // Process listing.
- // 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(); // now call procdump() wo. cons.lock held
- }
-}
-
-int
-consoleread(struct inode *ip, char *dst, int n)
-{
- uint target;
- int c;
-
- 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;
- }
- *dst++ = c;
- --n;
- if(c == '\n')
- break;
- }
- release(&cons.lock);
- ilock(ip);
-
- return target - n;
-}
-
-int
-consolewrite(struct inode *ip, char *buf, int n)
-{
- int i;
-
- iunlock(ip);
- acquire(&cons.lock);
- for(i = 0; i < n; i++)
- consputc(buf[i] & 0xff);
- release(&cons.lock);
- ilock(ip);
-
- return n;
-}
-
-void
-consoleinit(void)
-{
- initlock(&cons.lock, "console");
-
- devsw[CONSOLE].write = consolewrite;
- devsw[CONSOLE].read = consoleread;
- cons.locking = 1;
-
- ioapicenable(IRQ_KBD, 0);
-}
-
+++ /dev/null
-#!/usr/bin/perl
-
-$| = 1;
-
-sub writefile($@){
- my ($file, @lines) = @_;
-
- sleep(1);
- open(F, ">$file") || die "open >$file: $!";
- print F @lines;
- close(F);
-}
-
-# Cut out #include lines that don't contribute anything.
-for($i=0; $i<@ARGV; $i++){
- $file = $ARGV[$i];
- if(!open(F, $file)){
- print STDERR "open $file: $!\n";
- next;
- }
- @lines = <F>;
- close(F);
-
- $obj = "$file.o";
- $obj =~ s/\.c\.o$/.o/;
- system("touch $file");
-
- if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
- print STDERR "make $obj failed: $rv\n";
- next;
- }
-
- system("cp $file =$file");
- for($j=@lines-1; $j>=0; $j--){
- if($lines[$j] =~ /^#include/){
- $old = $lines[$j];
- $lines[$j] = "/* CUT-H */\n";
- writefile($file, @lines);
- if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
- $lines[$j] = $old;
- }else{
- print STDERR "$file $old";
- }
- }
- }
- writefile($file, grep {!/CUT-H/} @lines);
- system("rm =$file");
-}
+++ /dev/null
-struct rtcdate {
- uint second;
- uint minute;
- uint hour;
- uint day;
- uint month;
- uint year;
-};
+++ /dev/null
-struct buf;
-struct context;
-struct file;
-struct inode;
-struct pipe;
-struct proc;
-struct rtcdate;
-struct spinlock;
-struct sleeplock;
-struct stat;
-struct superblock;
-
-// bio.c
-void binit(void);
-struct buf* bread(uint, uint);
-void brelse(struct buf*);
-void bwrite(struct buf*);
-
-// console.c
-void consoleinit(void);
-void cprintf(char*, ...);
-void consoleintr(int(*)(void));
-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*, char*, int n);
-int filestat(struct file*, struct stat*);
-int filewrite(struct file*, char*, 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*, char*, uint, uint);
-void stati(struct inode*, struct stat*);
-int writei(struct inode*, char*, uint, uint);
-
-// ide.c
-void ideinit(void);
-void ideintr(void);
-void iderw(struct buf*);
-
-// ioapic.c
-void ioapicenable(int irq, int cpu);
-extern uchar ioapicid;
-void ioapicinit(void);
-
-// kalloc.c
-char* kalloc(void);
-void kfree(char*);
-void kinit1(void*, void*);
-void kinit2(void*, void*);
-
-// 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*, char*, int);
-int pipewrite(struct pipe*, char*, int);
-
-//PAGEBREAK: 16
-// proc.c
-int cpuid(void);
-void exit(void);
-int fork(void);
-int growproc(int);
-int kill(int);
-struct cpu* mycpu(void);
-struct proc* myproc();
-void pinit(void);
-void procdump(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);
-
-// swtch.S
-void swtch(struct context**, struct context*);
-
-// spinlock.c
-void acquire(struct spinlock*);
-void getcallerpcs(void*, uint*);
-int holding(struct spinlock*);
-void initlock(struct spinlock*, char*);
-void release(struct spinlock*);
-void pushcli(void);
-void popcli(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, char**, int);
-int argstr(int, char**);
-int fetchint(uint, int*);
-int fetchstr(uint, char**);
-void syscall(void);
-
-// timer.c
-void timerinit(void);
-
-// trap.c
-void idtinit(void);
-extern uint ticks;
-void tvinit(void);
-extern struct spinlock tickslock;
-
-// uart.c
-void uartinit(void);
-void uartintr(void);
-void uartputc(int);
-
-// vm.c
-void seginit(void);
-void kvmalloc(void);
-pde_t* setupkvm(void);
-char* uva2ka(pde_t*, char*);
-int allocuvm(pde_t*, uint, uint);
-int deallocuvm(pde_t*, uint, uint);
-void freevm(pde_t*);
-void inituvm(pde_t*, char*, uint);
-int loaduvm(pde_t*, char*, struct inode*, uint, uint);
-pde_t* copyuvm(pde_t*, uint);
-void switchuvm(struct proc*);
-void switchkvm(void);
-int copyout(pde_t*, uint, void*, uint);
-void clearpteu(pde_t *pgdir, char *uva);
-
-// number of elements in fixed-size array
-#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
+++ /dev/null
-# You may now use double quotes around pathnames, in case
-# your pathname includes spaces.
-
-#=======================================================================
-# CONFIG_INTERFACE
-#
-# The configuration interface is a series of menus or dialog boxes that
-# allows you to change all the settings that control Bochs's behavior.
-# There are two choices of configuration interface: a text mode version
-# called "textconfig" and a graphical version called "wx". The text
-# mode version uses stdin/stdout and is always compiled in. The graphical
-# version is only available when you use "--with-wx" on the configure
-# command. If you do not write a config_interface line, Bochs will
-# choose a default for you.
-#
-# NOTE: if you use the "wx" configuration interface, you must also use
-# the "wx" display library.
-#=======================================================================
-#config_interface: textconfig
-#config_interface: wx
-
-#=======================================================================
-# DISPLAY_LIBRARY
-#
-# The display library is the code that displays the Bochs VGA screen. Bochs
-# has a selection of about 10 different display library implementations for
-# different platforms. If you run configure with multiple --with-* options,
-# the display_library command lets you choose which one you want to run with.
-# If you do not write a display_library line, Bochs will choose a default for
-# you.
-#
-# The choices are:
-# x use X windows interface, cross platform
-# win32 use native win32 libraries
-# carbon use Carbon library (for MacOS X)
-# beos use native BeOS libraries
-# macintosh use MacOS pre-10
-# amigaos use native AmigaOS libraries
-# sdl use SDL library, cross platform
-# svga use SVGALIB library for Linux, allows graphics without X11
-# term text only, uses curses/ncurses library, cross platform
-# rfb provides an interface to AT&T's VNC viewer, cross platform
-# wx use wxWidgets library, cross platform
-# nogui no display at all
-#
-# NOTE: if you use the "wx" configuration interface, you must also use
-# the "wx" display library.
-#
-# Specific options:
-# Some display libraries now support specific option to control their
-# behaviour. See the examples below for currently supported options.
-#=======================================================================
-#display_library: amigaos
-#display_library: beos
-#display_library: carbon
-#display_library: macintosh
-#display_library: nogui
-#display_library: rfb, options="timeout=60" # time to wait for client
-#display_library: sdl, options="fullscreen" # startup in fullscreen mode
-#display_library: term
-#display_library: win32, options="legacyF12" # use F12 to toggle mouse
-#display_library: wx
-#display_library: x
-
-#=======================================================================
-# ROMIMAGE:
-# The ROM BIOS controls what the PC does when it first powers on.
-# Normally, you can use a precompiled BIOS in the source or binary
-# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded
-# starting at address 0xf0000, and it is exactly 64k long.
-# You can also use the environment variable $BXSHARE to specify the
-# location of the BIOS.
-# The usage of external large BIOS images (up to 512k) at memory top is
-# now supported, but we still recommend to use the BIOS distributed with
-# Bochs. Now the start address can be calculated from image size.
-#=======================================================================
-romimage: file=$BXSHARE/BIOS-bochs-latest
-#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top
-#romimage: file=mybios.bin # calculate start address from image size
-
-#=======================================================================
-# CPU:
-# This defines cpu-related parameters inside Bochs:
-#
-# COUNT:
-# Set the number of processors when Bochs is compiled for SMP emulation.
-# Bochs currently supports up to 8 processors. If Bochs is compiled
-# without SMP support, it won't accept values different from 1.
-#
-# IPS:
-# Emulated Instructions Per Second. This is the number of IPS that bochs
-# is capable of running on your machine. You can recompile Bochs with
-# --enable-show-ips option enabled, to find your workstation's capability.
-# Measured IPS value will then be logged into your log file or status bar
-# (if supported by the gui).
-#
-# IPS is used to calibrate many time-dependent events within the bochs
-# simulation. For example, changing IPS affects the frequency of VGA
-# updates, the duration of time before a key starts to autorepeat, and
-# the measurement of BogoMips and other benchmarks.
-#
-# Examples:
-# Machine Mips
-# ________________________________________________________________
-# 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips
-# 1.6Ghz Intel P4 with Win2000/g++ 3.3 5 to 7 Mips
-# 650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66 2 to 2.5 Mips
-# 400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3 1 to 1.8 Mips
-#=======================================================================
-cpu: count=2, ips=10000000
-
-#=======================================================================
-# MEGS
-# Set the number of Megabytes of physical memory you want to emulate.
-# The default is 32MB, most OS's won't need more than that.
-# The maximum amount of memory supported is 2048Mb.
-#=======================================================================
-#megs: 256
-#megs: 128
-#megs: 64
-megs: 32
-#megs: 16
-#megs: 8
-
-#=======================================================================
-# OPTROMIMAGE[1-4]:
-# You may now load up to 4 optional ROM images. Be sure to use a
-# read-only area, typically between C8000 and EFFFF. These optional
-# ROM images should not overwrite the rombios (located at
-# F0000-FFFFF) and the videobios (located at C0000-C7FFF).
-# Those ROM images will be initialized by the bios if they contain
-# the right signature (0x55AA) and a valid checksum.
-# It can also be a convenient way to upload some arbitrary code/data
-# in the simulation, that can be retrieved by the boot loader
-#=======================================================================
-#optromimage1: file=optionalrom.bin, address=0xd0000
-#optromimage2: file=optionalrom.bin, address=0xd1000
-#optromimage3: file=optionalrom.bin, address=0xd2000
-#optromimage4: file=optionalrom.bin, address=0xd3000
-
-#optramimage1: file=/path/file1.img, address=0x0010000
-#optramimage2: file=/path/file2.img, address=0x0020000
-#optramimage3: file=/path/file3.img, address=0x0030000
-#optramimage4: file=/path/file4.img, address=0x0040000
-
-#=======================================================================
-# VGAROMIMAGE
-# You now need to load a VGA ROM BIOS into C0000.
-#=======================================================================
-#vgaromimage: file=bios/VGABIOS-elpin-2.40
-vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
-#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus
-
-#=======================================================================
-# VGA:
-# Here you can specify the display extension to be used. With the value
-# 'none' you can use standard VGA with no extension. Other supported
-# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support.
-#=======================================================================
-#vga: extension=cirrus
-#vga: extension=vbe
-vga: extension=none
-
-#=======================================================================
-# FLOPPYA:
-# Point this to pathname of floppy image file or device
-# This should be of a bootable floppy(image/device) if you're
-# booting from 'a' (or 'floppy').
-#
-# You can set the initial status of the media to 'ejected' or 'inserted'.
-# floppya: 2_88=path, status=ejected (2.88M 3.5" floppy)
-# floppya: 1_44=path, status=inserted (1.44M 3.5" floppy)
-# floppya: 1_2=path, status=ejected (1.2M 5.25" floppy)
-# floppya: 720k=path, status=inserted (720K 3.5" floppy)
-# floppya: 360k=path, status=inserted (360K 5.25" floppy)
-# floppya: 320k=path, status=inserted (320K 5.25" floppy)
-# floppya: 180k=path, status=inserted (180K 5.25" floppy)
-# floppya: 160k=path, status=inserted (160K 5.25" floppy)
-# floppya: image=path, status=inserted (guess type from image size)
-#
-# The path should be the name of a disk image file. On Unix, you can use a raw
-# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters
-# such as a: or b: as the path. The parameter 'image' works with image files
-# only. In that case the size must match one of the supported types.
-#=======================================================================
-floppya: 1_44=/dev/fd0, status=inserted
-#floppya: image=../1.44, status=inserted
-#floppya: 1_44=/dev/fd0H1440, status=inserted
-#floppya: 1_2=../1_2, status=inserted
-#floppya: 1_44=a:, status=inserted
-#floppya: 1_44=a.img, status=inserted
-#floppya: 1_44=/dev/rfd0a, status=inserted
-
-#=======================================================================
-# FLOPPYB:
-# See FLOPPYA above for syntax
-#=======================================================================
-#floppyb: 1_44=b:, status=inserted
-floppyb: 1_44=b.img, status=inserted
-
-#=======================================================================
-# ATA0, ATA1, ATA2, ATA3
-# ATA controller for hard disks and cdroms
-#
-# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number
-#
-# These options enables up to 4 ata channels. For each channel
-# the two base io addresses and the irq must be specified.
-#
-# ata0 and ata1 are enabled by default with the values shown below
-#
-# Examples:
-# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
-# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
-# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
-# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9
-#=======================================================================
-ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
-ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
-ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
-ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
-
-#=======================================================================
-# ATA[0-3]-MASTER, ATA[0-3]-SLAVE
-#
-# This defines the type and characteristics of all attached ata devices:
-# type= type of attached device [disk|cdrom]
-# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3]
-# mode= only valid for disks [undoable|growing|volatile]
-# path= path of the image
-# cylinders= only valid for disks
-# heads= only valid for disks
-# spt= only valid for disks
-# status= only valid for cdroms [inserted|ejected]
-# biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos]
-# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto]
-# model= string returned by identify device command
-# journal= optional filename of the redolog for undoable and volatile disks
-#
-# Point this at a hard disk image file, cdrom iso file, or physical cdrom
-# device. To create a hard disk image, try running bximage. It will help you
-# choose the size and then suggest a line that works with it.
-#
-# In UNIX it may be possible to use a raw device as a Bochs hard disk,
-# but WE DON'T RECOMMEND IT. In Windows there is no easy way.
-#
-# In windows, the drive letter + colon notation should be used for cdroms.
-# Depending on versions of windows and drivers, you may only be able to
-# access the "first" cdrom in the system. On MacOSX, use path="drive"
-# to access the physical drive.
-#
-# The path is always mandatory. For flat hard disk images created with
-# bximage geometry autodetection can be used (cylinders=0 -> cylinders are
-# calculated using heads=16 and spt=63). For other hard disk images and modes
-# the cylinders, heads, and spt are mandatory.
-#
-# Default values are:
-# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234"
-#
-# The biosdetect option has currently no effect on the bios
-#
-# Examples:
-# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17
-# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17
-# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17
-# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17
-# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17
-# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17
-# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63
-# ata3-slave: type=cdrom, path=iso.sample, status=inserted
-#=======================================================================
-ata0-master: type=disk, mode=flat, path="xv6.img", cylinders=100, heads=10, spt=10
-ata0-slave: type=disk, mode=flat, path="fs.img", cylinders=1024, heads=1, spt=1
-#ata0-slave: type=cdrom, path=D:, status=inserted
-#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted
-#ata0-slave: type=cdrom, path="drive", status=inserted
-#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted
-
-#=======================================================================
-# BOOT:
-# This defines the boot sequence. Now you can specify up to 3 boot drives.
-# You can either boot from 'floppy', 'disk' or 'cdrom'
-# legacy 'a' and 'c' are also supported
-# Examples:
-# boot: floppy
-# boot: disk
-# boot: cdrom
-# boot: c
-# boot: a
-# boot: cdrom, floppy, disk
-#=======================================================================
-#boot: floppy
-boot: disk
-
-#=======================================================================
-# CLOCK:
-# This defines the parameters of the clock inside Bochs:
-#
-# SYNC:
-# TO BE COMPLETED (see Greg explanation in feature request #536329)
-#
-# TIME0:
-# Specifies the start (boot) time of the virtual machine. Use a time
-# value as returned by the time(2) system call. If no time0 value is
-# set or if time0 equal to 1 (special case) or if time0 equal 'local',
-# the simulation will be started at the current local host time.
-# If time0 equal to 2 (special case) or if time0 equal 'utc',
-# the simulation will be started at the current utc time.
-#
-# Syntax:
-# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc]
-#
-# Example:
-# clock: sync=none, time0=local # Now (localtime)
-# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980
-# clock: sync=none, time0=631148400 # Mon Jan 1 00:00:00 1990
-# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999
-# clock: sync=realtime, time0=946681200 # Sat Jan 1 00:00:00 2000
-# clock: sync=none, time0=1 # Now (localtime)
-# clock: sync=none, time0=utc # Now (utc/gmt)
-#
-# Default value are sync=none, time0=local
-#=======================================================================
-#clock: sync=none, time0=local
-
-
-#=======================================================================
-# FLOPPY_BOOTSIG_CHECK: disabled=[0|1]
-# Enables or disables the 0xaa55 signature check on boot floppies
-# Defaults to disabled=0
-# Examples:
-# floppy_bootsig_check: disabled=0
-# floppy_bootsig_check: disabled=1
-#=======================================================================
-#floppy_bootsig_check: disabled=1
-floppy_bootsig_check: disabled=0
-
-#=======================================================================
-# LOG:
-# Give the path of the log file you'd like Bochs debug and misc. verbiage
-# to be written to. If you don't use this option or set the filename to
-# '-' the output is written to the console. If you really don't want it,
-# make it "/dev/null" (Unix) or "nul" (win32). :^(
-#
-# Examples:
-# log: ./bochs.out
-# log: /dev/tty
-#=======================================================================
-#log: /dev/null
-log: bochsout.txt
-
-#=======================================================================
-# LOGPREFIX:
-# This handles the format of the string prepended to each log line.
-# You may use those special tokens :
-# %t : 11 decimal digits timer tick
-# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration)
-# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror)
-# %d : 5 characters string of the device, between brackets
-#
-# Default : %t%e%d
-# Examples:
-# logprefix: %t-%e-@%i-%d
-# logprefix: %i%e%d
-#=======================================================================
-#logprefix: %t%e%d
-
-#=======================================================================
-# LOG CONTROLS
-#
-# Bochs now has four severity levels for event logging.
-# panic: cannot proceed. If you choose to continue after a panic,
-# don't be surprised if you get strange behavior or crashes.
-# error: something went wrong, but it is probably safe to continue the
-# simulation.
-# info: interesting or useful messages.
-# debug: messages useful only when debugging the code. This may
-# spit out thousands per second.
-#
-# For events of each level, you can choose to crash, report, or ignore.
-# TODO: allow choice based on the facility: e.g. crash on panics from
-# everything except the cdrom, and only report those.
-#
-# If you are experiencing many panics, it can be helpful to change
-# the panic action to report instead of fatal. However, be aware
-# that anything executed after a panic is uncharted territory and can
-# cause bochs to become unstable. The panic is a "graceful exit," so
-# if you disable it you may get a spectacular disaster instead.
-#=======================================================================
-panic: action=ask
-error: action=report
-info: action=report
-debug: action=ignore
-#pass: action=fatal
-
-#=======================================================================
-# DEBUGGER_LOG:
-# Give the path of the log file you'd like Bochs to log debugger output.
-# If you really don't want it, make it /dev/null or '-'. :^(
-#
-# Examples:
-# debugger_log: ./debugger.out
-#=======================================================================
-#debugger_log: /dev/null
-#debugger_log: debugger.out
-debugger_log: -
-
-#=======================================================================
-# COM1, COM2, COM3, COM4:
-# This defines a serial port (UART type 16550A). In the 'term' you can specify
-# a device to use as com1. This can be a real serial line, or a pty. To use
-# a pty (under X/Unix), create two windows (xterms, usually). One of them will
-# run bochs, and the other will act as com1. Find out the tty the com1
-# window using the `tty' command, and use that as the `dev' parameter.
-# Then do `sleep 1000000' in the com1 window to keep the shell from
-# messing with things, and run bochs in the other window. Serial I/O to
-# com1 (port 0x3f8) will all go to the other window.
-# Other serial modes are 'null' (no input/output), 'file' (output to a file
-# specified as the 'dev' parameter), 'raw' (use the real serial port - under
-# construction for win32), 'mouse' (standard serial mouse - requires
-# mouse option setting 'type=serial' or 'type=serial_wheel') and 'socket'
-# (connect a networking socket).
-#
-# Examples:
-# com1: enabled=1, mode=null
-# com1: enabled=1, mode=mouse
-# com2: enabled=1, mode=file, dev=serial.out
-# com3: enabled=1, mode=raw, dev=com1
-# com3: enabled=1, mode=socket, dev=localhost:8888
-#=======================================================================
-#com1: enabled=1, mode=term, dev=/dev/ttyp9
-
-
-#=======================================================================
-# PARPORT1, PARPORT2:
-# This defines a parallel (printer) port. When turned on and an output file is
-# defined the emulated printer port sends characters printed by the guest OS
-# into the output file. On some platforms a device filename can be used to
-# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on
-# win32 platforms).
-#
-# Examples:
-# parport1: enabled=1, file="parport.out"
-# parport2: enabled=1, file="/dev/lp0"
-# parport1: enabled=0
-#=======================================================================
-parport1: enabled=1, file="/dev/stdout"
-
-#=======================================================================
-# SB16:
-# This defines the SB16 sound emulation. It can have several of the
-# following properties.
-# All properties are in the format sb16: property=value
-# midi: The filename is where the midi data is sent. This can be a
-# device or just a file if you want to record the midi data.
-# midimode:
-# 0=no data
-# 1=output to device (system dependent. midi denotes the device driver)
-# 2=SMF file output, including headers
-# 3=output the midi data stream to the file (no midi headers and no
-# delta times, just command and data bytes)
-# wave: This is the device/file where wave output is stored
-# wavemode:
-# 0=no data
-# 1=output to device (system dependent. wave denotes the device driver)
-# 2=VOC file output, incl. headers
-# 3=output the raw wave stream to the file
-# log: The file to write the sb16 emulator messages to.
-# loglevel:
-# 0=no log
-# 1=resource changes, midi program and bank changes
-# 2=severe errors
-# 3=all errors
-# 4=all errors plus all port accesses
-# 5=all errors and port accesses plus a lot of extra info
-# dmatimer:
-# microseconds per second for a DMA cycle. Make it smaller to fix
-# non-continuous sound. 750000 is usually a good value. This needs a
-# reasonably correct setting for the IPS parameter of the CPU option.
-#
-# For an example look at the next line:
-#=======================================================================
-
-#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000
-
-#=======================================================================
-# VGA_UPDATE_INTERVAL:
-# Video memory is scanned for updates and screen updated every so many
-# virtual seconds. The default is 40000, about 25Hz. Keep in mind that
-# you must tweak the 'cpu: ips=N' directive to be as close to the number
-# of emulated instructions-per-second your workstation can do, for this
-# to be accurate.
-#
-# Examples:
-# vga_update_interval: 250000
-#=======================================================================
-vga_update_interval: 300000
-
-# using for Winstone '98 tests
-#vga_update_interval: 100000
-
-#=======================================================================
-# KEYBOARD_SERIAL_DELAY:
-# Approximate time in microseconds that it takes one character to
-# be transfered from the keyboard to controller over the serial path.
-# Examples:
-# keyboard_serial_delay: 200
-#=======================================================================
-keyboard_serial_delay: 250
-
-#=======================================================================
-# KEYBOARD_PASTE_DELAY:
-# Approximate time in microseconds between attempts to paste
-# characters to the keyboard controller. This leaves time for the
-# guest os to deal with the flow of characters. The ideal setting
-# depends on how your operating system processes characters. The
-# default of 100000 usec (.1 seconds) was chosen because it works
-# consistently in Windows.
-#
-# If your OS is losing characters during a paste, increase the paste
-# delay until it stops losing characters.
-#
-# Examples:
-# keyboard_paste_delay: 100000
-#=======================================================================
-keyboard_paste_delay: 100000
-
-#=======================================================================
-# MOUSE:
-# This option prevents Bochs from creating mouse "events" unless a mouse
-# is enabled. The hardware emulation itself is not disabled by this.
-# You can turn the mouse on by setting enabled to 1, or turn it off by
-# setting enabled to 0. Unless you have a particular reason for enabling
-# the mouse by default, it is recommended that you leave it off.
-# You can also toggle the mouse usage at runtime (control key + middle
-# mouse button on X11, SDL, wxWidgets and Win32).
-# With the mouse type option you can select the type of mouse to emulate.
-# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse
-# on PS/2), 'serial', 'serial_wheel' (one com port requires setting
-# 'mode=mouse') and 'usb' (3-button mouse - one of the USB ports must be
-# connected with the 'mouse' device - requires PCI and USB support).
-#
-# Examples:
-# mouse: enabled=1
-# mouse: enabled=1, type=imps2
-# mouse: enabled=1, type=serial
-# mouse: enabled=0
-#=======================================================================
-mouse: enabled=0
-
-#=======================================================================
-# private_colormap: Request that the GUI create and use it's own
-# non-shared colormap. This colormap will be used
-# when in the bochs window. If not enabled, a
-# shared colormap scheme may be used. Not implemented
-# on all GUI's.
-#
-# Examples:
-# private_colormap: enabled=1
-# private_colormap: enabled=0
-#=======================================================================
-private_colormap: enabled=0
-
-#=======================================================================
-# fullscreen: ONLY IMPLEMENTED ON AMIGA
-# Request that Bochs occupy the entire screen instead of a
-# window.
-#
-# Examples:
-# fullscreen: enabled=0
-# fullscreen: enabled=1
-#=======================================================================
-#fullscreen: enabled=0
-#screenmode: name="sample"
-
-#=======================================================================
-# ne2k: NE2000 compatible ethernet adapter
-#
-# Examples:
-# ne2k: ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT
-#
-# ioaddr, irq: You probably won't need to change ioaddr and irq, unless there
-# are IRQ conflicts.
-#
-# mac: The MAC address MUST NOT match the address of any machine on the net.
-# Also, the first byte must be an even number (bit 0 set means a multicast
-# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast
-# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may
-# be other restrictions too. To be safe, just use the b0:c4... address.
-#
-# ethdev: The ethdev value is the name of the network interface on your host
-# platform. On UNIX machines, you can get the name by running ifconfig. On
-# Windows machines, you must run niclist to get the name of the ethdev.
-# Niclist source code is in misc/niclist.c and it is included in Windows
-# binary releases.
-#
-# script: The script value is optional, and is the name of a script that
-# is executed after bochs initialize the network interface. You can use
-# this script to configure this network interface, or enable masquerading.
-# This is mainly useful for the tun/tap devices that only exist during
-# Bochs execution. The network interface name is supplied to the script
-# as first parameter
-#
-# If you don't want to make connections to any physical networks,
-# you can use the following 'ethmod's to simulate a virtual network.
-# null: All packets are discarded, but logged to a few files.
-# arpback: ARP is simulated. Disabled by default.
-# vde: Virtual Distributed Ethernet
-# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated.
-# The virtual host uses 192.168.10.1.
-# DHCP assigns 192.168.10.2 to the guest.
-# TFTP uses the ethdev value for the root directory and doesn't
-# overwrite files.
-#
-#=======================================================================
-# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD
-# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
-# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl"
-# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp"
-
-#=======================================================================
-# KEYBOARD_MAPPING:
-# This enables a remap of a physical localized keyboard to a
-# virtualized us keyboard, as the PC architecture expects.
-# If enabled, the keymap file must be specified.
-#
-# Examples:
-# keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-de.map
-#=======================================================================
-keyboard_mapping: enabled=0, map=
-
-#=======================================================================
-# KEYBOARD_TYPE:
-# Type of keyboard return by a "identify keyboard" command to the
-# keyboard controler. It must be one of "xt", "at" or "mf".
-# Defaults to "mf". It should be ok for almost everybody. A known
-# exception is french macs, that do have a "at"-like keyboard.
-#
-# Examples:
-# keyboard_type: mf
-#=======================================================================
-#keyboard_type: mf
-
-#=======================================================================
-# USER_SHORTCUT:
-# This defines the keyboard shortcut to be sent when you press the "user"
-# button in the headerbar. The shortcut string is a combination of maximum
-# 3 key names (listed below) separated with a '-' character. The old-style
-# syntax (without the '-') still works for the key combinations supported
-# in Bochs 2.2.1.
-# Valid key names:
-# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc",
-# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup",
-# "plus", "right", "shift", "space", "tab", "up", and "win".
-#
-# Example:
-# user_shortcut: keys=ctrl-alt-del
-#=======================================================================
-#user_shortcut: keys=ctrl-alt-del
-
-#=======================================================================
-# I440FXSUPPORT:
-# This option controls the presence of the i440FX PCI chipset. You can
-# also specify the devices connected to PCI slots. Up to 5 slots are
-# available now. These devices are currently supported: ne2k, pcivga,
-# pcidev and pcipnic. If Bochs is compiled with Cirrus SVGA support
-# you'll have the additional choice 'cirrus'.
-#
-# Example:
-# i440fxsupport: enabled=1, slot1=pcivga, slot2=ne2k
-#=======================================================================
-#i440fxsupport: enabled=1
-
-#=======================================================================
-# USB1:
-# This option controls the presence of the USB root hub which is a part
-# of the i440FX PCI chipset. With the portX option you can connect devices
-# to the hub (currently supported: 'mouse' and 'keypad'). If you connect
-# the mouse to one of the ports and use the mouse option 'type=usb' you'll
-# have a 3-button USB mouse.
-#
-# Example:
-# usb1: enabled=1, port1=mouse, port2=keypad
-#=======================================================================
-#usb1: enabled=1
-
-#=======================================================================
-# CMOSIMAGE:
-# This defines image file that can be loaded into the CMOS RAM at startup.
-# The rtc_init parameter controls whether initialize the RTC with values stored
-# in the image. By default the time0 argument given to the clock option is used.
-# With 'rtc_init=image' the image is the source for the initial time.
-#
-# Example:
-# cmosimage: file=cmos.img, rtc_init=image
-#=======================================================================
-#cmosimage: file=cmos.img, rtc_init=time0
-
-#=======================================================================
-# other stuff
-#=======================================================================
-#magic_break: enabled=1
-#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
-#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
-#text_snapshot_check: enable
-
-#-------------------------
-# PCI host device mapping
-#-------------------------
-#pcidev: vendor=0x1234, device=0x5678
-
-#=======================================================================
-# GDBSTUB:
-# Enable GDB stub. See user documentation for details.
-# Default value is enabled=0.
-#=======================================================================
-#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0
-
-#=======================================================================
-# IPS:
-# The IPS directive is DEPRECATED. Use the parameter IPS of the CPU
-# directive instead.
-#=======================================================================
-#ips: 10000000
-
-#=======================================================================
-# for Macintosh, use the style of pathnames in the following
-# examples.
-#
-# vgaromimage: :bios:VGABIOS-elpin-2.40
-# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000
-# floppya: 1_44=[fd:], status=inserted
-#=======================================================================
+++ /dev/null
-#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();
-}
+++ /dev/null
-// 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;
- uint entry;
- uint phoff;
- uint shoff;
- uint flags;
- ushort ehsize;
- ushort phentsize;
- ushort phnum;
- ushort shentsize;
- ushort shnum;
- ushort shstrndx;
-};
-
-// Program section header
-struct proghdr {
- uint type;
- uint off;
- uint vaddr;
- uint paddr;
- uint filesz;
- uint memsz;
- uint flags;
- uint 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
+++ /dev/null
-# The xv6 kernel starts executing in this file. This file is linked with
-# the kernel C code, so it can refer to kernel symbols such as main().
-# The boot block (bootasm.S and bootmain.c) jumps to entry below.
-
-# Multiboot header, for multiboot boot loaders like GNU Grub.
-# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
-#
-# Using GRUB 2, you can boot xv6 from a file stored in a
-# Linux file system by copying kernel or kernelmemfs to /boot
-# and then adding this menu entry:
-#
-# menuentry "xv6" {
-# insmod ext2
-# set root='(hd0,msdos1)'
-# set kernel='/boot/kernel'
-# echo "Loading ${kernel}..."
-# multiboot ${kernel} ${kernel}
-# boot
-# }
-
-#include "asm.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "param.h"
-
-# Multiboot header. Data to direct multiboot loader.
-.p2align 2
-.text
-.globl multiboot_header
-multiboot_header:
- #define magic 0x1badb002
- #define flags 0
- .long magic
- .long flags
- .long (-magic-flags)
-
-# By convention, the _start symbol specifies the ELF entry point.
-# Since we haven't set up virtual memory yet, our entry point is
-# the physical address of 'entry'.
-.globl _start
-_start = V2P_WO(entry)
-
-# Entering xv6 on boot processor, with paging off.
-.globl entry
-entry:
- # Turn on page size extension for 4Mbyte pages
- movl %cr4, %eax
- orl $(CR4_PSE), %eax
- movl %eax, %cr4
- # Set page directory
- movl $(V2P_WO(entrypgdir)), %eax
- movl %eax, %cr3
- # Turn on paging.
- movl %cr0, %eax
- orl $(CR0_PG|CR0_WP), %eax
- movl %eax, %cr0
-
- # Set up the stack pointer.
- movl $(stack + KSTACKSIZE), %esp
-
- # Jump to main(), and switch to executing at
- # high addresses. The indirect call is needed because
- # the assembler produces a PC-relative instruction
- # for a direct jump.
- mov $main, %eax
- jmp *%eax
-
-.comm stack, KSTACKSIZE
+++ /dev/null
-#include "asm.h"
-#include "memlayout.h"
-#include "mmu.h"
-
-# Each non-boot CPU ("AP") is started up in response to a STARTUP
-# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
-# Specification says that the AP will start in real mode with CS:IP
-# set to XY00:0000, where XY is an 8-bit value sent with the
-# STARTUP. Thus this code must start at a 4096-byte boundary.
-#
-# Because this code sets DS to zero, it must sit
-# at an address in the low 2^16 bytes.
-#
-# Startothers (in main.c) sends the STARTUPs one at a time.
-# It copies this code (start) at 0x7000. It puts the address of
-# a newly allocated per-core stack in start-4,the address of the
-# place to jump to (mpenter) in start-8, and the physical address
-# of entrypgdir in start-12.
-#
-# This code combines elements of bootasm.S and entry.S.
-
-.code16
-.globl start
-start:
- cli
-
- # Zero data segment registers DS, ES, and SS.
- xorw %ax,%ax
- movw %ax,%ds
- movw %ax,%es
- movw %ax,%ss
-
- # Switch from real to protected mode. Use a bootstrap GDT that makes
- # virtual addresses map directly to physical addresses so that the
- # effective memory map doesn't change during the transition.
- lgdt gdtdesc
- movl %cr0, %eax
- orl $CR0_PE, %eax
- movl %eax, %cr0
-
- # Complete the transition to 32-bit protected mode by using a long jmp
- # to reload %cs and %eip. The segment descriptors are set up with no
- # translation, so that the mapping is still the identity mapping.
- ljmpl $(SEG_KCODE<<3), $(start32)
-
-//PAGEBREAK!
-.code32 # Tell assembler to generate 32-bit code now.
-start32:
- # Set up the protected-mode data segment registers
- movw $(SEG_KDATA<<3), %ax # Our data segment selector
- movw %ax, %ds # -> DS: Data Segment
- movw %ax, %es # -> ES: Extra Segment
- movw %ax, %ss # -> SS: Stack Segment
- movw $0, %ax # Zero segments not ready for use
- movw %ax, %fs # -> FS
- movw %ax, %gs # -> GS
-
- # Turn on page size extension for 4Mbyte pages
- movl %cr4, %eax
- orl $(CR4_PSE), %eax
- movl %eax, %cr4
- # Use entrypgdir as our initial page table
- movl (start-12), %eax
- movl %eax, %cr3
- # Turn on paging.
- movl %cr0, %eax
- orl $(CR0_PE|CR0_PG|CR0_WP), %eax
- movl %eax, %cr0
-
- # Switch to the stack allocated by startothers()
- movl (start-4), %esp
- # Call mpenter()
- call *(start-8)
-
- movw $0x8a00, %ax
- movw %ax, %dx
- outw %ax, %dx
- movw $0x8ae0, %ax
- outw %ax, %dx
-spin:
- jmp spin
-
-.p2align 2
-gdt:
- SEG_NULLASM
- SEG_ASM(STA_X|STA_R, 0, 0xffffffff)
- SEG_ASM(STA_W, 0, 0xffffffff)
-
-
-gdtdesc:
- .word (gdtdesc - gdt - 1)
- .long gdt
-
+++ /dev/null
-#include "types.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "defs.h"
-#include "x86.h"
-#include "elf.h"
-
-int
-exec(char *path, char **argv)
-{
- char *s, *last;
- int i, off;
- uint argc, sz, sp, ustack[3+MAXARG+1];
- struct elfhdr elf;
- struct inode *ip;
- struct proghdr ph;
- pde_t *pgdir, *oldpgdir;
- struct proc *curproc = myproc();
-
- begin_op();
-
- if((ip = namei(path)) == 0){
- end_op();
- cprintf("exec: fail\n");
- return -1;
- }
- ilock(ip);
- pgdir = 0;
-
- // Check ELF header
- if(readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf))
- goto bad;
- if(elf.magic != ELF_MAGIC)
- goto bad;
-
- if((pgdir = setupkvm()) == 0)
- goto bad;
-
- // Load program into memory.
- sz = 0;
- for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
- if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
- goto bad;
- if(ph.type != ELF_PROG_LOAD)
- continue;
- if(ph.memsz < ph.filesz)
- goto bad;
- if(ph.vaddr + ph.memsz < ph.vaddr)
- goto bad;
- if((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0)
- goto bad;
- if(ph.vaddr % PGSIZE != 0)
- goto bad;
- if(loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0)
- goto bad;
- }
- iunlockput(ip);
- end_op();
- ip = 0;
-
- // Allocate two pages at the next page boundary.
- // Make the first inaccessible. Use the second as the user stack.
- sz = PGROUNDUP(sz);
- if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0)
- goto bad;
- clearpteu(pgdir, (char*)(sz - 2*PGSIZE));
- sp = sz;
-
- // Push argument strings, prepare rest of stack in ustack.
- for(argc = 0; argv[argc]; argc++) {
- if(argc >= MAXARG)
- goto bad;
- sp = (sp - (strlen(argv[argc]) + 1)) & ~3;
- if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
- goto bad;
- ustack[3+argc] = sp;
- }
- ustack[3+argc] = 0;
-
- ustack[0] = 0xffffffff; // fake return PC
- ustack[1] = argc;
- ustack[2] = sp - (argc+1)*4; // argv pointer
-
- sp -= (3+argc+1) * 4;
- if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
- goto bad;
-
- // Save program name for debugging.
- for(last=s=path; *s; s++)
- if(*s == '/')
- last = s+1;
- safestrcpy(curproc->name, last, sizeof(curproc->name));
-
- // Commit to the user image.
- oldpgdir = curproc->pgdir;
- curproc->pgdir = pgdir;
- curproc->sz = sz;
- curproc->tf->eip = elf.entry; // main
- curproc->tf->esp = sp;
- switchuvm(curproc);
- freevm(oldpgdir);
- return 0;
-
- bad:
- if(pgdir)
- freevm(pgdir);
- if(ip){
- iunlockput(ip);
- end_op();
- }
- return -1;
-}
+++ /dev/null
-#define O_RDONLY 0x000
-#define O_WRONLY 0x001
-#define O_RDWR 0x002
-#define O_CREATE 0x200
+++ /dev/null
-//
-// File descriptors
-//
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "fs.h"
-#include "spinlock.h"
-#include "sleeplock.h"
-#include "file.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.
-int
-filestat(struct file *f, struct stat *st)
-{
- if(f->type == FD_INODE){
- ilock(f->ip);
- stati(f->ip, st);
- iunlock(f->ip);
- return 0;
- }
- return -1;
-}
-
-// Read from file f.
-int
-fileread(struct file *f, char *addr, int n)
-{
- int r;
-
- if(f->readable == 0)
- return -1;
- if(f->type == FD_PIPE)
- return piperead(f->pipe, addr, n);
- if(f->type == FD_INODE){
- ilock(f->ip);
- if((r = readi(f->ip, addr, f->off, n)) > 0)
- f->off += r;
- iunlock(f->ip);
- return r;
- }
- panic("fileread");
-}
-
-//PAGEBREAK!
-// Write to file f.
-int
-filewrite(struct file *f, char *addr, int n)
-{
- int r;
-
- if(f->writable == 0)
- return -1;
- if(f->type == FD_PIPE)
- return pipewrite(f->pipe, addr, n);
- 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, 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;
- }
- return i == n ? n : -1;
- }
- panic("filewrite");
-}
-
+++ /dev/null
-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*, char*, int);
- int (*write)(struct inode*, char*, int);
-};
-
-extern struct devsw devsw[];
-
-#define CONSOLE 1
+++ /dev/null
-// 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<N; n++){
- pid = fork();
- if(pid < 0)
- break;
- if(pid == 0)
- exit();
- }
-
- if(n == N){
- printf(1, "fork claimed to work N times!\n", 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");
-}
-
-int
-main(void)
-{
- forktest();
- exit();
-}
+++ /dev/null
-// 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 "defs.h"
-#include "param.h"
-#include "stat.h"
-#include "mmu.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;
-
- 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);
- cprintf("sb: size %d nblocks %d ninodes %d nlog %d logstart %d\
- inodestart %d bmap start %d\n", sb.size, sb.nblocks,
- sb.ninodes, sb.nlog, sb.logstart, sb.inodestart,
- sb.bmapstart);
-}
-
-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)
-{
- acquiresleep(&ip->lock);
- if(ip->valid && ip->nlink == 0){
- acquire(&icache.lock);
- int r = ip->ref;
- release(&icache.lock);
- if(r == 1){
- // inode has no links and no other references: truncate and free.
- 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.
-int
-readi(struct inode *ip, char *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, dst, n);
- }
-
- if(off > ip->size || off + n < off)
- return -1;
- if(off + n > ip->size)
- n = ip->size - off;
-
- for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
- bp = bread(ip->dev, bmap(ip, off/BSIZE));
- m = min(n - tot, BSIZE - off%BSIZE);
- memmove(dst, bp->data + off%BSIZE, m);
- brelse(bp);
- }
- return n;
-}
-
-// PAGEBREAK!
-// Write data to inode.
-// Caller must hold ip->lock.
-int
-writei(struct inode *ip, char *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, src, n);
- }
-
- if(off > ip->size || off + n < off)
- return -1;
- if(off + n > MAXFILE*BSIZE)
- return -1;
-
- for(tot=0; tot<n; tot+=m, off+=m, src+=m){
- bp = bread(ip->dev, bmap(ip, off/BSIZE));
- m = min(n - tot, BSIZE - off%BSIZE);
- memmove(bp->data + off%BSIZE, src, m);
- 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, (char*)&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, (char*)&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, (char*)&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);
-}
+++ /dev/null
-// On-disk file system format.
-// Both the kernel and user programs use this header file.
-
-
-#define ROOTINO 1 // root i-number
-#define BSIZE 512 // 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 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 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];
-};
-
+++ /dev/null
-// 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;
-}
-
+++ /dev/null
-// Simple PIO-based (non-DMA) IDE driver code.
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-#include "traps.h"
-#include "spinlock.h"
-#include "sleeplock.h"
-#include "fs.h"
-#include "buf.h"
-
-#define SECTOR_SIZE 512
-#define IDE_BSY 0x80
-#define IDE_DRDY 0x40
-#define IDE_DF 0x20
-#define IDE_ERR 0x01
-
-#define IDE_CMD_READ 0x20
-#define IDE_CMD_WRITE 0x30
-#define IDE_CMD_RDMUL 0xc4
-#define IDE_CMD_WRMUL 0xc5
-
-// idequeue points to the buf now being read/written to the disk.
-// idequeue->qnext points to the next buf to be processed.
-// You must hold idelock while manipulating queue.
-
-static struct spinlock idelock;
-static struct buf *idequeue;
-
-static int havedisk1;
-static void idestart(struct buf*);
-
-// Wait for IDE disk to become ready.
-static int
-idewait(int checkerr)
-{
- int r;
-
- while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
- ;
- if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0)
- return -1;
- return 0;
-}
-
-void
-ideinit(void)
-{
- int i;
-
- initlock(&idelock, "ide");
- ioapicenable(IRQ_IDE, ncpu - 1);
- idewait(0);
-
- // Check if disk 1 is present
- outb(0x1f6, 0xe0 | (1<<4));
- for(i=0; i<1000; i++){
- if(inb(0x1f7) != 0){
- havedisk1 = 1;
- break;
- }
- }
-
- // Switch back to disk 0.
- outb(0x1f6, 0xe0 | (0<<4));
-}
-
-// Start the request for b. Caller must hold idelock.
-static void
-idestart(struct buf *b)
-{
- if(b == 0)
- panic("idestart");
- if(b->blockno >= FSSIZE)
- panic("incorrect blockno");
- int sector_per_block = BSIZE/SECTOR_SIZE;
- int sector = b->blockno * sector_per_block;
- int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL;
- int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL;
-
- if (sector_per_block > 7) panic("idestart");
-
- idewait(0);
- outb(0x3f6, 0); // generate interrupt
- outb(0x1f2, sector_per_block); // number of sectors
- outb(0x1f3, sector & 0xff);
- outb(0x1f4, (sector >> 8) & 0xff);
- outb(0x1f5, (sector >> 16) & 0xff);
- outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((sector>>24)&0x0f));
- if(b->flags & B_DIRTY){
- outb(0x1f7, write_cmd);
- outsl(0x1f0, b->data, BSIZE/4);
- } else {
- outb(0x1f7, read_cmd);
- }
-}
-
-// Interrupt handler.
-void
-ideintr(void)
-{
- struct buf *b;
-
- // First queued buffer is the active request.
- acquire(&idelock);
-
- if((b = idequeue) == 0){
- release(&idelock);
- return;
- }
- idequeue = b->qnext;
-
- // Read data if needed.
- if(!(b->flags & B_DIRTY) && idewait(1) >= 0)
- insl(0x1f0, b->data, BSIZE/4);
-
- // Wake process waiting for this buf.
- b->flags |= B_VALID;
- b->flags &= ~B_DIRTY;
- wakeup(b);
-
- // Start disk on next buf in queue.
- if(idequeue != 0)
- idestart(idequeue);
-
- release(&idelock);
-}
-
-//PAGEBREAK!
-// Sync buf with disk.
-// 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
-iderw(struct buf *b)
-{
- struct buf **pp;
-
- if(!holdingsleep(&b->lock))
- panic("iderw: buf not locked");
- if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
- panic("iderw: nothing to do");
- if(b->dev != 0 && !havedisk1)
- panic("iderw: ide disk 1 not present");
-
- acquire(&idelock); //DOC:acquire-lock
-
- // Append b to idequeue.
- b->qnext = 0;
- for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue
- ;
- *pp = b;
-
- // Start disk if necessary.
- if(idequeue == b)
- idestart(b);
-
- // Wait for request to finish.
- while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
- sleep(b, &idelock);
- }
-
-
- release(&idelock);
-}
--- /dev/null
+//
+// assembler macros to create x86 segments
+//
+#ifndef INCLUDE_ASM_ASM_h_
+#define INCLUDE_ASM_ASM_h_
+
+#define SEG_NULLASM \
+ .word 0, 0; \
+ .byte 0, 0, 0, 0
+
+// The 0xC0 means the limit is in 4096-byte units
+// and (for executable segments) 32-bit mode.
+#define SEG_ASM(type,base,lim) \
+ .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
+ .byte (((base) >> 16) & 0xff), (0x90 | (type)), \
+ (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
+
+#define STA_X 0x8 // Executable segment
+#define STA_W 0x2 // Writeable (non-executable segments)
+#define STA_R 0x2 // Readable (executable segments)
+
+#endif // INCLUDE_ASM_ASM_h_
--- /dev/null
+// Routines to let C code use special x86 instructions.
+#ifndef INCLUDE_ASM_X86_h_
+#define INCLUDE_ASM_X86_h_
+
+#include "types.h"
+
+static inline uchar
+inb(ushort port)
+{
+ uchar data;
+
+ __asm__ volatile("in %1,%0" : "=a" (data) : "d" (port));
+ return data;
+}
+
+static inline void
+insl(int port, void *addr, int cnt)
+{
+ __asm__ volatile("cld; rep insl" :
+ "=D" (addr), "=c" (cnt) :
+ "d" (port), "0" (addr), "1" (cnt) :
+ "memory", "cc");
+}
+
+static inline void
+outb(ushort port, uchar data)
+{
+ __asm__ volatile("out %0,%1" : : "a" (data), "d" (port));
+}
+
+static inline void
+outw(ushort port, ushort data)
+{
+ __asm__ volatile("out %0,%1" : : "a" (data), "d" (port));
+}
+
+static inline void
+outsl(int port, const void *addr, int cnt)
+{
+ __asm__ volatile("cld; rep outsl" :
+ "=S" (addr), "=c" (cnt) :
+ "d" (port), "0" (addr), "1" (cnt) :
+ "cc");
+}
+
+static inline void
+stosb(void *addr, int data, int cnt)
+{
+ __asm__ volatile("cld; rep stosb" :
+ "=D" (addr), "=c" (cnt) :
+ "0" (addr), "1" (cnt), "a" (data) :
+ "memory", "cc");
+}
+
+static inline void
+stosl(void *addr, int data, int cnt)
+{
+ __asm__ volatile("cld; rep stosl" :
+ "=D" (addr), "=c" (cnt) :
+ "0" (addr), "1" (cnt), "a" (data) :
+ "memory", "cc");
+}
+
+struct segdesc;
+
+static inline void
+lgdt(struct segdesc *p, int size)
+{
+ volatile ushort pd[3];
+
+ pd[0] = size-1;
+ pd[1] = (uint)p;
+ pd[2] = (uint)p >> 16;
+
+ __asm__ volatile("lgdt (%0)" : : "r" (pd));
+}
+
+struct gatedesc;
+
+static inline void
+lidt(struct gatedesc *p, int size)
+{
+ volatile ushort pd[3];
+
+ pd[0] = size-1;
+ pd[1] = (uint)p;
+ pd[2] = (uint)p >> 16;
+
+ __asm__ volatile("lidt (%0)" : : "r" (pd));
+}
+
+static inline void
+ltr(ushort sel)
+{
+ __asm__ volatile("ltr %0" : : "r" (sel));
+}
+
+static inline uint
+readeflags(void)
+{
+ uint eflags;
+ __asm__ volatile("pushfl; popl %0" : "=r" (eflags));
+ return eflags;
+}
+
+static inline void
+loadgs(ushort v)
+{
+ __asm__ volatile("movw %0, %%gs" : : "r" (v));
+}
+
+static inline void
+cli(void)
+{
+ __asm__ volatile("cli");
+}
+
+static inline void
+sti(void)
+{
+ __asm__ volatile("sti");
+}
+
+static inline uint
+rcr2(void)
+{
+ uint val;
+ __asm__ volatile("movl %%cr2,%0" : "=r" (val));
+ return val;
+}
+
+static inline void
+lcr3(uint val)
+{
+ __asm__ volatile("movl %0,%%cr3" : : "r" (val));
+}
+
+//PAGEBREAK: 36
+// Layout of the trap frame built on the stack by the
+// hardware and by trapasm.S, and passed to trap().
+struct trapframe {
+ // registers as pushed by pusha
+ uint edi;
+ uint esi;
+ uint ebp;
+ uint oesp; // useless & ignored
+ uint ebx;
+ uint edx;
+ uint ecx;
+ uint eax;
+
+ // rest of trap frame
+ ushort gs;
+ ushort padding1;
+ ushort fs;
+ ushort padding2;
+ ushort es;
+ ushort padding3;
+ ushort ds;
+ ushort padding4;
+ uint trapno;
+
+ // below here defined by x86 hardware
+ uint err;
+ uint eip;
+ ushort cs;
+ ushort padding5;
+ uint eflags;
+
+ // below here only when crossing rings, such as from user to kernel
+ uint esp;
+ ushort ss;
+ ushort padding6;
+};
+
+#endif // INCLUDE_ASM_X86_h_
--- /dev/null
+// Format of an ELF executable file
+#ifndef INCLUDE_ELF_h_
+#define INCLUDE_ELF_h_
+
+#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;
+ uint entry;
+ uint phoff;
+ uint shoff;
+ uint flags;
+ ushort ehsize;
+ ushort phentsize;
+ ushort phnum;
+ ushort shentsize;
+ ushort shnum;
+ ushort shstrndx;
+};
+
+// Program section header
+struct proghdr {
+ uint type;
+ uint off;
+ uint vaddr;
+ uint paddr;
+ uint filesz;
+ uint memsz;
+ uint flags;
+ uint 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
+
+#endif // INCLUDE_ELF_h_
--- /dev/null
+#ifndef INCLUDE_FCNTL_h_
+#define INCLUDE_FCNTL_h_
+
+#define O_RDONLY 0x000
+#define O_WRONLY 0x001
+#define O_RDWR 0x002
+#define O_CREATE 0x200
+
+#endif // INCLUDE_FCNTL_h_
--- /dev/null
+// On-disk file system format.
+// Both the kernel and user programs use this header file.
+
+
+#define ROOTINO 1 // root i-number
+#define BSIZE 512 // 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 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 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];
+};
+
--- /dev/null
+// Memory layout
+#ifndef INCLUDE_MEMLAYOUT_h_
+#define INCLUDE_MEMLAYOUT_h_
+
+#define EXTMEM 0x100000 // Start of extended memory
+#define PHYSTOP 0xE000000 // Top physical memory
+#define DEVSPACE 0xFE000000 // Other devices are at high addresses
+
+// Key addresses for address space layout (see kmap in vm.c for layout)
+#define KERNBASE 0x80000000 // First kernel virtual address
+#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
+
+#define V2P(a) (((uint) (a)) - KERNBASE)
+#define P2V(a) ((void *)(((char *) (a)) + KERNBASE))
+
+#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
+#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts
+
+#endif // INCLUDE_MEMLAYOUT_h_
--- /dev/null
+// This file contains definitions for the
+// x86 memory management unit (MMU).
+#ifndef INCLUDE_MMU_h_
+#define INCLUDE_MMU_h_
+
+// Eflags register
+#define FL_IF 0x00000200 // Interrupt Enable
+
+// Control Register flags
+#define CR0_PE 0x00000001 // Protection Enable
+#define CR0_WP 0x00010000 // Write Protect
+#define CR0_PG 0x80000000 // Paging
+
+#define CR4_PSE 0x00000010 // Page size extension
+
+// various segment selectors.
+#define SEG_KCODE 1 // kernel code
+#define SEG_KDATA 2 // kernel data+stack
+#define SEG_UCODE 3 // user code
+#define SEG_UDATA 4 // user data+stack
+#define SEG_TSS 5 // this process's task state
+
+// cpu->gdt[NSEGS] holds the above segments.
+#define NSEGS 6
+
+#ifndef __ASSEMBLER__
+// Segment Descriptor
+struct segdesc {
+ uint lim_15_0 : 16; // Low bits of segment limit
+ uint base_15_0 : 16; // Low bits of segment base address
+ uint base_23_16 : 8; // Middle bits of segment base address
+ uint type : 4; // Segment type (see STS_ constants)
+ uint s : 1; // 0 = system, 1 = application
+ uint dpl : 2; // Descriptor Privilege Level
+ uint p : 1; // Present
+ uint lim_19_16 : 4; // High bits of segment limit
+ uint avl : 1; // Unused (available for software use)
+ uint rsv1 : 1; // Reserved
+ uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
+ uint g : 1; // Granularity: limit scaled by 4K when set
+ uint base_31_24 : 8; // High bits of segment base address
+};
+
+// Normal segment
+#define SEG(type, base, lim, dpl) (struct segdesc) \
+{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
+ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
+ (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
+#define SEG16(type, base, lim, dpl) (struct segdesc) \
+{ (lim) & 0xffff, (uint)(base) & 0xffff, \
+ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
+ (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
+#endif
+
+#define DPL_USER 0x3 // User DPL
+
+// Application segment type bits
+#define STA_X 0x8 // Executable segment
+#define STA_W 0x2 // Writeable (non-executable segments)
+#define STA_R 0x2 // Readable (executable segments)
+
+// System segment type bits
+#define STS_T32A 0x9 // Available 32-bit TSS
+#define STS_IG32 0xE // 32-bit Interrupt Gate
+#define STS_TG32 0xF // 32-bit Trap Gate
+
+// A virtual address 'la' has a three-part structure as follows:
+//
+// +--------10------+-------10-------+---------12----------+
+// | Page Directory | Page Table | Offset within Page |
+// | Index | Index | |
+// +----------------+----------------+---------------------+
+// \--- PDX(va) --/ \--- PTX(va) --/
+
+// page directory index
+#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)
+
+// page table index
+#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)
+
+// construct virtual address from indexes and offset
+#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
+
+// Page directory and page table constants.
+#define NPDENTRIES 1024 // # directory entries per page directory
+#define NPTENTRIES 1024 // # PTEs per page table
+#define PGSIZE 4096 // bytes mapped by a page
+
+#define PTXSHIFT 12 // offset of PTX in a linear address
+#define PDXSHIFT 22 // offset of PDX in a linear address
+
+#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
+#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
+
+// Page table/directory entry flags.
+#define PTE_P 0x001 // Present
+#define PTE_W 0x002 // Writeable
+#define PTE_U 0x004 // User
+#define PTE_PS 0x080 // Page Size
+
+// Address in page table or page directory entry
+#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
+#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)
+
+#ifndef __ASSEMBLER__
+typedef uint pte_t;
+
+// Task state segment format
+struct taskstate {
+ uint link; // Old ts selector
+ uint esp0; // Stack pointers and segment selectors
+ ushort ss0; // after an increase in privilege level
+ ushort padding1;
+ uint *esp1;
+ ushort ss1;
+ ushort padding2;
+ uint *esp2;
+ ushort ss2;
+ ushort padding3;
+ void *cr3; // Page directory base
+ uint *eip; // Saved state from last task switch
+ uint eflags;
+ uint eax; // More saved state (registers)
+ uint ecx;
+ uint edx;
+ uint ebx;
+ uint *esp;
+ uint *ebp;
+ uint esi;
+ uint edi;
+ ushort es; // Even more saved state (segment selectors)
+ ushort padding4;
+ ushort cs;
+ ushort padding5;
+ ushort ss;
+ ushort padding6;
+ ushort ds;
+ ushort padding7;
+ ushort fs;
+ ushort padding8;
+ ushort gs;
+ ushort padding9;
+ ushort ldt;
+ ushort padding10;
+ ushort t; // Trap on task switch
+ ushort iomb; // I/O map base address
+};
+
+// Gate descriptors for interrupts and traps
+struct gatedesc {
+ uint off_15_0 : 16; // low 16 bits of offset in segment
+ uint cs : 16; // code segment selector
+ uint args : 5; // # args, 0 for interrupt/trap gates
+ uint rsv1 : 3; // reserved(should be zero I guess)
+ uint type : 4; // type(STS_{IG32,TG32})
+ uint s : 1; // must be 0 (system)
+ uint dpl : 2; // descriptor(meaning new) privilege level
+ uint p : 1; // Present
+ uint off_31_16 : 16; // high bits of offset in segment
+};
+
+// Set up a normal interrupt/trap gate descriptor.
+// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
+// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
+// - sel: Code segment selector for interrupt/trap handler
+// - off: Offset in code segment for interrupt/trap handler
+// - dpl: Descriptor Privilege Level -
+// the privilege level required for software to invoke
+// this interrupt/trap gate explicitly using an int instruction.
+#define SETGATE(gate, istrap, sel, off, d) \
+{ \
+ (gate).off_15_0 = (uint)(off) & 0xffff; \
+ (gate).cs = (sel); \
+ (gate).args = 0; \
+ (gate).rsv1 = 0; \
+ (gate).type = (istrap) ? STS_TG32 : STS_IG32; \
+ (gate).s = 0; \
+ (gate).dpl = (d); \
+ (gate).p = 1; \
+ (gate).off_31_16 = (uint)(off) >> 16; \
+}
+
+#endif
+
+#endif // INCLUDE_MMU_h_
--- /dev/null
+#ifndef INCLUDE_PARAM_h_
+#define INCLUDE_PARAM_h_
+
+#define NPROC 64 // maximum number of processes
+#define KSTACKSIZE 4096 // size of per-process kernel stack
+#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
+
+#endif // INCLUDE_PARAM_h_
--- /dev/null
+#ifndef INCLUDE_STAT_h_
+#define INCLUDE_STAT_h_
+
+#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
+};
+
+#endif // INCLUDE_STAT_h_
--- /dev/null
+// 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
--- /dev/null
+// x86 trap and interrupt constants.
+
+// Processor-defined:
+#define T_DIVIDE 0 // divide error
+#define T_DEBUG 1 // debug exception
+#define T_NMI 2 // non-maskable interrupt
+#define T_BRKPT 3 // breakpoint
+#define T_OFLOW 4 // overflow
+#define T_BOUND 5 // bounds check
+#define T_ILLOP 6 // illegal opcode
+#define T_DEVICE 7 // device not available
+#define T_DBLFLT 8 // double fault
+// #define T_COPROC 9 // reserved (not used since 486)
+#define T_TSS 10 // invalid task switch segment
+#define T_SEGNP 11 // segment not present
+#define T_STACK 12 // stack exception
+#define T_GPFLT 13 // general protection fault
+#define T_PGFLT 14 // page fault
+// #define T_RES 15 // reserved
+#define T_FPERR 16 // floating point error
+#define T_ALIGN 17 // aligment check
+#define T_MCHK 18 // machine check
+#define T_SIMDERR 19 // SIMD floating point error
+
+// These are arbitrarily chosen, but with care not to overlap
+// processor defined exceptions or interrupt vectors.
+#define T_SYSCALL 64 // system call
+#define T_DEFAULT 500 // catchall
+
+#define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ
+
+#define IRQ_TIMER 0
+#define IRQ_KBD 1
+#define IRQ_COM1 4
+#define IRQ_IDE 14
+#define IRQ_ERROR 19
+#define IRQ_SPURIOUS 31
+
--- /dev/null
+#ifndef INCLUDE_TYPES_h_
+#define INCLUDE_TYPES_h_
+
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef uint pde_t;
+
+#endif //INCLUDE_TYPES_h_
+++ /dev/null
-// 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");
- }
-}
+++ /dev/null
-# Initial process execs /init.
-# This code runs in user space.
-
-#include "syscall.h"
-#include "traps.h"
-
-
-# exec(init, argv)
-.globl start
-start:
- pushl $argv
- pushl $init
- pushl $0 // where caller pc would be
- movl $SYS_exec, %eax
- int $T_SYSCALL
-
-# for(;;) exit();
-exit:
- movl $SYS_exit, %eax
- int $T_SYSCALL
- jmp exit
-
-# char init[] = "/init\0";
-init:
- .string "/init\0"
-
-# char *argv[] = { init, 0 };
-.p2align 2
-argv:
- .long init
- .long 0
-
+++ /dev/null
-// 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 "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 = (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);
-}
+++ /dev/null
-// 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 "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "spinlock.h"
-
-void freerange(void *vstart, void *vend);
-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;
- int use_lock;
- struct run *freelist;
-} kmem;
-
-// Initialization happens in two phases.
-// 1. main() calls kinit1() while still using entrypgdir to place just
-// the pages mapped by entrypgdir on free list.
-// 2. main() calls kinit2() with the rest of the physical pages
-// after installing a full page table that maps them on all cores.
-void
-kinit1(void *vstart, void *vend)
-{
- initlock(&kmem.lock, "kmem");
- kmem.use_lock = 0;
- freerange(vstart, vend);
-}
-
-void
-kinit2(void *vstart, void *vend)
-{
- freerange(vstart, vend);
- kmem.use_lock = 1;
-}
-
-void
-freerange(void *vstart, void *vend)
-{
- char *p;
- p = (char*)PGROUNDUP((uint)vstart);
- for(; p + PGSIZE <= (char*)vend; 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(char *v)
-{
- struct run *r;
-
- if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
- panic("kfree");
-
- // Fill with junk to catch dangling refs.
- memset(v, 1, PGSIZE);
-
- if(kmem.use_lock)
- acquire(&kmem.lock);
- r = (struct run*)v;
- r->next = kmem.freelist;
- kmem.freelist = r;
- if(kmem.use_lock)
- 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.
-char*
-kalloc(void)
-{
- struct run *r;
-
- if(kmem.use_lock)
- acquire(&kmem.lock);
- r = kmem.freelist;
- if(r)
- kmem.freelist = r->next;
- if(kmem.use_lock)
- release(&kmem.lock);
- return (char*)r;
-}
-
+++ /dev/null
-#include "types.h"
-#include "x86.h"
-#include "defs.h"
-#include "kbd.h"
-
-int
-kbdgetc(void)
-{
- static uint shift;
- static uchar *charcode[4] = {
- normalmap, shiftmap, ctlmap, ctlmap
- };
- uint st, data, c;
-
- st = inb(KBSTATP);
- if((st & KBS_DIB) == 0)
- return -1;
- data = inb(KBDATAP);
-
- if(data == 0xE0){
- shift |= E0ESC;
- return 0;
- } else if(data & 0x80){
- // Key released
- data = (shift & E0ESC ? data : data & 0x7F);
- shift &= ~(shiftcode[data] | E0ESC);
- return 0;
- } else if(shift & E0ESC){
- // Last character was an E0 escape; or with 0x80
- data |= 0x80;
- shift &= ~E0ESC;
- }
-
- shift |= shiftcode[data];
- shift ^= togglecode[data];
- c = charcode[shift & (CTL | SHIFT)][data];
- if(shift & CAPSLOCK){
- if('a' <= c && c <= 'z')
- c += 'A' - 'a';
- else if('A' <= c && c <= 'Z')
- c += 'a' - 'A';
- }
- return c;
-}
-
-void
-kbdintr(void)
-{
- consoleintr(kbdgetc);
-}
+++ /dev/null
-// PC keyboard interface constants
-
-#define KBSTATP 0x64 // kbd controller status port(I)
-#define KBS_DIB 0x01 // kbd data in buffer
-#define KBDATAP 0x60 // kbd data port(I)
-
-#define NO 0
-
-#define SHIFT (1<<0)
-#define CTL (1<<1)
-#define ALT (1<<2)
-
-#define CAPSLOCK (1<<3)
-#define NUMLOCK (1<<4)
-#define SCROLLLOCK (1<<5)
-
-#define E0ESC (1<<6)
-
-// Special keycodes
-#define KEY_HOME 0xE0
-#define KEY_END 0xE1
-#define KEY_UP 0xE2
-#define KEY_DN 0xE3
-#define KEY_LF 0xE4
-#define KEY_RT 0xE5
-#define KEY_PGUP 0xE6
-#define KEY_PGDN 0xE7
-#define KEY_INS 0xE8
-#define KEY_DEL 0xE9
-
-// C('A') == Control-A
-#define C(x) (x - '@')
-
-static uchar shiftcode[256] =
-{
- [0x1D] CTL,
- [0x2A] SHIFT,
- [0x36] SHIFT,
- [0x38] ALT,
- [0x9D] CTL,
- [0xB8] ALT
-};
-
-static uchar togglecode[256] =
-{
- [0x3A] CAPSLOCK,
- [0x45] NUMLOCK,
- [0x46] SCROLLLOCK
-};
-
-static uchar normalmap[256] =
-{
- NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
- '7', '8', '9', '0', '-', '=', '\b', '\t',
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
- 'o', 'p', '[', ']', '\n', NO, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
- '\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
- 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
- NO, ' ', NO, NO, NO, NO, NO, NO,
- NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
- '8', '9', '-', '4', '5', '6', '+', '1',
- '2', '3', '0', '.', NO, NO, NO, NO, // 0x50
- [0x9C] '\n', // KP_Enter
- [0xB5] '/', // KP_Div
- [0xC8] KEY_UP, [0xD0] KEY_DN,
- [0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
- [0xCB] KEY_LF, [0xCD] KEY_RT,
- [0x97] KEY_HOME, [0xCF] KEY_END,
- [0xD2] KEY_INS, [0xD3] KEY_DEL
-};
-
-static uchar shiftmap[256] =
-{
- NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
- '&', '*', '(', ')', '_', '+', '\b', '\t',
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
- 'O', 'P', '{', '}', '\n', NO, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
- '"', '~', NO, '|', 'Z', 'X', 'C', 'V',
- 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
- NO, ' ', NO, NO, NO, NO, NO, NO,
- NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
- '8', '9', '-', '4', '5', '6', '+', '1',
- '2', '3', '0', '.', NO, NO, NO, NO, // 0x50
- [0x9C] '\n', // KP_Enter
- [0xB5] '/', // KP_Div
- [0xC8] KEY_UP, [0xD0] KEY_DN,
- [0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
- [0xCB] KEY_LF, [0xCD] KEY_RT,
- [0x97] KEY_HOME, [0xCF] KEY_END,
- [0xD2] KEY_INS, [0xD3] KEY_DEL
-};
-
-static uchar ctlmap[256] =
-{
- NO, NO, NO, NO, NO, NO, NO, NO,
- NO, NO, NO, NO, NO, NO, NO, NO,
- C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
- C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
- C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
- NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
- C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
- [0x9C] '\r', // KP_Enter
- [0xB5] C('/'), // KP_Div
- [0xC8] KEY_UP, [0xD0] KEY_DN,
- [0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
- [0xCB] KEY_LF, [0xCD] KEY_RT,
- [0x97] KEY_HOME, [0xCF] KEY_END,
- [0xD2] KEY_INS, [0xD3] KEY_DEL
-};
-
+++ /dev/null
-/* Simple linker script for the JOS kernel.
- See the GNU ld 'info' manual ("info ld") to learn the syntax. */
-
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-ENTRY(_start)
-
-SECTIONS
-{
- /* Link the kernel at this address: "." means the current address */
- /* Must be equal to KERNLINK */
- . = 0x80100000;
-
- .text : AT(0x100000) {
- *(.text .stub .text.* .gnu.linkonce.t.*)
- }
-
- PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
-
- .rodata : {
- *(.rodata .rodata.* .gnu.linkonce.r.*)
- }
-
- /* Include debugging information in kernel memory */
- .stab : {
- PROVIDE(__STAB_BEGIN__ = .);
- *(.stab);
- PROVIDE(__STAB_END__ = .);
- BYTE(0) /* Force the linker to allocate space
- for this section */
- }
-
- .stabstr : {
- PROVIDE(__STABSTR_BEGIN__ = .);
- *(.stabstr);
- PROVIDE(__STABSTR_END__ = .);
- BYTE(0) /* Force the linker to allocate space
- for this section */
- }
-
- /* Adjust the address for the data segment to the next page */
- . = ALIGN(0x1000);
-
- /* Conventionally, Unix linkers provide pseudo-symbols
- * etext, edata, and end, at the end of the text, data, and bss.
- * For the kernel mapping, we need the address at the beginning
- * of the data section, but that's not one of the conventional
- * symbols, because the convention started before there was a
- * read-only rodata section between text and data. */
- PROVIDE(data = .);
-
- /* The data segment */
- .data : {
- *(.data)
- }
-
- PROVIDE(edata = .);
-
- .bss : {
- *(.bss)
- }
-
- PROVIDE(end = .);
-
- /DISCARD/ : {
- *(.eh_frame .note.GNU-stack)
- }
-}
+++ /dev/null
-#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<argc; i++)
- kill(atoi(argv[i]));
- exit();
-}
+++ /dev/null
-// The local APIC manages internal (non-I/O) interrupts.
-// See Chapter 8 & Appendix C of Intel processor manual volume 3.
-
-#include "param.h"
-#include "types.h"
-#include "defs.h"
-#include "date.h"
-#include "memlayout.h"
-#include "traps.h"
-#include "mmu.h"
-#include "x86.h"
-
-// Local APIC registers, divided by 4 for use as uint[] indices.
-#define ID (0x0020/4) // ID
-#define VER (0x0030/4) // Version
-#define TPR (0x0080/4) // Task Priority
-#define EOI (0x00B0/4) // EOI
-#define SVR (0x00F0/4) // Spurious Interrupt Vector
- #define ENABLE 0x00000100 // Unit Enable
-#define ESR (0x0280/4) // Error Status
-#define ICRLO (0x0300/4) // Interrupt Command
- #define INIT 0x00000500 // INIT/RESET
- #define STARTUP 0x00000600 // Startup IPI
- #define DELIVS 0x00001000 // Delivery status
- #define ASSERT 0x00004000 // Assert interrupt (vs deassert)
- #define DEASSERT 0x00000000
- #define LEVEL 0x00008000 // Level triggered
- #define BCAST 0x00080000 // Send to all APICs, including self.
- #define BUSY 0x00001000
- #define FIXED 0x00000000
-#define ICRHI (0x0310/4) // Interrupt Command [63:32]
-#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
- #define X1 0x0000000B // divide counts by 1
- #define PERIODIC 0x00020000 // Periodic
-#define PCINT (0x0340/4) // Performance Counter LVT
-#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
-#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
-#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
- #define MASKED 0x00010000 // Interrupt masked
-#define TICR (0x0380/4) // Timer Initial Count
-#define TCCR (0x0390/4) // Timer Current Count
-#define TDCR (0x03E0/4) // Timer Divide Configuration
-
-volatile uint *lapic; // Initialized in mp.c
-
-//PAGEBREAK!
-static void
-lapicw(int index, int value)
-{
- lapic[index] = value;
- lapic[ID]; // wait for write to finish, by reading
-}
-
-void
-lapicinit(void)
-{
- if(!lapic)
- return;
-
- // Enable local APIC; set spurious interrupt vector.
- lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
-
- // The timer repeatedly counts down at bus frequency
- // from lapic[TICR] and then issues an interrupt.
- // If xv6 cared more about precise timekeeping,
- // TICR would be calibrated using an external time source.
- lapicw(TDCR, X1);
- lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
- lapicw(TICR, 10000000);
-
- // Disable logical interrupt lines.
- lapicw(LINT0, MASKED);
- lapicw(LINT1, MASKED);
-
- // Disable performance counter overflow interrupts
- // on machines that provide that interrupt entry.
- if(((lapic[VER]>>16) & 0xFF) >= 4)
- lapicw(PCINT, MASKED);
-
- // Map error interrupt to IRQ_ERROR.
- lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
-
- // Clear error status register (requires back-to-back writes).
- lapicw(ESR, 0);
- lapicw(ESR, 0);
-
- // Ack any outstanding interrupts.
- lapicw(EOI, 0);
-
- // Send an Init Level De-Assert to synchronise arbitration ID's.
- lapicw(ICRHI, 0);
- lapicw(ICRLO, BCAST | INIT | LEVEL);
- while(lapic[ICRLO] & DELIVS)
- ;
-
- // Enable interrupts on the APIC (but not on the processor).
- lapicw(TPR, 0);
-}
-
-int
-lapicid(void)
-{
- if (!lapic)
- return 0;
- return lapic[ID] >> 24;
-}
-
-// Acknowledge interrupt.
-void
-lapiceoi(void)
-{
- if(lapic)
- lapicw(EOI, 0);
-}
-
-// Spin for a given number of microseconds.
-// On real hardware would want to tune this dynamically.
-void
-microdelay(int us)
-{
-}
-
-#define CMOS_PORT 0x70
-#define CMOS_RETURN 0x71
-
-// Start additional processor running entry code at addr.
-// See Appendix B of MultiProcessor Specification.
-void
-lapicstartap(uchar apicid, uint addr)
-{
- int i;
- ushort *wrv;
-
- // "The BSP must initialize CMOS shutdown code to 0AH
- // and the warm reset vector (DWORD based at 40:67) to point at
- // the AP startup code prior to the [universal startup algorithm]."
- outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code
- outb(CMOS_PORT+1, 0x0A);
- wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector
- wrv[0] = 0;
- wrv[1] = addr >> 4;
-
- // "Universal startup algorithm."
- // Send INIT (level-triggered) interrupt to reset other CPU.
- lapicw(ICRHI, apicid<<24);
- lapicw(ICRLO, INIT | LEVEL | ASSERT);
- microdelay(200);
- lapicw(ICRLO, INIT | LEVEL);
- microdelay(100); // should be 10ms, but too slow in Bochs!
-
- // Send startup IPI (twice!) to enter code.
- // Regular hardware is supposed to only accept a STARTUP
- // when it is in the halted state due to an INIT. So the second
- // should be ignored, but it is part of the official Intel algorithm.
- // Bochs complains about the second one. Too bad for Bochs.
- for(i = 0; i < 2; i++){
- lapicw(ICRHI, apicid<<24);
- lapicw(ICRLO, STARTUP | (addr>>12));
- microdelay(200);
- }
-}
-
-#define CMOS_STATA 0x0a
-#define CMOS_STATB 0x0b
-#define CMOS_UIP (1 << 7) // RTC update in progress
-
-#define SECS 0x00
-#define MINS 0x02
-#define HOURS 0x04
-#define DAY 0x07
-#define MONTH 0x08
-#define YEAR 0x09
-
-static uint
-cmos_read(uint reg)
-{
- outb(CMOS_PORT, reg);
- microdelay(200);
-
- return inb(CMOS_RETURN);
-}
-
-static void
-fill_rtcdate(struct rtcdate *r)
-{
- r->second = cmos_read(SECS);
- r->minute = cmos_read(MINS);
- r->hour = cmos_read(HOURS);
- r->day = cmos_read(DAY);
- r->month = cmos_read(MONTH);
- r->year = cmos_read(YEAR);
-}
-
-// qemu seems to use 24-hour GWT and the values are BCD encoded
-void
-cmostime(struct rtcdate *r)
-{
- struct rtcdate t1, t2;
- int sb, bcd;
-
- sb = cmos_read(CMOS_STATB);
-
- bcd = (sb & (1 << 2)) == 0;
-
- // make sure CMOS doesn't modify time while we read it
- for(;;) {
- fill_rtcdate(&t1);
- if(cmos_read(CMOS_STATA) & CMOS_UIP)
- continue;
- fill_rtcdate(&t2);
- if(memcmp(&t1, &t2, sizeof(t1)) == 0)
- break;
- }
-
- // convert
- if(bcd) {
-#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
- CONV(second);
- CONV(minute);
- CONV(hour );
- CONV(day );
- CONV(month );
- CONV(year );
-#undef CONV
- }
-
- *r = t1;
- r->year += 2000;
-}
+++ /dev/null
-#include "types.h"
-#include "stat.h"
-#include "user.h"
-
-int
-main(int argc, char *argv[])
-{
- if(argc != 3){
- printf(2, "Usage: ln old new\n");
- exit();
- }
- if(link(argv[1], argv[2]) < 0)
- printf(2, "link %s %s: failed\n", argv[1], argv[2]);
- exit();
-}
+++ /dev/null
-#include "types.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);
-}
-
+++ /dev/null
-#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<argc; i++)
- ls(argv[i]);
- exit();
-}
+++ /dev/null
-#include <stdatomic.h>
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-
-static void startothers(void);
-static void mpmain(void) __attribute__((noreturn));
-extern pde_t *kpgdir;
-extern char end[]; // first address after kernel loaded from ELF file
-
-// 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.
-int
-main(void)
-{
- kinit1(end, P2V(4*1024*1024)); // phys page allocator
- kvmalloc(); // kernel page table
- mpinit(); // detect other processors
- lapicinit(); // interrupt controller
- seginit(); // segment descriptors
- picinit(); // disable pic
- ioapicinit(); // another interrupt controller
- consoleinit(); // console hardware
- uartinit(); // serial port
- pinit(); // process table
- tvinit(); // trap vectors
- binit(); // buffer cache
- fileinit(); // file table
- ideinit(); // disk
- startothers(); // start other processors
- kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
- userinit(); // first user process
- mpmain(); // finish this processor's setup
-}
-
-// Other CPUs jump here from entryother.S.
-static void
-mpenter(void)
-{
- switchkvm();
- seginit();
- lapicinit();
- mpmain();
-}
-
-// Common CPU setup code.
-static void
-mpmain(void)
-{
- cprintf("cpu%d: starting %d\n", cpuid(), cpuid());
- idtinit(); // load idt register
- atomic_store(&mycpu()->started, 1); // tell startothers() we're up -- atomically
- scheduler(); // start running processes
-}
-
-pde_t entrypgdir[]; // For entry.S
-
-// Start the non-boot (AP) processors.
-static void
-startothers(void)
-{
- extern uchar _binary_entryother_start[], _binary_entryother_size[];
- uchar *code;
- struct cpu *c;
- char *stack;
-
- // Write entry code to unused memory at 0x7000.
- // The linker has placed the image of entryother.S in
- // _binary_entryother_start.
- code = P2V(0x7000);
- memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);
-
- for(c = cpus; c < cpus+ncpu; c++){
- if(c == mycpu()) // We've started already.
- continue;
-
- // Tell entryother.S what stack to use, where to enter, and what
- // pgdir to use. We cannot use kpgdir yet, because the AP processor
- // is running in low memory, so we use entrypgdir for the APs too.
- stack = kalloc();
- *(void**)(code-4) = stack + KSTACKSIZE;
- *(void(**)(void))(code-8) = mpenter;
- *(int**)(code-12) = (void *) V2P(entrypgdir);
-
- lapicstartap(c->apicid, V2P(code));
-
- // wait for cpu to finish mpmain()
- while(atomic_load(&c->started) == 0)
- ;
- }
-}
-
-// The boot page table used in entry.S and entryother.S.
-// Page directories (and page tables) must start on page boundaries,
-// hence the __aligned__ attribute.
-// PTE_PS in a page directory entry enables 4Mbyte pages.
-
-__attribute__((__aligned__(PGSIZE)))
-pde_t entrypgdir[NPDENTRIES] = {
- // Map VA's [0, 4MB) to PA's [0, 4MB)
- [0] = (0) | PTE_P | PTE_W | PTE_PS,
- // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
- [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
-};
-
-//PAGEBREAK!
-// Blank page.
-//PAGEBREAK!
-// Blank page.
-//PAGEBREAK!
-// Blank page.
-
+++ /dev/null
-// Fake IDE disk; stores blocks in memory.
-// Useful for running kernel without scratch disk.
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-#include "traps.h"
-#include "spinlock.h"
-#include "sleeplock.h"
-#include "fs.h"
-#include "buf.h"
-
-extern uchar _binary_fs_img_start[], _binary_fs_img_size[];
-
-static int disksize;
-static uchar *memdisk;
-
-void
-ideinit(void)
-{
- memdisk = _binary_fs_img_start;
- disksize = (uint)_binary_fs_img_size/BSIZE;
-}
-
-// Interrupt handler.
-void
-ideintr(void)
-{
- // no-op
-}
-
-// Sync buf with disk.
-// 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
-iderw(struct buf *b)
-{
- uchar *p;
-
- if(!holdingsleep(&b->lock))
- panic("iderw: buf not locked");
- if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
- panic("iderw: nothing to do");
- if(b->dev != 1)
- panic("iderw: request not for disk 1");
- if(b->blockno >= disksize)
- panic("iderw: block out of range");
-
- p = memdisk + b->blockno*BSIZE;
-
- if(b->flags & B_DIRTY){
- b->flags &= ~B_DIRTY;
- memmove(p, b->data, BSIZE);
- } else
- memmove(b->data, p, BSIZE);
- b->flags |= B_VALID;
-}
+++ /dev/null
-// Memory layout
-
-#define EXTMEM 0x100000 // Start of extended memory
-#define PHYSTOP 0xE000000 // Top physical memory
-#define DEVSPACE 0xFE000000 // Other devices are at high addresses
-
-// Key addresses for address space layout (see kmap in vm.c for layout)
-#define KERNBASE 0x80000000 // First kernel virtual address
-#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
-
-#define V2P(a) (((uint) (a)) - KERNBASE)
-#define P2V(a) ((void *)(((char *) (a)) + KERNBASE))
-
-#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
-#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts
+++ /dev/null
-#include "types.h"
-#include "stat.h"
-#include "user.h"
-
-int
-main(int argc, char *argv[])
-{
- int i;
-
- if(argc < 2){
- printf(2, "Usage: mkdir files...\n");
- exit();
- }
-
- for(i = 1; i < argc; i++){
- if(mkdir(argv[i]) < 0){
- printf(2, "mkdir: %s failed to create\n", argv[i]);
- break;
- }
- }
-
- exit();
-}
+++ /dev/null
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <assert.h>
-
-#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.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);
-}
+++ /dev/null
-// This file contains definitions for the
-// x86 memory management unit (MMU).
-
-// Eflags register
-#define FL_IF 0x00000200 // Interrupt Enable
-
-// Control Register flags
-#define CR0_PE 0x00000001 // Protection Enable
-#define CR0_WP 0x00010000 // Write Protect
-#define CR0_PG 0x80000000 // Paging
-
-#define CR4_PSE 0x00000010 // Page size extension
-
-// various segment selectors.
-#define SEG_KCODE 1 // kernel code
-#define SEG_KDATA 2 // kernel data+stack
-#define SEG_UCODE 3 // user code
-#define SEG_UDATA 4 // user data+stack
-#define SEG_TSS 5 // this process's task state
-
-// cpu->gdt[NSEGS] holds the above segments.
-#define NSEGS 6
-
-#ifndef __ASSEMBLER__
-// Segment Descriptor
-struct segdesc {
- uint lim_15_0 : 16; // Low bits of segment limit
- uint base_15_0 : 16; // Low bits of segment base address
- uint base_23_16 : 8; // Middle bits of segment base address
- uint type : 4; // Segment type (see STS_ constants)
- uint s : 1; // 0 = system, 1 = application
- uint dpl : 2; // Descriptor Privilege Level
- uint p : 1; // Present
- uint lim_19_16 : 4; // High bits of segment limit
- uint avl : 1; // Unused (available for software use)
- uint rsv1 : 1; // Reserved
- uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
- uint g : 1; // Granularity: limit scaled by 4K when set
- uint base_31_24 : 8; // High bits of segment base address
-};
-
-// Normal segment
-#define SEG(type, base, lim, dpl) (struct segdesc) \
-{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
- ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
- (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
-#define SEG16(type, base, lim, dpl) (struct segdesc) \
-{ (lim) & 0xffff, (uint)(base) & 0xffff, \
- ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
- (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
-#endif
-
-#define DPL_USER 0x3 // User DPL
-
-// Application segment type bits
-#define STA_X 0x8 // Executable segment
-#define STA_W 0x2 // Writeable (non-executable segments)
-#define STA_R 0x2 // Readable (executable segments)
-
-// System segment type bits
-#define STS_T32A 0x9 // Available 32-bit TSS
-#define STS_IG32 0xE // 32-bit Interrupt Gate
-#define STS_TG32 0xF // 32-bit Trap Gate
-
-// A virtual address 'la' has a three-part structure as follows:
-//
-// +--------10------+-------10-------+---------12----------+
-// | Page Directory | Page Table | Offset within Page |
-// | Index | Index | |
-// +----------------+----------------+---------------------+
-// \--- PDX(va) --/ \--- PTX(va) --/
-
-// page directory index
-#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)
-
-// page table index
-#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)
-
-// construct virtual address from indexes and offset
-#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
-
-// Page directory and page table constants.
-#define NPDENTRIES 1024 // # directory entries per page directory
-#define NPTENTRIES 1024 // # PTEs per page table
-#define PGSIZE 4096 // bytes mapped by a page
-
-#define PTXSHIFT 12 // offset of PTX in a linear address
-#define PDXSHIFT 22 // offset of PDX in a linear address
-
-#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
-#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
-
-// Page table/directory entry flags.
-#define PTE_P 0x001 // Present
-#define PTE_W 0x002 // Writeable
-#define PTE_U 0x004 // User
-#define PTE_PS 0x080 // Page Size
-
-// Address in page table or page directory entry
-#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
-#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)
-
-#ifndef __ASSEMBLER__
-typedef uint pte_t;
-
-// Task state segment format
-struct taskstate {
- uint link; // Old ts selector
- uint esp0; // Stack pointers and segment selectors
- ushort ss0; // after an increase in privilege level
- ushort padding1;
- uint *esp1;
- ushort ss1;
- ushort padding2;
- uint *esp2;
- ushort ss2;
- ushort padding3;
- void *cr3; // Page directory base
- uint *eip; // Saved state from last task switch
- uint eflags;
- uint eax; // More saved state (registers)
- uint ecx;
- uint edx;
- uint ebx;
- uint *esp;
- uint *ebp;
- uint esi;
- uint edi;
- ushort es; // Even more saved state (segment selectors)
- ushort padding4;
- ushort cs;
- ushort padding5;
- ushort ss;
- ushort padding6;
- ushort ds;
- ushort padding7;
- ushort fs;
- ushort padding8;
- ushort gs;
- ushort padding9;
- ushort ldt;
- ushort padding10;
- ushort t; // Trap on task switch
- ushort iomb; // I/O map base address
-};
-
-// Gate descriptors for interrupts and traps
-struct gatedesc {
- uint off_15_0 : 16; // low 16 bits of offset in segment
- uint cs : 16; // code segment selector
- uint args : 5; // # args, 0 for interrupt/trap gates
- uint rsv1 : 3; // reserved(should be zero I guess)
- uint type : 4; // type(STS_{IG32,TG32})
- uint s : 1; // must be 0 (system)
- uint dpl : 2; // descriptor(meaning new) privilege level
- uint p : 1; // Present
- uint off_31_16 : 16; // high bits of offset in segment
-};
-
-// Set up a normal interrupt/trap gate descriptor.
-// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
-// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
-// - sel: Code segment selector for interrupt/trap handler
-// - off: Offset in code segment for interrupt/trap handler
-// - dpl: Descriptor Privilege Level -
-// the privilege level required for software to invoke
-// this interrupt/trap gate explicitly using an int instruction.
-#define SETGATE(gate, istrap, sel, off, d) \
-{ \
- (gate).off_15_0 = (uint)(off) & 0xffff; \
- (gate).cs = (sel); \
- (gate).args = 0; \
- (gate).rsv1 = 0; \
- (gate).type = (istrap) ? STS_TG32 : STS_IG32; \
- (gate).s = 0; \
- (gate).dpl = (d); \
- (gate).p = 1; \
- (gate).off_31_16 = (uint)(off) >> 16; \
-}
-
-#endif
+++ /dev/null
-// Multiprocessor support
-// Search memory for MP description structures.
-// http://developer.intel.com/design/pentium/datashts/24201606.pdf
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mp.h"
-#include "x86.h"
-#include "mmu.h"
-#include "proc.h"
-
-struct cpu cpus[NCPU];
-int ncpu;
-uchar ioapicid;
-
-static uchar
-sum(uchar *addr, int len)
-{
- int i, sum;
-
- sum = 0;
- for(i=0; i<len; i++)
- sum += addr[i];
- return sum;
-}
-
-// Look for an MP structure in the len bytes at addr.
-static struct mp*
-mpsearch1(uint a, int len)
-{
- uchar *e, *p, *addr;
-
- addr = P2V(a);
- e = addr+len;
- for(p = addr; p < e; p += sizeof(struct mp))
- if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
- return (struct mp*)p;
- return 0;
-}
-
-// Search for the MP Floating Pointer Structure, which according to the
-// spec is in one of the following three locations:
-// 1) in the first KB of the EBDA;
-// 2) in the last KB of system base memory;
-// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
-static struct mp*
-mpsearch(void)
-{
- uchar *bda;
- uint p;
- struct mp *mp;
-
- bda = (uchar *) P2V(0x400);
- if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
- if((mp = mpsearch1(p, 1024)))
- return mp;
- } else {
- p = ((bda[0x14]<<8)|bda[0x13])*1024;
- if((mp = mpsearch1(p-1024, 1024)))
- return mp;
- }
- return mpsearch1(0xF0000, 0x10000);
-}
-
-// Search for an MP configuration table. For now,
-// don't accept the default configurations (physaddr == 0).
-// Check for correct signature, calculate the checksum and,
-// if correct, check the version.
-// To do: check extended table checksum.
-static struct mpconf*
-mpconfig(struct mp **pmp)
-{
- struct mpconf *conf;
- struct mp *mp;
-
- if((mp = mpsearch()) == 0 || mp->physaddr == 0)
- return 0;
- conf = (struct mpconf*) P2V((uint) mp->physaddr);
- if(memcmp(conf, "PCMP", 4) != 0)
- return 0;
- if(conf->version != 1 && conf->version != 4)
- return 0;
- if(sum((uchar*)conf, conf->length) != 0)
- return 0;
- *pmp = mp;
- return conf;
-}
-
-void
-mpinit(void)
-{
- uchar *p, *e;
- int ismp;
- struct mp *mp;
- struct mpconf *conf;
- struct mpproc *proc;
- struct mpioapic *ioapic;
-
- if((conf = mpconfig(&mp)) == 0)
- panic("Expect to run on an SMP");
- ismp = 1;
- lapic = (uint*)conf->lapicaddr;
- for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
- switch(*p){
- case MPPROC:
- proc = (struct mpproc*)p;
- if(ncpu < NCPU) {
- cpus[ncpu].apicid = proc->apicid; // apicid may differ from ncpu
- ncpu++;
- }
- p += sizeof(struct mpproc);
- continue;
- case MPIOAPIC:
- ioapic = (struct mpioapic*)p;
- ioapicid = ioapic->apicno;
- p += sizeof(struct mpioapic);
- continue;
- case MPBUS:
- case MPIOINTR:
- case MPLINTR:
- p += 8;
- continue;
- default:
- ismp = 0;
- break;
- }
- }
- if(!ismp)
- panic("Didn't find a suitable machine");
-
- if(mp->imcrp){
- // Bochs doesn't support IMCR, so this doesn't run on Bochs.
- // But it would on real hardware.
- outb(0x22, 0x70); // Select IMCR
- outb(0x23, inb(0x23) | 1); // Mask external interrupts.
- }
-}
+++ /dev/null
-// See MultiProcessor Specification Version 1.[14]
-
-struct mp { // floating pointer
- uchar signature[4]; // "_MP_"
- void *physaddr; // phys addr of MP config table
- uchar length; // 1
- uchar specrev; // [14]
- uchar checksum; // all bytes must add up to 0
- uchar type; // MP system config type
- uchar imcrp;
- uchar reserved[3];
-};
-
-struct mpconf { // configuration table header
- uchar signature[4]; // "PCMP"
- ushort length; // total table length
- uchar version; // [14]
- uchar checksum; // all bytes must add up to 0
- uchar product[20]; // product id
- uint *oemtable; // OEM table pointer
- ushort oemlength; // OEM table length
- ushort entry; // entry count
- uint *lapicaddr; // address of local APIC
- ushort xlength; // extended table length
- uchar xchecksum; // extended table checksum
- uchar reserved;
-};
-
-struct mpproc { // processor table entry
- uchar type; // entry type (0)
- uchar apicid; // local APIC id
- uchar version; // local APIC verison
- uchar flags; // CPU flags
- #define MPBOOT 0x02 // This proc is the bootstrap processor.
- uchar signature[4]; // CPU signature
- uint feature; // feature flags from CPUID instruction
- uchar reserved[8];
-};
-
-struct mpioapic { // I/O APIC table entry
- uchar type; // entry type (2)
- uchar apicno; // I/O APIC id
- uchar version; // I/O APIC version
- uchar flags; // I/O APIC flags
- uint *addr; // I/O APIC address
-};
-
-// Table entry types
-#define MPPROC 0x00 // One per processor
-#define MPBUS 0x01 // One per bus
-#define MPIOAPIC 0x02 // One per I/O APIC
-#define MPIOINTR 0x03 // One per bus interrupt source
-#define MPLINTR 0x04 // One per system interrupt source
-
-//PAGEBREAK!
-// Blank page.
+++ /dev/null
-#define NPROC 64 // maximum number of processes
-#define KSTACKSIZE 4096 // size of per-process kernel stack
-#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
-
+++ /dev/null
-#include "types.h"
-#include "x86.h"
-#include "traps.h"
-
-// I/O Addresses of the two programmable interrupt controllers
-#define IO_PIC1 0x20 // Master (IRQs 0-7)
-#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
-
-// Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware.
-void
-picinit(void)
-{
- // mask all interrupts
- outb(IO_PIC1+1, 0xFF);
- outb(IO_PIC2+1, 0xFF);
-}
-
-//PAGEBREAK!
-// Blank page.
+++ /dev/null
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "mmu.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, char *addr, int n)
-{
- int i;
-
- 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
- }
- p->data[p->nwrite++ % PIPESIZE] = addr[i];
- }
- wakeup(&p->nread); //DOC: pipewrite-wakeup1
- release(&p->lock);
- return n;
-}
-
-int
-piperead(struct pipe *p, char *addr, int n)
-{
- int i;
-
- 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;
- addr[i] = p->data[p->nread++ % PIPESIZE];
- }
- wakeup(&p->nwrite); //DOC: piperead-wakeup
- release(&p->lock);
- return i;
-}
+++ /dev/null
-#!/usr/bin/perl
-
-use POSIX qw(strftime);
-
-if($ARGV[0] eq "-h"){
- shift @ARGV;
- $h = $ARGV[0];
- shift @ARGV;
-}else{
- $h = $ARGV[0];
-}
-
-$page = 0;
-$now = strftime "%b %e %H:%M %Y", localtime;
-
-@lines = <>;
-for($i=0; $i<@lines; $i+=50){
- print "\n\n";
- ++$page;
- print "$now $h Page $page\n";
- print "\n\n";
- for($j=$i; $j<@lines && $j<$i +50; $j++){
- $lines[$j] =~ s!//DOC.*!!;
- print $lines[$j];
- }
- for(; $j<$i+50; $j++){
- print "\n";
- }
- $sheet = "";
- if($lines[$i] =~ /^([0-9][0-9])[0-9][0-9] /){
- $sheet = "Sheet $1";
- }
- print "\n\n";
- print "$sheet\n";
- print "\n\n";
-}
+++ /dev/null
-#include "types.h"
-#include "stat.h"
-#include "user.h"
-
-static void
-putc(int fd, char c)
-{
- write(fd, &c, 1);
-}
-
-static void
-printint(int fd, int xx, int base, int sgn)
-{
- static char digits[] = "0123456789ABCDEF";
- 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]);
-}
-
-// Print to the given fd. Only understands %d, %x, %p, %s.
-void
-printf(int fd, const char *fmt, ...)
-{
- char *s;
- int c, i, state;
- uint *ap;
-
- state = 0;
- ap = (uint*)(void*)&fmt + 1;
- 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, *ap, 10, 1);
- ap++;
- } else if(c == 'x' || c == 'p'){
- printint(fd, *ap, 16, 0);
- ap++;
- } else if(c == 's'){
- s = (char*)*ap;
- ap++;
- if(s == 0)
- s = "(null)";
- while(*s != 0){
- putc(fd, *s);
- s++;
- }
- } else if(c == 'c'){
- putc(fd, *ap);
- ap++;
- } else if(c == '%'){
- putc(fd, c);
- } else {
- // Unknown % sequence. Print it to draw attention.
- putc(fd, '%');
- putc(fd, c);
- }
- state = 0;
- }
- }
-}
+++ /dev/null
-#!/bin/sh
-
-# Decode the symbols from a panic EIP list
-
-# Find a working addr2line
-for p in i386-jos-elf-addr2line addr2line; do
- if which $p 2>&1 >/dev/null && \
- $p -h 2>&1 | grep -q '\belf32-i386\b'; then
- break
- fi
-done
-
-# Enable as much pretty-printing as this addr2line can do
-$p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@"
+++ /dev/null
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "x86.h"
-#include "proc.h"
-#include "spinlock.h"
-
-struct {
- struct spinlock lock;
- struct proc proc[NPROC];
-} ptable;
-
-static struct proc *initproc;
-
-int nextpid = 1;
-extern void forkret(void);
-extern void trapret(void);
-
-static void wakeup1(void *chan);
-
-void
-pinit(void)
-{
- initlock(&ptable.lock, "ptable");
-}
-
-// Must be called with interrupts disabled
-int
-cpuid() {
- return mycpu()-cpus;
-}
-
-// Must be called with interrupts disabled to avoid the caller being
-// rescheduled between reading lapicid and running through the loop.
-struct cpu*
-mycpu(void)
-{
- int apicid, i;
-
- if(readeflags()&FL_IF)
- panic("mycpu called with interrupts enabled\n");
-
- apicid = lapicid();
- // APIC IDs are not guaranteed to be contiguous. Maybe we should have
- // a reverse map, or reserve a register to store &cpus[i].
- for (i = 0; i < ncpu; ++i) {
- if (cpus[i].apicid == apicid)
- return &cpus[i];
- }
- panic("unknown apicid\n");
-}
-
-// Disable interrupts so that we are not rescheduled
-// while reading proc from the cpu structure
-struct proc*
-myproc(void) {
- struct cpu *c;
- struct proc *p;
- pushcli();
- c = mycpu();
- p = c->proc;
- popcli();
- 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;
- char *sp;
-
- 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 kernel stack.
- if((p->kstack = kalloc()) == 0){
- p->state = UNUSED;
- return 0;
- }
- sp = p->kstack + KSTACKSIZE;
-
- // Leave room for trap frame.
- sp -= sizeof *p->tf;
- p->tf = (struct trapframe*)sp;
-
- // Set up new context to start executing at forkret,
- // which returns to trapret.
- sp -= 4;
- *(uint*)sp = (uint)trapret;
-
- sp -= sizeof *p->context;
- p->context = (struct context*)sp;
- memset(p->context, 0, sizeof *p->context);
- p->context->eip = (uint)forkret;
-
- return p;
-}
-
-//PAGEBREAK: 32
-// Set up first user process.
-void
-userinit(void)
-{
- struct proc *p;
- extern char _binary_initcode_start[], _binary_initcode_size[];
-
- p = allocproc();
-
- initproc = p;
- if((p->pgdir = setupkvm()) == 0)
- panic("userinit: out of memory?");
- inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
- p->sz = PGSIZE;
- memset(p->tf, 0, sizeof(*p->tf));
- p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
- p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
- p->tf->es = p->tf->ds;
- p->tf->ss = p->tf->ds;
- p->tf->eflags = FL_IF;
- p->tf->esp = PGSIZE;
- p->tf->eip = 0; // beginning of initcode.S
-
- 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 *curproc = myproc();
-
- sz = curproc->sz;
- if(n > 0){
- if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0)
- return -1;
- } else if(n < 0){
- if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0)
- return -1;
- }
- curproc->sz = sz;
- switchuvm(curproc);
- return 0;
-}
-
-// Create a new process copying p as the parent.
-// Sets up stack to return as if from system call.
-// Caller must set state of returned proc to RUNNABLE.
-int
-fork(void)
-{
- int i, pid;
- struct proc *np;
- struct proc *curproc = myproc();
-
- // Allocate process.
- if((np = allocproc()) == 0){
- return -1;
- }
-
- // Copy process state from proc.
- if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){
- kfree(np->kstack);
- np->kstack = 0;
- np->state = UNUSED;
- return -1;
- }
- np->sz = curproc->sz;
- np->parent = curproc;
- *np->tf = *curproc->tf;
-
- // Clear %eax so that fork returns 0 in the child.
- np->tf->eax = 0;
-
- for(i = 0; i < NOFILE; i++)
- if(curproc->ofile[i])
- np->ofile[i] = filedup(curproc->ofile[i]);
- np->cwd = idup(curproc->cwd);
-
- safestrcpy(np->name, curproc->name, sizeof(curproc->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() to find out it exited.
-void
-exit(void)
-{
- struct proc *curproc = myproc();
- struct proc *p;
- int fd;
-
- if(curproc == initproc)
- panic("init exiting");
-
- // Close all open files.
- for(fd = 0; fd < NOFILE; fd++){
- if(curproc->ofile[fd]){
- fileclose(curproc->ofile[fd]);
- curproc->ofile[fd] = 0;
- }
- }
-
- begin_op();
- iput(curproc->cwd);
- end_op();
- curproc->cwd = 0;
-
- acquire(&ptable.lock);
-
- // Parent might be sleeping in wait().
- wakeup1(curproc->parent);
-
- // Pass abandoned children to init.
- for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
- if(p->parent == curproc){
- p->parent = initproc;
- if(p->state == ZOMBIE)
- wakeup1(initproc);
- }
- }
-
- // Jump into the scheduler, never to return.
- curproc->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 *p;
- int havekids, pid;
- struct proc *curproc = myproc();
-
- acquire(&ptable.lock);
- for(;;){
- // Scan through table looking for exited children.
- havekids = 0;
- for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
- if(p->parent != curproc)
- continue;
- havekids = 1;
- if(p->state == ZOMBIE){
- // Found one.
- pid = p->pid;
- kfree(p->kstack);
- p->kstack = 0;
- freevm(p->pgdir);
- p->pid = 0;
- p->parent = 0;
- p->name[0] = 0;
- p->killed = 0;
- p->state = UNUSED;
- release(&ptable.lock);
- return pid;
- }
- }
-
- // No point waiting if we don't have any children.
- if(!havekids || curproc->killed){
- release(&ptable.lock);
- return -1;
- }
-
- // Wait for children to exit. (See wakeup1 call in proc_exit.)
- sleep(curproc, &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.
- sti();
-
- // 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;
- switchuvm(p);
- p->state = RUNNING;
-
- swtch(&(c->scheduler), p->context);
- switchkvm();
-
- // 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->ncli, 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()->ncli != 1)
- panic("sched locks");
- if(p->state == RUNNING)
- panic("sched running");
- if(readeflags()&FL_IF)
- 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 here. "Return" to user space.
-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);
- }
-
- // Return to "caller", actually trapret (see allocproc).
-}
-
-// 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;
-}
-
-//PAGEBREAK: 36
-// 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"
- };
- int i;
- struct proc *p;
- char *state;
- uint pc[10];
-
- 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 = "???";
- cprintf("%d %s %s", p->pid, state, p->name);
- if(p->state == SLEEPING){
- getcallerpcs((uint*)p->context->ebp+2, pc);
- for(i=0; i<10 && pc[i] != 0; i++)
- cprintf(" %p", pc[i]);
- }
- cprintf("\n");
- }
-}
+++ /dev/null
-#include <stdatomic.h>
-
-// Per-CPU state
-struct cpu {
- uchar apicid; // Local APIC ID
- struct context *scheduler; // swtch() here to enter scheduler
- struct taskstate ts; // Used by x86 to find stack for interrupt
- struct segdesc gdt[NSEGS]; // x86 global descriptor table
- atomic_uint started; // Has the CPU started?
- int ncli; // Depth of pushcli nesting.
- int intena; // Were interrupts enabled before pushcli?
- struct proc *proc; // The process running on this cpu or null
-};
-
-extern struct cpu cpus[NCPU];
-extern int ncpu;
-
-//PAGEBREAK: 17
-// Saved registers for kernel context switches.
-// Don't need to save all the segment registers (%cs, etc),
-// because they are constant across kernel contexts.
-// Don't need to save %eax, %ecx, %edx, because the
-// x86 convention is that the caller has saved them.
-// Contexts are stored at the bottom of the stack they
-// describe; the stack pointer is the address of the context.
-// The layout of the context matches the layout of the stack in swtch.S
-// at the "Switch stacks" comment. Switch doesn't save eip explicitly,
-// but it is on the stack and allocproc() manipulates it.
-struct context {
- uint edi;
- uint esi;
- uint ebx;
- uint ebp;
- uint eip;
-};
-
-enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
-
-// Per-process state
-struct proc {
- uint sz; // Size of process memory (bytes)
- pde_t* pgdir; // Page table
- char *kstack; // Bottom of kernel stack for this process
- enum procstate state; // Process state
- int pid; // Process ID
- struct proc *parent; // Parent process
- struct trapframe *tf; // Trap frame for current syscall
- 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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#!/bin/sh
-
-echo This script takes a minute to run. Be patient. 1>&2
-
-LC_CTYPE=C export LC_CTYPE
-
-# pad stdin to multiple of 120 lines
-pad()
-{
- awk '{print} END{for(; NR%120!=0; NR++) print ""}'
-}
-
-# create formatted (numbered) files
-mkdir -p fmt
-rm -f fmt/*
-cp README fmt
-echo > fmt/blank
-files=`grep -v '^#' runoff.list | awk '{print $1}'`
-n=99
-for i in $files
-do
- ./runoff1 -n $n $i >fmt/$i
- nn=`tail -1 fmt/$i | sed 's/ .*//; s/^0*//'`
- if [ "x$nn" != x ]; then
- n=$nn
- fi
-done
-
-# create table of contents
-cat toc.hdr >fmt/toc
-pr -e8 -t runoff.list | awk '
-/^[a-z0-9]/ {
- s=$0
- f="fmt/"$1
- getline<f
- close(f)
- n=$1
- printf("%02d %s\n", n/100, s);
- printf("TOC: %04d %s\n", n, s) >"fmt/tocdata"
- next
-}
-{
- print
-}' | pr -3 -t >>fmt/toc
-cat toc.ftr >>fmt/toc
-
-# check for bad alignments
-perl -e '
- $leftwarn = 0;
- while(<>){
- chomp;
- s!#.*!!;
- s!\s+! !g;
- s! +$!!;
- next if /^$/;
-
- if(/TOC: (\d+) (.*)/){
- $toc{$2} = $1;
- next;
- }
-
- if(/sheet1: (left|right)$/){
- print STDERR "assuming that sheet 1 is a $1 page. double-check!\n";
- $left = $1 eq "left" ? "13579" : "02468";
- $right = $1 eq "left" ? "02468" : "13579";
- next;
- }
-
- if(/even: (.*)/){
- $file = $1;
- if(!defined($toc{$file})){
- print STDERR "Have no toc for $file\n";
- next;
- }
- if($toc{$file} =~ /^\d\d[^0]/){
- print STDERR "$file does not start on a fresh page.\n";
- }
- next;
- }
-
- if(/odd: (.*)/){
- $file = $1;
- if(!defined($toc{$file})){
- print STDERR "Have no toc for $file\n";
- next;
- }
- if($toc{$file} !~ /^\d\d5/){
- print STDERR "$file does not start on a second half page.\n";
- }
- next;
- }
-
- if(/(left|right): (.*)/){
- $what = $1;
- $file = $2;
- if(!defined($toc{$file})){
- print STDERR "Have no toc for $file\n";
- next;
- }
- if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
- print STDERR "$file does not start on a left page [$toc{$file}]\n";
- }
- # why does this not work if I inline $x in the if?
- $x = ($toc{$file} =~ /^\d[$right][05]/);
- if($what eq "right" && !$x){
- print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n";
- }
- next;
- }
-
- print STDERR "Unknown spec: $_\n";
- }
-' fmt/tocdata runoff.spec
-
-# make definition list
-cd fmt
-perl -e '
- while(<>) {
- chomp;
-
- s!//.*!!;
- s!/\*([^*]|[*][^/])*\*/!!g;
- s!\s! !g;
- s! +$!!;
-
- # look for declarations like char* x;
- if (/^[0-9]+ typedef .* u(int|short|long|char);/) {
- next;
- }
- if (/^[0-9]+ extern/) {
- next;
- }
- if (/^[0-9]+ struct [a-zA-Z0-9_]+;/) {
- next;
- }
- if (/^([0-9]+) #define +([A-za-z0-9_]+) +?\(.*/) {
- print "$1 $2\n"
- }
- elsif (/^([0-9]+) #define +([A-Za-z0-9_]+) +([^ ]+)/) {
- print "$1 $2 $3\n";
- }
- elsif (/^([0-9]+) #define +([A-Za-z0-9_]+)/) {
- print "$1 $2\n";
- }
-
- if(/^^([0-9]+) \.globl ([a-zA-Z0-9_]+)/){
- $isglobl{$2} = 1;
- }
- if(/^^([0-9]+) ([a-zA-Z0-9_]+):$/ && $isglobl{$2}){
- print "$1 $2\n";
- }
-
- if (/\(/) {
- next;
- }
-
- if (/^([0-9]+) (((static|struct|extern|union|enum) +)*([A-Za-z0-9_]+))( .*)? +([A-Za-z_][A-Za-z0-9_]*)(,|;|=| =)/) {
- print "$1 $7\n";
- }
-
- elsif(/^([0-9]+) (enum|struct|union) +([A-Za-z0-9_]+) +{/){
- print "$1 $3\n";
- }
- # TODO: enum members
- }
-' $files >defs
-
-(for i in $files
-do
- case "$i" in
- *.S)
- cat $i | sed 's;#.*;;; s;//.*;;;'
- ;;
- *)
- cat $i | sed 's;//.*;;; s;"([^"\\]|\\.)*";;;'
- esac
-done
-) >alltext
-
-perl -n -e 'print if s/^([0-9]+ [a-zA-Z0-9_]+)\(.*$/\1/;' alltext |
- egrep -v ' (STUB|usage|main|if|for)$' >>defs
-#perl -n -e 'print if s/^([0-9]+) STUB\(([a-zA-Z0-9_]+)\)$/\1 \2/;' alltext \
-# >>defs
-(
->s.defs
-
-# make reference list
-for i in `awk '{print $2}' defs | sort -f | uniq`
-do
- defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'`
- echo $i $defs >>s.defs
- uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
- if [ "x$defs" != "x$uses" ]; then
- echo $i $defs
- echo $uses |fmt -29 | sed 's/^/ /'
-# else
-# echo $i defined but not used >&2
- fi
-done
-) >refs
-
-# build defs list
-awk '
-{
- printf("%04d %s\n", $2, $1);
- for(i=3; i<=NF; i++)
- printf("%04d \" \n", $i);
-}
-' s.defs > t.defs
-
-# format the whole thing
-(
- ../pr.pl README
- ../pr.pl -h "table of contents" toc
- # pr -t -2 t.defs | ../pr.pl -h "definitions" | pad
- pr -t -l50 -2 refs | ../pr.pl -h "cross-references" | pad
- # pr.pl -h "definitions" -2 t.defs | pad
- # pr.pl -h "cross-references" -2 refs | pad
- ../pr.pl blank # make sheet 1 start on left page
- ../pr.pl blank
- for i in $files
- do
- ../pr.pl -h "xv6/$i" $i
- done
-) | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >all.ps
-grep Pages: all.ps
-
-# if we have the nice font, use it
-nicefont=LucidaSans-Typewriter83
-if [ ! -f ../$nicefont ]
-then
- if git cat-file blob font:$nicefont > ../$nicefont~; then
- mv ../$nicefont~ ../$nicefont
- fi
-fi
-if [ -f ../$nicefont ]
-then
- echo nicefont
- (sed 1q all.ps; cat ../$nicefont; sed "1d; s/Courier/$nicefont/" all.ps) >allf.ps
-else
- echo ugly font!
- cp all.ps allf.ps
-fi
-ps2pdf allf.ps ../xv6.pdf
-# cd ..
-# pdftops xv6.pdf xv6.ps
+++ /dev/null
-# basic headers
-types.h
-param.h
-memlayout.h
-defs.h
-x86.h
-asm.h
-mmu.h
-elf.h
-date.h
-
-# entering xv6
-entry.S
-entryother.S
-main.c
-
-# locks
-spinlock.h
-spinlock.c
-
-# processes
-vm.c
-proc.h
-proc.c
-swtch.S
-kalloc.c
-
-# system calls
-traps.h
-vectors.pl
-trapasm.S
-trap.c
-syscall.h
-syscall.c
-sysproc.c
-
-# file system
-buf.h
-sleeplock.h
-fcntl.h
-stat.h
-fs.h
-file.h
-ide.c
-bio.c
-sleeplock.c
-log.c
-fs.c
-file.c
-sysfile.c
-exec.c
-
-# pipes
-pipe.c
-
-# string operations
-string.c
-
-# low-level hardware
-mp.h
-mp.c
-lapic.c
-ioapic.c
-kbd.h
-kbd.c
-console.c
-uart.c
-
-# user-level
-initcode.S
-usys.S
-init.c
-sh.c
-
-# bootloader
-bootasm.S
-bootmain.c
-
-# link
-kernel.ld
+++ /dev/null
-# Is sheet 01 (after the TOC) a left sheet or a right sheet?
-sheet1: left
-
-# "left" and "right" specify which page of a two-page spread a file
-# must start on. "left" means that a file must start on the first of
-# the two pages. "right" means it must start on the second of the two
-# pages. The file may start in either column.
-#
-# "even" and "odd" specify which column a file must start on. "even"
-# means it must start in the left of the two columns (00). "odd" means it
-# must start in the right of the two columns (50).
-#
-# You'd think these would be the other way around.
-
-# types.h either
-# param.h either
-# defs.h either
-# x86.h either
-# asm.h either
-# mmu.h either
-# elf.h either
-# mp.h either
-
-even: entry.S # mild preference
-even: entryother.S # mild preference
-even: main.c
-# mp.c don't care at all
-# even: initcode.S
-# odd: init.c
-
-left: spinlock.h
-even: spinlock.h
-
-# This gets struct proc and allocproc on the same spread
-left: proc.h
-even: proc.h
-
-# goal is to have two action-packed 2-page spreads,
-# one with
-# userinit growproc fork exit wait
-# and another with
-# scheduler sched yield forkret sleep wakeup1 wakeup
-right: proc.c # VERY important
-even: proc.c # VERY important
-
-# A few more action packed spreads
-# page table creation and process loading
-# walkpgdir mappages setupkvm switch[ku]vm inituvm (loaduvm)
-# process memory management
-# allocuvm deallocuvm freevm
-left: vm.c
-
-even: kalloc.c # mild preference
-
-# syscall.h either
-# trapasm.S either
-# traps.h either
-# even: trap.c
-# vectors.pl either
-# syscall.c either
-# sysproc.c either
-
-# buf.h either
-# dev.h either
-# fcntl.h either
-# stat.h either
-# file.h either
-# fs.h either
-# fsvar.h either
-# left: ide.c # mild preference
-even: ide.c
-# odd: bio.c
-
-# log.c fits nicely in a spread
-even: log.c
-left: log.c
-
-# with fs.c starting on 2nd column of a left page, we get these 2-page spreads:
-# ialloc iupdate iget idup ilock iunlock iput iunlockput
-# bmap itrunc stati readi writei
-# namecmp dirlookup dirlink skipelem namex namei
-# fileinit filealloc filedup fileclose filestat fileread filewrite
-# starting on 2nd column of a right page is not terrible either
-odd: fs.c # VERY important
-left: fs.c # mild preference
-# file.c either
-# exec.c either
-# sysfile.c either
-
-# Mild preference, but makes spreads of mp.c, lapic.c, and ioapic.c+picirq.c
-even: mp.c
-left: mp.c
-
-# even: pipe.c # mild preference
-# string.c either
-# left: kbd.h # mild preference
-even: kbd.h
-even: console.c
-odd: sh.c
-
-even: bootasm.S # mild preference
-even: bootmain.c # mild preference
+++ /dev/null
-#!/usr/bin/perl
-
-$n = 0;
-$v = 0;
-if($ARGV[0] eq "-v") {
- $v = 1;
- shift @ARGV;
-}
-if($ARGV[0] eq "-n") {
- $n = $ARGV[1];
- shift @ARGV;
- shift @ARGV;
-}
-$n = int(($n+49)/50)*50 - 1;
-
-$file = $ARGV[0];
-@lines = <>;
-$linenum = 0;
-foreach (@lines) {
- $linenum++;
- chomp;
- s/\s+$//;
- if(length() >= 75){
- print STDERR "$file:$linenum: line too long\n";
- }
-}
-@outlines = ();
-$nextout = 0;
-
-for($i=0; $i<@lines; ){
- # Skip leading blank lines.
- $i++ while $i<@lines && $lines[$i] =~ /^$/;
- last if $i>=@lines;
-
- # If the rest of the file fits, use the whole thing.
- if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
- $breakbefore = @lines;
- }else{
- # Find a good next page break;
- # Hope for end of function.
- # but settle for a blank line (but not first blank line
- # in function, which comes after variable declarations).
- $breakbefore = $i;
- $lastblank = $i;
- $sawbrace = 0;
- $breaksize = 15; # 15 lines to get to function
- for($j=$i; $j<$i+50 && $j < @lines; $j++){
- if($lines[$j] =~ /PAGEBREAK!/){
- $lines[$j] = "";
- $breakbefore = $j;
- $breaksize = 100;
- last;
- }
- if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){
- $breaksize = $1;
- $breakbefore = $j;
- $lines[$j] = "";
- }
- if($lines[$j] =~ /^};?$/){
- $breakbefore = $j+1;
- $breaksize = 15;
- }
- if($lines[$j] =~ /^{$/){
- $sawbrace = 1;
- }
- if($lines[$j] =~ /^$/){
- if($sawbrace){
- $sawbrace = 0;
- }else{
- $lastblank = $j;
- }
- }
- }
- if($j<@lines && $lines[$j] =~ /^$/){
- $lastblank = $j;
- }
-
- # If we are not putting enough on a page, try a blank line.
- if($breakbefore - $i < 50 - $breaksize && $lastblank > $breakbefore && $lastblank >= $i+50 - 5){
- if($v){
- print STDERR "breakbefore $breakbefore i $i breaksize $breaksize\n";
- }
- $breakbefore = $lastblank;
- $breaksize = 5; # only 5 lines to get to blank line
- }
-
- # If we are not putting enough on a page, force a full page.
- if($breakbefore - $i < 50 - $breaksize && $breakbefore != @lines){
- $breakbefore = $i + 50;
- $breakbefore = @lines if @lines < $breakbefore;
- }
-
- if($breakbefore < $i+2){
- $breakbefore = $i+2;
- }
- }
-
- # Emit the page.
- $i50 = $i + 50;
- for(; $i<$breakbefore; $i++){
- printf "%04d %s\n", ++$n, $lines[$i];
- }
-
- # Finish page
- for($j=$i; $j<$i50; $j++){
- printf "%04d \n", ++$n;
- }
-}
+++ /dev/null
-// 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;
-}
+++ /dev/null
-#!/bin/sh
-
-runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps
+++ /dev/null
-#!/usr/bin/perl
-
-open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
-
-$n = sysread(SIG, $buf, 1000);
-
-if($n > 510){
- print STDERR "boot block too large: $n bytes (max 510)\n";
- exit 1;
-}
-
-print STDERR "boot block is $n bytes (max 510)\n";
-
-$buf .= "\0" x (510-$n);
-$buf .= "\x55\xAA";
-
-open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
-print SIG $buf;
-close SIG;
+++ /dev/null
-/*
-This file defines a Promela model for xv6's
-acquire, release, sleep, and wakeup, along with
-a model of a simple producer/consumer queue.
-
-To run:
- spinp sleep1.p
-
-(You may need to install Spin, available at http://spinroot.com/.)
-
-After a successful run spin prints something like:
-
- unreached in proctype consumer
- (0 of 37 states)
- unreached in proctype producer
- (0 of 23 states)
-
-After an unsuccessful run, the spinp script prints
-an execution trace that causes a deadlock.
-
-The safe body of producer reads:
-
- acquire(lk);
- x = value; value = x + 1; x = 0;
- wakeup(0);
- release(lk);
- i = i + 1;
-
-If this is changed to:
-
- x = value; value = x + 1; x = 0;
- acquire(lk);
- wakeup(0);
- release(lk);
- i = i + 1;
-
-then a deadlock can happen, because the non-atomic
-increment of value conflicts with the non-atomic
-decrement in consumer, causing value to have a bad value.
-Try this.
-
-If it is changed to:
-
- acquire(lk);
- x = value; value = x + 1; x = 0;
- release(lk);
- wakeup(0);
- i = i + 1;
-
-then nothing bad happens: it is okay to wakeup after release
-instead of before, although it seems morally wrong.
-*/
-
-#define ITER 4
-#define N 2
-
-bit lk;
-byte value;
-bit sleeping[N];
-
-inline acquire(x)
-{
- atomic { x == 0; x = 1 }
-}
-
-inline release(x)
-{
- assert x==1;
- x = 0
-}
-
-inline sleep(cond, lk)
-{
- assert !sleeping[_pid];
- if
- :: cond ->
- skip
- :: else ->
- atomic { release(lk); sleeping[_pid] = 1 };
- sleeping[_pid] == 0;
- acquire(lk)
- fi
-}
-
-inline wakeup()
-{
- w = 0;
- do
- :: w < N ->
- sleeping[w] = 0;
- w = w + 1
- :: else ->
- break
- od
-}
-
-active[N] proctype consumer()
-{
- byte i, x;
-
- i = 0;
- do
- :: i < ITER ->
- acquire(lk);
- sleep(value > 0, lk);
- x = value; value = x - 1; x = 0;
- release(lk);
- i = i + 1;
- :: else ->
- break
- od;
- i = 0;
- skip
-}
-
-active[N] proctype producer()
-{
- byte i, x, w;
-
- i = 0;
- do
- :: i < ITER ->
- acquire(lk);
- x = value; value = x + 1; x = 0;
- release(lk);
- wakeup();
- i = i + 1;
- :: else ->
- break
- od;
- i = 0;
- skip
-}
-
+++ /dev/null
-// Sleeping locks
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "x86.h"
-#include "memlayout.h"
-#include "mmu.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;
-}
-
-
-
+++ /dev/null
-// 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
-};
-
+++ /dev/null
-// Mutual exclusion spin locks.
-#include <stdatomic.h>
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "x86.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "spinlock.h"
-
-void
-initlock(struct spinlock *lk, char *name)
-{
- atomic_init(&lk->locked, 0);
-
- lk->name = name;
- 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)
-{
- pushcli(); // disable interrupts to avoid deadlock.
- if(holding(lk))
- panic("acquire");
-
- // Use c11 atomics to acquire the lock
- // Here we atomically exchange locked with 1. If locked was 0, then we've
- // just acquired the lock!
- // We use the acquire release semantics (orderings). We really only want
- // acquire semantics, but we are doing a read and modify operation at once
- // which requires acquire (write) and release (read) ordering semantics.
- while (atomic_exchange_explicit(&lk->locked, 1, memory_order_acq_rel) != 0)
- ;
-
- // Record info about lock acquisition for debugging.
- lk->cpu = mycpu();
- getcallerpcs(&lk, lk->pcs);
-}
-
-// Release the lock.
-void
-release(struct spinlock *lk)
-{
- if(!holding(lk))
- panic("release");
-
- lk->pcs[0] = 0;
- lk->cpu = 0;
-
-
- // Use c11 atomics to release the lock.
- // Here we set the locked value to 0 atomically
- // We also give it "release" semantics, as we're doing an unlock
- // (e.g. release) operation
- atomic_store_explicit(&lk->locked, 0, memory_order_release);
-
- popcli();
-}
-
-// Record the current call stack in pcs[] by following the %ebp chain.
-void
-getcallerpcs(void *v, uint pcs[])
-{
- uint *ebp;
- int i;
-
- ebp = (uint*)v - 2;
- for(i = 0; i < 10; i++){
- if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
- break;
- pcs[i] = ebp[1]; // saved %eip
- ebp = (uint*)ebp[0]; // saved %ebp
- }
- for(; i < 10; i++)
- pcs[i] = 0;
-}
-
-// Check whether this cpu is holding the lock.
-int
-holding(struct spinlock *lock)
-{
- int r;
- pushcli();
- r = lock->locked && lock->cpu == mycpu();
- popcli();
- return r;
-}
-
-
-// Pushcli/popcli are like cli/sti except that they are matched:
-// it takes two popcli to undo two pushcli. Also, if interrupts
-// are off, then pushcli, popcli leaves them off.
-
-void
-pushcli(void)
-{
- int eflags;
-
- eflags = readeflags();
- cli();
- if(mycpu()->ncli == 0)
- mycpu()->intena = eflags & FL_IF;
- mycpu()->ncli += 1;
-}
-
-void
-popcli(void)
-{
- if(readeflags()&FL_IF)
- panic("popcli - interruptible");
- if(--mycpu()->ncli < 0)
- panic("popcli");
- if(mycpu()->ncli == 0 && mycpu()->intena)
- sti();
-}
-
+++ /dev/null
-#include <stdatomic.h>
-
-// Mutual exclusion lock.
-struct spinlock {
- atomic_uint locked; // Is the lock held?
-
- // For debugging:
- char *name; // Name of lock.
- struct cpu *cpu; // The cpu holding the lock.
- uint pcs[10]; // The call stack (an array of program counters)
- // that locked the lock.
-};
-
+++ /dev/null
-#!/bin/sh
-
-if [ $# != 1 ] || [ ! -f "$1" ]; then
- echo 'usage: spinp file.p' 1>&2
- exit 1
-fi
-
-rm -f $1.trail
-spin -a $1 || exit 1
-cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c
-pan -i
-rm pan.* pan
-if [ -f $1.trail ]; then
- spin -t -p $1
-fi
-
+++ /dev/null
-#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
-};
+++ /dev/null
-// 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();
-}
+++ /dev/null
-#include "types.h"
-#include "x86.h"
-
-void*
-memset(void *dst, int c, uint n)
-{
- if ((int)dst%4 == 0 && n%4 == 0){
- c &= 0xFF;
- stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4);
- } else
- stosb(dst, c, n);
- 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;
-}
-
+++ /dev/null
-# Context switch
-#
-# void swtch(struct context **old, struct context *new);
-#
-# Save the current registers on the stack, creating
-# a struct context, and save its address in *old.
-# Switch stacks to new and pop previously-saved registers.
-
-.globl swtch
-swtch:
- movl 4(%esp), %eax
- movl 8(%esp), %edx
-
- # Save old callee-saved registers
- pushl %ebp
- pushl %ebx
- pushl %esi
- pushl %edi
-
- # Switch stacks
- movl %esp, (%eax)
- movl %edx, %esp
-
- # Load new callee-saved registers
- popl %edi
- popl %esi
- popl %ebx
- popl %ebp
- ret
+++ /dev/null
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-#include "syscall.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(uint addr, int *ip)
-{
- struct proc *curproc = myproc();
-
- if(addr >= curproc->sz || addr+4 > curproc->sz)
- return -1;
- *ip = *(int*)(addr);
- 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.
-int
-fetchstr(uint addr, char **pp)
-{
- char *s, *ep;
- struct proc *curproc = myproc();
-
- if(addr >= curproc->sz)
- return -1;
- *pp = (char*)addr;
- ep = (char*)curproc->sz;
- for(s = *pp; s < ep; s++){
- if(*s == 0)
- return s - *pp;
- }
- return -1;
-}
-
-// Fetch the nth 32-bit system call argument.
-int
-argint(int n, int *ip)
-{
- return fetchint((myproc()->tf->esp) + 4 + 4*n, ip);
-}
-
-// 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, char **pp, int size)
-{
- int i;
- struct proc *curproc = myproc();
-
- if(argint(n, &i) < 0)
- return -1;
- if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz)
- return -1;
- *pp = (char*)i;
- return 0;
-}
-
-// Fetch the nth word-sized system call argument as a string pointer.
-// Check that the pointer is valid and the string is nul-terminated.
-// (There is no shared writable memory, so the string can't change
-// between this check and being used by the kernel.)
-int
-argstr(int n, char **pp)
-{
- int addr;
- if(argint(n, &addr) < 0)
- return -1;
- return fetchstr(addr, pp);
-}
-
-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,
-};
-
-void
-syscall(void)
-{
- int num;
- struct proc *curproc = myproc();
-
- num = curproc->tf->eax;
- if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
- curproc->tf->eax = syscalls[num]();
- } else {
- cprintf("%d %s: unknown sys call %d\n",
- curproc->pid, curproc->name, num);
- curproc->tf->eax = -1;
- }
-}
+++ /dev/null
-// 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
+++ /dev/null
-//
-// 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 "defs.h"
-#include "param.h"
-#include "stat.h"
-#include "mmu.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 *curproc = myproc();
-
- for(fd = 0; fd < NOFILE; fd++){
- if(curproc->ofile[fd] == 0){
- curproc->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;
- char *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;
- char *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;
- struct stat *st;
-
- if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 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, *old;
- struct inode *dp, *ip;
-
- if(argstr(0, &old) < 0 || argstr(1, &new) < 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); off<dp->size; off+=sizeof(de)){
- if(readi(dp, (char*)&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;
- uint off;
-
- if(argstr(0, &path) < 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, (char*)&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)
-{
- struct inode *ip, *dp;
- char name[DIRSIZ];
-
- if((dp = nameiparent(path, name)) == 0)
- return 0;
- ilock(dp);
-
- if((ip = dirlookup(dp, name, 0)) != 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;
- int fd, omode;
- struct file *f;
- struct inode *ip;
-
- if(argstr(0, &path) < 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;
- struct inode *ip;
-
- begin_op();
- if(argstr(0, &path) < 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;
- int major, minor;
-
- begin_op();
- if((argstr(0, &path)) < 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;
- struct inode *ip;
- struct proc *curproc = myproc();
-
- begin_op();
- if(argstr(0, &path) < 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(curproc->cwd);
- end_op();
- curproc->cwd = ip;
- return 0;
-}
-
-int
-sys_exec(void)
-{
- char *path, *argv[MAXARG];
- int i;
- uint uargv, uarg;
-
- if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
- return -1;
- }
- memset(argv, 0, sizeof(argv));
- for(i=0;; i++){
- if(i >= NELEM(argv))
- return -1;
- if(fetchint(uargv+4*i, (int*)&uarg) < 0)
- return -1;
- if(uarg == 0){
- argv[i] = 0;
- break;
- }
- if(fetchstr(uarg, &argv[i]) < 0)
- return -1;
- }
- return exec(path, argv);
-}
-
-int
-sys_pipe(void)
-{
- int *fd;
- struct file *rf, *wf;
- int fd0, fd1;
-
- if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
- return -1;
- if(pipealloc(&rf, &wf) < 0)
- return -1;
- fd0 = -1;
- if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
- if(fd0 >= 0)
- myproc()->ofile[fd0] = 0;
- fileclose(rf);
- fileclose(wf);
- return -1;
- }
- fd[0] = fd0;
- fd[1] = fd1;
- return 0;
-}
+++ /dev/null
-#include "types.h"
-#include "x86.h"
-#include "defs.h"
-#include "date.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-
-int
-sys_fork(void)
-{
- return fork();
-}
-
-int
-sys_exit(void)
-{
- exit();
- return 0; // not reached
-}
-
-int
-sys_wait(void)
-{
- return wait();
-}
-
-int
-sys_kill(void)
-{
- int pid;
-
- if(argint(0, &pid) < 0)
- return -1;
- return kill(pid);
-}
-
-int
-sys_getpid(void)
-{
- return myproc()->pid;
-}
-
-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;
-}
-
-// return how many clock tick interrupts have occurred
-// since start.
-int
-sys_uptime(void)
-{
- uint xticks;
-
- acquire(&tickslock);
- xticks = ticks;
- release(&tickslock);
- return xticks;
-}
+++ /dev/null
-
-
-The source listing is preceded by a cross-reference that lists every defined
-constant, struct, global variable, and function in xv6. Each entry gives,
-on the same line as the name, the line number (or, in a few cases, numbers)
-where the name is defined. Successive lines in an entry list the line
-numbers where the name is used. For example, this entry:
-
- swtch 2658
- 0374 2428 2466 2657 2658
-
-indicates that swtch is defined on line 2658 and is mentioned on five lines
-on sheets 03, 24, and 26.
+++ /dev/null
-The numbers to the left of the file names in the table are sheet numbers.
-The source code has been printed in a double column format with fifty
-lines per column, giving one hundred lines per sheet (or page).
-Thus there is a convenient relationship between line numbers and sheet numbers.
-
-
--- /dev/null
+project(mkfs)
+
+add_executable(mkfs mkfs.c)
+
--- /dev/null
+#!/usr/bin/perl
+
+$| = 1;
+
+sub writefile($@){
+ my ($file, @lines) = @_;
+
+ sleep(1);
+ open(F, ">$file") || die "open >$file: $!";
+ print F @lines;
+ close(F);
+}
+
+# Cut out #include lines that don't contribute anything.
+for($i=0; $i<@ARGV; $i++){
+ $file = $ARGV[$i];
+ if(!open(F, $file)){
+ print STDERR "open $file: $!\n";
+ next;
+ }
+ @lines = <F>;
+ close(F);
+
+ $obj = "$file.o";
+ $obj =~ s/\.c\.o$/.o/;
+ system("touch $file");
+
+ if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
+ print STDERR "make $obj failed: $rv\n";
+ next;
+ }
+
+ system("cp $file =$file");
+ for($j=@lines-1; $j>=0; $j--){
+ if($lines[$j] =~ /^#include/){
+ $old = $lines[$j];
+ $lines[$j] = "/* CUT-H */\n";
+ writefile($file, @lines);
+ if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
+ $lines[$j] = $old;
+ }else{
+ print STDERR "$file $old";
+ }
+ }
+ }
+ writefile($file, grep {!/CUT-H/} @lines);
+ system("rm =$file");
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// This is a huge kludge and a hack. This file was written to use XV6
+// primitives that overlap with unix interfaces. I have to directly include
+// those shared headers, as including the whole directory (at compile time
+// -I../include) would result in ../include/fcntl.h being read instead of the
+// system fcntl (which we need for open).
+#define stat xv6_stat // avoid clash with host struct stat
+#include "../include/types.h"
+#include "../include/fs.h"
+#include "../include/stat.h"
+#include "../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.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);
+}
--- /dev/null
+#!/usr/bin/perl
+
+use POSIX qw(strftime);
+
+if($ARGV[0] eq "-h"){
+ shift @ARGV;
+ $h = $ARGV[0];
+ shift @ARGV;
+}else{
+ $h = $ARGV[0];
+}
+
+$page = 0;
+$now = strftime "%b %e %H:%M %Y", localtime;
+
+@lines = <>;
+for($i=0; $i<@lines; $i+=50){
+ print "\n\n";
+ ++$page;
+ print "$now $h Page $page\n";
+ print "\n\n";
+ for($j=$i; $j<@lines && $j<$i +50; $j++){
+ $lines[$j] =~ s!//DOC.*!!;
+ print $lines[$j];
+ }
+ for(; $j<$i+50; $j++){
+ print "\n";
+ }
+ $sheet = "";
+ if($lines[$i] =~ /^([0-9][0-9])[0-9][0-9] /){
+ $sheet = "Sheet $1";
+ }
+ print "\n\n";
+ print "$sheet\n";
+ print "\n\n";
+}
--- /dev/null
+#!/bin/sh
+
+# Decode the symbols from a panic EIP list
+
+# Find a working addr2line
+for p in i386-jos-elf-addr2line addr2line; do
+ if which $p 2>&1 >/dev/null && \
+ $p -h 2>&1 | grep -q '\belf32-i386\b'; then
+ break
+ fi
+done
+
+# Enable as much pretty-printing as this addr2line can do
+$p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@"
--- /dev/null
+#!/bin/sh
+
+echo This script takes a minute to run. Be patient. 1>&2
+
+LC_CTYPE=C export LC_CTYPE
+
+# pad stdin to multiple of 120 lines
+pad()
+{
+ awk '{print} END{for(; NR%120!=0; NR++) print ""}'
+}
+
+# create formatted (numbered) files
+mkdir -p fmt
+rm -f fmt/*
+cp README fmt
+echo > fmt/blank
+files=`grep -v '^#' runoff.list | awk '{print $1}'`
+n=99
+for i in $files
+do
+ ./runoff1 -n $n $i >fmt/$i
+ nn=`tail -1 fmt/$i | sed 's/ .*//; s/^0*//'`
+ if [ "x$nn" != x ]; then
+ n=$nn
+ fi
+done
+
+# create table of contents
+cat toc.hdr >fmt/toc
+pr -e8 -t runoff.list | awk '
+/^[a-z0-9]/ {
+ s=$0
+ f="fmt/"$1
+ getline<f
+ close(f)
+ n=$1
+ printf("%02d %s\n", n/100, s);
+ printf("TOC: %04d %s\n", n, s) >"fmt/tocdata"
+ next
+}
+{
+ print
+}' | pr -3 -t >>fmt/toc
+cat toc.ftr >>fmt/toc
+
+# check for bad alignments
+perl -e '
+ $leftwarn = 0;
+ while(<>){
+ chomp;
+ s!#.*!!;
+ s!\s+! !g;
+ s! +$!!;
+ next if /^$/;
+
+ if(/TOC: (\d+) (.*)/){
+ $toc{$2} = $1;
+ next;
+ }
+
+ if(/sheet1: (left|right)$/){
+ print STDERR "assuming that sheet 1 is a $1 page. double-check!\n";
+ $left = $1 eq "left" ? "13579" : "02468";
+ $right = $1 eq "left" ? "02468" : "13579";
+ next;
+ }
+
+ if(/even: (.*)/){
+ $file = $1;
+ if(!defined($toc{$file})){
+ print STDERR "Have no toc for $file\n";
+ next;
+ }
+ if($toc{$file} =~ /^\d\d[^0]/){
+ print STDERR "$file does not start on a fresh page.\n";
+ }
+ next;
+ }
+
+ if(/odd: (.*)/){
+ $file = $1;
+ if(!defined($toc{$file})){
+ print STDERR "Have no toc for $file\n";
+ next;
+ }
+ if($toc{$file} !~ /^\d\d5/){
+ print STDERR "$file does not start on a second half page.\n";
+ }
+ next;
+ }
+
+ if(/(left|right): (.*)/){
+ $what = $1;
+ $file = $2;
+ if(!defined($toc{$file})){
+ print STDERR "Have no toc for $file\n";
+ next;
+ }
+ if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
+ print STDERR "$file does not start on a left page [$toc{$file}]\n";
+ }
+ # why does this not work if I inline $x in the if?
+ $x = ($toc{$file} =~ /^\d[$right][05]/);
+ if($what eq "right" && !$x){
+ print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n";
+ }
+ next;
+ }
+
+ print STDERR "Unknown spec: $_\n";
+ }
+' fmt/tocdata runoff.spec
+
+# make definition list
+cd fmt
+perl -e '
+ while(<>) {
+ chomp;
+
+ s!//.*!!;
+ s!/\*([^*]|[*][^/])*\*/!!g;
+ s!\s! !g;
+ s! +$!!;
+
+ # look for declarations like char* x;
+ if (/^[0-9]+ typedef .* u(int|short|long|char);/) {
+ next;
+ }
+ if (/^[0-9]+ extern/) {
+ next;
+ }
+ if (/^[0-9]+ struct [a-zA-Z0-9_]+;/) {
+ next;
+ }
+ if (/^([0-9]+) #define +([A-za-z0-9_]+) +?\(.*/) {
+ print "$1 $2\n"
+ }
+ elsif (/^([0-9]+) #define +([A-Za-z0-9_]+) +([^ ]+)/) {
+ print "$1 $2 $3\n";
+ }
+ elsif (/^([0-9]+) #define +([A-Za-z0-9_]+)/) {
+ print "$1 $2\n";
+ }
+
+ if(/^^([0-9]+) \.globl ([a-zA-Z0-9_]+)/){
+ $isglobl{$2} = 1;
+ }
+ if(/^^([0-9]+) ([a-zA-Z0-9_]+):$/ && $isglobl{$2}){
+ print "$1 $2\n";
+ }
+
+ if (/\(/) {
+ next;
+ }
+
+ if (/^([0-9]+) (((static|struct|extern|union|enum) +)*([A-Za-z0-9_]+))( .*)? +([A-Za-z_][A-Za-z0-9_]*)(,|;|=| =)/) {
+ print "$1 $7\n";
+ }
+
+ elsif(/^([0-9]+) (enum|struct|union) +([A-Za-z0-9_]+) +{/){
+ print "$1 $3\n";
+ }
+ # TODO: enum members
+ }
+' $files >defs
+
+(for i in $files
+do
+ case "$i" in
+ *.S)
+ cat $i | sed 's;#.*;;; s;//.*;;;'
+ ;;
+ *)
+ cat $i | sed 's;//.*;;; s;"([^"\\]|\\.)*";;;'
+ esac
+done
+) >alltext
+
+perl -n -e 'print if s/^([0-9]+ [a-zA-Z0-9_]+)\(.*$/\1/;' alltext |
+ egrep -v ' (STUB|usage|main|if|for)$' >>defs
+#perl -n -e 'print if s/^([0-9]+) STUB\(([a-zA-Z0-9_]+)\)$/\1 \2/;' alltext \
+# >>defs
+(
+>s.defs
+
+# make reference list
+for i in `awk '{print $2}' defs | sort -f | uniq`
+do
+ defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'`
+ echo $i $defs >>s.defs
+ uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
+ if [ "x$defs" != "x$uses" ]; then
+ echo $i $defs
+ echo $uses |fmt -29 | sed 's/^/ /'
+# else
+# echo $i defined but not used >&2
+ fi
+done
+) >refs
+
+# build defs list
+awk '
+{
+ printf("%04d %s\n", $2, $1);
+ for(i=3; i<=NF; i++)
+ printf("%04d \" \n", $i);
+}
+' s.defs > t.defs
+
+# format the whole thing
+(
+ ../pr.pl README
+ ../pr.pl -h "table of contents" toc
+ # pr -t -2 t.defs | ../pr.pl -h "definitions" | pad
+ pr -t -l50 -2 refs | ../pr.pl -h "cross-references" | pad
+ # pr.pl -h "definitions" -2 t.defs | pad
+ # pr.pl -h "cross-references" -2 refs | pad
+ ../pr.pl blank # make sheet 1 start on left page
+ ../pr.pl blank
+ for i in $files
+ do
+ ../pr.pl -h "xv6/$i" $i
+ done
+) | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >all.ps
+grep Pages: all.ps
+
+# if we have the nice font, use it
+nicefont=LucidaSans-Typewriter83
+if [ ! -f ../$nicefont ]
+then
+ if git cat-file blob font:$nicefont > ../$nicefont~; then
+ mv ../$nicefont~ ../$nicefont
+ fi
+fi
+if [ -f ../$nicefont ]
+then
+ echo nicefont
+ (sed 1q all.ps; cat ../$nicefont; sed "1d; s/Courier/$nicefont/" all.ps) >allf.ps
+else
+ echo ugly font!
+ cp all.ps allf.ps
+fi
+ps2pdf allf.ps ../xv6.pdf
+# cd ..
+# pdftops xv6.pdf xv6.ps
--- /dev/null
+# basic headers
+types.h
+param.h
+memlayout.h
+defs.h
+x86.h
+asm.h
+mmu.h
+elf.h
+date.h
+
+# entering xv6
+entry.S
+entryother.S
+main.c
+
+# locks
+spinlock.h
+spinlock.c
+
+# processes
+vm.c
+proc.h
+proc.c
+swtch.S
+kalloc.c
+
+# system calls
+traps.h
+vectors.pl
+trapasm.S
+trap.c
+syscall.h
+syscall.c
+sysproc.c
+
+# file system
+buf.h
+sleeplock.h
+fcntl.h
+stat.h
+fs.h
+file.h
+ide.c
+bio.c
+sleeplock.c
+log.c
+fs.c
+file.c
+sysfile.c
+exec.c
+
+# pipes
+pipe.c
+
+# string operations
+string.c
+
+# low-level hardware
+mp.h
+mp.c
+lapic.c
+ioapic.c
+kbd.h
+kbd.c
+console.c
+uart.c
+
+# user-level
+initcode.S
+usys.S
+init.c
+sh.c
+
+# bootloader
+bootasm.S
+bootmain.c
+
+# link
+kernel.ld
--- /dev/null
+# Is sheet 01 (after the TOC) a left sheet or a right sheet?
+sheet1: left
+
+# "left" and "right" specify which page of a two-page spread a file
+# must start on. "left" means that a file must start on the first of
+# the two pages. "right" means it must start on the second of the two
+# pages. The file may start in either column.
+#
+# "even" and "odd" specify which column a file must start on. "even"
+# means it must start in the left of the two columns (00). "odd" means it
+# must start in the right of the two columns (50).
+#
+# You'd think these would be the other way around.
+
+# types.h either
+# param.h either
+# defs.h either
+# x86.h either
+# asm.h either
+# mmu.h either
+# elf.h either
+# mp.h either
+
+even: entry.S # mild preference
+even: entryother.S # mild preference
+even: main.c
+# mp.c don't care at all
+# even: initcode.S
+# odd: init.c
+
+left: spinlock.h
+even: spinlock.h
+
+# This gets struct proc and allocproc on the same spread
+left: proc.h
+even: proc.h
+
+# goal is to have two action-packed 2-page spreads,
+# one with
+# userinit growproc fork exit wait
+# and another with
+# scheduler sched yield forkret sleep wakeup1 wakeup
+right: proc.c # VERY important
+even: proc.c # VERY important
+
+# A few more action packed spreads
+# page table creation and process loading
+# walkpgdir mappages setupkvm switch[ku]vm inituvm (loaduvm)
+# process memory management
+# allocuvm deallocuvm freevm
+left: vm.c
+
+even: kalloc.c # mild preference
+
+# syscall.h either
+# trapasm.S either
+# traps.h either
+# even: trap.c
+# vectors.pl either
+# syscall.c either
+# sysproc.c either
+
+# buf.h either
+# dev.h either
+# fcntl.h either
+# stat.h either
+# file.h either
+# fs.h either
+# fsvar.h either
+# left: ide.c # mild preference
+even: ide.c
+# odd: bio.c
+
+# log.c fits nicely in a spread
+even: log.c
+left: log.c
+
+# with fs.c starting on 2nd column of a left page, we get these 2-page spreads:
+# ialloc iupdate iget idup ilock iunlock iput iunlockput
+# bmap itrunc stati readi writei
+# namecmp dirlookup dirlink skipelem namex namei
+# fileinit filealloc filedup fileclose filestat fileread filewrite
+# starting on 2nd column of a right page is not terrible either
+odd: fs.c # VERY important
+left: fs.c # mild preference
+# file.c either
+# exec.c either
+# sysfile.c either
+
+# Mild preference, but makes spreads of mp.c, lapic.c, and ioapic.c+picirq.c
+even: mp.c
+left: mp.c
+
+# even: pipe.c # mild preference
+# string.c either
+# left: kbd.h # mild preference
+even: kbd.h
+even: console.c
+odd: sh.c
+
+even: bootasm.S # mild preference
+even: bootmain.c # mild preference
--- /dev/null
+#!/usr/bin/perl
+
+$n = 0;
+$v = 0;
+if($ARGV[0] eq "-v") {
+ $v = 1;
+ shift @ARGV;
+}
+if($ARGV[0] eq "-n") {
+ $n = $ARGV[1];
+ shift @ARGV;
+ shift @ARGV;
+}
+$n = int(($n+49)/50)*50 - 1;
+
+$file = $ARGV[0];
+@lines = <>;
+$linenum = 0;
+foreach (@lines) {
+ $linenum++;
+ chomp;
+ s/\s+$//;
+ if(length() >= 75){
+ print STDERR "$file:$linenum: line too long\n";
+ }
+}
+@outlines = ();
+$nextout = 0;
+
+for($i=0; $i<@lines; ){
+ # Skip leading blank lines.
+ $i++ while $i<@lines && $lines[$i] =~ /^$/;
+ last if $i>=@lines;
+
+ # If the rest of the file fits, use the whole thing.
+ if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
+ $breakbefore = @lines;
+ }else{
+ # Find a good next page break;
+ # Hope for end of function.
+ # but settle for a blank line (but not first blank line
+ # in function, which comes after variable declarations).
+ $breakbefore = $i;
+ $lastblank = $i;
+ $sawbrace = 0;
+ $breaksize = 15; # 15 lines to get to function
+ for($j=$i; $j<$i+50 && $j < @lines; $j++){
+ if($lines[$j] =~ /PAGEBREAK!/){
+ $lines[$j] = "";
+ $breakbefore = $j;
+ $breaksize = 100;
+ last;
+ }
+ if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){
+ $breaksize = $1;
+ $breakbefore = $j;
+ $lines[$j] = "";
+ }
+ if($lines[$j] =~ /^};?$/){
+ $breakbefore = $j+1;
+ $breaksize = 15;
+ }
+ if($lines[$j] =~ /^{$/){
+ $sawbrace = 1;
+ }
+ if($lines[$j] =~ /^$/){
+ if($sawbrace){
+ $sawbrace = 0;
+ }else{
+ $lastblank = $j;
+ }
+ }
+ }
+ if($j<@lines && $lines[$j] =~ /^$/){
+ $lastblank = $j;
+ }
+
+ # If we are not putting enough on a page, try a blank line.
+ if($breakbefore - $i < 50 - $breaksize && $lastblank > $breakbefore && $lastblank >= $i+50 - 5){
+ if($v){
+ print STDERR "breakbefore $breakbefore i $i breaksize $breaksize\n";
+ }
+ $breakbefore = $lastblank;
+ $breaksize = 5; # only 5 lines to get to blank line
+ }
+
+ # If we are not putting enough on a page, force a full page.
+ if($breakbefore - $i < 50 - $breaksize && $breakbefore != @lines){
+ $breakbefore = $i + 50;
+ $breakbefore = @lines if @lines < $breakbefore;
+ }
+
+ if($breakbefore < $i+2){
+ $breakbefore = $i+2;
+ }
+ }
+
+ # Emit the page.
+ $i50 = $i + 50;
+ for(; $i<$breakbefore; $i++){
+ printf "%04d %s\n", ++$n, $lines[$i];
+ }
+
+ # Finish page
+ for($j=$i; $j<$i50; $j++){
+ printf "%04d \n", ++$n;
+ }
+}
--- /dev/null
+#!/bin/sh
+
+runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps
--- /dev/null
+#!/bin/sh
+
+if [ $# != 1 ] || [ ! -f "$1" ]; then
+ echo 'usage: spinp file.p' 1>&2
+ exit 1
+fi
+
+rm -f $1.trail
+spin -a $1 || exit 1
+cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c
+pan -i
+rm pan.* pan
+if [ -f $1.trail ]; then
+ spin -t -p $1
+fi
+
--- /dev/null
+
+
+The source listing is preceded by a cross-reference that lists every defined
+constant, struct, global variable, and function in xv6. Each entry gives,
+on the same line as the name, the line number (or, in a few cases, numbers)
+where the name is defined. Successive lines in an entry list the line
+numbers where the name is used. For example, this entry:
+
+ swtch 2658
+ 0374 2428 2466 2657 2658
+
+indicates that swtch is defined on line 2658 and is mentioned on five lines
+on sheets 03, 24, and 26.
--- /dev/null
+The numbers to the left of the file names in the table are sheet numbers.
+The source code has been printed in a double column format with fifty
+lines per column, giving one hundred lines per sheet (or page).
+Thus there is a convenient relationship between line numbers and sheet numbers.
+
+
+++ /dev/null
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-#include "traps.h"
-#include "spinlock.h"
-
-// Interrupt descriptor table (shared by all CPUs).
-struct gatedesc idt[256];
-extern uint vectors[]; // in vectors.S: array of 256 entry pointers
-struct spinlock tickslock;
-uint ticks;
-
-void
-tvinit(void)
-{
- int i;
-
- for(i = 0; i < 256; i++)
- SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
- SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);
-
- initlock(&tickslock, "time");
-}
-
-void
-idtinit(void)
-{
- lidt(idt, sizeof(idt));
-}
-
-//PAGEBREAK: 41
-void
-trap(struct trapframe *tf)
-{
- if(tf->trapno == T_SYSCALL){
- if(myproc()->killed)
- exit();
- myproc()->tf = tf;
- syscall();
- if(myproc()->killed)
- exit();
- return;
- }
-
- switch(tf->trapno){
- case T_IRQ0 + IRQ_TIMER:
- if(cpuid() == 0){
- acquire(&tickslock);
- ticks++;
- wakeup(&ticks);
- release(&tickslock);
- }
- lapiceoi();
- break;
- case T_IRQ0 + IRQ_IDE:
- ideintr();
- lapiceoi();
- break;
- case T_IRQ0 + IRQ_IDE+1:
- // Bochs generates spurious IDE1 interrupts.
- break;
- case T_IRQ0 + IRQ_KBD:
- kbdintr();
- lapiceoi();
- break;
- case T_IRQ0 + IRQ_COM1:
- uartintr();
- lapiceoi();
- break;
- case T_IRQ0 + 7:
- case T_IRQ0 + IRQ_SPURIOUS:
- cprintf("cpu%d: spurious interrupt at %x:%x\n",
- cpuid(), tf->cs, tf->eip);
- lapiceoi();
- break;
-
- //PAGEBREAK: 13
- default:
- if(myproc() == 0 || (tf->cs&3) == 0){
- // In kernel, it must be our mistake.
- cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
- tf->trapno, cpuid(), tf->eip, rcr2());
- panic("trap");
- }
- // In user space, assume process misbehaved.
- cprintf("pid %d %s: trap %d err %d on cpu %d "
- "eip 0x%x addr 0x%x--kill proc\n",
- myproc()->pid, myproc()->name, tf->trapno,
- tf->err, cpuid(), tf->eip, rcr2());
- myproc()->killed = 1;
- }
-
- // Force process exit if it has been killed and is in user space.
- // (If it is still executing in the kernel, let it keep running
- // until it gets to the regular system call return.)
- if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
- exit();
-
- // Force process to give up CPU on clock tick.
- // If interrupts were on while locks held, would need to check nlock.
- if(myproc() && myproc()->state == RUNNING &&
- tf->trapno == T_IRQ0+IRQ_TIMER)
- yield();
-
- // Check if the process has been killed since we yielded
- if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
- exit();
-}
+++ /dev/null
-#include "mmu.h"
-
- # vectors.S sends all traps here.
-.globl alltraps
-alltraps:
- # Build trap frame.
- pushl %ds
- pushl %es
- pushl %fs
- pushl %gs
- pushal
-
- # Set up data segments.
- movw $(SEG_KDATA<<3), %ax
- movw %ax, %ds
- movw %ax, %es
-
- # Call trap(tf), where tf=%esp
- pushl %esp
- call trap
- addl $4, %esp
-
- # Return falls through to trapret...
-.globl trapret
-trapret:
- popal
- popl %gs
- popl %fs
- popl %es
- popl %ds
- addl $0x8, %esp # trapno and errcode
- iret
+++ /dev/null
-// x86 trap and interrupt constants.
-
-// Processor-defined:
-#define T_DIVIDE 0 // divide error
-#define T_DEBUG 1 // debug exception
-#define T_NMI 2 // non-maskable interrupt
-#define T_BRKPT 3 // breakpoint
-#define T_OFLOW 4 // overflow
-#define T_BOUND 5 // bounds check
-#define T_ILLOP 6 // illegal opcode
-#define T_DEVICE 7 // device not available
-#define T_DBLFLT 8 // double fault
-// #define T_COPROC 9 // reserved (not used since 486)
-#define T_TSS 10 // invalid task switch segment
-#define T_SEGNP 11 // segment not present
-#define T_STACK 12 // stack exception
-#define T_GPFLT 13 // general protection fault
-#define T_PGFLT 14 // page fault
-// #define T_RES 15 // reserved
-#define T_FPERR 16 // floating point error
-#define T_ALIGN 17 // aligment check
-#define T_MCHK 18 // machine check
-#define T_SIMDERR 19 // SIMD floating point error
-
-// These are arbitrarily chosen, but with care not to overlap
-// processor defined exceptions or interrupt vectors.
-#define T_SYSCALL 64 // system call
-#define T_DEFAULT 500 // catchall
-
-#define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ
-
-#define IRQ_TIMER 0
-#define IRQ_KBD 1
-#define IRQ_COM1 4
-#define IRQ_IDE 14
-#define IRQ_ERROR 19
-#define IRQ_SPURIOUS 31
-
+++ /dev/null
-typedef unsigned int uint;
-typedef unsigned short ushort;
-typedef unsigned char uchar;
-typedef uint pde_t;
+++ /dev/null
-// Intel 8250 serial port (UART).
-
-#include "types.h"
-#include "defs.h"
-#include "param.h"
-#include "traps.h"
-#include "spinlock.h"
-#include "sleeplock.h"
-#include "fs.h"
-#include "file.h"
-#include "mmu.h"
-#include "proc.h"
-#include "x86.h"
-
-#define COM1 0x3f8
-
-static int uart; // is there a uart?
-
-void
-uartinit(void)
-{
- char *p;
-
- // Turn off the FIFO
- outb(COM1+2, 0);
-
- // 9600 baud, 8 data bits, 1 stop bit, parity off.
- outb(COM1+3, 0x80); // Unlock divisor
- outb(COM1+0, 115200/9600);
- outb(COM1+1, 0);
- outb(COM1+3, 0x03); // Lock divisor, 8 data bits.
- outb(COM1+4, 0);
- outb(COM1+1, 0x01); // Enable receive interrupts.
-
- // If status is 0xFF, no serial port.
- if(inb(COM1+5) == 0xFF)
- return;
- uart = 1;
-
- // Acknowledge pre-existing interrupt conditions;
- // enable interrupts.
- inb(COM1+2);
- inb(COM1+0);
- ioapicenable(IRQ_COM1, 0);
-
- // Announce that we're here.
- for(p="xv6...\n"; *p; p++)
- uartputc(*p);
-}
-
-void
-uartputc(int c)
-{
- int i;
-
- if(!uart)
- return;
- for(i = 0; i < 128 && !(inb(COM1+5) & 0x20); i++)
- microdelay(10);
- outb(COM1+0, c);
-}
-
-static int
-uartgetc(void)
-{
- if(!uart)
- return -1;
- if(!(inb(COM1+5) & 0x01))
- return -1;
- return inb(COM1+0);
-}
-
-void
-uartintr(void)
-{
- consoleintr(uartgetc);
-}
+++ /dev/null
-#include "types.h"
-#include "stat.h"
-#include "fcntl.h"
-#include "user.h"
-#include "x86.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)
-{
- stosb(dst, c, n);
- 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;
-}
+++ /dev/null
-#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;
- }
-}
+++ /dev/null
-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*);
--- /dev/null
+project(user ASM)
+
+include_directories(include)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -fno-builtin -nostdinc -nostartfiles -nodefaultlibs -fno-pic -m32")
+set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}")
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -N -e main -Ttext 0")
+# FIXME: Maybe use... unclear
+set(CMAKE_C_LINK_EXECUTABLE "ld -m elf_i386 -N -e main -Ttext 0 <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
+
+# We need to do several things --
+# Need ulib sources (easy enough)
+# Need to compile the programs
+# -- binary should add a _...
+# Need to create disassembly
+# Need to create symbol files
+
+set(ulib_SOURCES
+ asm/usys.S
+
+ src/ulib.c
+ src/umalloc.c
+ src/printf.c
+ )
+
+# Start with just init...
+set(user_SOURCES
+ src/init.c
+ src/sh.c
+ src/ls.c
+ )
+
+set_property(GLOBAL PROPERTY user_programs)
+
+add_library(ulib OBJECT
+ ${ulib_SOURCES})
+
+function(add_user_program program_src)
+ # Strip out src/ and .c
+ get_filename_component(prog_base ${program_src} NAME_WE)
+ # Add _
+ set(output_exe "_${prog_base}")
+ message("Have output_exe ${output_exe}")
+
+ get_property(l_user_programs GLOBAL PROPERTY user_programs)
+ list(APPEND l_user_programs "${output_exe}")
+ set_property(GLOBAL PROPERTY user_programs ${l_user_programs})
+
+ # First, build the program
+ add_executable(${output_exe} ${program_src} $<TARGET_OBJECTS:ulib>)
+ # Second, disassemble the program
+ # Third, get symbols
+endfunction(add_user_program)
+
+foreach(prog_src ${user_SOURCES})
+ add_user_program(${prog_src})
+endforeach(prog_src)
+
+get_property(l_user_programs GLOBAL PROPERTY user_programs)
+message("USER_PROGRAMS includes: ${l_user_programs}")
+
+configure_file(${CMAKE_SOURCE_DIR}/README ${CMAKE_CURRENT_BINARY_DIR}/README COPYONLY)
+
+add_custom_command(
+ OUTPUT fs.img
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../tools/mkfs fs.img README ${l_user_programs}
+ DEPENDS mkfs ${CMAKE_SOURCE_DIR}/README ${l_user_programs}
+)
+
+add_custom_target(
+ makeuserfs ALL
+ DEPENDS fs.img)
+
--- /dev/null
+#include "syscall.h"
+#include "traps.h"
+
+#define SYSCALL(name) \
+ .globl name; \
+ name: \
+ movl $SYS_ ## name, %eax; \
+ int $T_SYSCALL; \
+ ret
+
+SYSCALL(fork)
+SYSCALL(exit)
+SYSCALL(wait)
+SYSCALL(pipe)
+SYSCALL(read)
+SYSCALL(write)
+SYSCALL(close)
+SYSCALL(kill)
+SYSCALL(exec)
+SYSCALL(open)
+SYSCALL(mknod)
+SYSCALL(unlink)
+SYSCALL(fstat)
+SYSCALL(link)
+SYSCALL(mkdir)
+SYSCALL(chdir)
+SYSCALL(dup)
+SYSCALL(getpid)
+SYSCALL(sbrk)
+SYSCALL(sleep)
+SYSCALL(uptime)
--- /dev/null
+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*);
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+// 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<N; n++){
+ pid = fork();
+ if(pid < 0)
+ break;
+ if(pid == 0)
+ exit();
+ }
+
+ if(n == N){
+ printf(1, "fork claimed to work N times!\n", 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");
+}
+
+int
+main(void)
+{
+ forktest();
+ exit();
+}
--- /dev/null
+// 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;
+}
+
--- /dev/null
+// 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");
+ }
+}
--- /dev/null
+#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<argc; i++)
+ kill(atoi(argv[i]));
+ exit();
+}
--- /dev/null
+#include "types.h"
+#include "stat.h"
+#include "user.h"
+
+int
+main(int argc, char *argv[])
+{
+ if(argc != 3){
+ printf(2, "Usage: ln old new\n");
+ exit();
+ }
+ if(link(argv[1], argv[2]) < 0)
+ printf(2, "link %s %s: failed\n", argv[1], argv[2]);
+ exit();
+}
--- /dev/null
+#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<argc; i++)
+ ls(argv[i]);
+ exit();
+}
--- /dev/null
+#include "types.h"
+#include "stat.h"
+#include "user.h"
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ if(argc < 2){
+ printf(2, "Usage: mkdir files...\n");
+ exit();
+ }
+
+ for(i = 1; i < argc; i++){
+ if(mkdir(argv[i]) < 0){
+ printf(2, "mkdir: %s failed to create\n", argv[i]);
+ break;
+ }
+ }
+
+ exit();
+}
--- /dev/null
+#include "types.h"
+#include "stat.h"
+#include "user.h"
+
+static void
+putc(int fd, char c)
+{
+ write(fd, &c, 1);
+}
+
+static void
+printint(int fd, int xx, int base, int sgn)
+{
+ static char digits[] = "0123456789ABCDEF";
+ 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]);
+}
+
+// Print to the given fd. Only understands %d, %x, %p, %s.
+void
+printf(int fd, const char *fmt, ...)
+{
+ char *s;
+ int c, i, state;
+ uint *ap;
+
+ state = 0;
+ ap = (uint*)(void*)&fmt + 1;
+ 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, *ap, 10, 1);
+ ap++;
+ } else if(c == 'x' || c == 'p'){
+ printint(fd, *ap, 16, 0);
+ ap++;
+ } else if(c == 's'){
+ s = (char*)*ap;
+ ap++;
+ if(s == 0)
+ s = "(null)";
+ while(*s != 0){
+ putc(fd, *s);
+ s++;
+ }
+ } else if(c == 'c'){
+ putc(fd, *ap);
+ ap++;
+ } else if(c == '%'){
+ putc(fd, c);
+ } else {
+ // Unknown % sequence. Print it to draw attention.
+ putc(fd, '%');
+ putc(fd, c);
+ }
+ state = 0;
+ }
+ }
+}
--- /dev/null
+#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();
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+#include "types.h"
+#include "stat.h"
+#include "fcntl.h"
+#include "user.h"
+#include "asm/x86.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)
+{
+ stosb(dst, c, n);
+ 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;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#include "param.h"
+#include "types.h"
+#include "stat.h"
+#include "user.h"
+#include "fs.h"
+#include "fcntl.h"
+#include "syscall.h"
+#include "traps.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)
+ for(;;)
+ ;
+
+ pid2 = fork();
+ if(pid2 == 0)
+ for(;;)
+ ;
+
+ pipe(pfds);
+ pid3 = fork();
+ 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 };
+ int pid, i, fd, pi;
+ char name[32];
+
+ printf(1, "createdelete test\n");
+
+ for(pi = 0; pi < 4; 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 < 4; pi++){
+ wait();
+ }
+
+ name[0] = name[1] = name[2] = 0;
+ for(i = 0; i < N; i++){
+ for(pi = 0; pi < 4; 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 < 4; 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 == 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 fds[2], pid, pids[10], ppid;
+ char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch;
+ uint amt;
+
+ printf(stdout, "sbrk test\n");
+ oldbrk = sbrk(0);
+
+ // can one sbrk() less than a page?
+ a = sbrk(0);
+ int i;
+ 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?
+#define BIG (100*1024*1024)
+ a = sbrk(0);
+ amt = (BIG) - (uint)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 - (uint)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)
+{
+ 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;
+ uint 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
+uio()
+{
+ #define RTC_ADDR 0x70
+ #define RTC_DATA 0x71
+
+ ushort port = 0;
+ uchar val = 0;
+ int pid;
+
+ printf(1, "uio test\n");
+ pid = fork();
+ if(pid == 0){
+ port = RTC_ADDR;
+ val = 0x09; /* year */
+ /* http://wiki.osdev.org/Inline_Assembly/Examples */
+ __asm__ volatile("outb %0,%1"::"a"(val), "d" (port));
+ port = RTC_DATA;
+ __asm__ volatile("inb %1,%0" : "=a" (val) : "d" (port));
+ printf(1, "uio: uio succeeded; test FAILED\n");
+ exit();
+ } else if(pid < 0){
+ printf (1, "fork failed\n");
+ exit();
+ }
+ wait();
+ printf(1, "uio test done\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
+
+ uio();
+
+ exectest();
+
+ exit();
+}
--- /dev/null
+#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<n; i++){
+ c++;
+ if(buf[i] == '\n')
+ l++;
+ if(strchr(" \r\t\n\v", buf[i]))
+ inword = 0;
+ else if(!inword){
+ w++;
+ inword = 1;
+ }
+ }
+ }
+ if(n < 0){
+ printf(1, "wc: read error\n");
+ exit();
+ }
+ printf(1, "%d %d %d %s\n", l, w, c, name);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd, i;
+
+ if(argc <= 1){
+ wc(0, "");
+ exit();
+ }
+
+ for(i = 1; i < argc; i++){
+ if((fd = open(argv[i], 0)) < 0){
+ printf(1, "wc: cannot open %s\n", argv[i]);
+ exit();
+ }
+ wc(fd, argv[i]);
+ close(fd);
+ }
+ exit();
+}
--- /dev/null
+// Create a zombie process that
+// must be reparented at exit.
+
+#include "types.h"
+#include "stat.h"
+#include "user.h"
+
+int
+main(void)
+{
+ if(fork() > 0)
+ sleep(5); // Let child exit before parent.
+ exit();
+}
+++ /dev/null
-#include "param.h"
-#include "types.h"
-#include "stat.h"
-#include "user.h"
-#include "fs.h"
-#include "fcntl.h"
-#include "syscall.h"
-#include "traps.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)
- for(;;)
- ;
-
- pid2 = fork();
- if(pid2 == 0)
- for(;;)
- ;
-
- pipe(pfds);
- pid3 = fork();
- 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 };
- int pid, i, fd, pi;
- char name[32];
-
- printf(1, "createdelete test\n");
-
- for(pi = 0; pi < 4; 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 < 4; pi++){
- wait();
- }
-
- name[0] = name[1] = name[2] = 0;
- for(i = 0; i < N; i++){
- for(pi = 0; pi < 4; 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 < 4; 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 == 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 fds[2], pid, pids[10], ppid;
- char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch;
- uint amt;
-
- printf(stdout, "sbrk test\n");
- oldbrk = sbrk(0);
-
- // can one sbrk() less than a page?
- a = sbrk(0);
- int i;
- 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?
-#define BIG (100*1024*1024)
- a = sbrk(0);
- amt = (BIG) - (uint)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 - (uint)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)
-{
- 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;
- uint 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
-uio()
-{
- #define RTC_ADDR 0x70
- #define RTC_DATA 0x71
-
- ushort port = 0;
- uchar val = 0;
- int pid;
-
- printf(1, "uio test\n");
- pid = fork();
- if(pid == 0){
- port = RTC_ADDR;
- val = 0x09; /* year */
- /* http://wiki.osdev.org/Inline_Assembly/Examples */
- __asm__ volatile("outb %0,%1"::"a"(val), "d" (port));
- port = RTC_DATA;
- __asm__ volatile("inb %1,%0" : "=a" (val) : "d" (port));
- printf(1, "uio: uio succeeded; test FAILED\n");
- exit();
- } else if(pid < 0){
- printf (1, "fork failed\n");
- exit();
- }
- wait();
- printf(1, "uio test done\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
-
- uio();
-
- exectest();
-
- exit();
-}
+++ /dev/null
-#include "syscall.h"
-#include "traps.h"
-
-#define SYSCALL(name) \
- .globl name; \
- name: \
- movl $SYS_ ## name, %eax; \
- int $T_SYSCALL; \
- ret
-
-SYSCALL(fork)
-SYSCALL(exit)
-SYSCALL(wait)
-SYSCALL(pipe)
-SYSCALL(read)
-SYSCALL(write)
-SYSCALL(close)
-SYSCALL(kill)
-SYSCALL(exec)
-SYSCALL(open)
-SYSCALL(mknod)
-SYSCALL(unlink)
-SYSCALL(fstat)
-SYSCALL(link)
-SYSCALL(mkdir)
-SYSCALL(chdir)
-SYSCALL(dup)
-SYSCALL(getpid)
-SYSCALL(sbrk)
-SYSCALL(sleep)
-SYSCALL(uptime)
+++ /dev/null
-#!/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 " pushl \$0\n";
- }
- print " pushl \$$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 " .long vector$i\n";
-}
-
-# sample output:
-# # handlers
-# .globl alltraps
-# .globl vector0
-# vector0:
-# pushl $0
-# pushl $0
-# jmp alltraps
-# ...
-#
-# # vector table
-# .data
-# .globl vectors
-# vectors:
-# .long vector0
-# .long vector1
-# .long vector2
-# ...
-
+++ /dev/null
-#include "param.h"
-#include "types.h"
-#include "defs.h"
-#include "x86.h"
-#include "memlayout.h"
-#include "mmu.h"
-#include "proc.h"
-#include "elf.h"
-
-extern char data[]; // defined by kernel.ld
-pde_t *kpgdir; // for use in scheduler()
-
-// Set up CPU's kernel segment descriptors.
-// Run once on entry on each CPU.
-void
-seginit(void)
-{
- struct cpu *c;
-
- // Map "logical" addresses to virtual addresses using identity map.
- // Cannot share a CODE descriptor for both kernel and user
- // because it would have to have DPL_USR, but the CPU forbids
- // an interrupt from CPL=0 to DPL=3.
- c = &cpus[cpuid()];
- c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
- c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
- c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
- c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
- lgdt(c->gdt, sizeof(c->gdt));
-}
-
-// Return the address of the PTE in page table pgdir
-// that corresponds to virtual address va. If alloc!=0,
-// create any required page table pages.
-static pte_t *
-walkpgdir(pde_t *pgdir, const void *va, int alloc)
-{
- pde_t *pde;
- pte_t *pgtab;
-
- pde = &pgdir[PDX(va)];
- if(*pde & PTE_P){
- pgtab = (pte_t*)P2V(PTE_ADDR(*pde));
- } else {
- if(!alloc || (pgtab = (pte_t*)kalloc()) == 0)
- return 0;
- // Make sure all those PTE_P bits are zero.
- memset(pgtab, 0, PGSIZE);
- // The permissions here are overly generous, but they can
- // be further restricted by the permissions in the page table
- // entries, if necessary.
- *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
- }
- return &pgtab[PTX(va)];
-}
-
-// Create PTEs for virtual addresses starting at va that refer to
-// physical addresses starting at pa. va and size might not
-// be page-aligned.
-static int
-mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
-{
- char *a, *last;
- pte_t *pte;
-
- a = (char*)PGROUNDDOWN((uint)va);
- last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
- for(;;){
- if((pte = walkpgdir(pgdir, a, 1)) == 0)
- return -1;
- if(*pte & PTE_P)
- panic("remap");
- *pte = pa | perm | PTE_P;
- if(a == last)
- break;
- a += PGSIZE;
- pa += PGSIZE;
- }
- return 0;
-}
-
-// There is one page table per process, plus one that's used when
-// a CPU is not running any process (kpgdir). The kernel uses the
-// current process's page table during system calls and interrupts;
-// page protection bits prevent user code from using the kernel's
-// mappings.
-//
-// setupkvm() and exec() set up every page table like this:
-//
-// 0..KERNBASE: user memory (text+data+stack+heap), mapped to
-// phys memory allocated by the kernel
-// KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)
-// KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)
-// for the kernel's instructions and r/o data
-// data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,
-// rw data + free physical memory
-// 0xfe000000..0: mapped direct (devices such as ioapic)
-//
-// The kernel allocates physical memory for its heap and for user memory
-// between V2P(end) and the end of physical memory (PHYSTOP)
-// (directly addressable from end..P2V(PHYSTOP)).
-
-// This table defines the kernel's mappings, which are present in
-// every process's page table.
-static struct kmap {
- void *virt;
- uint phys_start;
- uint phys_end;
- int perm;
-} kmap[] = {
- { (void*)KERNBASE, 0, EXTMEM, PTE_W}, // I/O space
- { (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kern text+rodata
- { (void*)data, V2P(data), PHYSTOP, PTE_W}, // kern data+memory
- { (void*)DEVSPACE, DEVSPACE, 0, PTE_W}, // more devices
-};
-
-// Set up kernel part of a page table.
-pde_t*
-setupkvm(void)
-{
- pde_t *pgdir;
- struct kmap *k;
-
- if((pgdir = (pde_t*)kalloc()) == 0)
- return 0;
- memset(pgdir, 0, PGSIZE);
- if (P2V(PHYSTOP) > (void*)DEVSPACE)
- panic("PHYSTOP too high");
- for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
- if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
- (uint)k->phys_start, k->perm) < 0) {
- freevm(pgdir);
- return 0;
- }
- return pgdir;
-}
-
-// Allocate one page table for the machine for the kernel address
-// space for scheduler processes.
-void
-kvmalloc(void)
-{
- kpgdir = setupkvm();
- switchkvm();
-}
-
-// Switch h/w page table register to the kernel-only page table,
-// for when no process is running.
-void
-switchkvm(void)
-{
- lcr3(V2P(kpgdir)); // switch to the kernel page table
-}
-
-// Switch TSS and h/w page table to correspond to process p.
-void
-switchuvm(struct proc *p)
-{
- if(p == 0)
- panic("switchuvm: no process");
- if(p->kstack == 0)
- panic("switchuvm: no kstack");
- if(p->pgdir == 0)
- panic("switchuvm: no pgdir");
-
- pushcli();
- mycpu()->gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()->ts,
- sizeof(mycpu()->ts)-1, 0);
- mycpu()->gdt[SEG_TSS].s = 0;
- mycpu()->ts.ss0 = SEG_KDATA << 3;
- mycpu()->ts.esp0 = (uint)p->kstack + KSTACKSIZE;
- // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
- // forbids I/O instructions (e.g., inb and outb) from user space
- mycpu()->ts.iomb = (ushort) 0xFFFF;
- ltr(SEG_TSS << 3);
- lcr3(V2P(p->pgdir)); // switch to process's address space
- popcli();
-}
-
-// Load the initcode into address 0 of pgdir.
-// sz must be less than a page.
-void
-inituvm(pde_t *pgdir, char *init, uint sz)
-{
- char *mem;
-
- if(sz >= PGSIZE)
- panic("inituvm: more than a page");
- mem = kalloc();
- memset(mem, 0, PGSIZE);
- mappages(pgdir, 0, PGSIZE, V2P(mem), PTE_W|PTE_U);
- memmove(mem, init, sz);
-}
-
-// Load a program segment into pgdir. addr must be page-aligned
-// and the pages from addr to addr+sz must already be mapped.
-int
-loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
-{
- uint i, pa, n;
- pte_t *pte;
-
- if((uint) addr % PGSIZE != 0)
- panic("loaduvm: addr must be page aligned");
- for(i = 0; i < sz; i += PGSIZE){
- if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
- panic("loaduvm: address should exist");
- pa = PTE_ADDR(*pte);
- if(sz - i < PGSIZE)
- n = sz - i;
- else
- n = PGSIZE;
- if(readi(ip, P2V(pa), offset+i, n) != n)
- return -1;
- }
- return 0;
-}
-
-// Allocate page tables and physical memory to grow process from oldsz to
-// newsz, which need not be page aligned. Returns new size or 0 on error.
-int
-allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
-{
- char *mem;
- uint a;
-
- if(newsz >= KERNBASE)
- return 0;
- if(newsz < oldsz)
- return oldsz;
-
- a = PGROUNDUP(oldsz);
- for(; a < newsz; a += PGSIZE){
- mem = kalloc();
- if(mem == 0){
- cprintf("allocuvm out of memory\n");
- deallocuvm(pgdir, newsz, oldsz);
- return 0;
- }
- memset(mem, 0, PGSIZE);
- if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
- cprintf("allocuvm out of memory (2)\n");
- deallocuvm(pgdir, newsz, oldsz);
- kfree(mem);
- return 0;
- }
- }
- 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.
-int
-deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
-{
- pte_t *pte;
- uint a, pa;
-
- if(newsz >= oldsz)
- return oldsz;
-
- a = PGROUNDUP(newsz);
- for(; a < oldsz; a += PGSIZE){
- pte = walkpgdir(pgdir, (char*)a, 0);
- if(!pte)
- a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
- else if((*pte & PTE_P) != 0){
- pa = PTE_ADDR(*pte);
- if(pa == 0)
- panic("kfree");
- char *v = P2V(pa);
- kfree(v);
- *pte = 0;
- }
- }
- return newsz;
-}
-
-// Free a page table and all the physical memory pages
-// in the user part.
-void
-freevm(pde_t *pgdir)
-{
- uint i;
-
- if(pgdir == 0)
- panic("freevm: no pgdir");
- deallocuvm(pgdir, KERNBASE, 0);
- for(i = 0; i < NPDENTRIES; i++){
- if(pgdir[i] & PTE_P){
- char * v = P2V(PTE_ADDR(pgdir[i]));
- kfree(v);
- }
- }
- kfree((char*)pgdir);
-}
-
-// Clear PTE_U on a page. Used to create an inaccessible
-// page beneath the user stack.
-void
-clearpteu(pde_t *pgdir, char *uva)
-{
- pte_t *pte;
-
- pte = walkpgdir(pgdir, uva, 0);
- if(pte == 0)
- panic("clearpteu");
- *pte &= ~PTE_U;
-}
-
-// Given a parent process's page table, create a copy
-// of it for a child.
-pde_t*
-copyuvm(pde_t *pgdir, uint sz)
-{
- pde_t *d;
- pte_t *pte;
- uint pa, i, flags;
- char *mem;
-
- if((d = setupkvm()) == 0)
- return 0;
- for(i = 0; i < sz; i += PGSIZE){
- if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0)
- panic("copyuvm: pte should exist");
- if(!(*pte & PTE_P))
- panic("copyuvm: page not present");
- pa = PTE_ADDR(*pte);
- flags = PTE_FLAGS(*pte);
- if((mem = kalloc()) == 0)
- goto bad;
- memmove(mem, (char*)P2V(pa), PGSIZE);
- if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) {
- kfree(mem);
- goto bad;
- }
- }
- return d;
-
-bad:
- freevm(d);
- return 0;
-}
-
-//PAGEBREAK!
-// Map user virtual address to kernel address.
-char*
-uva2ka(pde_t *pgdir, char *uva)
-{
- pte_t *pte;
-
- pte = walkpgdir(pgdir, uva, 0);
- if((*pte & PTE_P) == 0)
- return 0;
- if((*pte & PTE_U) == 0)
- return 0;
- return (char*)P2V(PTE_ADDR(*pte));
-}
-
-// Copy len bytes from p to user address va in page table pgdir.
-// Most useful when pgdir is not the current page table.
-// uva2ka ensures this only works for PTE_U pages.
-int
-copyout(pde_t *pgdir, uint va, void *p, uint len)
-{
- char *buf, *pa0;
- uint n, va0;
-
- buf = (char*)p;
- while(len > 0){
- va0 = (uint)PGROUNDDOWN(va);
- pa0 = uva2ka(pgdir, (char*)va0);
- if(pa0 == 0)
- return -1;
- n = PGSIZE - (va - va0);
- if(n > len)
- n = len;
- memmove(pa0 + (va - va0), buf, n);
- len -= n;
- buf += n;
- va = va0 + PGSIZE;
- }
- return 0;
-}
-
-//PAGEBREAK!
-// Blank page.
-//PAGEBREAK!
-// Blank page.
-//PAGEBREAK!
-// Blank page.
-
+++ /dev/null
-#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<n; i++){
- c++;
- if(buf[i] == '\n')
- l++;
- if(strchr(" \r\t\n\v", buf[i]))
- inword = 0;
- else if(!inword){
- w++;
- inword = 1;
- }
- }
- }
- if(n < 0){
- printf(1, "wc: read error\n");
- exit();
- }
- printf(1, "%d %d %d %s\n", l, w, c, name);
-}
-
-int
-main(int argc, char *argv[])
-{
- int fd, i;
-
- if(argc <= 1){
- wc(0, "");
- exit();
- }
-
- for(i = 1; i < argc; i++){
- if((fd = open(argv[i], 0)) < 0){
- printf(1, "wc: cannot open %s\n", argv[i]);
- exit();
- }
- wc(fd, argv[i]);
- close(fd);
- }
- exit();
-}
+++ /dev/null
-// Routines to let C code use special x86 instructions.
-
-static inline uchar
-inb(ushort port)
-{
- uchar data;
-
- __asm__ volatile("in %1,%0" : "=a" (data) : "d" (port));
- return data;
-}
-
-static inline void
-insl(int port, void *addr, int cnt)
-{
- __asm__ volatile("cld; rep insl" :
- "=D" (addr), "=c" (cnt) :
- "d" (port), "0" (addr), "1" (cnt) :
- "memory", "cc");
-}
-
-static inline void
-outb(ushort port, uchar data)
-{
- __asm__ volatile("out %0,%1" : : "a" (data), "d" (port));
-}
-
-static inline void
-outw(ushort port, ushort data)
-{
- __asm__ volatile("out %0,%1" : : "a" (data), "d" (port));
-}
-
-static inline void
-outsl(int port, const void *addr, int cnt)
-{
- __asm__ volatile("cld; rep outsl" :
- "=S" (addr), "=c" (cnt) :
- "d" (port), "0" (addr), "1" (cnt) :
- "cc");
-}
-
-static inline void
-stosb(void *addr, int data, int cnt)
-{
- __asm__ volatile("cld; rep stosb" :
- "=D" (addr), "=c" (cnt) :
- "0" (addr), "1" (cnt), "a" (data) :
- "memory", "cc");
-}
-
-static inline void
-stosl(void *addr, int data, int cnt)
-{
- __asm__ volatile("cld; rep stosl" :
- "=D" (addr), "=c" (cnt) :
- "0" (addr), "1" (cnt), "a" (data) :
- "memory", "cc");
-}
-
-struct segdesc;
-
-static inline void
-lgdt(struct segdesc *p, int size)
-{
- volatile ushort pd[3];
-
- pd[0] = size-1;
- pd[1] = (uint)p;
- pd[2] = (uint)p >> 16;
-
- __asm__ volatile("lgdt (%0)" : : "r" (pd));
-}
-
-struct gatedesc;
-
-static inline void
-lidt(struct gatedesc *p, int size)
-{
- volatile ushort pd[3];
-
- pd[0] = size-1;
- pd[1] = (uint)p;
- pd[2] = (uint)p >> 16;
-
- __asm__ volatile("lidt (%0)" : : "r" (pd));
-}
-
-static inline void
-ltr(ushort sel)
-{
- __asm__ volatile("ltr %0" : : "r" (sel));
-}
-
-static inline uint
-readeflags(void)
-{
- uint eflags;
- __asm__ volatile("pushfl; popl %0" : "=r" (eflags));
- return eflags;
-}
-
-static inline void
-loadgs(ushort v)
-{
- __asm__ volatile("movw %0, %%gs" : : "r" (v));
-}
-
-static inline void
-cli(void)
-{
- __asm__ volatile("cli");
-}
-
-static inline void
-sti(void)
-{
- __asm__ volatile("sti");
-}
-
-static inline uint
-rcr2(void)
-{
- uint val;
- __asm__ volatile("movl %%cr2,%0" : "=r" (val));
- return val;
-}
-
-static inline void
-lcr3(uint val)
-{
- __asm__ volatile("movl %0,%%cr3" : : "r" (val));
-}
-
-//PAGEBREAK: 36
-// Layout of the trap frame built on the stack by the
-// hardware and by trapasm.S, and passed to trap().
-struct trapframe {
- // registers as pushed by pusha
- uint edi;
- uint esi;
- uint ebp;
- uint oesp; // useless & ignored
- uint ebx;
- uint edx;
- uint ecx;
- uint eax;
-
- // rest of trap frame
- ushort gs;
- ushort padding1;
- ushort fs;
- ushort padding2;
- ushort es;
- ushort padding3;
- ushort ds;
- ushort padding4;
- uint trapno;
-
- // below here defined by x86 hardware
- uint err;
- uint eip;
- ushort cs;
- ushort padding5;
- uint eflags;
-
- // below here only when crossing rings, such as from user to kernel
- uint esp;
- ushort ss;
- ushort padding6;
-};
+++ /dev/null
-// Create a zombie process that
-// must be reparented at exit.
-
-#include "types.h"
-#include "stat.h"
-#include "user.h"
-
-int
-main(void)
-{
- if(fork() > 0)
- sleep(5); // Let child exit before parent.
- exit();
-}