From: David Devecsery Date: Thu, 14 May 2020 13:37:42 +0000 (-0400) Subject: Cmake builds and runs! X-Git-Url: https://git.devinivas.org/?a=commitdiff_plain;h=603e48d56a7e71c6966d79efbe77876947426ce9;p=cs3210-lab0.git Cmake builds and runs! --- diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e3d975d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +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 +) + diff --git a/asm.h b/asm.h deleted file mode 100644 index b8a7353..0000000 --- a/asm.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// 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) diff --git a/bio.c b/bio.c deleted file mode 100644 index a45ff3e..0000000 --- a/bio.c +++ /dev/null @@ -1,144 +0,0 @@ -// Buffer cache. -// -// The buffer cache is a linked list of buf structures holding -// cached copies of disk block contents. Caching disk blocks -// in memory reduces the number of disk reads and also provides -// a synchronization point for disk blocks used by multiple processes. -// -// Interface: -// * To get a buffer for a particular disk block, call bread. -// * After changing buffer data, call bwrite to write it to disk. -// * When done with the buffer, call brelse. -// * Do not use the buffer after calling brelse. -// * Only one process at a time can use a buffer, -// so do not keep them longer than necessary. -// -// The implementation uses two state flags internally: -// * B_VALID: the buffer data has been read from the disk. -// * B_DIRTY: the buffer data has been modified -// and needs to be written to disk. - -#include "types.h" -#include "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. - diff --git a/bootasm.S b/bootasm.S deleted file mode 100644 index 257867c..0000000 --- a/bootasm.S +++ /dev/null @@ -1,88 +0,0 @@ -#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 - diff --git a/bootmain.c b/bootmain.c deleted file mode 100644 index 1f20e5b..0000000 --- a/bootmain.c +++ /dev/null @@ -1,96 +0,0 @@ -// 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); -} diff --git a/buf.h b/buf.h deleted file mode 100644 index 3266495..0000000 --- a/buf.h +++ /dev/null @@ -1,14 +0,0 @@ -struct buf { - int flags; - uint dev; - uint blockno; - struct sleeplock lock; - uint refcnt; - struct buf *prev; // LRU cache list - struct buf *next; - struct buf *qnext; // disk queue - uchar data[BSIZE]; -}; -#define B_VALID 0x2 // buffer has been read from disk -#define B_DIRTY 0x4 // buffer needs to be written to disk - diff --git a/cat.c b/cat.c deleted file mode 100644 index 5ddc820..0000000 --- a/cat.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[512]; - -void -cat(int fd) -{ - int n; - - while((n = read(fd, buf, sizeof(buf))) > 0) { - if (write(1, buf, n) != n) { - printf(1, "cat: write error\n"); - exit(); - } - } - if(n < 0){ - printf(1, "cat: read error\n"); - exit(); - } -} - -int -main(int argc, char *argv[]) -{ - int fd, i; - - if(argc <= 1){ - cat(0); - exit(); - } - - for(i = 1; i < argc; i++){ - if((fd = open(argv[i], 0)) < 0){ - printf(1, "cat: cannot open %s\n", argv[i]); - exit(); - } - cat(fd); - close(fd); - } - exit(); -} diff --git a/console.c b/console.c deleted file mode 100644 index a280d2b..0000000 --- a/console.c +++ /dev/null @@ -1,299 +0,0 @@ -// 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); -} - diff --git a/cuth b/cuth deleted file mode 100755 index cce8c0c..0000000 --- a/cuth +++ /dev/null @@ -1,48 +0,0 @@ -#!/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 = ; - 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"); -} diff --git a/date.h b/date.h deleted file mode 100644 index 94aec4b..0000000 --- a/date.h +++ /dev/null @@ -1,8 +0,0 @@ -struct rtcdate { - uint second; - uint minute; - uint hour; - uint day; - uint month; - uint year; -}; diff --git a/defs.h b/defs.h deleted file mode 100644 index 82fb982..0000000 --- a/defs.h +++ /dev/null @@ -1,190 +0,0 @@ -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])) diff --git a/dot-bochsrc b/dot-bochsrc deleted file mode 100755 index ba13db7..0000000 --- a/dot-bochsrc +++ /dev/null @@ -1,738 +0,0 @@ -# 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 -#======================================================================= diff --git a/echo.c b/echo.c deleted file mode 100644 index 806dee0..0000000 --- a/echo.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char *argv[]) -{ - int i; - - for(i = 1; i < argc; i++) - printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); - exit(); -} diff --git a/elf.h b/elf.h deleted file mode 100644 index d16c967..0000000 --- a/elf.h +++ /dev/null @@ -1,42 +0,0 @@ -// Format of an ELF executable file - -#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian - -// File header -struct elfhdr { - uint magic; // must equal ELF_MAGIC - uchar elf[12]; - ushort type; - ushort machine; - uint version; - 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 diff --git a/entry.S b/entry.S deleted file mode 100644 index bc79bab..0000000 --- a/entry.S +++ /dev/null @@ -1,68 +0,0 @@ -# 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 diff --git a/entryother.S b/entryother.S deleted file mode 100644 index a3b6dc2..0000000 --- a/entryother.S +++ /dev/null @@ -1,93 +0,0 @@ -#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 - diff --git a/exec.c b/exec.c deleted file mode 100644 index b40134f..0000000 --- a/exec.c +++ /dev/null @@ -1,114 +0,0 @@ -#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= 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; -} diff --git a/fcntl.h b/fcntl.h deleted file mode 100644 index d565483..0000000 --- a/fcntl.h +++ /dev/null @@ -1,4 +0,0 @@ -#define O_RDONLY 0x000 -#define O_WRONLY 0x001 -#define O_RDWR 0x002 -#define O_CREATE 0x200 diff --git a/file.c b/file.c deleted file mode 100644 index 24b32c2..0000000 --- a/file.c +++ /dev/null @@ -1,157 +0,0 @@ -// -// 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"); -} - diff --git a/file.h b/file.h deleted file mode 100644 index 0990c82..0000000 --- a/file.h +++ /dev/null @@ -1,37 +0,0 @@ -struct file { - enum { FD_NONE, FD_PIPE, FD_INODE } type; - int ref; // reference count - char readable; - char writable; - struct pipe *pipe; - struct inode *ip; - uint off; -}; - - -// in-memory copy of an inode -struct inode { - uint dev; // Device number - uint inum; // Inode number - int ref; // Reference count - struct sleeplock lock; // protects everything below here - int valid; // inode has been read from disk? - - short type; // copy of disk inode - short major; - short minor; - short nlink; - uint size; - uint addrs[NDIRECT+1]; -}; - -// table mapping major device number to -// device functions -struct devsw { - int (*read)(struct inode*, char*, int); - int (*write)(struct inode*, char*, int); -}; - -extern struct devsw devsw[]; - -#define CONSOLE 1 diff --git a/forktest.c b/forktest.c deleted file mode 100644 index 8bc984d..0000000 --- a/forktest.c +++ /dev/null @@ -1,56 +0,0 @@ -// Test that fork fails gracefully. -// Tiny executable so that the limit can be filling the proc table. - -#include "types.h" -#include "stat.h" -#include "user.h" - -#define N 1000 - -void -printf(int fd, const char *s, ...) -{ - write(fd, s, strlen(s)); -} - -void -forktest(void) -{ - int n, pid; - - printf(1, "fork test\n"); - - for(n=0; n 0; n--){ - if(wait() < 0){ - printf(1, "wait stopped early\n"); - exit(); - } - } - - if(wait() != -1){ - printf(1, "wait got too many\n"); - exit(); - } - - printf(1, "fork test OK\n"); -} - -int -main(void) -{ - forktest(); - exit(); -} diff --git a/fs.c b/fs.c deleted file mode 100644 index f77275f..0000000 --- a/fs.c +++ /dev/null @@ -1,670 +0,0 @@ -// File system implementation. Five layers: -// + Blocks: allocator for raw disk blocks. -// + Log: crash recovery for multi-step updates. -// + Files: inode allocator, reading, writing, metadata. -// + Directories: inode with special contents (list of other inodes!) -// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. -// -// This file contains the low-level file system manipulation -// routines. The (higher-level) system call implementations -// are in sysfile.c. - -#include "types.h" -#include "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; totdev, 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; totdev, 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); -} diff --git a/fs.h b/fs.h deleted file mode 100644 index 3214f1d..0000000 --- a/fs.h +++ /dev/null @@ -1,57 +0,0 @@ -// On-disk file system format. -// Both the kernel and user programs use this header file. - - -#define ROOTINO 1 // root i-number -#define BSIZE 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]; -}; - diff --git a/grep.c b/grep.c deleted file mode 100644 index adc4835..0000000 --- a/grep.c +++ /dev/null @@ -1,107 +0,0 @@ -// Simple grep. Only supports ^ . * $ operators. - -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[1024]; -int match(char*, char*); - -void -grep(char *pattern, int fd) -{ - int n, m; - char *p, *q; - - m = 0; - while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ - m += n; - buf[m] = '\0'; - p = buf; - while((q = strchr(p, '\n')) != 0){ - *q = 0; - if(match(pattern, p)){ - *q = '\n'; - write(1, p, q+1 - p); - } - p = q+1; - } - if(p == buf) - m = 0; - if(m > 0){ - m -= p - buf; - memmove(buf, p, m); - } - } -} - -int -main(int argc, char *argv[]) -{ - int fd, i; - char *pattern; - - if(argc <= 1){ - printf(2, "usage: grep pattern [file ...]\n"); - exit(); - } - pattern = argv[1]; - - if(argc <= 2){ - grep(pattern, 0); - exit(); - } - - for(i = 2; i < argc; i++){ - if((fd = open(argv[i], 0)) < 0){ - printf(1, "grep: cannot open %s\n", argv[i]); - exit(); - } - grep(pattern, fd); - close(fd); - } - exit(); -} - -// Regexp matcher from Kernighan & Pike, -// The Practice of Programming, Chapter 9. - -int matchhere(char*, char*); -int matchstar(int, char*, char*); - -int -match(char *re, char *text) -{ - if(re[0] == '^') - return matchhere(re+1, text); - do{ // must look at empty string - if(matchhere(re, text)) - return 1; - }while(*text++ != '\0'); - return 0; -} - -// matchhere: search for re at beginning of text -int matchhere(char *re, char *text) -{ - if(re[0] == '\0') - return 1; - if(re[1] == '*') - return matchstar(re[0], re+2, text); - if(re[0] == '$' && re[1] == '\0') - return *text == '\0'; - if(*text!='\0' && (re[0]=='.' || re[0]==*text)) - return matchhere(re+1, text+1); - return 0; -} - -// matchstar: search for c*re at beginning of text -int matchstar(int c, char *re, char *text) -{ - do{ // a * matches zero or more instances - if(matchhere(re, text)) - return 1; - }while(*text!='\0' && (*text++==c || c=='.')); - return 0; -} - diff --git a/ide.c b/ide.c deleted file mode 100644 index b4c0b1f..0000000 --- a/ide.c +++ /dev/null @@ -1,168 +0,0 @@ -// 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); -} diff --git a/include/asm/asm.h b/include/asm/asm.h new file mode 100644 index 0000000..7b582b3 --- /dev/null +++ b/include/asm/asm.h @@ -0,0 +1,22 @@ +// +// 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_ diff --git a/include/asm/x86.h b/include/asm/x86.h new file mode 100644 index 0000000..12ff7d2 --- /dev/null +++ b/include/asm/x86.h @@ -0,0 +1,176 @@ +// 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_ diff --git a/include/elf.h b/include/elf.h new file mode 100644 index 0000000..3047821 --- /dev/null +++ b/include/elf.h @@ -0,0 +1,46 @@ +// 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_ diff --git a/include/fcntl.h b/include/fcntl.h new file mode 100644 index 0000000..2f2f5b6 --- /dev/null +++ b/include/fcntl.h @@ -0,0 +1,9 @@ +#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_ diff --git a/include/fs.h b/include/fs.h new file mode 100644 index 0000000..3214f1d --- /dev/null +++ b/include/fs.h @@ -0,0 +1,57 @@ +// 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]; +}; + diff --git a/include/memlayout.h b/include/memlayout.h new file mode 100644 index 0000000..bd57410 --- /dev/null +++ b/include/memlayout.h @@ -0,0 +1,19 @@ +// 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_ diff --git a/include/mmu.h b/include/mmu.h new file mode 100644 index 0000000..96900aa --- /dev/null +++ b/include/mmu.h @@ -0,0 +1,185 @@ +// 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_ diff --git a/include/param.h b/include/param.h new file mode 100644 index 0000000..4502792 --- /dev/null +++ b/include/param.h @@ -0,0 +1,18 @@ +#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_ diff --git a/include/stat.h b/include/stat.h new file mode 100644 index 0000000..2bee692 --- /dev/null +++ b/include/stat.h @@ -0,0 +1,16 @@ +#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_ diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..bc5f356 --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,22 @@ +// System call numbers +#define SYS_fork 1 +#define SYS_exit 2 +#define SYS_wait 3 +#define SYS_pipe 4 +#define SYS_read 5 +#define SYS_kill 6 +#define SYS_exec 7 +#define SYS_fstat 8 +#define SYS_chdir 9 +#define SYS_dup 10 +#define SYS_getpid 11 +#define SYS_sbrk 12 +#define SYS_sleep 13 +#define SYS_uptime 14 +#define SYS_open 15 +#define SYS_write 16 +#define SYS_mknod 17 +#define SYS_unlink 18 +#define SYS_link 19 +#define SYS_mkdir 20 +#define SYS_close 21 diff --git a/include/traps.h b/include/traps.h new file mode 100644 index 0000000..0bd1fd8 --- /dev/null +++ b/include/traps.h @@ -0,0 +1,38 @@ +// 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 + diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..97bff57 --- /dev/null +++ b/include/types.h @@ -0,0 +1,9 @@ +#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_ diff --git a/init.c b/init.c deleted file mode 100644 index 046b551..0000000 --- a/init.c +++ /dev/null @@ -1,37 +0,0 @@ -// init: The initial user-level program - -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fcntl.h" - -char *argv[] = { "sh", 0 }; - -int -main(void) -{ - int pid, wpid; - - if(open("console", O_RDWR) < 0){ - mknod("console", 1, 1); - open("console", O_RDWR); - } - dup(0); // stdout - dup(0); // stderr - - for(;;){ - printf(1, "init: starting sh\n"); - pid = fork(); - if(pid < 0){ - printf(1, "init: fork failed\n"); - exit(); - } - if(pid == 0){ - exec("sh", argv); - printf(1, "init: exec sh failed\n"); - exit(); - } - while((wpid=wait()) >= 0 && wpid != pid) - printf(1, "zombie!\n"); - } -} diff --git a/initcode.S b/initcode.S deleted file mode 100644 index 80ac5d8..0000000 --- a/initcode.S +++ /dev/null @@ -1,32 +0,0 @@ -# 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 - diff --git a/ioapic.c b/ioapic.c deleted file mode 100644 index cb0f015..0000000 --- a/ioapic.c +++ /dev/null @@ -1,75 +0,0 @@ -// The I/O APIC manages hardware interrupts for an SMP system. -// http://www.intel.com/design/chipsets/datashts/29056601.pdf -// See also picirq.c. - -#include "types.h" -#include "defs.h" -#include "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); -} diff --git a/kalloc.c b/kalloc.c deleted file mode 100644 index 14cd4f4..0000000 --- a/kalloc.c +++ /dev/null @@ -1,96 +0,0 @@ -// Physical memory allocator, intended to allocate -// memory for user processes, kernel stacks, page table pages, -// and pipe buffers. Allocates 4096-byte pages. - -#include "types.h" -#include "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; -} - diff --git a/kbd.c b/kbd.c deleted file mode 100644 index 32c1463..0000000 --- a/kbd.c +++ /dev/null @@ -1,50 +0,0 @@ -#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); -} diff --git a/kbd.h b/kbd.h deleted file mode 100644 index babbd6e..0000000 --- a/kbd.h +++ /dev/null @@ -1,112 +0,0 @@ -// 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 -}; - diff --git a/kernel.ld b/kernel.ld deleted file mode 100644 index e24c860..0000000 --- a/kernel.ld +++ /dev/null @@ -1,68 +0,0 @@ -/* 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) - } -} diff --git a/kill.c b/kill.c deleted file mode 100644 index 364f6af..0000000 --- a/kill.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char **argv) -{ - int i; - - if(argc < 2){ - printf(2, "usage: kill pid...\n"); - exit(); - } - for(i=1; i>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; -} diff --git a/ln.c b/ln.c deleted file mode 100644 index cf8a64e..0000000 --- a/ln.c +++ /dev/null @@ -1,15 +0,0 @@ -#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(); -} diff --git a/log.c b/log.c deleted file mode 100644 index a64c0f6..0000000 --- a/log.c +++ /dev/null @@ -1,234 +0,0 @@ -#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); -} - diff --git a/ls.c b/ls.c deleted file mode 100644 index 2862913..0000000 --- a/ls.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fs.h" - -char* -fmtname(char *path) -{ - static char buf[DIRSIZ+1]; - char *p; - - // Find first character after last slash. - for(p=path+strlen(path); p >= path && *p != '/'; p--) - ; - p++; - - // Return blank-padded name. - if(strlen(p) >= DIRSIZ) - return p; - memmove(buf, p, strlen(p)); - memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); - return buf; -} - -void -ls(char *path) -{ - char buf[512], *p; - int fd; - struct dirent de; - struct stat st; - - if((fd = open(path, 0)) < 0){ - printf(2, "ls: cannot open %s\n", path); - return; - } - - if(fstat(fd, &st) < 0){ - printf(2, "ls: cannot stat %s\n", path); - close(fd); - return; - } - - switch(st.type){ - case T_FILE: - printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); - break; - - case T_DIR: - if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ - printf(1, "ls: path too long\n"); - break; - } - strcpy(buf, path); - p = buf+strlen(buf); - *p++ = '/'; - while(read(fd, &de, sizeof(de)) == sizeof(de)){ - if(de.inum == 0) - continue; - memmove(p, de.name, DIRSIZ); - p[DIRSIZ] = 0; - if(stat(buf, &st) < 0){ - printf(1, "ls: cannot stat %s\n", buf); - continue; - } - printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); - } - break; - } - close(fd); -} - -int -main(int argc, char *argv[]) -{ - int i; - - if(argc < 2){ - ls("."); - exit(); - } - for(i=1; i - -#include "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. - diff --git a/memide.c b/memide.c deleted file mode 100644 index ba267ac..0000000 --- a/memide.c +++ /dev/null @@ -1,60 +0,0 @@ -// 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; -} diff --git a/memlayout.h b/memlayout.h deleted file mode 100644 index d1615f7..0000000 --- a/memlayout.h +++ /dev/null @@ -1,15 +0,0 @@ -// 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 diff --git a/mkdir.c b/mkdir.c deleted file mode 100644 index 6e4c954..0000000 --- a/mkdir.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char *argv[]) -{ - int i; - - if(argc < 2){ - printf(2, "Usage: 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(); -} diff --git a/mkfs.c b/mkfs.c deleted file mode 100644 index 8e011a7..0000000 --- a/mkfs.c +++ /dev/null @@ -1,297 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define stat xv6_stat // avoid clash with host struct stat -#include "types.h" -#include "fs.h" -#include "stat.h" -#include "param.h" - -#ifndef static_assert -#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) -#endif - -#define NINODES 200 - -// Disk layout: -// [ boot block | sb block | log | inode blocks | free bit map | data blocks ] - -int nbitmap = FSSIZE/(BSIZE*8) + 1; -int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGSIZE; -int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) -int nblocks; // Number of data blocks - -int fsfd; -struct superblock sb; -char zeroes[BSIZE]; -uint freeinode = 1; -uint freeblock; - - -void balloc(int); -void wsect(uint, void*); -void winode(uint, struct dinode*); -void rinode(uint inum, struct dinode *ip); -void rsect(uint sec, void *buf); -uint ialloc(ushort type); -void iappend(uint inum, void *p, int n); - -// convert to intel byte order -ushort -xshort(ushort x) -{ - ushort y; - uchar *a = (uchar*)&y; - a[0] = x; - a[1] = x >> 8; - return y; -} - -uint -xint(uint x) -{ - uint y; - uchar *a = (uchar*)&y; - a[0] = x; - a[1] = x >> 8; - a[2] = x >> 16; - a[3] = x >> 24; - return y; -} - -int -main(int argc, char *argv[]) -{ - int i, cc, fd; - uint rootino, inum, off; - struct dirent de; - char buf[BSIZE]; - struct dinode din; - - - static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); - - if(argc < 2){ - fprintf(stderr, "Usage: mkfs fs.img files...\n"); - exit(1); - } - - assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct dirent)) == 0); - - fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); - if(fsfd < 0){ - perror(argv[1]); - exit(1); - } - - // 1 fs block = 1 disk sector - nmeta = 2 + nlog + ninodeblocks + nbitmap; - nblocks = FSSIZE - nmeta; - - sb.size = xint(FSSIZE); - sb.nblocks = xint(nblocks); - sb.ninodes = xint(NINODES); - sb.nlog = xint(nlog); - sb.logstart = xint(2); - sb.inodestart = xint(2+nlog); - sb.bmapstart = xint(2+nlog+ninodeblocks); - - printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", - nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); - - freeblock = nmeta; // the first free block that we can allocate - - for(i = 0; i < FSSIZE; i++) - wsect(i, zeroes); - - memset(buf, 0, sizeof(buf)); - memmove(buf, &sb, sizeof(sb)); - wsect(1, buf); - - rootino = ialloc(T_DIR); - assert(rootino == ROOTINO); - - bzero(&de, sizeof(de)); - de.inum = xshort(rootino); - strcpy(de.name, "."); - iappend(rootino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(rootino); - strcpy(de.name, ".."); - iappend(rootino, &de, sizeof(de)); - - for(i = 2; i < argc; i++){ - assert(index(argv[i], '/') == 0); - - if((fd = open(argv[i], 0)) < 0){ - perror(argv[i]); - exit(1); - } - - // Skip leading _ in name when writing to file system. - // The binaries are named _rm, _cat, etc. to keep the - // build operating system from trying to execute them - // in place of system binaries like rm and cat. - if(argv[i][0] == '_') - ++argv[i]; - - inum = ialloc(T_FILE); - - bzero(&de, sizeof(de)); - de.inum = xshort(inum); - strncpy(de.name, argv[i], DIRSIZ); - iappend(rootino, &de, sizeof(de)); - - while((cc = read(fd, buf, sizeof(buf))) > 0) - iappend(inum, buf, cc); - - close(fd); - } - - // fix size of root inode dir - rinode(rootino, &din); - off = xint(din.size); - off = ((off/BSIZE) + 1) * BSIZE; - din.size = xint(off); - winode(rootino, &din); - - balloc(freeblock); - - exit(0); -} - -void -wsect(uint sec, void *buf) -{ - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ - perror("lseek"); - exit(1); - } - if(write(fsfd, buf, BSIZE) != BSIZE){ - perror("write"); - exit(1); - } -} - -void -winode(uint inum, struct dinode *ip) -{ - char buf[BSIZE]; - uint bn; - struct dinode *dip; - - bn = IBLOCK(inum, sb); - rsect(bn, buf); - dip = ((struct dinode*)buf) + (inum % IPB); - *dip = *ip; - wsect(bn, buf); -} - -void -rinode(uint inum, struct dinode *ip) -{ - char buf[BSIZE]; - uint bn; - struct dinode *dip; - - bn = IBLOCK(inum, sb); - rsect(bn, buf); - dip = ((struct dinode*)buf) + (inum % IPB); - *ip = *dip; -} - -void -rsect(uint sec, void *buf) -{ - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ - perror("lseek"); - exit(1); - } - if(read(fsfd, buf, BSIZE) != BSIZE){ - perror("read"); - exit(1); - } -} - -uint -ialloc(ushort type) -{ - uint inum = freeinode++; - struct dinode din; - - bzero(&din, sizeof(din)); - din.type = xshort(type); - din.nlink = xshort(1); - din.size = xint(0); - winode(inum, &din); - return inum; -} - -void -balloc(int used) -{ - uchar buf[BSIZE]; - int i; - - printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BSIZE*8); - bzero(buf, BSIZE); - for(i = 0; i < used; i++){ - buf[i/8] = buf[i/8] | (0x1 << (i%8)); - } - printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); - wsect(sb.bmapstart, buf); -} - -#define min(a, b) ((a) < (b) ? (a) : (b)) - -void -iappend(uint inum, void *xp, int n) -{ - char *p = (char*)xp; - uint fbn, off, n1; - struct dinode din; - char buf[BSIZE]; - uint indirect[NINDIRECT]; - uint x; - - rinode(inum, &din); - off = xint(din.size); - // printf("append inum %d at off %d sz %d\n", inum, off, n); - while(n > 0){ - fbn = off / BSIZE; - assert(fbn < MAXFILE); - if(fbn < NDIRECT){ - if(xint(din.addrs[fbn]) == 0){ - din.addrs[fbn] = xint(freeblock++); - } - x = xint(din.addrs[fbn]); - } else { - if(xint(din.addrs[NDIRECT]) == 0){ - din.addrs[NDIRECT] = xint(freeblock++); - } - rsect(xint(din.addrs[NDIRECT]), (char*)indirect); - if(indirect[fbn - NDIRECT] == 0){ - indirect[fbn - NDIRECT] = xint(freeblock++); - wsect(xint(din.addrs[NDIRECT]), (char*)indirect); - } - x = xint(indirect[fbn-NDIRECT]); - } - n1 = min(n, (fbn + 1) * BSIZE - off); - rsect(x, buf); - bcopy(p, buf + off - (fbn * BSIZE), n1); - wsect(x, buf); - n -= n1; - off += n1; - p += n1; - } - din.size = xint(off); - winode(inum, &din); -} diff --git a/mmu.h b/mmu.h deleted file mode 100644 index a82d8e2..0000000 --- a/mmu.h +++ /dev/null @@ -1,181 +0,0 @@ -// 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 diff --git a/mp.c b/mp.c deleted file mode 100644 index 79bb0ad..0000000 --- a/mp.c +++ /dev/null @@ -1,139 +0,0 @@ -// 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; iphysaddr == 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; papicid; // 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. - } -} diff --git a/mp.h b/mp.h deleted file mode 100644 index 4d17283..0000000 --- a/mp.h +++ /dev/null @@ -1,56 +0,0 @@ -// 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. diff --git a/param.h b/param.h deleted file mode 100644 index a7e90ef..0000000 --- a/param.h +++ /dev/null @@ -1,14 +0,0 @@ -#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 - diff --git a/picirq.c b/picirq.c deleted file mode 100644 index e26957f..0000000 --- a/picirq.c +++ /dev/null @@ -1,19 +0,0 @@ -#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. diff --git a/pipe.c b/pipe.c deleted file mode 100644 index e9abe7f..0000000 --- a/pipe.c +++ /dev/null @@ -1,121 +0,0 @@ -#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; -} diff --git a/pr.pl b/pr.pl deleted file mode 100755 index 46905bd..0000000 --- a/pr.pl +++ /dev/null @@ -1,36 +0,0 @@ -#!/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"; -} diff --git a/printf.c b/printf.c deleted file mode 100644 index b3298aa..0000000 --- a/printf.c +++ /dev/null @@ -1,85 +0,0 @@ -#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; - } - } -} diff --git a/printpcs b/printpcs deleted file mode 100755 index 81d039b..0000000 --- a/printpcs +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 "$@" diff --git a/proc.c b/proc.c deleted file mode 100644 index 806b1b1..0000000 --- a/proc.c +++ /dev/null @@ -1,534 +0,0 @@ -#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"); - } -} diff --git a/proc.h b/proc.h deleted file mode 100644 index 0c574b9..0000000 --- a/proc.h +++ /dev/null @@ -1,60 +0,0 @@ -#include - -// 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 diff --git a/rm.c b/rm.c deleted file mode 100644 index 4fd33c8..0000000 --- a/rm.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -int -main(int argc, char *argv[]) -{ - int i; - - if(argc < 2){ - printf(2, "Usage: rm files...\n"); - exit(); - } - - for(i = 1; i < argc; i++){ - if(unlink(argv[i]) < 0){ - printf(2, "rm: %s failed to delete\n", argv[i]); - break; - } - } - - exit(); -} diff --git a/runoff b/runoff deleted file mode 100755 index be362d0..0000000 --- a/runoff +++ /dev/null @@ -1,246 +0,0 @@ -#!/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"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 diff --git a/runoff.list b/runoff.list deleted file mode 100644 index 2df9b81..0000000 --- a/runoff.list +++ /dev/null @@ -1,80 +0,0 @@ -# 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 diff --git a/runoff.spec b/runoff.spec deleted file mode 100644 index 9247948..0000000 --- a/runoff.spec +++ /dev/null @@ -1,102 +0,0 @@ -# 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 diff --git a/runoff1 b/runoff1 deleted file mode 100755 index 532f844..0000000 --- a/runoff1 +++ /dev/null @@ -1,108 +0,0 @@ -#!/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; - } -} diff --git a/sh.c b/sh.c deleted file mode 100644 index 054bab9..0000000 --- a/sh.c +++ /dev/null @@ -1,493 +0,0 @@ -// Shell. - -#include "types.h" -#include "user.h" -#include "fcntl.h" - -// Parsed command representation -#define EXEC 1 -#define REDIR 2 -#define PIPE 3 -#define LIST 4 -#define BACK 5 - -#define MAXARGS 10 - -struct cmd { - int type; -}; - -struct execcmd { - int type; - char *argv[MAXARGS]; - char *eargv[MAXARGS]; -}; - -struct redircmd { - int type; - struct cmd *cmd; - char *file; - char *efile; - int mode; - int fd; -}; - -struct pipecmd { - int type; - struct cmd *left; - struct cmd *right; -}; - -struct listcmd { - int type; - struct cmd *left; - struct cmd *right; -}; - -struct backcmd { - int type; - struct cmd *cmd; -}; - -int fork1(void); // Fork but panics on failure. -void panic(char*); -struct cmd *parsecmd(char*); - -// Execute cmd. Never returns. -void -runcmd(struct cmd *cmd) -{ - int p[2]; - struct backcmd *bcmd; - struct execcmd *ecmd; - struct listcmd *lcmd; - struct pipecmd *pcmd; - struct redircmd *rcmd; - - if(cmd == 0) - exit(); - - switch(cmd->type){ - default: - panic("runcmd"); - - case EXEC: - ecmd = (struct execcmd*)cmd; - if(ecmd->argv[0] == 0) - exit(); - exec(ecmd->argv[0], ecmd->argv); - printf(2, "exec %s failed\n", ecmd->argv[0]); - break; - - case REDIR: - rcmd = (struct redircmd*)cmd; - close(rcmd->fd); - if(open(rcmd->file, rcmd->mode) < 0){ - printf(2, "open %s failed\n", rcmd->file); - exit(); - } - runcmd(rcmd->cmd); - break; - - case LIST: - lcmd = (struct listcmd*)cmd; - if(fork1() == 0) - runcmd(lcmd->left); - wait(); - runcmd(lcmd->right); - break; - - case PIPE: - pcmd = (struct pipecmd*)cmd; - if(pipe(p) < 0) - panic("pipe"); - if(fork1() == 0){ - close(1); - dup(p[1]); - close(p[0]); - close(p[1]); - runcmd(pcmd->left); - } - if(fork1() == 0){ - close(0); - dup(p[0]); - close(p[0]); - close(p[1]); - runcmd(pcmd->right); - } - close(p[0]); - close(p[1]); - wait(); - wait(); - break; - - case BACK: - bcmd = (struct backcmd*)cmd; - if(fork1() == 0) - runcmd(bcmd->cmd); - break; - } - exit(); -} - -int -getcmd(char *buf, int nbuf) -{ - printf(2, "$ "); - memset(buf, 0, nbuf); - gets(buf, nbuf); - if(buf[0] == 0) // EOF - return -1; - return 0; -} - -int -main(void) -{ - static char buf[100]; - int fd; - - // Ensure that three file descriptors are open. - while((fd = open("console", O_RDWR)) >= 0){ - if(fd >= 3){ - close(fd); - break; - } - } - - // Read and run input commands. - while(getcmd(buf, sizeof(buf)) >= 0){ - if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ - // Chdir must be called by the parent, not the child. - buf[strlen(buf)-1] = 0; // chop \n - if(chdir(buf+3) < 0) - printf(2, "cannot cd %s\n", buf+3); - continue; - } - if(fork1() == 0) - runcmd(parsecmd(buf)); - wait(); - } - exit(); -} - -void -panic(char *s) -{ - printf(2, "%s\n", s); - exit(); -} - -int -fork1(void) -{ - int pid; - - pid = fork(); - if(pid == -1) - panic("fork"); - return pid; -} - -//PAGEBREAK! -// Constructors - -struct cmd* -execcmd(void) -{ - struct execcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = EXEC; - return (struct cmd*)cmd; -} - -struct cmd* -redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) -{ - struct redircmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = REDIR; - cmd->cmd = subcmd; - cmd->file = file; - cmd->efile = efile; - cmd->mode = mode; - cmd->fd = fd; - return (struct cmd*)cmd; -} - -struct cmd* -pipecmd(struct cmd *left, struct cmd *right) -{ - struct pipecmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = PIPE; - cmd->left = left; - cmd->right = right; - return (struct cmd*)cmd; -} - -struct cmd* -listcmd(struct cmd *left, struct cmd *right) -{ - struct listcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = LIST; - cmd->left = left; - cmd->right = right; - return (struct cmd*)cmd; -} - -struct cmd* -backcmd(struct cmd *subcmd) -{ - struct backcmd *cmd; - - cmd = malloc(sizeof(*cmd)); - memset(cmd, 0, sizeof(*cmd)); - cmd->type = BACK; - cmd->cmd = subcmd; - return (struct cmd*)cmd; -} -//PAGEBREAK! -// Parsing - -char whitespace[] = " \t\r\n\v"; -char symbols[] = "<|>&;()"; - -int -gettoken(char **ps, char *es, char **q, char **eq) -{ - char *s; - int ret; - - s = *ps; - while(s < es && strchr(whitespace, *s)) - s++; - if(q) - *q = s; - ret = *s; - switch(*s){ - case 0: - break; - case '|': - case '(': - case ')': - case ';': - case '&': - case '<': - s++; - break; - case '>': - s++; - if(*s == '>'){ - ret = '+'; - s++; - } - break; - default: - ret = 'a'; - while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) - s++; - break; - } - if(eq) - *eq = s; - - while(s < es && strchr(whitespace, *s)) - s++; - *ps = s; - return ret; -} - -int -peek(char **ps, char *es, char *toks) -{ - char *s; - - s = *ps; - while(s < es && strchr(whitespace, *s)) - s++; - *ps = s; - return *s && strchr(toks, *s); -} - -struct cmd *parseline(char**, char*); -struct cmd *parsepipe(char**, char*); -struct cmd *parseexec(char**, char*); -struct cmd *nulterminate(struct cmd*); - -struct cmd* -parsecmd(char *s) -{ - char *es; - struct cmd *cmd; - - es = s + strlen(s); - cmd = parseline(&s, es); - peek(&s, es, ""); - if(s != es){ - printf(2, "leftovers: %s\n", s); - panic("syntax"); - } - nulterminate(cmd); - return cmd; -} - -struct cmd* -parseline(char **ps, char *es) -{ - struct cmd *cmd; - - cmd = parsepipe(ps, es); - while(peek(ps, es, "&")){ - gettoken(ps, es, 0, 0); - cmd = backcmd(cmd); - } - if(peek(ps, es, ";")){ - gettoken(ps, es, 0, 0); - cmd = listcmd(cmd, parseline(ps, es)); - } - return cmd; -} - -struct cmd* -parsepipe(char **ps, char *es) -{ - struct cmd *cmd; - - cmd = parseexec(ps, es); - if(peek(ps, es, "|")){ - gettoken(ps, es, 0, 0); - cmd = pipecmd(cmd, parsepipe(ps, es)); - } - return cmd; -} - -struct cmd* -parseredirs(struct cmd *cmd, char **ps, char *es) -{ - int tok; - char *q, *eq; - - while(peek(ps, es, "<>")){ - tok = gettoken(ps, es, 0, 0); - if(gettoken(ps, es, &q, &eq) != 'a') - panic("missing file for redirection"); - switch(tok){ - case '<': - cmd = redircmd(cmd, q, eq, O_RDONLY, 0); - break; - case '>': - cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); - break; - case '+': // >> - cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); - break; - } - } - return cmd; -} - -struct cmd* -parseblock(char **ps, char *es) -{ - struct cmd *cmd; - - if(!peek(ps, es, "(")) - panic("parseblock"); - gettoken(ps, es, 0, 0); - cmd = parseline(ps, es); - if(!peek(ps, es, ")")) - panic("syntax - missing )"); - gettoken(ps, es, 0, 0); - cmd = parseredirs(cmd, ps, es); - return cmd; -} - -struct cmd* -parseexec(char **ps, char *es) -{ - char *q, *eq; - int tok, argc; - struct execcmd *cmd; - struct cmd *ret; - - if(peek(ps, es, "(")) - return parseblock(ps, es); - - ret = execcmd(); - cmd = (struct execcmd*)ret; - - argc = 0; - ret = parseredirs(ret, ps, es); - while(!peek(ps, es, "|)&;")){ - if((tok=gettoken(ps, es, &q, &eq)) == 0) - break; - if(tok != 'a') - panic("syntax"); - cmd->argv[argc] = q; - cmd->eargv[argc] = eq; - argc++; - if(argc >= MAXARGS) - panic("too many args"); - ret = parseredirs(ret, ps, es); - } - cmd->argv[argc] = 0; - cmd->eargv[argc] = 0; - return ret; -} - -// NUL-terminate all the counted strings. -struct cmd* -nulterminate(struct cmd *cmd) -{ - int i; - struct backcmd *bcmd; - struct execcmd *ecmd; - struct listcmd *lcmd; - struct pipecmd *pcmd; - struct redircmd *rcmd; - - if(cmd == 0) - return 0; - - switch(cmd->type){ - case EXEC: - ecmd = (struct execcmd*)cmd; - for(i=0; ecmd->argv[i]; i++) - *ecmd->eargv[i] = 0; - break; - - case REDIR: - rcmd = (struct redircmd*)cmd; - nulterminate(rcmd->cmd); - *rcmd->efile = 0; - break; - - case PIPE: - pcmd = (struct pipecmd*)cmd; - nulterminate(pcmd->left); - nulterminate(pcmd->right); - break; - - case LIST: - lcmd = (struct listcmd*)cmd; - nulterminate(lcmd->left); - nulterminate(lcmd->right); - break; - - case BACK: - bcmd = (struct backcmd*)cmd; - nulterminate(bcmd->cmd); - break; - } - return cmd; -} diff --git a/show1 b/show1 deleted file mode 100755 index e0d3d83..0000000 --- a/show1 +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps diff --git a/sign.pl b/sign.pl deleted file mode 100755 index d793035..0000000 --- a/sign.pl +++ /dev/null @@ -1,19 +0,0 @@ -#!/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; diff --git a/sleep1.p b/sleep1.p deleted file mode 100644 index af69772..0000000 --- a/sleep1.p +++ /dev/null @@ -1,134 +0,0 @@ -/* -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 -} - diff --git a/sleeplock.c b/sleeplock.c deleted file mode 100644 index e0750ea..0000000 --- a/sleeplock.c +++ /dev/null @@ -1,56 +0,0 @@ -// 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; -} - - - diff --git a/sleeplock.h b/sleeplock.h deleted file mode 100644 index 110e6f3..0000000 --- a/sleeplock.h +++ /dev/null @@ -1,10 +0,0 @@ -// Long-term locks for processes -struct sleeplock { - uint locked; // Is the lock held? - struct spinlock lk; // spinlock protecting this sleep lock - - // For debugging: - char *name; // Name of lock. - int pid; // Process holding lock -}; - diff --git a/spinlock.c b/spinlock.c deleted file mode 100644 index 78f2325..0000000 --- a/spinlock.c +++ /dev/null @@ -1,123 +0,0 @@ -// Mutual exclusion spin locks. -#include - -#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(); -} - diff --git a/spinlock.h b/spinlock.h deleted file mode 100644 index 947aaee..0000000 --- a/spinlock.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -// 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. -}; - diff --git a/spinp b/spinp deleted file mode 100755 index db9614b..0000000 --- a/spinp +++ /dev/null @@ -1,16 +0,0 @@ -#!/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 - diff --git a/stat.h b/stat.h deleted file mode 100644 index 8a80933..0000000 --- a/stat.h +++ /dev/null @@ -1,11 +0,0 @@ -#define T_DIR 1 // Directory -#define T_FILE 2 // File -#define T_DEV 3 // Device - -struct stat { - short type; // Type of file - int dev; // File system's disk device - uint ino; // Inode number - short nlink; // Number of links to file - uint size; // Size of file in bytes -}; diff --git a/stressfs.c b/stressfs.c deleted file mode 100644 index c0a4743..0000000 --- a/stressfs.c +++ /dev/null @@ -1,49 +0,0 @@ -// Demonstrate that moving the "acquire" in iderw after the loop that -// appends to the idequeue results in a race. - -// For this to work, you should also add a spin within iderw's -// idequeue traversal loop. Adding the following demonstrated a panic -// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: -// for (i = 0; i < 40000; i++) -// asm volatile(""); - -#include "types.h" -#include "stat.h" -#include "user.h" -#include "fs.h" -#include "fcntl.h" - -int -main(int argc, char *argv[]) -{ - int fd, i; - char path[] = "stressfs0"; - char data[512]; - - printf(1, "stressfs starting\n"); - memset(data, 'a', sizeof(data)); - - for(i = 0; i < 4; i++) - if(fork() > 0) - break; - - printf(1, "write %d\n", i); - - path[8] += i; - fd = open(path, O_CREATE | O_RDWR); - for(i = 0; i < 20; i++) -// printf(fd, "%d\n", i); - write(fd, data, sizeof(data)); - close(fd); - - printf(1, "read\n"); - - fd = open(path, O_RDONLY); - for (i = 0; i < 20; i++) - read(fd, data, sizeof(data)); - close(fd); - - wait(); - - exit(); -} diff --git a/string.c b/string.c deleted file mode 100644 index a7cc61f..0000000 --- a/string.c +++ /dev/null @@ -1,105 +0,0 @@ -#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; -} - diff --git a/swtch.S b/swtch.S deleted file mode 100644 index 63a7dcc..0000000 --- a/swtch.S +++ /dev/null @@ -1,29 +0,0 @@ -# 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 diff --git a/syscall.c b/syscall.c deleted file mode 100644 index ee85261..0000000 --- a/syscall.c +++ /dev/null @@ -1,145 +0,0 @@ -#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; - } -} diff --git a/syscall.h b/syscall.h deleted file mode 100644 index bc5f356..0000000 --- a/syscall.h +++ /dev/null @@ -1,22 +0,0 @@ -// System call numbers -#define SYS_fork 1 -#define SYS_exit 2 -#define SYS_wait 3 -#define SYS_pipe 4 -#define SYS_read 5 -#define SYS_kill 6 -#define SYS_exec 7 -#define SYS_fstat 8 -#define SYS_chdir 9 -#define SYS_dup 10 -#define SYS_getpid 11 -#define SYS_sbrk 12 -#define SYS_sleep 13 -#define SYS_uptime 14 -#define SYS_open 15 -#define SYS_write 16 -#define SYS_mknod 17 -#define SYS_unlink 18 -#define SYS_link 19 -#define SYS_mkdir 20 -#define SYS_close 21 diff --git a/sysfile.c b/sysfile.c deleted file mode 100644 index bfe61b7..0000000 --- a/sysfile.c +++ /dev/null @@ -1,444 +0,0 @@ -// -// File-system system calls. -// Mostly argument checking, since we don't trust -// user code, and calls into file.c and fs.c. -// - -#include "types.h" -#include "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); offsize; 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; -} diff --git a/sysproc.c b/sysproc.c deleted file mode 100644 index 0686d29..0000000 --- a/sysproc.c +++ /dev/null @@ -1,91 +0,0 @@ -#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; -} diff --git a/toc.ftr b/toc.ftr deleted file mode 100644 index 0061c1d..0000000 --- a/toc.ftr +++ /dev/null @@ -1,13 +0,0 @@ - - -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. diff --git a/toc.hdr b/toc.hdr deleted file mode 100644 index 3698d81..0000000 --- a/toc.hdr +++ /dev/null @@ -1,6 +0,0 @@ -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. - - diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..a45860a --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,4 @@ +project(mkfs) + +add_executable(mkfs mkfs.c) + diff --git a/tools/cuth b/tools/cuth new file mode 100755 index 0000000..cce8c0c --- /dev/null +++ b/tools/cuth @@ -0,0 +1,48 @@ +#!/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 = ; + 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"); +} diff --git a/tools/mkfs.c b/tools/mkfs.c new file mode 100644 index 0000000..753f49c --- /dev/null +++ b/tools/mkfs.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// 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); +} diff --git a/tools/pr.pl b/tools/pr.pl new file mode 100755 index 0000000..46905bd --- /dev/null +++ b/tools/pr.pl @@ -0,0 +1,36 @@ +#!/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"; +} diff --git a/tools/printpcs b/tools/printpcs new file mode 100755 index 0000000..81d039b --- /dev/null +++ b/tools/printpcs @@ -0,0 +1,14 @@ +#!/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 "$@" diff --git a/tools/runoff b/tools/runoff new file mode 100755 index 0000000..be362d0 --- /dev/null +++ b/tools/runoff @@ -0,0 +1,246 @@ +#!/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"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 diff --git a/tools/runoff.list b/tools/runoff.list new file mode 100644 index 0000000..2df9b81 --- /dev/null +++ b/tools/runoff.list @@ -0,0 +1,80 @@ +# 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 diff --git a/tools/runoff.spec b/tools/runoff.spec new file mode 100644 index 0000000..9247948 --- /dev/null +++ b/tools/runoff.spec @@ -0,0 +1,102 @@ +# 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 diff --git a/tools/runoff1 b/tools/runoff1 new file mode 100755 index 0000000..532f844 --- /dev/null +++ b/tools/runoff1 @@ -0,0 +1,108 @@ +#!/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; + } +} diff --git a/tools/show1 b/tools/show1 new file mode 100755 index 0000000..e0d3d83 --- /dev/null +++ b/tools/show1 @@ -0,0 +1,3 @@ +#!/bin/sh + +runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps diff --git a/tools/spinp b/tools/spinp new file mode 100755 index 0000000..db9614b --- /dev/null +++ b/tools/spinp @@ -0,0 +1,16 @@ +#!/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 + diff --git a/tools/toc.ftr b/tools/toc.ftr new file mode 100644 index 0000000..0061c1d --- /dev/null +++ b/tools/toc.ftr @@ -0,0 +1,13 @@ + + +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. diff --git a/tools/toc.hdr b/tools/toc.hdr new file mode 100644 index 0000000..3698d81 --- /dev/null +++ b/tools/toc.hdr @@ -0,0 +1,6 @@ +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. + + diff --git a/trap.c b/trap.c deleted file mode 100644 index 41c66eb..0000000 --- a/trap.c +++ /dev/null @@ -1,112 +0,0 @@ -#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(); -} diff --git a/trapasm.S b/trapasm.S deleted file mode 100644 index da8aefc..0000000 --- a/trapasm.S +++ /dev/null @@ -1,32 +0,0 @@ -#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 diff --git a/traps.h b/traps.h deleted file mode 100644 index 0bd1fd8..0000000 --- a/traps.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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 - diff --git a/types.h b/types.h deleted file mode 100644 index e4adf64..0000000 --- a/types.h +++ /dev/null @@ -1,4 +0,0 @@ -typedef unsigned int uint; -typedef unsigned short ushort; -typedef unsigned char uchar; -typedef uint pde_t; diff --git a/uart.c b/uart.c deleted file mode 100644 index b8946da..0000000 --- a/uart.c +++ /dev/null @@ -1,77 +0,0 @@ -// 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); -} diff --git a/ulib.c b/ulib.c deleted file mode 100644 index 8e1e1a2..0000000 --- a/ulib.c +++ /dev/null @@ -1,106 +0,0 @@ -#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; -} diff --git a/umalloc.c b/umalloc.c deleted file mode 100644 index a7e7d2c..0000000 --- a/umalloc.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" -#include "param.h" - -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. - -typedef long Align; - -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; -}; - -typedef union header Header; - -static Header base; -static Header *freep; - -void -free(void *ap) -{ - Header *bp, *p; - - bp = (Header*)ap - 1; - for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) - if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) - break; - if(bp + bp->s.size == p->s.ptr){ - bp->s.size += p->s.ptr->s.size; - bp->s.ptr = p->s.ptr->s.ptr; - } else - bp->s.ptr = p->s.ptr; - if(p + p->s.size == bp){ - p->s.size += bp->s.size; - p->s.ptr = bp->s.ptr; - } else - p->s.ptr = bp; - freep = p; -} - -static Header* -morecore(uint nu) -{ - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == (char*)-1) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; -} - -void* -malloc(uint nbytes) -{ - Header *p, *prevp; - uint nunits; - - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; - } - for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ - if(p->s.size >= nunits){ - if(p->s.size == nunits) - prevp->s.ptr = p->s.ptr; - else { - p->s.size -= nunits; - p += p->s.size; - p->s.size = nunits; - } - freep = prevp; - return (void*)(p + 1); - } - if(p == freep) - if((p = morecore(nunits)) == 0) - return 0; - } -} diff --git a/user.h b/user.h deleted file mode 100644 index 4f99c52..0000000 --- a/user.h +++ /dev/null @@ -1,39 +0,0 @@ -struct stat; -struct rtcdate; - -// system calls -int fork(void); -int exit(void) __attribute__((noreturn)); -int wait(void); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); -int close(int); -int kill(int); -int exec(char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); -int dup(int); -int getpid(void); -char* sbrk(int); -int sleep(int); -int uptime(void); - -// ulib.c -int stat(const char*, struct stat*); -char* strcpy(char*, const char*); -void *memmove(void*, const void*, int); -char* strchr(const char*, char c); -int strcmp(const char*, const char*); -void printf(int, const char*, ...); -char* gets(char*, int max); -uint strlen(const char*); -void* memset(void*, int, uint); -void* malloc(uint); -void free(void*); -int atoi(const char*); diff --git a/user/CMakeLists.txt b/user/CMakeLists.txt new file mode 100644 index 0000000..afee636 --- /dev/null +++ b/user/CMakeLists.txt @@ -0,0 +1,73 @@ +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 -o ") + +# 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} $) + # 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) + diff --git a/user/asm/usys.S b/user/asm/usys.S new file mode 100644 index 0000000..8bfd8a1 --- /dev/null +++ b/user/asm/usys.S @@ -0,0 +1,31 @@ +#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) diff --git a/user/include/user.h b/user/include/user.h new file mode 100644 index 0000000..4f99c52 --- /dev/null +++ b/user/include/user.h @@ -0,0 +1,39 @@ +struct stat; +struct rtcdate; + +// system calls +int fork(void); +int exit(void) __attribute__((noreturn)); +int wait(void); +int pipe(int*); +int write(int, const void*, int); +int read(int, void*, int); +int close(int); +int kill(int); +int exec(char*, char**); +int open(const char*, int); +int mknod(const char*, short, short); +int unlink(const char*); +int fstat(int fd, struct stat*); +int link(const char*, const char*); +int mkdir(const char*); +int chdir(const char*); +int dup(int); +int getpid(void); +char* sbrk(int); +int sleep(int); +int uptime(void); + +// ulib.c +int stat(const char*, struct stat*); +char* strcpy(char*, const char*); +void *memmove(void*, const void*, int); +char* strchr(const char*, char c); +int strcmp(const char*, const char*); +void printf(int, const char*, ...); +char* gets(char*, int max); +uint strlen(const char*); +void* memset(void*, int, uint); +void* malloc(uint); +void free(void*); +int atoi(const char*); diff --git a/user/src/cat.c b/user/src/cat.c new file mode 100644 index 0000000..5ddc820 --- /dev/null +++ b/user/src/cat.c @@ -0,0 +1,43 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[512]; + +void +cat(int fd) +{ + int n; + + while((n = read(fd, buf, sizeof(buf))) > 0) { + if (write(1, buf, n) != n) { + printf(1, "cat: write error\n"); + exit(); + } + } + if(n < 0){ + printf(1, "cat: read error\n"); + exit(); + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + + if(argc <= 1){ + cat(0); + exit(); + } + + for(i = 1; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "cat: cannot open %s\n", argv[i]); + exit(); + } + cat(fd); + close(fd); + } + exit(); +} diff --git a/user/src/echo.c b/user/src/echo.c new file mode 100644 index 0000000..806dee0 --- /dev/null +++ b/user/src/echo.c @@ -0,0 +1,13 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + for(i = 1; i < argc; i++) + printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); + exit(); +} diff --git a/user/src/forktest.c b/user/src/forktest.c new file mode 100644 index 0000000..8bc984d --- /dev/null +++ b/user/src/forktest.c @@ -0,0 +1,56 @@ +// Test that fork fails gracefully. +// Tiny executable so that the limit can be filling the proc table. + +#include "types.h" +#include "stat.h" +#include "user.h" + +#define N 1000 + +void +printf(int fd, const char *s, ...) +{ + write(fd, s, strlen(s)); +} + +void +forktest(void) +{ + int n, pid; + + printf(1, "fork test\n"); + + for(n=0; n 0; n--){ + if(wait() < 0){ + printf(1, "wait stopped early\n"); + exit(); + } + } + + if(wait() != -1){ + printf(1, "wait got too many\n"); + exit(); + } + + printf(1, "fork test OK\n"); +} + +int +main(void) +{ + forktest(); + exit(); +} diff --git a/user/src/grep.c b/user/src/grep.c new file mode 100644 index 0000000..adc4835 --- /dev/null +++ b/user/src/grep.c @@ -0,0 +1,107 @@ +// Simple grep. Only supports ^ . * $ operators. + +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[1024]; +int match(char*, char*); + +void +grep(char *pattern, int fd) +{ + int n, m; + char *p, *q; + + m = 0; + while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ + m += n; + buf[m] = '\0'; + p = buf; + while((q = strchr(p, '\n')) != 0){ + *q = 0; + if(match(pattern, p)){ + *q = '\n'; + write(1, p, q+1 - p); + } + p = q+1; + } + if(p == buf) + m = 0; + if(m > 0){ + m -= p - buf; + memmove(buf, p, m); + } + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + char *pattern; + + if(argc <= 1){ + printf(2, "usage: grep pattern [file ...]\n"); + exit(); + } + pattern = argv[1]; + + if(argc <= 2){ + grep(pattern, 0); + exit(); + } + + for(i = 2; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "grep: cannot open %s\n", argv[i]); + exit(); + } + grep(pattern, fd); + close(fd); + } + exit(); +} + +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9. + +int matchhere(char*, char*); +int matchstar(int, char*, char*); + +int +match(char *re, char *text) +{ + if(re[0] == '^') + return matchhere(re+1, text); + do{ // must look at empty string + if(matchhere(re, text)) + return 1; + }while(*text++ != '\0'); + return 0; +} + +// matchhere: search for re at beginning of text +int matchhere(char *re, char *text) +{ + if(re[0] == '\0') + return 1; + if(re[1] == '*') + return matchstar(re[0], re+2, text); + if(re[0] == '$' && re[1] == '\0') + return *text == '\0'; + if(*text!='\0' && (re[0]=='.' || re[0]==*text)) + return matchhere(re+1, text+1); + return 0; +} + +// matchstar: search for c*re at beginning of text +int matchstar(int c, char *re, char *text) +{ + do{ // a * matches zero or more instances + if(matchhere(re, text)) + return 1; + }while(*text!='\0' && (*text++==c || c=='.')); + return 0; +} + diff --git a/user/src/init.c b/user/src/init.c new file mode 100644 index 0000000..046b551 --- /dev/null +++ b/user/src/init.c @@ -0,0 +1,37 @@ +// init: The initial user-level program + +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fcntl.h" + +char *argv[] = { "sh", 0 }; + +int +main(void) +{ + int pid, wpid; + + if(open("console", O_RDWR) < 0){ + mknod("console", 1, 1); + open("console", O_RDWR); + } + dup(0); // stdout + dup(0); // stderr + + for(;;){ + printf(1, "init: starting sh\n"); + pid = fork(); + if(pid < 0){ + printf(1, "init: fork failed\n"); + exit(); + } + if(pid == 0){ + exec("sh", argv); + printf(1, "init: exec sh failed\n"); + exit(); + } + while((wpid=wait()) >= 0 && wpid != pid) + printf(1, "zombie!\n"); + } +} diff --git a/user/src/kill.c b/user/src/kill.c new file mode 100644 index 0000000..364f6af --- /dev/null +++ b/user/src/kill.c @@ -0,0 +1,17 @@ +#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= 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= 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; + } + } +} diff --git a/user/src/rm.c b/user/src/rm.c new file mode 100644 index 0000000..4fd33c8 --- /dev/null +++ b/user/src/rm.c @@ -0,0 +1,23 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + printf(2, "Usage: rm files...\n"); + exit(); + } + + for(i = 1; i < argc; i++){ + if(unlink(argv[i]) < 0){ + printf(2, "rm: %s failed to delete\n", argv[i]); + break; + } + } + + exit(); +} diff --git a/user/src/sh.c b/user/src/sh.c new file mode 100644 index 0000000..054bab9 --- /dev/null +++ b/user/src/sh.c @@ -0,0 +1,493 @@ +// Shell. + +#include "types.h" +#include "user.h" +#include "fcntl.h" + +// Parsed command representation +#define EXEC 1 +#define REDIR 2 +#define PIPE 3 +#define LIST 4 +#define BACK 5 + +#define MAXARGS 10 + +struct cmd { + int type; +}; + +struct execcmd { + int type; + char *argv[MAXARGS]; + char *eargv[MAXARGS]; +}; + +struct redircmd { + int type; + struct cmd *cmd; + char *file; + char *efile; + int mode; + int fd; +}; + +struct pipecmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct listcmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct backcmd { + int type; + struct cmd *cmd; +}; + +int fork1(void); // Fork but panics on failure. +void panic(char*); +struct cmd *parsecmd(char*); + +// Execute cmd. Never returns. +void +runcmd(struct cmd *cmd) +{ + int p[2]; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + exit(); + + switch(cmd->type){ + default: + panic("runcmd"); + + case EXEC: + ecmd = (struct execcmd*)cmd; + if(ecmd->argv[0] == 0) + exit(); + exec(ecmd->argv[0], ecmd->argv); + printf(2, "exec %s failed\n", ecmd->argv[0]); + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + close(rcmd->fd); + if(open(rcmd->file, rcmd->mode) < 0){ + printf(2, "open %s failed\n", rcmd->file); + exit(); + } + runcmd(rcmd->cmd); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + if(fork1() == 0) + runcmd(lcmd->left); + wait(); + runcmd(lcmd->right); + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + if(pipe(p) < 0) + panic("pipe"); + if(fork1() == 0){ + close(1); + dup(p[1]); + close(p[0]); + close(p[1]); + runcmd(pcmd->left); + } + if(fork1() == 0){ + close(0); + dup(p[0]); + close(p[0]); + close(p[1]); + runcmd(pcmd->right); + } + close(p[0]); + close(p[1]); + wait(); + wait(); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + if(fork1() == 0) + runcmd(bcmd->cmd); + break; + } + exit(); +} + +int +getcmd(char *buf, int nbuf) +{ + printf(2, "$ "); + memset(buf, 0, nbuf); + gets(buf, nbuf); + if(buf[0] == 0) // EOF + return -1; + return 0; +} + +int +main(void) +{ + static char buf[100]; + int fd; + + // Ensure that three file descriptors are open. + while((fd = open("console", O_RDWR)) >= 0){ + if(fd >= 3){ + close(fd); + break; + } + } + + // Read and run input commands. + while(getcmd(buf, sizeof(buf)) >= 0){ + if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ + // Chdir must be called by the parent, not the child. + buf[strlen(buf)-1] = 0; // chop \n + if(chdir(buf+3) < 0) + printf(2, "cannot cd %s\n", buf+3); + continue; + } + if(fork1() == 0) + runcmd(parsecmd(buf)); + wait(); + } + exit(); +} + +void +panic(char *s) +{ + printf(2, "%s\n", s); + exit(); +} + +int +fork1(void) +{ + int pid; + + pid = fork(); + if(pid == -1) + panic("fork"); + return pid; +} + +//PAGEBREAK! +// Constructors + +struct cmd* +execcmd(void) +{ + struct execcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = EXEC; + return (struct cmd*)cmd; +} + +struct cmd* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ + struct redircmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = REDIR; + cmd->cmd = subcmd; + cmd->file = file; + cmd->efile = efile; + cmd->mode = mode; + cmd->fd = fd; + return (struct cmd*)cmd; +} + +struct cmd* +pipecmd(struct cmd *left, struct cmd *right) +{ + struct pipecmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = PIPE; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +listcmd(struct cmd *left, struct cmd *right) +{ + struct listcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = LIST; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +backcmd(struct cmd *subcmd) +{ + struct backcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = BACK; + cmd->cmd = subcmd; + return (struct cmd*)cmd; +} +//PAGEBREAK! +// Parsing + +char whitespace[] = " \t\r\n\v"; +char symbols[] = "<|>&;()"; + +int +gettoken(char **ps, char *es, char **q, char **eq) +{ + char *s; + int ret; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + if(q) + *q = s; + ret = *s; + switch(*s){ + case 0: + break; + case '|': + case '(': + case ')': + case ';': + case '&': + case '<': + s++; + break; + case '>': + s++; + if(*s == '>'){ + ret = '+'; + s++; + } + break; + default: + ret = 'a'; + while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) + s++; + break; + } + if(eq) + *eq = s; + + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return ret; +} + +int +peek(char **ps, char *es, char *toks) +{ + char *s; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return *s && strchr(toks, *s); +} + +struct cmd *parseline(char**, char*); +struct cmd *parsepipe(char**, char*); +struct cmd *parseexec(char**, char*); +struct cmd *nulterminate(struct cmd*); + +struct cmd* +parsecmd(char *s) +{ + char *es; + struct cmd *cmd; + + es = s + strlen(s); + cmd = parseline(&s, es); + peek(&s, es, ""); + if(s != es){ + printf(2, "leftovers: %s\n", s); + panic("syntax"); + } + nulterminate(cmd); + return cmd; +} + +struct cmd* +parseline(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parsepipe(ps, es); + while(peek(ps, es, "&")){ + gettoken(ps, es, 0, 0); + cmd = backcmd(cmd); + } + if(peek(ps, es, ";")){ + gettoken(ps, es, 0, 0); + cmd = listcmd(cmd, parseline(ps, es)); + } + return cmd; +} + +struct cmd* +parsepipe(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parseexec(ps, es); + if(peek(ps, es, "|")){ + gettoken(ps, es, 0, 0); + cmd = pipecmd(cmd, parsepipe(ps, es)); + } + return cmd; +} + +struct cmd* +parseredirs(struct cmd *cmd, char **ps, char *es) +{ + int tok; + char *q, *eq; + + while(peek(ps, es, "<>")){ + tok = gettoken(ps, es, 0, 0); + if(gettoken(ps, es, &q, &eq) != 'a') + panic("missing file for redirection"); + switch(tok){ + case '<': + cmd = redircmd(cmd, q, eq, O_RDONLY, 0); + break; + case '>': + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + case '+': // >> + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + } + } + return cmd; +} + +struct cmd* +parseblock(char **ps, char *es) +{ + struct cmd *cmd; + + if(!peek(ps, es, "(")) + panic("parseblock"); + gettoken(ps, es, 0, 0); + cmd = parseline(ps, es); + if(!peek(ps, es, ")")) + panic("syntax - missing )"); + gettoken(ps, es, 0, 0); + cmd = parseredirs(cmd, ps, es); + return cmd; +} + +struct cmd* +parseexec(char **ps, char *es) +{ + char *q, *eq; + int tok, argc; + struct execcmd *cmd; + struct cmd *ret; + + if(peek(ps, es, "(")) + return parseblock(ps, es); + + ret = execcmd(); + cmd = (struct execcmd*)ret; + + argc = 0; + ret = parseredirs(ret, ps, es); + while(!peek(ps, es, "|)&;")){ + if((tok=gettoken(ps, es, &q, &eq)) == 0) + break; + if(tok != 'a') + panic("syntax"); + cmd->argv[argc] = q; + cmd->eargv[argc] = eq; + argc++; + if(argc >= MAXARGS) + panic("too many args"); + ret = parseredirs(ret, ps, es); + } + cmd->argv[argc] = 0; + cmd->eargv[argc] = 0; + return ret; +} + +// NUL-terminate all the counted strings. +struct cmd* +nulterminate(struct cmd *cmd) +{ + int i; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + return 0; + + switch(cmd->type){ + case EXEC: + ecmd = (struct execcmd*)cmd; + for(i=0; ecmd->argv[i]; i++) + *ecmd->eargv[i] = 0; + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + nulterminate(rcmd->cmd); + *rcmd->efile = 0; + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + nulterminate(pcmd->left); + nulterminate(pcmd->right); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + nulterminate(lcmd->left); + nulterminate(lcmd->right); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + nulterminate(bcmd->cmd); + break; + } + return cmd; +} diff --git a/user/src/stressfs.c b/user/src/stressfs.c new file mode 100644 index 0000000..c0a4743 --- /dev/null +++ b/user/src/stressfs.c @@ -0,0 +1,49 @@ +// Demonstrate that moving the "acquire" in iderw after the loop that +// appends to the idequeue results in a race. + +// For this to work, you should also add a spin within iderw's +// idequeue traversal loop. Adding the following demonstrated a panic +// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: +// for (i = 0; i < 40000; i++) +// asm volatile(""); + +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" +#include "fcntl.h" + +int +main(int argc, char *argv[]) +{ + int fd, i; + char path[] = "stressfs0"; + char data[512]; + + printf(1, "stressfs starting\n"); + memset(data, 'a', sizeof(data)); + + for(i = 0; i < 4; i++) + if(fork() > 0) + break; + + printf(1, "write %d\n", i); + + path[8] += i; + fd = open(path, O_CREATE | O_RDWR); + for(i = 0; i < 20; i++) +// printf(fd, "%d\n", i); + write(fd, data, sizeof(data)); + close(fd); + + printf(1, "read\n"); + + fd = open(path, O_RDONLY); + for (i = 0; i < 20; i++) + read(fd, data, sizeof(data)); + close(fd); + + wait(); + + exit(); +} diff --git a/user/src/ulib.c b/user/src/ulib.c new file mode 100644 index 0000000..bca3839 --- /dev/null +++ b/user/src/ulib.c @@ -0,0 +1,106 @@ +#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; +} diff --git a/user/src/umalloc.c b/user/src/umalloc.c new file mode 100644 index 0000000..a7e7d2c --- /dev/null +++ b/user/src/umalloc.c @@ -0,0 +1,90 @@ +#include "types.h" +#include "stat.h" +#include "user.h" +#include "param.h" + +// Memory allocator by Kernighan and Ritchie, +// The C programming Language, 2nd ed. Section 8.7. + +typedef long Align; + +union header { + struct { + union header *ptr; + uint size; + } s; + Align x; +}; + +typedef union header Header; + +static Header base; +static Header *freep; + +void +free(void *ap) +{ + Header *bp, *p; + + bp = (Header*)ap - 1; + for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) + if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) + break; + if(bp + bp->s.size == p->s.ptr){ + bp->s.size += p->s.ptr->s.size; + bp->s.ptr = p->s.ptr->s.ptr; + } else + bp->s.ptr = p->s.ptr; + if(p + p->s.size == bp){ + p->s.size += bp->s.size; + p->s.ptr = bp->s.ptr; + } else + p->s.ptr = bp; + freep = p; +} + +static Header* +morecore(uint nu) +{ + char *p; + Header *hp; + + if(nu < 4096) + nu = 4096; + p = sbrk(nu * sizeof(Header)); + if(p == (char*)-1) + return 0; + hp = (Header*)p; + hp->s.size = nu; + free((void*)(hp + 1)); + return freep; +} + +void* +malloc(uint nbytes) +{ + Header *p, *prevp; + uint nunits; + + nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; + if((prevp = freep) == 0){ + base.s.ptr = freep = prevp = &base; + base.s.size = 0; + } + for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ + if(p->s.size >= nunits){ + if(p->s.size == nunits) + prevp->s.ptr = p->s.ptr; + else { + p->s.size -= nunits; + p += p->s.size; + p->s.size = nunits; + } + freep = prevp; + return (void*)(p + 1); + } + if(p == freep) + if((p = morecore(nunits)) == 0) + return 0; + } +} diff --git a/user/src/usertests.c b/user/src/usertests.c new file mode 100644 index 0000000..44e57f7 --- /dev/null +++ b/user/src/usertests.c @@ -0,0 +1,1803 @@ +#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(); +} diff --git a/user/src/wc.c b/user/src/wc.c new file mode 100644 index 0000000..d6a54df --- /dev/null +++ b/user/src/wc.c @@ -0,0 +1,54 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[512]; + +void +wc(int fd, char *name) +{ + int i, n; + int l, w, c, inword; + + l = w = c = 0; + inword = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i=0; i 0) + sleep(5); // Let child exit before parent. + exit(); +} diff --git a/usertests.c b/usertests.c deleted file mode 100644 index 44e57f7..0000000 --- a/usertests.c +++ /dev/null @@ -1,1803 +0,0 @@ -#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(); -} diff --git a/usys.S b/usys.S deleted file mode 100644 index 8bfd8a1..0000000 --- a/usys.S +++ /dev/null @@ -1,31 +0,0 @@ -#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) diff --git a/vectors.pl b/vectors.pl deleted file mode 100755 index 57b49dd..0000000 --- a/vectors.pl +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/perl -w - -# Generate vectors.S, the trap/interrupt entry points. -# There has to be one entry point per interrupt number -# since otherwise there's no way for trap() to discover -# the interrupt number. - -print "# generated by vectors.pl - do not edit\n"; -print "# handlers\n"; -print ".globl alltraps\n"; -for(my $i = 0; $i < 256; $i++){ - print ".globl vector$i\n"; - print "vector$i:\n"; - if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ - print " 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 -# ... - diff --git a/vm.c b/vm.c deleted file mode 100644 index 7134cff..0000000 --- a/vm.c +++ /dev/null @@ -1,394 +0,0 @@ -#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. - diff --git a/wc.c b/wc.c deleted file mode 100644 index d6a54df..0000000 --- a/wc.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "types.h" -#include "stat.h" -#include "user.h" - -char buf[512]; - -void -wc(int fd, char *name) -{ - int i, n; - int l, w, c, inword; - - l = w = c = 0; - inword = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(i=0; i> 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; -}; diff --git a/zombie.c b/zombie.c deleted file mode 100644 index ee817da..0000000 --- a/zombie.c +++ /dev/null @@ -1,14 +0,0 @@ -// 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(); -}