Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial checking of xv6-rev2

  • Loading branch information...
commit b89ce29493470118f07105e95d573691a878cb75 0 parents
Charles Childers authored
Showing with 9,438 additions and 0 deletions.
  1. +24 −0 LICENSE
  2. +141 −0 Makefile
  3. +47 −0 README
  4. +19 −0 asm.h
  5. +137 −0 bio.c
  6. +84 −0 bootasm.S
  7. +96 −0 bootmain.c
  8. +73 −0 bootother.S
  9. +13 −0 buf.h
  10. +39 −0 cat.c
  11. +305 −0 console.c
  12. +149 −0 defs.h
  13. +8 −0 dev.h
  14. +738 −0 dot-bochsrc
  15. +13 −0 echo.c
  16. +60 −0 elf.h
  17. +115 −0 exec.c
  18. +4 −0 fcntl.h
  19. +127 −0 file.c
  20. +9 −0 file.h
  21. +54 −0 forktest.c
  22. +619 −0 fs.c
  23. +56 −0 fs.h
  24. +18 −0 fsvar.h
  25. +106 −0 grep.c
  26. +150 −0 ide.c
  27. +37 −0 init.c
  28. +30 −0 initcode.S
  29. +81 −0 ioapic.c
  30. +116 −0 kalloc.c
  31. +50 −0 kbd.c
  32. +112 −0 kbd.h
  33. +17 −0 kill.c
  34. +170 −0 lapic.c
  35. +15 −0 ln.c
  36. +85 −0 ls.c
  37. +86 −0 main.c
  38. +23 −0 mkdir.c
  39. +285 −0 mkfs.c
  40. +156 −0 mmu.h
  41. +145 −0 mp.c
  42. +54 −0 mp.h
  43. +10 −0 param.h
  44. +84 −0 picirq.c
  45. +122 −0 pipe.c
  46. +35 −0 pr.pl
  47. +85 −0 printf.c
  48. +482 −0 proc.c
  49. +69 −0 proc.h
  50. +23 −0 rm.c
  51. +208 −0 runoff
  52. +73 −0 runoff.list
  53. +1 −0  runoff.spec
  54. +108 −0 runoff1
  55. +492 −0 sh.c
  56. +19 −0 sign.pl
  57. +117 −0 spinlock.c
  58. +11 −0 spinlock.h
  59. +7 −0 stat.h
  60. +98 −0 string.c
  61. +32 −0 swtch.S
  62. +136 −0 syscall.c
  63. +21 −0 syscall.h
  64. +394 −0 sysfile.c
  65. +80 −0 sysproc.c
  66. +65 −0 timer.c
  67. +14 −0 toc.ftr
  68. +6 −0 toc.hdr
  69. +94 −0 trap.c
  70. +38 −0 trapasm.S
  71. +36 −0 traps.h
  72. +3 −0  types.h
  73. +108 −0 ulib.c
  74. +90 −0 umalloc.c
  75. +37 −0 user.h
  76. +1,272 −0 usertests.c
  77. +30 −0 usys.S
  78. +49 −0 vectors.pl
  79. +54 −0 wc.c
  80. +155 −0 x86.h
  81. +14 −0 zombie.c
24 LICENSE
@@ -0,0 +1,24 @@
+The xv6 software is:
+
+Copyright (c) 2006-2009 Frans Kaashoek, Robert Morris, Russ Cox,
+ Massachusetts Institute of Technology
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
141 Makefile
@@ -0,0 +1,141 @@
+OBJS = \
+ bio.o\
+ console.o\
+ exec.o\
+ file.o\
+ fs.o\
+ ide.o\
+ ioapic.o\
+ kalloc.o\
+ kbd.o\
+ lapic.o\
+ main.o\
+ mp.o\
+ picirq.o\
+ pipe.o\
+ proc.o\
+ spinlock.o\
+ string.o\
+ swtch.o\
+ syscall.o\
+ sysfile.o\
+ sysproc.o\
+ timer.o\
+ trapasm.o\
+ trap.o\
+ vectors.o\
+
+# Cross-compiling (e.g., on Mac OS X)
+# TOOLPREFIX = i386-jos-elf-
+
+# Using native tools (e.g., on X86 Linux)
+TOOLPREFIX =
+
+CC = $(TOOLPREFIX)gcc
+AS = $(TOOLPREFIX)gas
+LD = $(TOOLPREFIX)ld
+OBJCOPY = $(TOOLPREFIX)objcopy
+OBJDUMP = $(TOOLPREFIX)objdump
+CFLAGS = -fno-builtin -O2 -Wall -MD -ggdb -m32
+CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
+ASFLAGS = -m32
+# FreeBSD ld wants ``elf_i386_fbsd''
+LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null)
+
+xv6.img: bootblock kernel fs.img
+ dd if=/dev/zero of=xv6.img count=10000
+ dd if=bootblock of=xv6.img conv=notrunc
+ dd if=kernel of=xv6.img seek=1 conv=notrunc
+
+bootblock: bootasm.S bootmain.c
+ $(CC) $(CFLAGS) -O -nostdinc -I. -c bootmain.c
+ $(CC) $(CFLAGS) -nostdinc -I. -c bootasm.S
+ $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
+ $(OBJDUMP) -S bootblock.o > bootblock.asm
+ $(OBJCOPY) -S -O binary bootblock.o bootblock
+ ./sign.pl bootblock
+
+bootother: bootother.S
+ $(CC) $(CFLAGS) -nostdinc -I. -c bootother.S
+ $(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootother.out bootother.o
+ $(OBJCOPY) -S -O binary bootother.out bootother
+ $(OBJDUMP) -S bootother.o > bootother.asm
+
+initcode: initcode.S
+ $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
+ $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
+ $(OBJCOPY) -S -O binary initcode.out initcode
+ $(OBJDUMP) -S initcode.o > initcode.asm
+
+kernel: $(OBJS) bootother initcode
+ $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother
+ $(OBJDUMP) -S kernel > kernel.asm
+ $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
+
+tags: $(OBJS) bootother.S _init
+ etags *.S *.c
+
+vectors.S: vectors.pl
+ perl vectors.pl > vectors.S
+
+ULIB = ulib.o usys.o printf.o umalloc.o
+
+_%: %.o $(ULIB)
+ $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
+ $(OBJDUMP) -S $@ > $*.asm
+ $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
+
+_forktest: forktest.o $(ULIB)
+ # forktest has less library code linked in - needs to be small
+ # in order to be able to max out the proc table.
+ $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
+ $(OBJDUMP) -S _forktest > forktest.asm
+
+mkfs: mkfs.c fs.h
+ gcc $(CFLAGS) -Wall -o mkfs mkfs.c
+
+UPROGS=\
+ _cat\
+ _echo\
+ _forktest\
+ _grep\
+ _init\
+ _kill\
+ _ln\
+ _ls\
+ _mkdir\
+ _rm\
+ _sh\
+ _usertests\
+ _wc\
+ _zombie\
+
+fs.img: mkfs README $(UPROGS)
+ ./mkfs fs.img README $(UPROGS)
+
+-include *.d
+
+clean:
+ rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
+ *.o *.d *.asm *.sym vectors.S parport.out \
+ bootblock kernel xv6.img fs.img mkfs \
+ $(UPROGS)
+
+# make a printout
+FILES = $(shell grep -v '^\#' runoff.list)
+PRINT = runoff.list $(FILES)
+
+xv6.pdf: $(PRINT)
+ ./runoff
+
+print: xv6.pdf
+
+# run in emulators
+
+bochs : fs.img xv6.img
+ if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi
+ bochs -q
+
+qemu: fs.img xv6.img
+ qemu -parallel stdio -hdb fs.img xv6.img
+
47 README
@@ -0,0 +1,47 @@
+xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
+Version 6 (v6). xv6 loosely follows the structure and style of v6,
+but is implemented for a modern x86-based multiprocessor using ANSI C.
+
+ACKNOWLEDGMENTS
+
+xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
+to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
+2000)). See also http://pdos.csail.mit.edu/6.828/2007/v6.html, which
+provides pointers to on-line resources for v6.
+
+xv6 borrows code from the following sources:
+ JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others)
+ Plan 9 (bootother.S, mp.h, mp.c, lapic.c)
+ FreeBSD (ioapic.c)
+ NetBSD (console.c)
+
+The following people made contributions:
+ Russ Cox (context switching, locking)
+ Cliff Frey (MP)
+ Xiao Yu (MP)
+
+The code in the files that constitute xv6 is
+Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox.
+
+ERROR REPORTS
+
+If you spot errors or have suggestions for improvement, please send
+email to Frans Kaashoek and Robert Morris (kaashoek,rtm@csail.mit.edu).
+
+BUILDING AND RUNNING XV6
+
+To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run "make".
+On non-x86 or non-ELF machines (like OS X, even on x86), you will
+need to install a cross-compiler gcc suite capable of producing x86 ELF
+binaries. See http://pdos.csail.mit.edu/6.828/2007/tools.html.
+Then run "make TOOLPREFIX=i386-jos-elf-".
+
+To run xv6, you can use Bochs or QEMU, both PC simulators.
+Bochs makes debugging easier, but QEMU is much faster.
+To run in Bochs, run "make bochs" and then type "c" at the bochs prompt.
+To run in QEMU, run "make qemu". Both log the xv6 screen output to
+standard output.
+
+To create a typeset version of the code, run "make xv6.pdf".
+This requires the "mpage" text formatting utility.
+See http://www.mesa.nl/pub/mpage/.
19 asm.h
@@ -0,0 +1,19 @@
+//
+// assembler macros to create x86 segments
+//
+
+#define SEG_NULLASM \
+ .word 0, 0; \
+ .byte 0, 0, 0, 0
+
+#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_E 0x4 // Expand down (non-executable segments)
+#define STA_C 0x4 // Conforming code segment (executable only)
+#define STA_W 0x2 // Writeable (non-executable segments)
+#define STA_R 0x2 // Readable (executable segments)
+#define STA_A 0x1 // Accessed
137 bio.c
@@ -0,0 +1,137 @@
+// 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 flush 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 three state flags internally:
+// * B_BUSY: the block has been returned from bread
+// and has not been passed back to brelse.
+// * B_VALID: the buffer data has been initialized
+// with the associated disk block contents.
+// * 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 "buf.h"
+
+struct buf buf[NBUF];
+struct spinlock buf_table_lock;
+
+// Linked list of all buffers, through prev/next.
+// bufhead->next is most recently used.
+// bufhead->tail is least recently used.
+struct buf bufhead;
+
+void
+binit(void)
+{
+ struct buf *b;
+
+ initlock(&buf_table_lock, "buf_table");
+
+ // Create linked list of buffers
+ bufhead.prev = &bufhead;
+ bufhead.next = &bufhead;
+ for(b = buf; b < buf+NBUF; b++){
+ b->next = bufhead.next;
+ b->prev = &bufhead;
+ bufhead.next->prev = b;
+ bufhead.next = b;
+ }
+}
+
+// Look through buffer cache for sector on device dev.
+// If not found, allocate fresh block.
+// In either case, return locked buffer.
+static struct buf*
+bget(uint dev, uint sector)
+{
+ struct buf *b;
+
+ acquire(&buf_table_lock);
+
+ loop:
+ // Try for cached block.
+ for(b = bufhead.next; b != &bufhead; b = b->next){
+ if((b->flags & (B_BUSY|B_VALID)) &&
+ b->dev == dev && b->sector == sector){
+ if(b->flags & B_BUSY){
+ sleep(buf, &buf_table_lock);
+ goto loop;
+ }
+ b->flags |= B_BUSY;
+ release(&buf_table_lock);
+ return b;
+ }
+ }
+
+ // Allocate fresh block.
+ for(b = bufhead.prev; b != &bufhead; b = b->prev){
+ if((b->flags & B_BUSY) == 0){
+ b->flags = B_BUSY;
+ b->dev = dev;
+ b->sector = sector;
+ release(&buf_table_lock);
+ return b;
+ }
+ }
+ panic("bget: no buffers");
+}
+
+// Return a B_BUSY buf with the contents of the indicated disk sector.
+struct buf*
+bread(uint dev, uint sector)
+{
+ struct buf *b;
+
+ b = bget(dev, sector);
+ if(!(b->flags & B_VALID))
+ ide_rw(b);
+ return b;
+}
+
+// Write buf's contents to disk. Must be locked.
+void
+bwrite(struct buf *b)
+{
+ if((b->flags & B_BUSY) == 0)
+ panic("bwrite");
+ b->flags |= B_DIRTY;
+ ide_rw(b);
+}
+
+// Release the buffer buf.
+void
+brelse(struct buf *b)
+{
+ if((b->flags & B_BUSY) == 0)
+ panic("brelse");
+
+ acquire(&buf_table_lock);
+
+ b->next->prev = b->prev;
+ b->prev->next = b->next;
+ b->next = bufhead.next;
+ b->prev = &bufhead;
+ bufhead.next->prev = b;
+ bufhead.next = b;
+
+ b->flags &= ~B_BUSY;
+ wakeup(buf);
+
+ release(&buf_table_lock);
+}
+
84 bootasm.S
@@ -0,0 +1,84 @@
+#include "asm.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.
+
+.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
+.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
+.set CR0_PE_ON, 0x1 # protected mode enable flag
+
+.globl start
+start:
+ .code16 # Assemble for 16-bit mode
+ cli # Disable interrupts
+ cld # String operations increment
+
+ # Set up the important data segment registers (DS, ES, SS).
+ xorw %ax,%ax # Segment number zero
+ movw %ax,%ds # -> Data Segment
+ movw %ax,%es # -> Extra Segment
+ movw %ax,%ss # -> Stack Segment
+
+ # Enable A20:
+ # For backwards compatibility with the earliest PCs, physical
+ # address line 20 is tied low, so that addresses higher than
+ # 1MB wrap around to zero by default. This code undoes this.
+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, using a bootstrap GDT
+ # and segment translation that makes virtual addresses
+ # identical to physical addresses, so that the
+ # effective memory map does not change during the switch.
+ lgdt gdtdesc
+ movl %cr0, %eax
+ orl $CR0_PE_ON, %eax
+ movl %eax, %cr0
+
+ # Jump to next instruction, but in 32-bit code segment.
+ # Switches processor into 32-bit mode.
+ ljmp $PROT_MODE_CSEG, $protcseg
+
+ .code32 # Assemble for 32-bit mode
+protcseg:
+ # Set up the protected-mode data segment registers
+ movw $PROT_MODE_DSEG, %ax # Our data segment selector
+ movw %ax, %ds # -> DS: Data Segment
+ movw %ax, %es # -> ES: Extra Segment
+ movw %ax, %fs # -> FS
+ movw %ax, %gs # -> GS
+ movw %ax, %ss # -> SS: Stack Segment
+
+ # Set up the stack pointer and call into C.
+ movl $start, %esp
+ call bootmain
+
+ # If bootmain returns (it shouldn't), loop.
+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 0x17 # sizeof(gdt) - 1
+ .long gdt # address gdt
96 bootmain.c
@@ -0,0 +1,96 @@
+// Boot loader.
+//
+// Part of the boot sector, 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"
+
+#define SECTSIZE 512
+
+void readseg(uint, uint, uint);
+
+void
+bootmain(void)
+{
+ struct elfhdr *elf;
+ struct proghdr *ph, *eph;
+ void (*entry)(void);
+
+ elf = (struct elfhdr*)0x10000; // scratch space
+
+ // Read 1st page off disk
+ readseg((uint)elf, SECTSIZE*8, 0);
+
+ // Is this an ELF executable?
+ if(elf->magic != ELF_MAGIC)
+ goto bad;
+
+ // Load each program segment (ignores ph flags).
+ ph = (struct proghdr*)((uchar*)elf + elf->phoff);
+ eph = ph + elf->phnum;
+ for(; ph < eph; ph++)
+ readseg(ph->va & 0xFFFFFF, ph->memsz, ph->offset);
+
+ // Call the entry point from the ELF header.
+ // Does not return!
+ entry = (void(*)(void))(elf->entry & 0xFFFFFF);
+ entry();
+
+bad:
+ outw(0x8A00, 0x8A00);
+ outw(0x8A00, 0x8E00);
+ for(;;)
+ ;
+}
+
+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 virtual address 'va'.
+// Might copy more than asked.
+void
+readseg(uint va, uint count, uint offset)
+{
+ uint eva;
+
+ eva = va + count;
+
+ // Round down to sector boundary.
+ va &= ~(SECTSIZE - 1);
+
+ // 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(; va < eva; va += SECTSIZE, offset++)
+ readsect((uchar*)va, offset);
+}
73 bootother.S
@@ -0,0 +1,73 @@
+#include "asm.h"
+
+# Start an Application Processor. This must be placed on a 4KB boundary
+# somewhere in the 1st MB of conventional memory (APBOOTSTRAP). However,
+# due to some shortcuts below it's restricted further to within the 1st
+# 64KB. The AP starts in real-mode, with
+# CS selector set to the startup memory address/16;
+# CS base set to startup memory address;
+# CS limit set to 64KB;
+# CPL and IP set to 0.
+#
+# Bootothers (in main.c) starts each non-boot CPU in turn.
+# It puts the correct %esp in start-4,
+# and the place to jump to in start-8.
+#
+# This code is identical to bootasm.S except:
+# - it does not need to enable A20
+# - it uses the address at start-4 for the %esp
+# - it jumps to the address at start-8 instead of calling bootmain
+
+.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
+.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
+.set CR0_PE_ON, 0x1 # protected mode enable flag
+
+.globl start
+start:
+ .code16 # Assemble for 16-bit mode
+ cli # Disable interrupts
+ cld # String operations increment
+
+ # Set up the important data segment registers (DS, ES, SS).
+ xorw %ax,%ax # Segment number zero
+ movw %ax,%ds # -> Data Segment
+ movw %ax,%es # -> Extra Segment
+ movw %ax,%ss # -> Stack Segment
+
+ # Switch from real to protected mode, using a bootstrap GDT
+ # and segment translation that makes virtual addresses
+ # identical to their physical addresses, so that the
+ # effective memory map does not change during the switch.
+ lgdt gdtdesc
+ movl %cr0, %eax
+ orl $CR0_PE_ON, %eax
+ movl %eax, %cr0
+
+ # Jump to next instruction, but in 32-bit code segment.
+ # Switches processor into 32-bit mode.
+ ljmp $PROT_MODE_CSEG, $protcseg
+
+ .code32 # Assemble for 32-bit mode
+protcseg:
+ # Set up the protected-mode data segment registers
+ movw $PROT_MODE_DSEG, %ax # Our data segment selector
+ movw %ax, %ds # -> DS: Data Segment
+ movw %ax, %es # -> ES: Extra Segment
+ movw %ax, %fs # -> FS
+ movw %ax, %gs # -> GS
+ movw %ax, %ss # -> SS: Stack Segment
+
+ movl start-4, %esp
+ movl start-8, %eax
+ jmp *%eax
+
+# 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 0x17 # sizeof(gdt) - 1
+ .long gdt # address gdt
13 buf.h
@@ -0,0 +1,13 @@
+struct buf {
+ int flags;
+ uint dev;
+ uint sector;
+ struct buf *prev; // LRU cache list
+ struct buf *next;
+ struct buf *qnext; // disk queue
+ uchar data[512];
+};
+#define B_BUSY 0x1 // buffer is locked by some process
+#define B_VALID 0x2 // buffer has been read from disk
+#define B_DIRTY 0x4 // buffer needs to be written to disk
+
39 cat.c
@@ -0,0 +1,39 @@
+#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)
+ write(1, buf, n);
+ 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();
+}
305 console.c
@@ -0,0 +1,305 @@
+// Console input and output.
+// Input is from the keyboard only.
+// Output is written to the screen and the printer port.
+
+#include "types.h"
+#include "defs.h"
+#include "param.h"
+#include "traps.h"
+#include "spinlock.h"
+#include "dev.h"
+#include "mmu.h"
+#include "proc.h"
+#include "x86.h"
+
+#define CRTPORT 0x3d4
+#define LPTPORT 0x378
+#define BACKSPACE 0x100
+
+static ushort *crt = (ushort*)0xb8000; // CGA memory
+
+static struct spinlock console_lock;
+int panicked = 0;
+int use_console_lock = 0;
+
+// Copy console output to parallel port, which you can tell
+// .bochsrc to copy to the stdout:
+// parport1: enabled=1, file="/dev/stdout"
+static void
+lpt_putc(int c)
+{
+ int i;
+
+ for(i = 0; !(inb(LPTPORT+1) & 0x80) && i < 12800; i++)
+ ;
+ if(c == BACKSPACE)
+ c = '\b';
+ outb(LPTPORT+0, c);
+ outb(LPTPORT+2, 0x08|0x04|0x01);
+ outb(LPTPORT+2, 0x08);
+}
+
+static void
+cga_putc(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)
+ crt[--pos] = ' ' | 0x0700;
+ } else
+ crt[pos++] = (c&0xff) | 0x0700; // black on white
+
+ 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
+cons_putc(int c)
+{
+ if(panicked){
+ cli();
+ for(;;)
+ ;
+ }
+
+ lpt_putc(c);
+ cga_putc(c);
+}
+
+void
+printint(int xx, int base, int sgn)
+{
+ static char digits[] = "0123456789ABCDEF";
+ char buf[16];
+ int i = 0, neg = 0;
+ uint x;
+
+ if(sgn && xx < 0){
+ neg = 1;
+ x = 0 - xx;
+ } else {
+ x = xx;
+ }
+
+ do{
+ buf[i++] = digits[x % base];
+ }while((x /= base) != 0);
+ if(neg)
+ buf[i++] = '-';
+
+ while(--i >= 0)
+ cons_putc(buf[i]);
+}
+
+// Print to the console. only understands %d, %x, %p, %s.
+void
+cprintf(char *fmt, ...)
+{
+ int i, c, state, locking;
+ uint *argp;
+ char *s;
+
+ locking = use_console_lock;
+ if(locking)
+ acquire(&console_lock);
+
+ argp = (uint*)(void*)&fmt + 1;
+ state = 0;
+ for(i = 0; fmt[i]; i++){
+ c = fmt[i] & 0xff;
+ switch(state){
+ case 0:
+ if(c == '%')
+ state = '%';
+ else
+ cons_putc(c);
+ break;
+
+ case '%':
+ switch(c){
+ case 'd':
+ printint(*argp++, 10, 1);
+ break;
+ case 'x':
+ case 'p':
+ printint(*argp++, 16, 0);
+ break;
+ case 's':
+ s = (char*)*argp++;
+ if(s == 0)
+ s = "(null)";
+ for(; *s; s++)
+ cons_putc(*s);
+ break;
+ case '%':
+ cons_putc('%');
+ break;
+ default:
+ // Print unknown % sequence to draw attention.
+ cons_putc('%');
+ cons_putc(c);
+ break;
+ }
+ state = 0;
+ break;
+ }
+ }
+
+ if(locking)
+ release(&console_lock);
+}
+
+int
+console_write(struct inode *ip, char *buf, int n)
+{
+ int i;
+
+ iunlock(ip);
+ acquire(&console_lock);
+ for(i = 0; i < n; i++)
+ cons_putc(buf[i] & 0xff);
+ release(&console_lock);
+ ilock(ip);
+
+ return n;
+}
+
+#define INPUT_BUF 128
+struct {
+ struct spinlock lock;
+ char buf[INPUT_BUF];
+ int r; // Read index
+ int w; // Write index
+ int e; // Edit index
+} input;
+
+#define C(x) ((x)-'@') // Control-x
+
+void
+console_intr(int (*getc)(void))
+{
+ int c;
+
+ acquire(&input.lock);
+ while((c = getc()) >= 0){
+ switch(c){
+ case C('P'): // Process listing.
+ procdump();
+ break;
+ case C('U'): // Kill line.
+ while(input.e > input.w &&
+ input.buf[(input.e-1) % INPUT_BUF] != '\n'){
+ input.e--;
+ cons_putc(BACKSPACE);
+ }
+ break;
+ case C('H'): // Backspace
+ if(input.e > input.w){
+ input.e--;
+ cons_putc(BACKSPACE);
+ }
+ break;
+ default:
+ if(c != 0 && input.e < input.r+INPUT_BUF){
+ input.buf[input.e++ % INPUT_BUF] = c;
+ cons_putc(c);
+ if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
+ input.w = input.e;
+ wakeup(&input.r);
+ }
+ }
+ break;
+ }
+ }
+ release(&input.lock);
+}
+
+int
+console_read(struct inode *ip, char *dst, int n)
+{
+ uint target;
+ int c;
+
+ iunlock(ip);
+ target = n;
+ acquire(&input.lock);
+ while(n > 0){
+ while(input.r == input.w){
+ if(cp->killed){
+ release(&input.lock);
+ ilock(ip);
+ return -1;
+ }
+ sleep(&input.r, &input.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(&input.lock);
+ ilock(ip);
+
+ return target - n;
+}
+
+void
+console_init(void)
+{
+ initlock(&console_lock, "console");
+ initlock(&input.lock, "console input");
+
+ devsw[CONSOLE].write = console_write;
+ devsw[CONSOLE].read = console_read;
+ use_console_lock = 1;
+
+ pic_enable(IRQ_KBD);
+ ioapic_enable(IRQ_KBD, 0);
+}
+
+void
+panic(char *s)
+{
+ int i;
+ uint pcs[10];
+
+ __asm __volatile("cli");
+ use_console_lock = 0;
+ cprintf("cpu%d: panic: ", cpu());
+ cprintf(s, 0);
+ cprintf("\n", 0);
+ getcallerpcs(&s, pcs);
+ for(i=0; i<10; i++)
+ cprintf(" %p", pcs[i]);
+ panicked = 1; // freeze other CPU
+ for(;;)
+ ;
+}
+
149 defs.h
@@ -0,0 +1,149 @@
+struct buf;
+struct context;
+struct file;
+struct inode;
+struct pipe;
+struct proc;
+struct spinlock;
+struct stat;
+
+// bio.c
+void binit(void);
+struct buf* bread(uint, uint);
+void brelse(struct buf*);
+void bwrite(struct buf*);
+
+// console.c
+void console_init(void);
+void cprintf(char*, ...);
+void console_intr(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
+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(void);
+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 ide_init(void);
+void ide_intr(void);
+void ide_rw(struct buf *);
+
+// ioapic.c
+void ioapic_enable(int irq, int cpu);
+extern uchar ioapic_id;
+void ioapic_init(void);
+
+// kalloc.c
+char* kalloc(int);
+void kfree(char*, int);
+void kinit(void);
+
+// kbd.c
+void kbd_intr(void);
+
+// lapic.c
+int cpu(void);
+extern volatile uint* lapic;
+void lapic_eoi(void);
+void lapic_init(int);
+void lapic_startap(uchar, uint);
+
+// mp.c
+extern int ismp;
+int mp_bcpu(void);
+void mp_init(void);
+void mp_startthem(void);
+
+// picirq.c
+void pic_enable(int);
+void pic_init(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);
+
+// proc.c
+struct proc* copyproc(struct proc*);
+struct proc* curproc(void);
+void exit(void);
+int growproc(int);
+int kill(int);
+void pinit(void);
+void procdump(void);
+void scheduler(void) __attribute__((noreturn));
+void setupsegs(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 popcli();
+
+// 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(struct proc*, uint, int*);
+int fetchstr(struct proc*, uint, char**);
+void syscall(void);
+
+// timer.c
+void timer_init(void);
+
+// trap.c
+void idtinit(void);
+extern int ticks;
+void tvinit(void);
+extern struct spinlock tickslock;
+
+// number of elements in fixed-size array
+#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
+
8 dev.h
@@ -0,0 +1,8 @@
+struct devsw {
+ int (*read)(struct inode*, char*, int);
+ int (*write)(struct inode*, char*, int);
+};
+
+extern struct devsw devsw[];
+
+#define CONSOLE 1
738 dot-bochsrc
@@ -0,0 +1,738 @@
+# 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, reset_on_triple_fault=0
+
+#=======================================================================
+# 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
+#=======================================================================
13 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();
+}
60 elf.h
@@ -0,0 +1,60 @@
+// 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 offset;
+ uint va;
+ uint pa;
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// Blank page.
115 exec.c
@@ -0,0 +1,115 @@
+#include "types.h"
+#include "param.h"
+#include "mmu.h"
+#include "proc.h"
+#include "defs.h"
+#include "x86.h"
+#include "elf.h"
+
+int
+exec(char *path, char **argv)
+{
+ char *mem, *s, *last;
+ int i, argc, arglen, len, off;
+ uint sz, sp, argp;
+ struct elfhdr elf;
+ struct inode *ip;
+ struct proghdr ph;
+
+ if((ip = namei(path)) == 0)
+ return -1;
+ ilock(ip);
+
+ // Compute memory size of new process.
+ mem = 0;
+ sz = 0;
+
+ // Program segments.
+ if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))
+ goto bad;
+ if(elf.magic != ELF_MAGIC)
+ goto bad;
+ for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
+ if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
+ goto bad;
+ if(ph.type != ELF_PROG_LOAD)
+ continue;
+ if(ph.memsz < ph.filesz)
+ goto bad;
+ sz += ph.memsz;
+ }
+
+ // Arguments.
+ arglen = 0;
+ for(argc=0; argv[argc]; argc++)
+ arglen += strlen(argv[argc]) + 1;
+ arglen = (arglen+3) & ~3;
+ sz += arglen + 4*(argc+1);
+
+ // Stack.
+ sz += PAGE;
+
+ // Allocate program memory.
+ sz = (sz+PAGE-1) & ~(PAGE-1);
+ mem = kalloc(sz);
+ if(mem == 0)
+ goto bad;
+ memset(mem, 0, sz);
+
+ // Load program into memory.
+ for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
+ if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
+ goto bad;
+ if(ph.type != ELF_PROG_LOAD)
+ continue;
+ if(ph.va + ph.memsz > sz)
+ goto bad;
+ if(readi(ip, mem + ph.va, ph.offset, ph.filesz) != ph.filesz)
+ goto bad;
+ memset(mem + ph.va + ph.filesz, 0, ph.memsz - ph.filesz);
+ }
+ iunlockput(ip);
+
+ // Initialize stack.
+ sp = sz;
+ argp = sz - arglen - 4*(argc+1);
+
+ // Copy argv strings and pointers to stack.
+ *(uint*)(mem+argp + 4*argc) = 0; // argv[argc]
+ for(i=argc-1; i>=0; i--){
+ len = strlen(argv[i]) + 1;
+ sp -= len;
+ memmove(mem+sp, argv[i], len);
+ *(uint*)(mem+argp + 4*i) = sp; // argv[i]
+ }
+
+ // Stack frame for main(argc, argv), below arguments.
+ sp = argp;
+ sp -= 4;
+ *(uint*)(mem+sp) = argp;
+ sp -= 4;
+ *(uint*)(mem+sp) = argc;
+ sp -= 4;
+ *(uint*)(mem+sp) = 0xffffffff; // fake return pc
+
+ // Save program name for debugging.
+ for(last=s=path; *s; s++)
+ if(*s == '/')
+ last = s+1;
+ safestrcpy(cp->name, last, sizeof(cp->name));
+
+ // Commit to the new image.
+ kfree(cp->mem, cp->sz);
+ cp->mem = mem;
+ cp->sz = sz;
+ cp->tf->eip = elf.entry; // main
+ cp->tf->esp = sp;
+ setupsegs(cp);
+ return 0;
+
+ bad:
+ if(mem)
+ kfree(mem, sz);
+ iunlockput(ip);
+ return -1;
+}
4 fcntl.h
@@ -0,0 +1,4 @@
+#define O_RDONLY 0x000
+#define O_WRONLY 0x001
+#define O_RDWR 0x002
+#define O_CREATE 0x200
127 file.c
@@ -0,0 +1,127 @@
+#include "types.h"
+#include "defs.h"
+#include "param.h"
+#include "file.h"
+#include "spinlock.h"
+#include "dev.h"
+
+struct devsw devsw[NDEV];
+struct spinlock file_table_lock;
+struct file file[NFILE];
+
+void
+fileinit(void)
+{
+ initlock(&file_table_lock, "file_table");
+}
+
+// Allocate a file structure.
+struct file*
+filealloc(void)
+{
+ int i;
+
+ acquire(&file_table_lock);
+ for(i = 0; i < NFILE; i++){
+ if(file[i].type == FD_CLOSED){
+ file[i].type = FD_NONE;
+ file[i].ref = 1;
+ release(&file_table_lock);
+ return file + i;
+ }
+ }
+ release(&file_table_lock);
+ return 0;
+}
+
+// Increment ref count for file f.
+struct file*
+filedup(struct file *f)
+{
+ acquire(&file_table_lock);
+ if(f->ref < 1 || f->type == FD_CLOSED)
+ panic("filedup");
+ f->ref++;
+ release(&file_table_lock);
+ return f;
+}
+
+// Close file f. (Decrement ref count, close when reaches 0.)
+void
+fileclose(struct file *f)
+{
+ struct file ff;
+
+ acquire(&file_table_lock);
+ if(f->ref < 1 || f->type == FD_CLOSED)
+ panic("fileclose");
+ if(--f->ref > 0){
+ release(&file_table_lock);
+ return;
+ }
+ ff = *f;
+ f->ref = 0;
+ f->type = FD_CLOSED;
+ release(&file_table_lock);
+
+ if(ff.type == FD_PIPE)
+ pipeclose(ff.pipe, ff.writable);
+ else if(ff.type == FD_INODE)
+ iput(ff.ip);
+ else
+ panic("fileclose");
+}
+
+// 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. Addr is kernel address.
+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");
+}
+
+// Write to file f. Addr is kernel address.
+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){
+ ilock(f->ip);
+ if((r = writei(f->ip, addr, f->off, n)) > 0)
+ f->off += r;
+ iunlock(f->ip);
+ return r;
+ }
+ panic("filewrite");
+}
+
9 file.h
@@ -0,0 +1,9 @@
+struct file {
+ enum { FD_CLOSED, FD_NONE, FD_PIPE, FD_INODE } type;
+ int ref; // reference count
+ char readable;
+ char writable;
+ struct pipe *pipe;
+ struct inode *ip;
+ uint off;
+};
54 forktest.c
@@ -0,0 +1,54 @@
+// 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"
+
+void
+printf(int fd, char *s, ...)
+{
+ write(fd, s, strlen(s));
+}
+
+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");
+}
+
+int
+main(void)
+{
+ forktest();
+ exit();
+}
619 fs.c
@@ -0,0 +1,619 @@
+// File system implementation. Four layers:
+// + Blocks: allocator for raw disk blocks.
+// + 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.
+//
+// Disk layout is: superblock, inodes, block in-use bitmap, data blocks.
+//
+// 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 "buf.h"
+#include "fs.h"
+#include "fsvar.h"
+#include "dev.h"
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+static void itrunc(struct inode*);
+
+// Read the super block.
+static 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);
+ bwrite(bp);
+ brelse(bp);
+}
+
+// Blocks.
+
+// Allocate a disk block.
+static uint
+balloc(uint dev)
+{
+ int b, bi, m;
+ struct buf *bp;
+ struct superblock sb;
+
+ bp = 0;
+ readsb(dev, &sb);
+ for(b = 0; b < sb.size; b += BPB){
+ bp = bread(dev, BBLOCK(b, sb.ninodes));
+ for(bi = 0; bi < BPB; bi++){
+ m = 1 << (bi % 8);
+ if((bp->data[bi/8] & m) == 0){ // Is block free?
+ bp->data[bi/8] |= m; // Mark block in use on disk.
+ bwrite(bp);
+ brelse(bp);
+ return b + bi;
+ }
+ }
+ brelse(bp);
+ }
+ panic("balloc: out of blocks");
+}
+
+// Free a disk block.
+static void
+bfree(int dev, uint b)
+{
+ struct buf *bp;
+ struct superblock sb;
+ int bi, m;
+
+ bzero(dev, b);
+
+ readsb(dev, &sb);
+ bp = bread(dev, BBLOCK(b, sb.ninodes));
+ bi = b % BPB;
+ m = 1 << (bi % 8);
+ if((bp->data[bi/8] & m) == 0)
+ panic("freeing free block");
+ bp->data[bi/8] &= ~m; // Mark block free on disk.
+ bwrite(bp);
+ brelse(bp);
+}
+
+// Inodes.
+//
+// An inode is a single, unnamed file in the file system.
+// The inode disk structure holds metadata (the type, device numbers,
+// and data size) along with a list of blocks where the associated
+// data can be found.
+//
+// The inodes are laid out sequentially on disk immediately after
+// the superblock. The kernel keeps a cache of the in-use
+// on-disk structures to provide a place for synchronizing access
+// to inodes shared between multiple processes.
+//
+// ip->ref counts the number of pointer references to this cached
+// inode; references are typically kept in struct file and in cp->cwd.
+// When ip->ref falls to zero, the inode is no longer cached.
+// It is an error to use an inode without holding a reference to it.
+//
+// Processes are only allowed to read and write inode
+// metadata and contents when holding the inode's lock,
+// represented by the I_BUSY flag in the in-memory copy.
+// Because inode locks are held during disk accesses,
+// they are implemented using a flag rather than with