Merge commit 'origin/master' into page

This commit is contained in:
Frans Kaashoek 2011-07-27 20:49:45 -04:00
commit dccb915282
61 changed files with 1606 additions and 1616 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
*~
_* _*
*.o *.o
*.d *.d

View file

@ -26,19 +26,53 @@ OBJS = \
uart.o\ uart.o\
vectors.o\ vectors.o\
vm.o\ vm.o\
log.o\
# Cross-compiling (e.g., on Mac OS X) # Cross-compiling (e.g., on Mac OS X)
TOOLPREFIX = i386-jos-elf- #TOOLPREFIX = i386-jos-elf-
# Using native tools (e.g., on X86 Linux) # Using native tools (e.g., on X86 Linux)
#TOOLPREFIX = #TOOLPREFIX =
# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# If the makefile can't find QEMU, specify its path here
#QEMU =
# Try to infer the correct QEMU
ifndef QEMU
QEMU = $(shell if which qemu > /dev/null; \
then echo qemu; exit; \
else \
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
echo "***" 1>&2; \
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
echo "***" 1>&2; exit 1)
endif
CC = $(TOOLPREFIX)gcc CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASFLAGS = -m32 -gdwarf-2 ASFLAGS = -m32 -gdwarf-2
# FreeBSD ld wants ``elf_i386_fbsd'' # FreeBSD ld wants ``elf_i386_fbsd''
@ -49,6 +83,11 @@ xv6.img: bootblock kernel fs.img
dd if=bootblock of=xv6.img conv=notrunc dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc
xv6memfs.img: bootblock kernelmemfs
dd if=/dev/zero of=xv6memfs.img count=10000
dd if=bootblock of=xv6memfs.img conv=notrunc
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
bootblock: bootasm.S bootmain.c bootblock: bootasm.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
@ -69,11 +108,23 @@ initcode: initcode.S
$(OBJCOPY) -S -O binary initcode.out initcode $(OBJCOPY) -S -O binary initcode.out initcode
$(OBJDUMP) -S initcode.o > initcode.asm $(OBJDUMP) -S initcode.o > initcode.asm
kernel: $(OBJS) bootother initcode kernel: $(OBJS) multiboot.o data.o bootother initcode
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
$(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
# kernelmemfs is a copy of kernel that maintains the
# disk image in memory instead of writing to a disk.
# This is not so useful for testing persistent storage or
# exploring disk buffering implementations, but it is
# great for testing the kernel on real hardware without
# needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) multiboot.o data.o bootother initcode fs.img
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o data.o $(MEMFSOBJS) -b binary initcode bootother fs.img
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
tags: $(OBJS) bootother.S _init tags: $(OBJS) bootother.S _init
etags *.S *.c etags *.S *.c
@ -94,7 +145,7 @@ _forktest: forktest.o $(ULIB)
$(OBJDUMP) -S _forktest > forktest.asm $(OBJDUMP) -S _forktest > forktest.asm
mkfs: mkfs.c fs.h mkfs: mkfs.c fs.h
gcc -Wall -o mkfs mkfs.c gcc -m32 -Werror -Wall -o mkfs mkfs.c
UPROGS=\ UPROGS=\
_cat\ _cat\
@ -126,7 +177,7 @@ clean:
# make a printout # make a printout
FILES = $(shell grep -v '^\#' runoff.list) FILES = $(shell grep -v '^\#' runoff.list)
PRINT = runoff.list $(FILES) PRINT = runoff.list runoff.spec $(FILES)
xv6.pdf: $(PRINT) xv6.pdf: $(PRINT)
./runoff ./runoff
@ -143,27 +194,33 @@ bochs : fs.img xv6.img
# try to generate a unique GDB port # try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000) GDBPORT = $(shell expr `id -u` % 5000 + 25000)
# QEMU's gdb stub command line changed in 0.11 # QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if qemu -help | grep -q '^-gdb'; \ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \ then echo "-gdb tcp::$(GDBPORT)"; \
else echo "-s -p $(GDBPORT)"; fi) else echo "-s -p $(GDBPORT)"; fi)
QEMUOPTS = -smp 2 -hdb fs.img xv6.img ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS)
qemu: fs.img xv6.img qemu: fs.img xv6.img
qemu -serial mon:stdio $(QEMUOPTS) $(QEMU) -serial mon:stdio $(QEMUOPTS)
qemu-memfs: xv6memfs.img
$(QEMU) xv6memfs.img -smp $(CPUS)
qemu-nox: fs.img xv6.img qemu-nox: fs.img xv6.img
qemu -nographic $(QEMUOPTS) $(QEMU) -nographic $(QEMUOPTS)
.gdbinit: .gdbinit.tmpl .gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
qemu-gdb: fs.img xv6.img .gdbinit qemu-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2 @echo "*** Now run 'gdb'." 1>&2
qemu -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
qemu-nox-gdb: fs.img xv6.img .gdbinit qemu-nox-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2 @echo "*** Now run 'gdb'." 1>&2
qemu -nographic $(QEMUOPTS) -S $(QEMUGDB) $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
# CUT HERE # CUT HERE
# prepare dist for students # prepare dist for students
@ -195,14 +252,16 @@ dist-test:
rm -rf dist-test rm -rf dist-test
mkdir dist-test mkdir dist-test
cp dist/* dist-test cp dist/* dist-test
cd dist-test; ../m print cd dist-test; $(MAKE) print
cd dist-test; ../m bochs || true cd dist-test; $(MAKE) bochs || true
cd dist-test; ../m qemu cd dist-test; $(MAKE) qemu
# update this rule (change rev1) when it is time to # update this rule (change rev#) when it is time to
# make a new revision. # make a new revision.
tar: tar:
rm -rf /tmp/xv6 rm -rf /tmp/xv6
mkdir -p /tmp/xv6 mkdir -p /tmp/xv6
cp dist/* dist/.gdbinit.tmpl /tmp/xv6 cp dist/* dist/.gdbinit.tmpl /tmp/xv6
(cd /tmp; tar cf - xv6) | gzip >xv6-rev3.tar.gz (cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz
.PHONY: dist-test dist

13
README
View file

@ -19,6 +19,11 @@ The following people made contributions:
Russ Cox (context switching, locking) Russ Cox (context switching, locking)
Cliff Frey (MP) Cliff Frey (MP)
Xiao Yu (MP) Xiao Yu (MP)
Nickolai Zeldovich
Austin Clements
In addition, we are grateful for the patches contributed by Greg
Price, Yandong Mao, and Hitoshi Mitake.
The code in the files that constitute xv6 is The code in the files that constitute xv6 is
Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox.
@ -39,9 +44,7 @@ Then run "make TOOLPREFIX=i386-jos-elf-".
To run xv6, you can use Bochs or QEMU, both PC simulators. To run xv6, you can use Bochs or QEMU, both PC simulators.
Bochs makes debugging easier, but QEMU is much faster. 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 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 To run in QEMU, run "make qemu".
standard output.
To create a typeset version of the code, run "make xv6.pdf". To create a typeset version of the code, run "make xv6.pdf". This
This requires the "mpage" text formatting utility. requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/.
See http://www.mesa.nl/pub/mpage/.

View file

@ -13,7 +13,7 @@
.code16 # Assemble for 16-bit mode .code16 # Assemble for 16-bit mode
.globl start .globl start
start: start:
cli # Disable interrupts cli # BIOS enabled interrupts; disable
# Set up the important data segment registers (DS, ES, SS). # Set up the important data segment registers (DS, ES, SS).
xorw %ax,%ax # Segment number zero xorw %ax,%ax # Segment number zero
@ -21,10 +21,8 @@ start:
movw %ax,%es # -> Extra Segment movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment movw %ax,%ss # -> Stack Segment
# Enable A20: # Physical address line A20 is tied to zero so that the first PCs
# For backwards compatibility with the earliest PCs, physical # with 2 MB would run software that assumed 1 MB. Undo that.
# address line 20 is tied low, so that addresses higher than
# 1MB wrap around to zero by default. This code undoes this.
seta20.1: seta20.1:
inb $0x64,%al # Wait for not busy inb $0x64,%al # Wait for not busy
testb $0x2,%al testb $0x2,%al
@ -41,23 +39,21 @@ seta20.2:
movb $0xdf,%al # 0xdf -> port 0x60 movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60 outb %al,$0x60
//PAGEBREAK! # Switch from real to protected mode. Use a bootstrap GDT that makes
# Switch from real to protected mode, using a bootstrap GDT # virtual addresses map dierctly to physical addresses so that the
# and segment translation that makes virtual addresses # effective memory map doesn't change during the transition.
# identical to physical addresses, so that the
# effective memory map does not change during the switch.
lgdt gdtdesc lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
# This ljmp is how you load the CS (Code Segment) register. //PAGEBREAK!
# SEG_ASM produces segment descriptors with the 32-bit mode # Complete transition to 32-bit protected mode by using long jmp
# flag set (the D flag), so addresses and word operands will # to reload %cs and %eip. The segment registers are set up with no
# default to 32 bits after this jump. # translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
.code32 # Assemble for 32-bit mode .code32 # Tell assembler to generate 32-bit code now.
start32: start32:
# Set up the protected-mode data segment registers # Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector movw $(SEG_KDATA<<3), %ax # Our data segment selector

View file

@ -33,8 +33,8 @@ bootmain(void)
// Load each program segment (ignores ph flags). // Load each program segment (ignores ph flags).
ph = (struct proghdr*)((uchar*)elf + elf->phoff); ph = (struct proghdr*)((uchar*)elf + elf->phoff);
eph = ph + elf->phnum; eph = ph + elf->phnum;
for(; ph < eph; ph++) { for(; ph < eph; ph++){
va = (uchar*)(ph->va & 0xFFFFFF); va = (uchar*)ph->va;
readseg(va, ph->filesz, ph->offset); readseg(va, ph->filesz, ph->offset);
if(ph->memsz > ph->filesz) if(ph->memsz > ph->filesz)
stosb(va + ph->filesz, 0, ph->memsz - ph->filesz); stosb(va + ph->filesz, 0, ph->memsz - ph->filesz);
@ -42,7 +42,7 @@ bootmain(void)
// Call the entry point from the ELF header. // Call the entry point from the ELF header.
// Does not return! // Does not return!
entry = (void(*)(void))(elf->entry & 0xFFFFFF); entry = (void(*)(void))(elf->entry);
entry(); entry();
} }

View file

@ -9,80 +9,69 @@
# Because this code sets DS to zero, it must sit # Because this code sets DS to zero, it must sit
# at an address in the low 2^16 bytes. # at an address in the low 2^16 bytes.
# #
# Bootothers (in main.c) sends the STARTUPs, one at a time. # Bootothers (in main.c) sends the STARTUPs one at a time.
# It puts this code (start) at 0x7000. # It copies this code (start) at 0x7000.
# It puts the correct %esp in start-4, # It puts the address of a newly allocated per-core stack in start-4,
# and the place to jump to in start-8. # and the address of the place to jump to (mpmain) in start-8.
# #
# This code is identical to bootasm.S except: # This code is identical to bootasm.S except:
# - it does not need to enable A20 # - it does not need to enable A20
# - it uses the address at start-4 for the %esp # - it uses the address at start-4 for the %esp
# - it jumps to the address at start-8 instead of calling bootmain # - it jumps to the address at start-8 instead of calling bootmain
#define SEG_KCODE 1 // kernel code #define SEG_KCODE 1
#define SEG_KDATA 2 // kernel data+stack #define SEG_KDATA 2
#define CR0_PE 1 // protected mode enable bit #define CR0_PE 1
.code16 # Assemble for 16-bit mode .code16
.globl start .globl start
start: start:
cli # Disable interrupts cli
# Set up the important data segment registers (DS, ES, SS). xorw %ax,%ax
xorw %ax,%ax # Segment number zero movw %ax,%ds
movw %ax,%ds # -> Data Segment movw %ax,%es
movw %ax,%es # -> Extra Segment movw %ax,%ss
movw %ax,%ss # -> Stack Segment
//PAGEBREAK!
# 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 lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
# This ljmp is how you load the CS (Code Segment) register. //PAGEBREAK!
# SEG_ASM produces segment descriptors with the 32-bit mode
# flag set (the D flag), so addresses and word operands will
# default to 32 bits after this jump.
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
.code32 # Assemble for 32-bit mode .code32
start32: start32:
# Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax
movw $(SEG_KDATA<<3), %ax # Our data segment selector movw %ax, %ds
movw %ax, %ds # -> DS: Data Segment movw %ax, %es
movw %ax, %es # -> ES: Extra Segment movw %ax, %ss
movw %ax, %ss # -> SS: Stack Segment movw $0, %ax
movw $0, %ax # Zero segments not ready for use movw %ax, %fs
movw %ax, %fs # -> FS movw %ax, %gs
movw %ax, %gs # -> GS
# Set up the stack pointer and call into C. # switch to the stack allocated by bootothers()
movl start-4, %esp movl start-4, %esp
# call mpmain()
call *(start-8) call *(start-8)
# If the call returns (it shouldn't), trigger a Bochs movw $0x8a00, %ax
# breakpoint if running under Bochs, then loop.
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
movw %ax, %dx movw %ax, %dx
outw %ax, %dx outw %ax, %dx
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 movw $0x8ae0, %ax
outw %ax, %dx outw %ax, %dx
spin: spin:
jmp spin jmp spin
# Bootstrap GDT .p2align 2
.p2align 2 # force 4 byte alignment
gdt: gdt:
SEG_NULLASM # null seg SEG_NULLASM
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg SEG_ASM(STA_W, 0x0, 0xffffffff)
gdtdesc: gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 .word (gdtdesc - gdt - 1)
.long gdt # address gdt .long gdt

View file

@ -18,28 +18,29 @@ static void consputc(int);
static int panicked = 0; static int panicked = 0;
static struct { static struct {
struct spinlock lock; struct spinlock lock;
int locking; int locking;
} cons; } cons;
static void static void
printint(int xx, int base, int sgn) printint(int xx, int base, int sign)
{ {
static char digits[] = "0123456789abcdef"; static char digits[] = "0123456789abcdef";
char buf[16]; char buf[16];
int i = 0, neg = 0; int i;
uint x; uint x;
if(sgn && xx < 0){ if(sign && (sign = xx < 0))
neg = 1;
x = -xx; x = -xx;
} else else
x = xx; x = xx;
i = 0;
do{ do{
buf[i++] = digits[x % base]; buf[i++] = digits[x % base];
}while((x /= base) != 0); }while((x /= base) != 0);
if(neg)
if(sign)
buf[i++] = '-'; buf[i++] = '-';
while(--i >= 0) while(--i >= 0)
@ -136,8 +137,7 @@ cgaputc(int c)
if(c == '\n') if(c == '\n')
pos += 80 - pos%80; pos += 80 - pos%80;
else if(c == BACKSPACE){ else if(c == BACKSPACE){
if(pos > 0) if(pos > 0) --pos;
crt[--pos] = ' ' | 0x0700;
} else } else
crt[pos++] = (c&0xff) | 0x0700; // black on white crt[pos++] = (c&0xff) | 0x0700; // black on white
@ -163,16 +163,13 @@ consputc(int c)
; ;
} }
if (c == BACKSPACE) { if(c == BACKSPACE){
uartputc('\b'); uartputc('\b'); uartputc(' '); uartputc('\b');
uartputc(' ');
uartputc('\b');
} else } else
uartputc(c); uartputc(c);
cgaputc(c); cgaputc(c);
} }
//PAGEBREAK: 50
#define INPUT_BUF 128 #define INPUT_BUF 128
struct { struct {
struct spinlock lock; struct spinlock lock;
@ -202,8 +199,7 @@ consoleintr(int (*getc)(void))
consputc(BACKSPACE); consputc(BACKSPACE);
} }
break; break;
case C('H'): // Backspace case C('H'): case '\x7f': // Backspace
case '\x7f':
if(input.e != input.w){ if(input.e != input.w){
input.e--; input.e--;
consputc(BACKSPACE); consputc(BACKSPACE);
@ -211,9 +207,7 @@ consoleintr(int (*getc)(void))
break; break;
default: default:
if(c != 0 && input.e-input.r < INPUT_BUF){ if(c != 0 && input.e-input.r < INPUT_BUF){
// The serial port produces 0x13, not 0x10 c = (c == '\r') ? '\n' : c;
if(c == '\r')
c = '\n';
input.buf[input.e++ % INPUT_BUF] = c; input.buf[input.e++ % INPUT_BUF] = c;
consputc(c); consputc(c);
if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){

26
data.S Normal file
View file

@ -0,0 +1,26 @@
// The kernel layout is:
//
// text
// rodata
// data
// bss
//
// 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.
//
// To get the address of the data section, we define a symbol
// named data and make sure this is the first object passed to
// the linker, so that it will be the first symbol in the data section.
//
// Alternative approaches would be to parse our own ELF header
// or to write a linker script, but this is simplest.
.data
.align 4096
.globl data
data:
.word 1

31
defs.h
View file

@ -6,6 +6,7 @@ struct pipe;
struct proc; struct proc;
struct spinlock; struct spinlock;
struct stat; struct stat;
struct superblock;
// bio.c // bio.c
void binit(void); void binit(void);
@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*);
int filewrite(struct file*, char*, int n); int filewrite(struct file*, char*, int n);
// fs.c // fs.c
void readsb(int dev, struct superblock *sb);
int dirlink(struct inode*, char*, uint); int dirlink(struct inode*, char*, uint);
struct inode* dirlookup(struct inode*, char*, uint*); struct inode* dirlookup(struct inode*, char*, uint*);
struct inode* ialloc(uint, short); struct inode* ialloc(uint, short);
@ -62,7 +64,7 @@ void ioapicinit(void);
// kalloc.c // kalloc.c
char* kalloc(void); char* kalloc(void);
void kfree(char*); void kfree(char*);
void kinit(char*,uint); void kinit(void);
// kbd.c // kbd.c
void kbdintr(void); void kbdintr(void);
@ -75,6 +77,12 @@ void lapicinit(int);
void lapicstartap(uchar, uint); void lapicstartap(uchar, uint);
void microdelay(int); void microdelay(int);
// log.c
void initlog(void);
void log_write(struct buf*);
void begin_trans();
void commit_trans();
// mp.c // mp.c
extern int ismp; extern int ismp;
int mpbcpu(void); int mpbcpu(void);
@ -101,6 +109,7 @@ int kill(int);
void pinit(void); void pinit(void);
void procdump(void); void procdump(void);
void scheduler(void) __attribute__((noreturn)); void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep(void*, struct spinlock*); void sleep(void*, struct spinlock*);
void userinit(void); void userinit(void);
int wait(void); int wait(void);
@ -116,8 +125,8 @@ void getcallerpcs(void*, uint*);
int holding(struct spinlock*); int holding(struct spinlock*);
void initlock(struct spinlock*, char*); void initlock(struct spinlock*, char*);
void release(struct spinlock*); void release(struct spinlock*);
void pushcli(); void pushcli(void);
void popcli(); void popcli(void);
// string.c // string.c
int memcmp(const void*, const void*, uint); int memcmp(const void*, const void*, uint);
@ -151,20 +160,20 @@ void uartintr(void);
void uartputc(int); void uartputc(int);
// vm.c // vm.c
void pminit(void); void seginit(void);
void ksegment(void);
void kvmalloc(void); void kvmalloc(void);
void vmenable(void); void vmenable(void);
pde_t* setupkvm(void); pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*); char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, char*, uint); int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t *pgdir, char *addr, uint sz); int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*); void freevm(pde_t*);
void inituvm(pde_t*, char*, char*, uint); void inituvm(pde_t*, char*, uint);
int loaduvm(pde_t*, char*, struct inode *ip, uint, uint); int loaduvm(pde_t*, char*, struct inode*, uint, uint);
pde_t* copyuvm(pde_t*,uint); pde_t* copyuvm(pde_t*, uint);
void switchuvm(struct proc*); void switchuvm(struct proc*);
void switchkvm(); void switchkvm(void);
int copyout(pde_t*, uint, void*, uint);
// number of elements in fixed-size array // number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0])) #define NELEM(x) (sizeof(x)/sizeof((x)[0]))

75
exec.c
View file

@ -9,20 +9,18 @@
int int
exec(char *path, char **argv) exec(char *path, char **argv)
{ {
char *mem, *s, *last; char *s, *last;
int i, argc, arglen, len, off; int i, off;
uint sz, sp, spoffset, argp; uint argc, sz, sp, ustack[3+MAXARG+1];
struct elfhdr elf; struct elfhdr elf;
struct inode *ip; struct inode *ip;
struct proghdr ph; struct proghdr ph;
pde_t *pgdir, *oldpgdir; pde_t *pgdir, *oldpgdir;
pgdir = 0;
sz = 0;
if((ip = namei(path)) == 0) if((ip = namei(path)) == 0)
return -1; return -1;
ilock(ip); ilock(ip);
pgdir = 0;
// Check ELF header // Check ELF header
if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))
@ -30,10 +28,11 @@ exec(char *path, char **argv)
if(elf.magic != ELF_MAGIC) if(elf.magic != ELF_MAGIC)
goto bad; goto bad;
if (!(pgdir = setupkvm())) if((pgdir = setupkvm()) == 0)
goto bad; goto bad;
// Load program into memory. // Load program into memory.
sz = 0;
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad; goto bad;
@ -41,49 +40,39 @@ exec(char *path, char **argv)
continue; continue;
if(ph.memsz < ph.filesz) if(ph.memsz < ph.filesz)
goto bad; goto bad;
if (!allocuvm(pgdir, (char *)ph.va, ph.memsz)) if((sz = allocuvm(pgdir, sz, ph.va + ph.memsz)) == 0)
goto bad; goto bad;
if(ph.va + ph.memsz > sz) if(loaduvm(pgdir, (char*)ph.va, ip, ph.offset, ph.filesz) < 0)
sz = ph.va + ph.memsz;
if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz))
goto bad; goto bad;
} }
iunlockput(ip); iunlockput(ip);
ip = 0;
// Allocate and initialize stack at sz // Allocate a one-page stack at the next page boundary
sz = PGROUNDUP(sz); sz = PGROUNDUP(sz);
sz += PGSIZE; // leave an invalid page if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0)
if (!allocuvm(pgdir, (char *)sz, PGSIZE))
goto bad; goto bad;
mem = uva2ka(pgdir, (char *)sz);
spoffset = sz;
sz += PGSIZE;
arglen = 0;
for(argc=0; argv[argc]; argc++)
arglen += strlen(argv[argc]) + 1;
arglen = (arglen+3) & ~3;
// Push argument strings, prepare rest of stack in ustack.
sp = sz; sp = sz;
argp = sz - arglen - 4*(argc+1); for(argc = 0; argv[argc]; argc++) {
if(argc >= MAXARG)
// Copy argv strings and pointers to stack. goto bad;
*(uint*)(mem+argp-spoffset + 4*argc) = 0; // argv[argc] sp -= strlen(argv[argc]) + 1;
for(i=argc-1; i>=0; i--){ sp &= ~3;
len = strlen(argv[i]) + 1; if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
sp -= len; goto bad;
memmove(mem+sp-spoffset, argv[i], len); ustack[3+argc] = sp;
*(uint*)(mem+argp-spoffset + 4*i) = sp; // argv[i]
} }
ustack[3+argc] = 0;
// Stack frame for main(argc, argv), below arguments. ustack[0] = 0xffffffff; // fake return PC
sp = argp; ustack[1] = argc;
sp -= 4; ustack[2] = sp - (argc+1)*4; // argv pointer
*(uint*)(mem+sp-spoffset) = argp;
sp -= 4; sp -= (3+argc+1) * 4;
*(uint*)(mem+sp-spoffset) = argc; if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
sp -= 4; goto bad;
*(uint*)(mem+sp-spoffset) = 0xffffffff; // fake return pc
// Save program name for debugging. // Save program name for debugging.
for(last=s=path; *s; s++) for(last=s=path; *s; s++)
@ -97,15 +86,15 @@ exec(char *path, char **argv)
proc->sz = sz; proc->sz = sz;
proc->tf->eip = elf.entry; // main proc->tf->eip = elf.entry; // main
proc->tf->esp = sp; proc->tf->esp = sp;
switchuvm(proc); switchuvm(proc);
freevm(oldpgdir); freevm(oldpgdir);
return 0; return 0;
bad: bad:
if (pgdir) freevm(pgdir); if(pgdir)
iunlockput(ip); freevm(pgdir);
if(ip)
iunlockput(ip);
return -1; return -1;
} }

14
fs.c
View file

@ -25,7 +25,7 @@
static void itrunc(struct inode*); static void itrunc(struct inode*);
// Read the super block. // Read the super block.
static void void
readsb(int dev, struct superblock *sb) readsb(int dev, struct superblock *sb)
{ {
struct buf *bp; struct buf *bp;
@ -61,11 +61,11 @@ balloc(uint dev)
readsb(dev, &sb); readsb(dev, &sb);
for(b = 0; b < sb.size; b += BPB){ for(b = 0; b < sb.size; b += BPB){
bp = bread(dev, BBLOCK(b, sb.ninodes)); bp = bread(dev, BBLOCK(b, sb.ninodes));
for(bi = 0; bi < BPB; bi++){ for(bi = 0; bi < BPB && bi < (sb.size - b); bi++){
m = 1 << (bi % 8); m = 1 << (bi % 8);
if((bp->data[bi/8] & m) == 0){ // Is block free? if((bp->data[bi/8] & m) == 0){ // Is block free?
bp->data[bi/8] |= m; // Mark block in use on disk. bp->data[bi/8] |= m; // Mark block in use on disk.
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
return b + bi; return b + bi;
} }
@ -92,7 +92,7 @@ bfree(int dev, uint b)
if((bp->data[bi/8] & m) == 0) if((bp->data[bi/8] & m) == 0)
panic("freeing free block"); panic("freeing free block");
bp->data[bi/8] &= ~m; // Mark block free on disk. bp->data[bi/8] &= ~m; // Mark block free on disk.
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
@ -159,7 +159,7 @@ ialloc(uint dev, short type)
if(dip->type == 0){ // a free inode if(dip->type == 0){ // a free inode
memset(dip, 0, sizeof(*dip)); memset(dip, 0, sizeof(*dip));
dip->type = type; dip->type = type;
bwrite(bp); // mark it allocated on the disk log_write(bp); // mark it allocated on the disk
brelse(bp); brelse(bp);
return iget(dev, inum); return iget(dev, inum);
} }
@ -183,7 +183,7 @@ iupdate(struct inode *ip)
dip->nlink = ip->nlink; dip->nlink = ip->nlink;
dip->size = ip->size; dip->size = ip->size;
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn)
a = (uint*)bp->data; a = (uint*)bp->data;
if((addr = a[bn]) == 0){ if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev); a[bn] = addr = balloc(ip->dev);
bwrite(bp); log_write(bp);
} }
brelse(bp); brelse(bp);
return addr; return addr;

2
fs.h
View file

@ -13,6 +13,7 @@ struct superblock {
uint size; // Size of file system image (blocks) uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes. uint ninodes; // Number of inodes.
uint nlog; // Number of log blocks
}; };
#define NDIRECT 12 #define NDIRECT 12
@ -41,7 +42,6 @@ struct dinode {
// Block containing bit for block b // Block containing bit for block b
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)
// PAGEBREAK: 10
// Directory is a file containing a sequence of dirent structures. // Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14 #define DIRSIZ 14

6
ide.c
View file

@ -96,7 +96,7 @@ ideintr(void)
acquire(&idelock); acquire(&idelock);
if((b = idequeue) == 0){ if((b = idequeue) == 0){
release(&idelock); release(&idelock);
cprintf("Spurious IDE interrupt.\n"); // cprintf("spurious IDE interrupt\n");
return; return;
} }
idequeue = b->qnext; idequeue = b->qnext;
@ -131,7 +131,7 @@ iderw(struct buf *b)
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
panic("iderw: nothing to do"); panic("iderw: nothing to do");
if(b->dev != 0 && !havedisk1) if(b->dev != 0 && !havedisk1)
panic("idrw: ide disk 1 not present"); panic("iderw: ide disk 1 not present");
acquire(&idelock); acquire(&idelock);
@ -147,7 +147,7 @@ iderw(struct buf *b)
// Wait for request to finish. // Wait for request to finish.
// Assuming will not sleep too long: ignore proc->killed. // Assuming will not sleep too long: ignore proc->killed.
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) { while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
sleep(b, &idelock); sleep(b, &idelock);
} }

View file

@ -3,9 +3,12 @@
#include "syscall.h" #include "syscall.h"
#include "traps.h" #include "traps.h"
# exec(init, argv) # exec(init, argv)
.globl start .globl start
start: start:
movl $SYS_init, %eax
int $T_SYSCALL
pushl $argv pushl $argv
pushl $init pushl $init
pushl $0 // where caller pc would be pushl $0 // where caller pc would be

View file

@ -17,17 +17,21 @@ struct {
struct run *freelist; struct run *freelist;
} kmem; } kmem;
extern char end[]; // first address after kernel loaded from ELF file
// Initialize free list of physical pages. // Initialize free list of physical pages.
void void
kinit(char *p, uint len) kinit(void)
{ {
char *p;
initlock(&kmem.lock, "kmem"); initlock(&kmem.lock, "kmem");
char *p1 = (char*)PGROUNDUP((uint)p); p = (char*)PGROUNDUP((uint)end);
char *p2 = PGROUNDDOWN(p + len); for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE)
for( ; p1 < p2; p1 += 4096) kfree(p);
kfree(p1);
} }
//PAGEBREAK: 21
// Free the page of physical memory pointed at by v, // Free the page of physical memory pointed at by v,
// which normally should have been returned by a // which normally should have been returned by a
// call to kalloc(). (The exception is when // call to kalloc(). (The exception is when
@ -37,14 +41,14 @@ kfree(char *v)
{ {
struct run *r; struct run *r;
if(((uint) v) % PGSIZE || (uint)v < 1024*1024 || (uint)v >= PHYSTOP) if((uint)v % PGSIZE || v < end || (uint)v >= PHYSTOP)
panic("kfree"); panic("kfree");
// Fill with junk to catch dangling refs. // Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE); memset(v, 1, PGSIZE);
acquire(&kmem.lock); acquire(&kmem.lock);
r = (struct run *) v; r = (struct run*)v;
r->next = kmem.freelist; r->next = kmem.freelist;
kmem.freelist = r; kmem.freelist = r;
release(&kmem.lock); release(&kmem.lock);
@ -54,7 +58,7 @@ kfree(char *v)
// Returns a pointer that the kernel can use. // Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated. // Returns 0 if the memory cannot be allocated.
char* char*
kalloc() kalloc(void)
{ {
struct run *r; struct run *r;
@ -63,6 +67,6 @@ kalloc()
if(r) if(r)
kmem.freelist = r->next; kmem.freelist = r->next;
release(&kmem.lock); release(&kmem.lock);
return (char*) r; return (char*)r;
} }

164
log.c Normal file
View file

@ -0,0 +1,164 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "spinlock.h"
#include "fs.h"
#include "buf.h"
// Dirt simple "logging" supporting only one transaction. All file system calls
// that potentially write a block should be wrapped in begin_trans and commit_trans,
// so that there is never more than one transaction. This serializes all file system
// operations that potentially write, but simplifies recovery (only the last
// one transaction to recover) and concurrency (don't have to worry about reading a modified
// block from a transaction that hasn't committed yet).
// The header of the log. If head == 0, there are no log entries. All entries till head
// are committed. sector[] records the home sector for each block in the log
// (i.e., physical logging).
struct logheader {
int head;
int sector[LOGSIZE];
};
struct {
struct spinlock lock;
int start;
int size;
int intrans;
int dev;
struct logheader lh;
} log;
static void recover_from_log(void);
void
initlog(void)
{
if (sizeof(struct logheader) >= BSIZE)
panic("initlog: too big logheader");
struct superblock sb;
initlock(&log.lock, "log");
readsb(ROOTDEV, &sb);
log.start = sb.size - sb.nlog;
log.size = sb.nlog;
log.dev = ROOTDEV;
recover_from_log();
}
// Copy committed blocks from log to their home location
static void
install_trans(void)
{
int tail;
if (log.lh.head > 0)
cprintf("install_trans %d\n", log.lh.head);
for (tail = 0; tail < log.lh.head; tail++) {
cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]);
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read i'th block from log
struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst block
memmove(dbuf->data, lbuf->data, BSIZE);
bwrite(dbuf);
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.head = lh->head;
for (i = 0; i < log.lh.head; i++) {
log.lh.sector[i] = lh->sector[i];
}
brelse(buf);
if (log.lh.head > 0)
cprintf("read_head: %d\n", log.lh.head);
}
// Write the in-memory log header to disk, committing log entries till head
static void
write_head(void)
{
if (log.lh.head > 0)
cprintf("write_head: %d\n", log.lh.head);
struct buf *buf = bread(log.dev, log.start);
struct logheader *hb = (struct logheader *) (buf->data);
int i;
hb->head = log.lh.head;
for (i = 0; i < log.lh.head; i++) {
hb->sector[i] = log.lh.sector[i];
}
bwrite(buf);
brelse(buf);
}
static void
recover_from_log(void)
{
read_head();
install_trans(); // Install all transactions till head
log.lh.head = 0;
write_head(); // Reclaim log
}
void
begin_trans(void)
{
acquire(&log.lock);
while (log.intrans) {
sleep(&log, &log.lock);
}
log.intrans = 1;
release(&log.lock);
}
void
commit_trans(void)
{
write_head(); // This causes all blocks till log.head to be commited
install_trans(); // Install all the transactions till head
log.lh.head = 0;
write_head(); // Reclaim log
acquire(&log.lock);
log.intrans = 0;
wakeup(&log);
release(&log.lock);
}
// Write buffer into the log at log.head and record the block number log.lh.entry, but
// don't write the log header (which would commit the write).
void
log_write(struct buf *b)
{
int i;
if (log.lh.head >= LOGSIZE)
panic("too big a transaction");
if (!log.intrans)
panic("write outside of trans");
cprintf("log_write: %d %d\n", b->sector, log.lh.head);
for (i = 0; i < log.lh.head; i++) {
if (log.lh.sector[i] == b->sector) // log absorbtion?
break;
}
log.lh.sector[i] = b->sector;
struct buf *lbuf = bread(b->dev, log.start+i+1);
memmove(lbuf->data, b->data, BSIZE);
bwrite(lbuf);
brelse(lbuf);
if (i == log.lh.head)
log.lh.head++;
}

59
main.c
View file

@ -7,40 +7,45 @@
static void bootothers(void); static void bootothers(void);
static void mpmain(void); static void mpmain(void);
void jkstack(void) __attribute__((noreturn)); void jmpkstack(void) __attribute__((noreturn));
void mainc(void); void mainc(void);
// Bootstrap processor starts running C code here. // 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 int
main(void) main(void)
{ {
mpinit(); // collect info about this machine mpinit(); // collect info about this machine
lapicinit(mpbcpu()); lapicinit(mpbcpu());
ksegment(); // set up segments seginit(); // set up segments
picinit(); // interrupt controller kinit(); // initialize memory allocator
ioapicinit(); // another interrupt controller jmpkstack(); // call mainc() on a properly-allocated stack
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
pminit(); // discover how much memory there is
jkstack(); // call mainc() on a properly-allocated stack
} }
void void
jkstack(void) jmpkstack(void)
{ {
char *kstack = kalloc(); char *kstack, *top;
if (!kstack)
panic("jkstack\n"); kstack = kalloc();
char *top = kstack + PGSIZE; if(kstack == 0)
asm volatile("movl %0,%%esp" : : "r" (top)); panic("jmpkstack kalloc");
asm volatile("call mainc"); top = kstack + PGSIZE;
panic("jkstack"); asm volatile("movl %0,%%esp; call mainc" : : "r" (top));
panic("jmpkstack");
} }
// Set up hardware and software.
// Runs only on the boostrap processor.
void void
mainc(void) mainc(void)
{ {
cprintf("\ncpu%d: starting xv6\n\n", cpu->id); cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
picinit(); // interrupt controller
ioapicinit(); // another interrupt controller
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
kvmalloc(); // initialize the kernel page table kvmalloc(); // initialize the kernel page table
pinit(); // process table pinit(); // process table
tvinit(); // trap vectors tvinit(); // trap vectors
@ -63,17 +68,18 @@ mainc(void)
static void static void
mpmain(void) mpmain(void)
{ {
if(cpunum() != mpbcpu()) { if(cpunum() != mpbcpu()){
ksegment(); seginit();
lapicinit(cpunum()); lapicinit(cpunum());
} }
vmenable(); // turn on paging vmenable(); // turn on paging
cprintf("cpu%d: starting\n", cpu->id); cprintf("cpu%d: starting\n", cpu->id);
idtinit(); // load idt register idtinit(); // load idt register
xchg(&cpu->booted, 1); xchg(&cpu->booted, 1); // tell bootothers() we're up
scheduler(); // start running processes scheduler(); // start running processes
} }
// Start the non-boot processors.
static void static void
bootothers(void) bootothers(void)
{ {
@ -82,19 +88,23 @@ bootothers(void)
struct cpu *c; struct cpu *c;
char *stack; char *stack;
// Write bootstrap code to unused memory at 0x7000. The linker has // Write bootstrap code to unused memory at 0x7000.
// placed the start of bootother.S there. // The linker has placed the image of bootother.S in
code = (uchar *) 0x7000; // _binary_bootother_start.
code = (uchar*)0x7000;
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
for(c = cpus; c < cpus+ncpu; c++){ for(c = cpus; c < cpus+ncpu; c++){
if(c == cpus+cpunum()) // We've started already. if(c == cpus+cpunum()) // We've started already.
continue; continue;
// Fill in %esp, %eip and start code on cpu. // Tell bootother.S what stack to use and the address of mpmain;
// it expects to find these two addresses stored just before
// its first instruction.
stack = kalloc(); stack = kalloc();
*(void**)(code-4) = stack + KSTACKSIZE; *(void**)(code-4) = stack + KSTACKSIZE;
*(void**)(code-8) = mpmain; *(void**)(code-8) = mpmain;
lapicstartap(c->id, (uint)code); lapicstartap(c->id, (uint)code);
// Wait for cpu to finish mpmain() // Wait for cpu to finish mpmain()
@ -103,3 +113,6 @@ bootothers(void)
} }
} }
//PAGEBREAK!
// Blank page.

58
memide.c Normal file
View file

@ -0,0 +1,58 @@
// 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 "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/512;
}
// 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(!(b->flags & B_BUSY))
panic("iderw: buf not busy");
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->sector >= disksize)
panic("iderw: sector out of range");
p = memdisk + b->sector*512;
if(b->flags & B_DIRTY){
b->flags &= ~B_DIRTY;
memmove(p, b->data, 512);
} else
memmove(b->data, p, 512);
b->flags |= B_VALID;
}

47
mkfs.c
View file

@ -4,11 +4,15 @@
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <assert.h> #include <assert.h>
#define stat xv6_stat // avoid clash with host struct stat
#include "types.h" #include "types.h"
#include "fs.h" #include "fs.h"
#include "stat.h" #include "stat.h"
#include "param.h"
int nblocks = 995; int nblocks = 985;
int nlog = LOGSIZE;
int ninodes = 200; int ninodes = 200;
int size = 1024; int size = 1024;
@ -33,7 +37,7 @@ ushort
xshort(ushort x) xshort(ushort x)
{ {
ushort y; ushort y;
uchar *a = (uchar*) &y; uchar *a = (uchar*)&y;
a[0] = x; a[0] = x;
a[1] = x >> 8; a[1] = x >> 8;
return y; return y;
@ -43,7 +47,7 @@ uint
xint(uint x) xint(uint x)
{ {
uint y; uint y;
uchar *a = (uchar*) &y; uchar *a = (uchar*)&y;
a[0] = x; a[0] = x;
a[1] = x >> 8; a[1] = x >> 8;
a[2] = x >> 16; a[2] = x >> 16;
@ -77,20 +81,23 @@ main(int argc, char *argv[])
sb.size = xint(size); sb.size = xint(size);
sb.nblocks = xint(nblocks); // so whole disk is size sectors sb.nblocks = xint(nblocks); // so whole disk is size sectors
sb.ninodes = xint(ninodes); sb.ninodes = xint(ninodes);
sb.nlog = xint(nlog);
bitblocks = size/(512*8) + 1; bitblocks = size/(512*8) + 1;
usedblocks = ninodes / IPB + 3 + bitblocks; usedblocks = ninodes / IPB + 3 + bitblocks;
freeblock = usedblocks; freeblock = usedblocks;
printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks,
bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog);
assert(nblocks + usedblocks == size); assert(nblocks + usedblocks + nlog == size);
for(i = 0; i < nblocks + usedblocks; i++) for(i = 0; i < nblocks + usedblocks + nlog; i++)
wsect(i, zeroes); wsect(i, zeroes);
wsect(1, &sb); memset(buf, 0, sizeof(buf));
memmove(buf, &sb, sizeof(sb));
wsect(1, buf);
rootino = ialloc(T_DIR); rootino = ialloc(T_DIR);
assert(rootino == ROOTINO); assert(rootino == ROOTINO);
@ -173,7 +180,7 @@ winode(uint inum, struct dinode *ip)
bn = i2b(inum); bn = i2b(inum);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode*) buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*dip = *ip; *dip = *ip;
wsect(bn, buf); wsect(bn, buf);
} }
@ -187,7 +194,7 @@ rinode(uint inum, struct dinode *ip)
bn = i2b(inum); bn = i2b(inum);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode*) buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*ip = *dip; *ip = *dip;
} }
@ -225,12 +232,12 @@ balloc(int used)
int i; int i;
printf("balloc: first %d blocks have been allocated\n", used); printf("balloc: first %d blocks have been allocated\n", used);
assert(used < 512); assert(used < 512*8);
bzero(buf, 512); bzero(buf, 512);
for(i = 0; i < used; i++) { for(i = 0; i < used; i++){
buf[i/8] = buf[i/8] | (0x1 << (i%8)); buf[i/8] = buf[i/8] | (0x1 << (i%8));
} }
printf("balloc: write bitmap block at sector %lu\n", ninodes/IPB + 3); printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3);
wsect(ninodes / IPB + 3, buf); wsect(ninodes / IPB + 3, buf);
} }
@ -239,7 +246,7 @@ balloc(int used)
void void
iappend(uint inum, void *xp, int n) iappend(uint inum, void *xp, int n)
{ {
char *p = (char*) xp; char *p = (char*)xp;
uint fbn, off, n1; uint fbn, off, n1;
struct dinode din; struct dinode din;
char buf[512]; char buf[512];
@ -252,24 +259,24 @@ iappend(uint inum, void *xp, int n)
while(n > 0){ while(n > 0){
fbn = off / 512; fbn = off / 512;
assert(fbn < MAXFILE); assert(fbn < MAXFILE);
if(fbn < NDIRECT) { if(fbn < NDIRECT){
if(xint(din.addrs[fbn]) == 0) { if(xint(din.addrs[fbn]) == 0){
din.addrs[fbn] = xint(freeblock++); din.addrs[fbn] = xint(freeblock++);
usedblocks++; usedblocks++;
} }
x = xint(din.addrs[fbn]); x = xint(din.addrs[fbn]);
} else { } else {
if(xint(din.addrs[NDIRECT]) == 0) { if(xint(din.addrs[NDIRECT]) == 0){
// printf("allocate indirect block\n"); // printf("allocate indirect block\n");
din.addrs[NDIRECT] = xint(freeblock++); din.addrs[NDIRECT] = xint(freeblock++);
usedblocks++; usedblocks++;
} }
// printf("read indirect block\n"); // printf("read indirect block\n");
rsect(xint(din.addrs[NDIRECT]), (char*) indirect); rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
if(indirect[fbn - NDIRECT] == 0) { if(indirect[fbn - NDIRECT] == 0){
indirect[fbn - NDIRECT] = xint(freeblock++); indirect[fbn - NDIRECT] = xint(freeblock++);
usedblocks++; usedblocks++;
wsect(xint(din.addrs[NDIRECT]), (char*) indirect); wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
} }
x = xint(indirect[fbn-NDIRECT]); x = xint(indirect[fbn-NDIRECT]);
} }

47
mmu.h
View file

@ -24,6 +24,20 @@
#define FL_VIP 0x00100000 // Virtual Interrupt Pending #define FL_VIP 0x00100000 // Virtual Interrupt Pending
#define FL_ID 0x00200000 // ID flag #define FL_ID 0x00200000 // ID flag
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
//PAGEBREAK!
// Segment Descriptor // Segment Descriptor
struct segdesc { struct segdesc {
uint lim_15_0 : 16; // Low bits of segment limit uint lim_15_0 : 16; // Low bits of segment limit
@ -46,7 +60,6 @@ struct segdesc {
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (struct segdesc) \ #define SEG16(type, base, lim, dpl) (struct segdesc) \
{ (lim) & 0xffff, (uint)(base) & 0xffff, \ { (lim) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
@ -62,8 +75,6 @@ struct segdesc {
#define STA_R 0x2 // Readable (executable segments) #define STA_R 0x2 // Readable (executable segments)
#define STA_A 0x1 // Accessed #define STA_A 0x1 // Accessed
//
// System segment type bits // System segment type bits
#define STS_T16A 0x1 // Available 16-bit TSS #define STS_T16A 0x1 // Available 16-bit TSS
#define STS_LDT 0x2 // Local Descriptor Table #define STS_LDT 0x2 // Local Descriptor Table
@ -78,7 +89,6 @@ struct segdesc {
#define STS_IG32 0xE // 32-bit Interrupt Gate #define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate #define STS_TG32 0xF // 32-bit Trap Gate
// A linear address 'la' has a three-part structure as follows: // A linear address 'la' has a three-part structure as follows:
// //
// +--------10------+-------10-------+---------12----------+ // +--------10------+-------10-------+---------12----------+
@ -88,18 +98,18 @@ struct segdesc {
// \--- PDX(la) --/ \--- PTX(la) --/ // \--- PDX(la) --/ \--- PTX(la) --/
// page directory index // page directory index
#define PDX(la) ((((uint) (la)) >> PDXSHIFT) & 0x3FF) #define PDX(la) (((uint)(la) >> PDXSHIFT) & 0x3FF)
// page table index // page table index
#define PTX(la) ((((uint) (la)) >> PTXSHIFT) & 0x3FF) #define PTX(la) (((uint)(la) >> PTXSHIFT) & 0x3FF)
// construct linear address from indexes and offset // construct linear address from indexes and offset
#define PGADDR(d, t, o) ((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) #define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
// turn a kernel linear address into a physical address. // turn a kernel linear address into a physical address.
// all of the kernel data structures have linear and // all of the kernel data structures have linear and
// physical addresses that are equal. // physical addresses that are equal.
#define PADDR(a) ((uint) a) #define PADDR(a) ((uint)(a))
// Page directory and page table constants. // Page directory and page table constants.
#define NPDENTRIES 1024 // page directory entries per page directory #define NPDENTRIES 1024 // page directory entries per page directory
@ -126,25 +136,10 @@ struct segdesc {
#define PTE_MBZ 0x180 // Bits must be zero #define PTE_MBZ 0x180 // Bits must be zero
// Address in page table or page directory entry // Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint) (pte) & ~0xFFF) #define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
typedef uint pte_t; typedef uint pte_t;
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
// PAGEBREAK: 40
// Task state segment format // Task state segment format
struct taskstate { struct taskstate {
uint link; // Old ts selector uint link; // Old ts selector
@ -210,7 +205,7 @@ struct gatedesc {
// this interrupt/trap gate explicitly using an int instruction. // this interrupt/trap gate explicitly using an int instruction.
#define SETGATE(gate, istrap, sel, off, d) \ #define SETGATE(gate, istrap, sel, off, d) \
{ \ { \
(gate).off_15_0 = (uint) (off) & 0xffff; \ (gate).off_15_0 = (uint)(off) & 0xffff; \
(gate).cs = (sel); \ (gate).cs = (sel); \
(gate).args = 0; \ (gate).args = 0; \
(gate).rsv1 = 0; \ (gate).rsv1 = 0; \
@ -218,6 +213,6 @@ struct gatedesc {
(gate).s = 0; \ (gate).s = 0; \
(gate).dpl = (d); \ (gate).dpl = (d); \
(gate).p = 1; \ (gate).p = 1; \
(gate).off_31_16 = (uint) (off) >> 16; \ (gate).off_31_16 = (uint)(off) >> 16; \
} }

17
mp.c
View file

@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len)
{ {
uchar *e, *p; uchar *e, *p;
cprintf("mpsearch1 0x%x %d\n", addr, len);
e = addr+len; e = addr+len;
for(p = addr; p < e; p += sizeof(struct mp)) for(p = addr; p < e; p += sizeof(struct mp))
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
@ -113,9 +112,9 @@ mpinit(void)
switch(*p){ switch(*p){
case MPPROC: case MPPROC:
proc = (struct mpproc*)p; proc = (struct mpproc*)p;
if(ncpu != proc->apicid) { if(ncpu != proc->apicid){
cprintf("mpinit: ncpu=%d apicpid=%d", ncpu, proc->apicid); cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
panic("mpinit"); ismp = 0;
} }
if(proc->flags & MPBOOT) if(proc->flags & MPBOOT)
bcpu = &cpus[ncpu]; bcpu = &cpus[ncpu];
@ -135,9 +134,17 @@ mpinit(void)
continue; continue;
default: default:
cprintf("mpinit: unknown config type %x\n", *p); cprintf("mpinit: unknown config type %x\n", *p);
panic("mpinit"); ismp = 0;
} }
} }
if(!ismp){
// Didn't like what we found; fall back to no MP.
ncpu = 1;
lapic = 0;
ioapicid = 0;
return;
}
if(mp->imcrp){ if(mp->imcrp){
// Bochs doesn't support IMCR, so this doesn't run on Bochs. // Bochs doesn't support IMCR, so this doesn't run on Bochs.
// But it would on real hardware. // But it would on real hardware.

75
multiboot.S Normal file
View file

@ -0,0 +1,75 @@
# 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"
#define STACK 4096
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
# Multiboot header. Data to direct multiboot loader.
.p2align 2
.text
.globl multiboot_header
multiboot_header:
#define magic 0x1badb002
#define flags (1<<16 | 1<<0)
.long magic
.long flags
.long (-magic-flags)
.long multiboot_header # beginning of image
.long multiboot_header
.long edata
.long end
.long multiboot_entry
# Multiboot entry point. Machine is mostly set up.
# Configure the GDT to match the environment that our usual
# boot loader - bootasm.S - sets up.
.globl multiboot_entry
multiboot_entry:
lgdt gdtdesc
ljmp $(SEG_KCODE<<3), $mbstart32
mbstart32:
# 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 $(stack + STACK), %esp
call main
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
.comm stack, STACK

View file

@ -7,4 +7,8 @@
#define NINODE 50 // maximum number of active i-nodes #define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number #define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk #define ROOTDEV 1 // device number of file system root disk
#define USERTOP 0xA0000 // end of user address space
#define PHYSTOP 0x1000000 // use phys mem up to here as free pool #define PHYSTOP 0x1000000 // use phys mem up to here as free pool
#define MAXARG 32 // max exec arguments
#define LOGSIZE 10 // size of log

View file

@ -82,32 +82,3 @@ picinit(void)
if(irqmask != 0xFFFF) if(irqmask != 0xFFFF)
picsetmask(irqmask); picsetmask(irqmask);
} }
// Blank page.

4
pipe.c
View file

@ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable)
p->readopen = 0; p->readopen = 0;
wakeup(&p->nwrite); wakeup(&p->nwrite);
} }
if(p->readopen == 0 && p->writeopen == 0) { if(p->readopen == 0 && p->writeopen == 0){
release(&p->lock); release(&p->lock);
kfree((char*)p); kfree((char*)p);
} else } else
@ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n)
acquire(&p->lock); acquire(&p->lock);
for(i = 0; i < n; i++){ for(i = 0; i < n; i++){
while(p->nwrite == p->nread + PIPESIZE) { //DOC: pipewrite-full while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full
if(p->readopen == 0 || proc->killed){ if(p->readopen == 0 || proc->killed){
release(&p->lock); release(&p->lock);
return -1; return -1;

257
proc.c
View file

@ -17,53 +17,18 @@ int nextpid = 1;
extern void forkret(void); extern void forkret(void);
extern void trapret(void); extern void trapret(void);
static void wakeup1(void *chan);
void void
pinit(void) pinit(void)
{ {
initlock(&ptable.lock, "ptable"); initlock(&ptable.lock, "ptable");
} }
//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");
}
}
//PAGEBREAK: 32 //PAGEBREAK: 32
// Look in the process table for an UNUSED proc. // Look in the process table for an UNUSED proc.
// If found, change state to EMBRYO and return it. // If found, change state to EMBRYO and initialize
// state required to run in the kernel.
// Otherwise return 0. // Otherwise return 0.
static struct proc* static struct proc*
allocproc(void) allocproc(void)
@ -95,7 +60,7 @@ found:
p->tf = (struct trapframe*)sp; p->tf = (struct trapframe*)sp;
// Set up new context to start executing at forkret, // Set up new context to start executing at forkret,
// which returns to trapret (see below). // which returns to trapret.
sp -= 4; sp -= 4;
*(uint*)sp = (uint)trapret; *(uint*)sp = (uint)trapret;
@ -103,6 +68,7 @@ found:
p->context = (struct context*)sp; p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context); memset(p->context, 0, sizeof *p->context);
p->context->eip = (uint)forkret; p->context->eip = (uint)forkret;
return p; return p;
} }
@ -116,12 +82,10 @@ userinit(void)
p = allocproc(); p = allocproc();
initproc = p; initproc = p;
if (!(p->pgdir = setupkvm())) if((p->pgdir = setupkvm()) == 0)
panic("userinit: out of memory?"); panic("userinit: out of memory?");
if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
panic("userinit: out of memory?"); p->sz = PGSIZE;
inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size);
p->sz = PGROUNDUP((int)_binary_initcode_size);
memset(p->tf, 0, sizeof(*p->tf)); memset(p->tf, 0, sizeof(*p->tf));
p->tf->cs = (SEG_UCODE << 3) | DPL_USER; p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
p->tf->ds = (SEG_UDATA << 3) | DPL_USER; p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
@ -142,14 +106,17 @@ userinit(void)
int int
growproc(int n) growproc(int n)
{ {
uint sz;
sz = proc->sz;
if(n > 0){ if(n > 0){
if (!allocuvm(proc->pgdir, (char *)proc->sz, n)) if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)
return -1; return -1;
} else if(n < 0){ } else if(n < 0){
if (!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0)
return -1; return -1;
} }
proc->sz += n; proc->sz = sz;
switchuvm(proc); switchuvm(proc);
return 0; return 0;
} }
@ -168,7 +135,7 @@ fork(void)
return -1; return -1;
// Copy process state from p. // Copy process state from p.
if (!(np->pgdir = copyuvm(proc->pgdir, proc->sz))) { if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){
kfree(np->kstack); kfree(np->kstack);
np->kstack = 0; np->kstack = 0;
np->state = UNUSED; np->state = UNUSED;
@ -192,6 +159,92 @@ fork(void)
return pid; 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 *p;
int fd;
if(proc == initproc)
panic("init exiting");
// Close all open files.
for(fd = 0; fd < NOFILE; fd++){
if(proc->ofile[fd]){
fileclose(proc->ofile[fd]);
proc->ofile[fd] = 0;
}
}
iput(proc->cwd);
proc->cwd = 0;
acquire(&ptable.lock);
// Parent might be sleeping in wait().
wakeup1(proc->parent);
// Pass abandoned children to init.
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent == proc){
p->parent = initproc;
if(p->state == ZOMBIE)
wakeup1(initproc);
}
}
// Jump into the scheduler, never to return.
proc->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;
acquire(&ptable.lock);
for(;;){
// Scan through table looking for zombie children.
havekids = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent != proc)
continue;
havekids = 1;
if(p->state == ZOMBIE){
// Found one.
pid = p->pid;
kfree(p->kstack);
p->kstack = 0;
freevm(p->pgdir);
p->state = UNUSED;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
release(&ptable.lock);
return pid;
}
}
// No point waiting if we don't have any children.
if(!havekids || proc->killed){
release(&ptable.lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(proc, &ptable.lock); //DOC: wait-sleep
}
}
//PAGEBREAK: 42 //PAGEBREAK: 42
// Per-CPU process scheduler. // Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up. // Each CPU calls scheduler() after setting itself up.
@ -356,89 +409,41 @@ kill(int pid)
return -1; return -1;
} }
// Exit the current process. Does not return. //PAGEBREAK: 36
// An exited process remains in the zombie state // Print a process listing to console. For debugging.
// until its parent calls wait() to find out it exited. // Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void void
exit(void) procdump(void)
{ {
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
int i;
struct proc *p; struct proc *p;
int fd; char *state;
uint pc[10];
if(proc == initproc)
panic("init exiting");
// Close all open files.
for(fd = 0; fd < NOFILE; fd++){
if(proc->ofile[fd]){
fileclose(proc->ofile[fd]);
proc->ofile[fd] = 0;
}
}
iput(proc->cwd);
proc->cwd = 0;
acquire(&ptable.lock);
// Parent might be sleeping in wait().
wakeup1(proc->parent);
// Pass abandoned children to init.
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent == proc){ if(p->state == UNUSED)
p->parent = initproc; continue;
if(p->state == ZOMBIE) if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
wakeup1(initproc); 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");
// Jump into the scheduler, never to return.
proc->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;
acquire(&ptable.lock);
for(;;){
// Scan through table looking for zombie children.
havekids = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent != proc)
continue;
havekids = 1;
if(p->state == ZOMBIE){
// Found one.
pid = p->pid;
kfree(p->kstack);
p->kstack = 0;
freevm(p->pgdir);
p->state = UNUSED;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
release(&ptable.lock);
return pid;
}
}
// No point waiting if we don't have any children.
if(!havekids || proc->killed){
release(&ptable.lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(proc, &ptable.lock); //DOC: wait-sleep
} }
} }

64
proc.h
View file

@ -8,6 +8,36 @@
#define SEG_TSS 6 // this process's task state #define SEG_TSS 6 // this process's task state
#define NSEGS 7 #define NSEGS 7
// Per-CPU state
struct cpu {
uchar id; // Local APIC ID; index into cpus[] below
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
volatile uint booted; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
// Cpu-local storage variables; see below
struct cpu *cpu;
struct proc *proc; // The currently-running process.
};
extern struct cpu cpus[NCPU];
extern int ncpu;
// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// The asm suffix tells gcc to use "%gs:0" to refer to cpu
// and "%gs:4" to refer to proc. seginit sets up the
// %gs segment register so that %gs refers to the memory
// holding those two variables in the local cpu's struct cpu.
// This is similar to how thread-local variables are implemented
// in thread libraries such as Linux pthreads.
extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()]
extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc
//PAGEBREAK: 17
// Saved registers for kernel context switches. // Saved registers for kernel context switches.
// Don't need to save all the segment registers (%cs, etc), // Don't need to save all the segment registers (%cs, etc),
// because they are constant across kernel contexts. // because they are constant across kernel contexts.
@ -31,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state // Per-process state
struct proc { struct proc {
uint sz; // Size of process memory (bytes) uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Linear address of proc's pgdir pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state enum procstate state; // Process state
volatile int pid; // Process ID volatile int pid; // Process ID
struct proc *parent; // Parent process struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall struct trapframe *tf; // Trap frame for current syscall
struct context *context; // Switch here to run process struct context *context; // swtch() here to run process
void *chan; // If non-zero, sleeping on chan void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files struct file *ofile[NOFILE]; // Open files
@ -48,35 +78,5 @@ struct proc {
// Process memory is laid out contiguously, low addresses first: // Process memory is laid out contiguously, low addresses first:
// text // text
// original data and bss // original data and bss
// invalid page
// fixed-size stack // fixed-size stack
// expandable heap // expandable heap
// Per-CPU state
struct cpu {
uchar id; // Local APIC ID; index into cpus[] below
struct context *scheduler; // Switch here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table
volatile uint booted; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
// Cpu-local storage variables; see below
struct cpu *cpu;
struct proc *proc;
};
extern struct cpu cpus[NCPU];
extern int ncpu;
// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// The asm suffix tells gcc to use "%gs:0" to refer to cpu
// and "%gs:4" to refer to proc. ksegment sets up the
// %gs segment register so that %gs refers to the memory
// holding those two variables in the local cpu's struct cpu.
// This is similar to how thread-local variables are implemented
// in thread libraries such as Linux pthreads.
extern struct cpu *cpu asm("%gs:0"); // This cpu.
extern struct proc *proc asm("%gs:4"); // Current proc on this cpu.

24
runoff
View file

@ -58,6 +58,13 @@ perl -e '
next; 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: (.*)/){ if(/even: (.*)/){
$file = $1; $file = $1;
if(!defined($toc{$file})){ if(!defined($toc{$file})){
@ -89,18 +96,13 @@ perl -e '
print STDERR "Have no toc for $file\n"; print STDERR "Have no toc for $file\n";
next; next;
} }
# this assumes that sheet 1 of code is a left page if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
# double-check the PDF print STDERR "$file does not start on a left page [$toc{$file}]\n";
if(!$leftwarn++) {
print STDERR "assuming that sheet 1 is a left page. double-check!\n";
}
if($what eq "left" && !($toc{$file} =~ /^\d[13579]0/)){
print STDERR "$file does not start on a fresh left page [$toc{$file}]\n";
} }
# why does this not work if I inline $x in the if? # why does this not work if I inline $x in the if?
$x = ($toc{$file} =~ /^\d[02468]0/); $x = ($toc{$file} =~ /^\d[$right][05]/);
if($what eq "right" && !$x){ if($what eq "right" && !$x){
print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n";
} }
next; next;
} }
@ -189,7 +191,9 @@ do
uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
if [ "x$defs" != "x$uses" ]; then if [ "x$defs" != "x$uses" ]; then
echo $i $defs echo $i $defs
echo $uses |fmt -24 | sed 's/^/ /' echo $uses |fmt -29 | sed 's/^/ /'
# else
# echo $i defined but not used >&2
fi fi
done done
) >refs ) >refs

View file

@ -22,8 +22,8 @@ proc.h
proc.c proc.c
swtch.S swtch.S
kalloc.c kalloc.c
data.S
vm.c vm.c
# system calls # system calls
traps.h traps.h
vectors.pl vectors.pl
@ -46,11 +46,10 @@ file.c
sysfile.c sysfile.c
exec.c exec.c
# pipes # pipes
pipe.c pipe.c
# string operations # string operations
string.c string.c
@ -65,6 +64,7 @@ kbd.c
console.c console.c
timer.c timer.c
uart.c uart.c
multiboot.S
# user-level # user-level
initcode.S initcode.S
@ -73,3 +73,6 @@ init.c
sh.c sh.c

View file

@ -1,3 +1,16 @@
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 # types.h either
# param.h either # param.h either
# defs.h either # defs.h either
@ -9,25 +22,36 @@
even: bootasm.S # mild preference even: bootasm.S # mild preference
even: bootother.S # mild preference even: bootother.S # mild preference
# bootmain.c either even: bootmain.c # mild preference
even: main.c even: main.c
# mp.c don't care at all # mp.c don't care at all
# even: initcode.S # even: initcode.S
# odd: init.c # odd: init.c
# spinlock.h either # spinlock.h either
# spinlock.c either left: spinlock.h # mild preference
even: proc.h # mild preference even: spinlock.h # mild preference
# 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, # goal is to have two action-packed 2-page spreads,
# one with # one with
# ksegment usegment allocproc userinit growproc fork # userinit growproc fork exit wait
# and another with # and another with
# scheduler sched yield forkret sleep wakeup1 wakeup # scheduler sched yield forkret sleep wakeup1 wakeup
right: proc.c # VERY important 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 vmenable switch[ku]vm inituvm loaduvm
# process memory management
# allocuvm deallocuvm freevm
left: vm.c
odd: vm.c
# setjmp.S either
# vm.c either
# kalloc.c either # kalloc.c either
# syscall.h either # syscall.h either
@ -45,15 +69,25 @@ right: proc.c # VERY important
# file.h either # file.h either
# fs.h either # fs.h either
# fsvar.h either # fsvar.h either
left: ide.c # left: ide.c # mild preference
even: ide.c
# odd: bio.c # odd: bio.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
# fielinit filealloc filedup fileclose filestat fileread filewrite
# starting on 2nd column of a right page is not terrible either
odd: fs.c # VERY important odd: fs.c # VERY important
left: fs.c # mild preference
# file.c either # file.c either
# exec.c either # exec.c either
# sysfile.c either # sysfile.c either
# even: pipe.c # mild preference # even: pipe.c # mild preference
# string.c either # string.c either
left: kbd.h # left: kbd.h # mild preference
even: kbd.h
even: console.c even: console.c
odd: sh.c odd: sh.c

View file

@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){
last if $i>=@lines; last if $i>=@lines;
# If the rest of the file fits, use the whole thing. # If the rest of the file fits, use the whole thing.
if(@lines <= $i+50){ if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
$breakbefore = @lines; $breakbefore = @lines;
}else{ }else{
# Find a good next page break; # Find a good next page break;

View file

@ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name)
void void
acquire(struct spinlock *lk) acquire(struct spinlock *lk)
{ {
pushcli(); pushcli(); // disable interrupts to avoid deadlock.
if(holding(lk)) if(holding(lk))
panic("acquire"); panic("acquire");
@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[])
ebp = (uint*)v - 2; ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){ for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint *) 0x100000 || ebp == (uint*)0xffffffff) if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff)
break; break;
pcs[i] = ebp[1]; // saved %eip pcs[i] = ebp[1]; // saved %eip
ebp = (uint*)ebp[0]; // saved %ebp ebp = (uint*)ebp[0]; // saved %ebp

View file

@ -14,21 +14,20 @@
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int i; int fd, i;
char path[] = "stressfs0";
printf(1, "stressfs starting\n"); printf(1, "stressfs starting\n");
for (i = 0; i < 4; i++) { for(i = 0; i < 4; i++)
if (fork() > 0) { if(fork() > 0)
break; break;
}
}
printf(1, "%d\n", i); printf(1, "%d\n", i);
char path[] = "stressfs0";
path[8] += i; path[8] += i;
int fd = open(path, O_CREATE | O_RDWR); fd = open(path, O_CREATE | O_RDWR);
for (i = 0; i < 100; i++) for(i = 0; i < 100; i++)
printf(fd, "%d\n", i); printf(fd, "%d\n", i);
close(fd); close(fd);

View file

@ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip)
return 0; return 0;
} }
// XXX should we copy the string?
// Fetch the nul-terminated string at addr from process p. // Fetch the nul-terminated string at addr from process p.
// Doesn't actually copy the string - just sets *pp to point at it. // Doesn't actually copy the string - just sets *pp to point at it.
// Returns length of string, not including nul. // Returns length of string, not including nul.
@ -34,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp)
if(addr >= p->sz) if(addr >= p->sz)
return -1; return -1;
*pp = (char *) addr; *pp = (char*)addr;
ep = (char *) p->sz; ep = (char*)p->sz;
for(s = *pp; s < ep; s++) for(s = *pp; s < ep; s++)
if(*s == 0) if(*s == 0)
return s - *pp; return s - *pp;
@ -46,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp)
int int
argint(int n, int *ip) argint(int n, int *ip)
{ {
int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip); return fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
return x;
} }
// Fetch the nth word-sized system call argument as a pointer // Fetch the nth word-sized system call argument as a pointer
@ -60,10 +57,9 @@ argptr(int n, char **pp, int size)
if(argint(n, &i) < 0) if(argint(n, &i) < 0)
return -1; return -1;
if((uint)i >= proc->sz || (uint)i+size >= proc->sz) if((uint)i >= proc->sz || (uint)i+size > proc->sz)
return -1; return -1;
// *pp = proc->mem + i; // XXXXX *pp = (char*)i;
*pp = (char *) i; // XXXXX
return 0; return 0;
} }
@ -102,28 +98,37 @@ extern int sys_wait(void);
extern int sys_write(void); extern int sys_write(void);
extern int sys_uptime(void); extern int sys_uptime(void);
int
sys_init(void)
{
initlog();
return 0;
}
static int (*syscalls[])(void) = { static int (*syscalls[])(void) = {
[SYS_chdir] sys_chdir, [SYS_init] sys_init,
[SYS_close] sys_close,
[SYS_dup] sys_dup,
[SYS_exec] sys_exec,
[SYS_exit] sys_exit,
[SYS_fork] sys_fork, [SYS_fork] sys_fork,
[SYS_fstat] sys_fstat, [SYS_exit] sys_exit,
[SYS_getpid] sys_getpid, [SYS_wait] sys_wait,
[SYS_kill] sys_kill,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_mknod] sys_mknod,
[SYS_open] sys_open,
[SYS_pipe] sys_pipe, [SYS_pipe] sys_pipe,
[SYS_read] sys_read, [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_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep, [SYS_sleep] sys_sleep,
[SYS_unlink] sys_unlink,
[SYS_wait] sys_wait,
[SYS_write] sys_write,
[SYS_uptime] sys_uptime, [SYS_uptime] sys_uptime,
// File system calls that are run in a transaction:
[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 void
@ -132,9 +137,13 @@ syscall(void)
int num; int num;
num = proc->tf->eax; num = proc->tf->eax;
if(num >= 0 && num < NELEM(syscalls) && syscalls[num]) if(num >= 0 && num < SYS_open && syscalls[num]) {
proc->tf->eax = syscalls[num](); proc->tf->eax = syscalls[num]();
else { } else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
begin_trans();
proc->tf->eax = syscalls[num]();
commit_trans();
} else {
cprintf("%d %s: unknown sys call %d\n", cprintf("%d %s: unknown sys call %d\n",
proc->pid, proc->name, num); proc->pid, proc->name, num);
proc->tf->eax = -1; proc->tf->eax = -1;

View file

@ -1,22 +1,24 @@
// System call numbers // System call numbers
#define SYS_init 0
#define SYS_fork 1 #define SYS_fork 1
#define SYS_exit 2 #define SYS_exit 2
#define SYS_wait 3 #define SYS_wait 3
#define SYS_pipe 4 #define SYS_pipe 4
#define SYS_write 5 #define SYS_read 5
#define SYS_read 6 #define SYS_kill 6
#define SYS_close 7 #define SYS_exec 7
#define SYS_kill 8 #define SYS_fstat 8
#define SYS_exec 9 #define SYS_chdir 9
#define SYS_open 10 #define SYS_dup 10
#define SYS_mknod 11 #define SYS_getpid 11
#define SYS_unlink 12 #define SYS_sbrk 12
#define SYS_fstat 13 #define SYS_sleep 13
#define SYS_link 14 #define SYS_uptime 14
#define SYS_mkdir 15
#define SYS_chdir 16 #define SYS_open 15
#define SYS_dup 17 #define SYS_write 16
#define SYS_getpid 18 #define SYS_mknod 17
#define SYS_sbrk 19 #define SYS_unlink 18
#define SYS_sleep 20 #define SYS_link 19
#define SYS_uptime 21 #define SYS_mkdir 20
#define SYS_close 21

View file

@ -344,11 +344,11 @@ sys_chdir(void)
int int
sys_exec(void) sys_exec(void)
{ {
char *path, *argv[20]; char *path, *argv[MAXARG];
int i; int i;
uint uargv, uarg; uint uargv, uarg;
if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) { if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
return -1; return -1;
} }
memset(argv, 0, sizeof(argv)); memset(argv, 0, sizeof(argv));

View file

@ -6,9 +6,8 @@ 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 where the name is defined. Successive lines in an entry list the line
numbers where the name is used. For example, this entry: numbers where the name is used. For example, this entry:
swtch 2208 swtch 2358
0318 1928 1967 2207 0317 2128 2166 2357 2358
2208
indicates that swtch is defined on line 2208 and is mentioned on five lines indicates that swtch is defined on line 2358 and is mentioned on five lines
on sheets 03, 19, and 22. on sheets 03, 21, and 23.

8
trap.c
View file

@ -59,6 +59,9 @@ trap(struct trapframe *tf)
ideintr(); ideintr();
lapiceoi(); lapiceoi();
break; break;
case T_IRQ0 + IRQ_IDE+1:
// Bochs generates spurious IDE1 interrupts.
break;
case T_IRQ0 + IRQ_KBD: case T_IRQ0 + IRQ_KBD:
kbdintr(); kbdintr();
lapiceoi(); lapiceoi();
@ -83,9 +86,10 @@ trap(struct trapframe *tf)
panic("trap"); panic("trap");
} }
// In user space, assume process misbehaved. // 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", cprintf("pid %d %s: trap %d err %d on cpu %d "
"eip 0x%x addr 0x%x--kill proc\n",
proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,
rcr2()); rcr2());
proc->killed = 1; proc->killed = 1;
} }

2
ulib.c
View file

@ -45,7 +45,7 @@ strchr(const char *s, char c)
{ {
for(; *s; s++) for(; *s; s++)
if(*s == c) if(*s == c)
return (char*) s; return (char*)s;
return 0; return 0;
} }

View file

@ -26,7 +26,7 @@ free(void *ap)
{ {
Header *bp, *p; Header *bp, *p;
bp = (Header*) ap - 1; bp = (Header*)ap - 1;
for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; break;
@ -52,7 +52,7 @@ morecore(uint nu)
if(nu < 4096) if(nu < 4096)
nu = 4096; nu = 4096;
p = sbrk(nu * sizeof(Header)); p = sbrk(nu * sizeof(Header));
if(p == (char*) -1) if(p == (char*)-1)
return 0; return 0;
hp = (Header*)p; hp = (Header*)p;
hp->s.size = nu; hp->s.size = nu;
@ -81,7 +81,7 @@ malloc(uint nbytes)
p->s.size = nunits; p->s.size = nunits;
} }
freep = prevp; freep = prevp;
return (void*) (p + 1); return (void*)(p + 1);
} }
if(p == freep) if(p == freep)
if((p = morecore(nunits)) == 0) if((p = morecore(nunits)) == 0)

4
user.h
View file

@ -18,10 +18,10 @@ int link(char*, char*);
int mkdir(char*); int mkdir(char*);
int chdir(char*); int chdir(char*);
int dup(int); int dup(int);
int getpid(); int getpid(void);
char* sbrk(int); char* sbrk(int);
int sleep(int); int sleep(int);
int uptime(); int uptime(void);
// ulib.c // ulib.c
int stat(char*, struct stat*); int stat(char*, struct stat*);

View file

@ -3,6 +3,8 @@
#include "user.h" #include "user.h"
#include "fs.h" #include "fs.h"
#include "fcntl.h" #include "fcntl.h"
#include "syscall.h"
#include "traps.h"
char buf[2048]; char buf[2048];
char name[3]; char name[3];
@ -45,12 +47,12 @@ writetest(void)
printf(stdout, "error: creat small failed!\n"); printf(stdout, "error: creat small failed!\n");
exit(); exit();
} }
for(i = 0; i < 100; i++) { for(i = 0; i < 100; i++){
if(write(fd, "aaaaaaaaaa", 10) != 10) { if(write(fd, "aaaaaaaaaa", 10) != 10){
printf(stdout, "error: write aa %d new file failed\n", i); printf(stdout, "error: write aa %d new file failed\n", i);
exit(); exit();
} }
if(write(fd, "bbbbbbbbbb", 10) != 10) { if(write(fd, "bbbbbbbbbb", 10) != 10){
printf(stdout, "error: write bb %d new file failed\n", i); printf(stdout, "error: write bb %d new file failed\n", i);
exit(); exit();
} }
@ -65,7 +67,7 @@ writetest(void)
exit(); exit();
} }
i = read(fd, buf, 2000); i = read(fd, buf, 2000);
if(i == 2000) { if(i == 2000){
printf(stdout, "read succeeded ok\n"); printf(stdout, "read succeeded ok\n");
} else { } else {
printf(stdout, "read failed\n"); printf(stdout, "read failed\n");
@ -73,7 +75,7 @@ writetest(void)
} }
close(fd); close(fd);
if(unlink("small") < 0) { if(unlink("small") < 0){
printf(stdout, "unlink small failed\n"); printf(stdout, "unlink small failed\n");
exit(); exit();
} }
@ -93,9 +95,9 @@ writetest1(void)
exit(); exit();
} }
for(i = 0; i < MAXFILE; i++) { for(i = 0; i < MAXFILE; i++){
((int*) buf)[0] = i; ((int*)buf)[0] = i;
if(write(fd, buf, 512) != 512) { if(write(fd, buf, 512) != 512){
printf(stdout, "error: write big file failed\n", i); printf(stdout, "error: write big file failed\n", i);
exit(); exit();
} }
@ -110,19 +112,19 @@ writetest1(void)
} }
n = 0; n = 0;
for(;;) { for(;;){
i = read(fd, buf, 512); i = read(fd, buf, 512);
if(i == 0) { if(i == 0){
if(n == MAXFILE - 1) { if(n == MAXFILE - 1){
printf(stdout, "read only %d blocks from big", n); printf(stdout, "read only %d blocks from big", n);
exit(); exit();
} }
break; break;
} else if(i != 512) { } else if(i != 512){
printf(stdout, "read failed %d\n", i); printf(stdout, "read failed %d\n", i);
exit(); exit();
} }
if(((int*)buf)[0] != n) { if(((int*)buf)[0] != n){
printf(stdout, "read content of block %d is %d\n", printf(stdout, "read content of block %d is %d\n",
n, ((int*)buf)[0]); n, ((int*)buf)[0]);
exit(); exit();
@ -130,7 +132,7 @@ writetest1(void)
n++; n++;
} }
close(fd); close(fd);
if(unlink("big") < 0) { if(unlink("big") < 0){
printf(stdout, "unlink big failed\n"); printf(stdout, "unlink big failed\n");
exit(); exit();
} }
@ -146,14 +148,14 @@ createtest(void)
name[0] = 'a'; name[0] = 'a';
name[2] = '\0'; name[2] = '\0';
for(i = 0; i < 52; i++) { for(i = 0; i < 52; i++){
name[1] = '0' + i; name[1] = '0' + i;
fd = open(name, O_CREATE|O_RDWR); fd = open(name, O_CREATE|O_RDWR);
close(fd); close(fd);
} }
name[0] = 'a'; name[0] = 'a';
name[2] = '\0'; name[2] = '\0';
for(i = 0; i < 52; i++) { for(i = 0; i < 52; i++){
name[1] = '0' + i; name[1] = '0' + i;
unlink(name); unlink(name);
} }
@ -164,22 +166,22 @@ void dirtest(void)
{ {
printf(stdout, "mkdir test\n"); printf(stdout, "mkdir test\n");
if(mkdir("dir0") < 0) { if(mkdir("dir0") < 0){
printf(stdout, "mkdir failed\n"); printf(stdout, "mkdir failed\n");
exit(); exit();
} }
if(chdir("dir0") < 0) { if(chdir("dir0") < 0){
printf(stdout, "chdir dir0 failed\n"); printf(stdout, "chdir dir0 failed\n");
exit(); exit();
} }
if(chdir("..") < 0) { if(chdir("..") < 0){
printf(stdout, "chdir .. failed\n"); printf(stdout, "chdir .. failed\n");
exit(); exit();
} }
if(unlink("dir0") < 0) { if(unlink("dir0") < 0){
printf(stdout, "unlink dir0 failed\n"); printf(stdout, "unlink dir0 failed\n");
exit(); exit();
} }
@ -190,7 +192,7 @@ void
exectest(void) exectest(void)
{ {
printf(stdout, "exec test\n"); printf(stdout, "exec test\n");
if(exec("echo", echoargv) < 0) { if(exec("echo", echoargv) < 0){
printf(stdout, "exec echo failed\n"); printf(stdout, "exec echo failed\n");
exit(); exit();
} }
@ -324,20 +326,21 @@ mem(void)
void *m1, *m2; void *m1, *m2;
int pid, ppid; int pid, ppid;
printf(1, "mem test\n");
ppid = getpid(); ppid = getpid();
if((pid = fork()) == 0){ if((pid = fork()) == 0){
m1 = 0; m1 = 0;
while((m2 = malloc(10001)) != 0) { while((m2 = malloc(10001)) != 0){
*(char**) m2 = m1; *(char**)m2 = m1;
m1 = m2; m1 = m2;
} }
while(m1) { while(m1){
m2 = *(char**)m1; m2 = *(char**)m1;
free(m1); free(m1);
m1 = m2; m1 = m2;
} }
m1 = malloc(1024*20); m1 = malloc(1024*20);
if(m1 == 0) { if(m1 == 0){
printf(1, "couldn't allocate mem?!!\n"); printf(1, "couldn't allocate mem?!!\n");
kill(ppid); kill(ppid);
exit(); exit();
@ -1234,16 +1237,18 @@ forktest(void)
void void
sbrktest(void) sbrktest(void)
{ {
int pid; int fds[2], pid, pids[32], ppid;
char *oldbrk = sbrk(0); char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch;
uint amt;
printf(stdout, "sbrk test\n"); printf(stdout, "sbrk test\n");
oldbrk = sbrk(0);
// can one sbrk() less than a page? // can one sbrk() less than a page?
char *a = sbrk(0); a = sbrk(0);
int i; int i;
for(i = 0; i < 5000; i++){ for(i = 0; i < 5000; i++){
char *b = sbrk(1); b = sbrk(1);
if(b != a){ if(b != a){
printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); printf(stdout, "sbrk test failed %d %x %x\n", i, a, b);
exit(); exit();
@ -1256,7 +1261,7 @@ sbrktest(void)
printf(stdout, "sbrk test fork failed\n"); printf(stdout, "sbrk test fork failed\n");
exit(); exit();
} }
char *c = sbrk(1); c = sbrk(1);
c = sbrk(1); c = sbrk(1);
if(c != a + 1){ if(c != a + 1){
printf(stdout, "sbrk test failed post-fork\n"); printf(stdout, "sbrk test failed post-fork\n");
@ -1268,18 +1273,18 @@ sbrktest(void)
// can one allocate the full 640K? // can one allocate the full 640K?
a = sbrk(0); a = sbrk(0);
uint amt = (640 * 1024) - (uint) a; amt = (640 * 1024) - (uint)a;
char *p = sbrk(amt); p = sbrk(amt);
if(p != a){ if(p != a){
printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a); printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a);
exit(); exit();
} }
char *lastaddr = (char *)(640 * 1024 - 1); lastaddr = (char*)(640 * 1024 - 1);
*lastaddr = 99; *lastaddr = 99;
// is one forbidden from allocating more than 640K? // is one forbidden from allocating more than 640K?
c = sbrk(4096); c = sbrk(4096);
if(c != (char *) 0xffffffff){ if(c != (char*)0xffffffff){
printf(stdout, "sbrk allocated more than 640K, c %x\n", c); printf(stdout, "sbrk allocated more than 640K, c %x\n", c);
exit(); exit();
} }
@ -1287,7 +1292,7 @@ sbrktest(void)
// can one de-allocate? // can one de-allocate?
a = sbrk(0); a = sbrk(0);
c = sbrk(-4096); c = sbrk(-4096);
if(c == (char *) 0xffffffff){ if(c == (char*)0xffffffff){
printf(stdout, "sbrk could not deallocate\n"); printf(stdout, "sbrk could not deallocate\n");
exit(); exit();
} }
@ -1311,15 +1316,15 @@ sbrktest(void)
} }
c = sbrk(4096); c = sbrk(4096);
if(c != (char *) 0xffffffff){ if(c != (char*)0xffffffff){
printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c); printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c);
exit(); exit();
} }
// can we read the kernel's memory? // can we read the kernel's memory?
for(a = (char*)(640*1024); a < (char *)2000000; a += 50000){ for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){
int ppid = getpid(); ppid = getpid();
int pid = fork(); pid = fork();
if(pid < 0){ if(pid < 0){
printf(stdout, "fork failed\n"); printf(stdout, "fork failed\n");
exit(); exit();
@ -1332,6 +1337,38 @@ sbrktest(void)
wait(); wait();
} }
// if we run the system out of memory, does it clean up the last
// failed allocation?
sbrk(-(sbrk(0) - oldbrk));
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 the full 640K
sbrk((640 * 1024) - (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) if(sbrk(0) > oldbrk)
sbrk(-(sbrk(0) - oldbrk)); sbrk(-(sbrk(0) - oldbrk));
@ -1339,26 +1376,89 @@ sbrktest(void)
} }
void void
stacktest(void) validateint(int *p)
{ {
printf(stdout, "stack test\n"); int res;
char dummy = 1; asm("mov %%esp, %%ebx\n\t"
char *p = &dummy; "mov %3, %%esp\n\t"
int ppid = getpid(); "int %2\n\t"
int pid = fork(); "mov %%ebx, %%esp" :
if(pid < 0){ "=a" (res) :
printf(stdout, "fork failed\n"); "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) :
exit(); "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 do something sensible if the arguments
// are larger than a page?
void
bigargtest(void)
{
int pid, ppid;
ppid = getpid();
pid = fork();
if(pid == 0){ if(pid == 0){
// should cause a trap: char *args[32+1];
p[-4096] = 'z'; int i;
kill(ppid); for(i = 0; i < 32; i++)
printf(stdout, "stack test failed: page before stack was writeable\n"); args[i] = "bigargs test: failed\n ";
args[32] = 0;
printf(stdout, "bigarg test\n");
exec("echo", args);
printf(stdout, "bigarg test ok\n");
exit();
} else if(pid < 0){
printf(stdout, "bigargtest: fork failed\n");
exit(); exit();
} }
wait(); wait();
printf(stdout, "stack test OK\n");
} }
int int
@ -1372,8 +1472,10 @@ main(int argc, char *argv[])
} }
close(open("usertests.ran", O_CREATE)); close(open("usertests.ran", O_CREATE));
stacktest(); bigargtest();
bsstest();
sbrktest(); sbrktest();
validatetest();
opentest(); opentest();
writetest(); writetest();

605
vm.c
View file

@ -6,96 +6,22 @@
#include "proc.h" #include "proc.h"
#include "elf.h" #include "elf.h"
// The mappings from logical to linear are one to one (i.e., extern char data[]; // defined in data.S
// segmentation doesn't do anything).
// There is one page table per process, plus one that's used
// when a CPU is not running any process (kpgdir).
// A user process uses the same page table as the kernel; the
// page protection bits prevent it from using anything other
// than its memory.
//
// setupkvm() and exec() set up every page table like this:
// 0..640K : user memory (text, data, stack, heap)
// 640K..1M : mapped direct (for IO space)
// 1M..kernend : mapped direct (for the kernel's text and data)
// kernend..PHYSTOP : mapped direct (kernel heap and user pages)
// 0xfe000000..0 : mapped direct (devices such as ioapic)
//
// The kernel allocates memory for its heap and for user memory
// between kernend and the end of physical memory (PHYSTOP).
// The virtual address space of each user program includes the kernel
// (which is inaccessible in user mode). The user program addresses
// range from 0 till 640KB (USERTOP), which where the I/O hole starts
// (both in physical memory and in the kernel's virtual address
// space).
#define USERTOP 0xA0000
static uint kerntext; // Linker starts kernel at 1MB
static uint kerntsz;
static uint kerndata;
static uint kerndsz;
static uint kernend;
static uint freesz;
static pde_t *kpgdir; // for use in scheduler() static pde_t *kpgdir; // for use in scheduler()
// return the address of the PTE in page table pgdir // Allocate one page table for the machine for the kernel address
// that corresponds to linear address va. if create!=0, // space for scheduler processes.
// create any required page table pages. void
static pte_t * kvmalloc(void)
walkpgdir(pde_t *pgdir, const void *va, int create)
{ {
uint r; kpgdir = setupkvm();
pde_t *pde;
pte_t *pgtab;
pde = &pgdir[PDX(va)];
if (*pde & PTE_P) {
pgtab = (pte_t*) PTE_ADDR(*pde);
} else if (!create || !(r = (uint) kalloc()))
return 0;
else {
pgtab = (pte_t*) r;
// 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 = PADDR(r) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
}
// create PTEs for linear addresses starting at la that refer to
// physical addresses starting at pa. la and size might not
// be page-aligned.
static int
mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
{
char *first = PGROUNDDOWN(la);
char *last = PGROUNDDOWN(la + size - 1);
char *a = first;
while(1){
pte_t *pte = walkpgdir(pgdir, a, 1);
if(pte == 0)
return 0;
if(*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 1;
} }
// Set up CPU's kernel segment descriptors. // Set up CPU's kernel segment descriptors.
// Run once at boot time on each CPU. // Run once at boot time on each CPU.
void void
ksegment(void) seginit(void)
{ {
struct cpu *c; struct cpu *c;
@ -109,7 +35,7 @@ ksegment(void)
c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER); c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
// map cpu, and curproc // Map cpu, and curproc
c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0); c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0);
lgdt(c->gdt, sizeof(c->gdt)); lgdt(c->gdt, sizeof(c->gdt));
@ -120,250 +46,106 @@ ksegment(void)
proc = 0; proc = 0;
} }
// Switch h/w page table and TSS registers to point to process p. // Return the address of the PTE in page table pgdir
void // that corresponds to linear address va. If create!=0,
switchuvm(struct proc *p) // create any required page table pages.
static pte_t *
walkpgdir(pde_t *pgdir, const void *va, int create)
{ {
pushcli(); pde_t *pde;
pte_t *pgtab;
// Setup TSS pde = &pgdir[PDX(va)];
cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0); if(*pde & PTE_P){
cpu->gdt[SEG_TSS].s = 0; pgtab = (pte_t*)PTE_ADDR(*pde);
cpu->ts.ss0 = SEG_KDATA << 3; } else {
cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; if(!create || (pgtab = (pte_t*)kalloc()) == 0)
ltr(SEG_TSS << 3); return 0;
// Make sure all those PTE_P bits are zero.
if (p->pgdir == 0) memset(pgtab, 0, PGSIZE);
panic("switchuvm: no pgdir\n"); // The permissions here are overly generous, but they can
// be further restricted by the permissions in the page table
lcr3(PADDR(p->pgdir)); // switch to new address space // entries, if necessary.
popcli(); *pde = PADDR(pgtab) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
} }
// Switch h/w page table register to the kernel-only page table, for when // Create PTEs for linear addresses starting at la that refer to
// no process is running. // physical addresses starting at pa. la and size might not
void // be page-aligned.
switchkvm() static int
mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
{ {
lcr3(PADDR(kpgdir)); // Switch to the kernel page table char *a, *last;
pte_t *pte;
a = PGROUNDDOWN(la);
last = PGROUNDDOWN(la + size - 1);
for(;;){
pte = walkpgdir(pgdir, a, 1);
if(pte == 0)
return -1;
if(*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
} }
// The mappings from logical to linear are one to one (i.e.,
// segmentation doesn't do anything).
// There is one page table per process, plus one that's used
// when a CPU is not running any process (kpgdir).
// A user process uses the same page table as the kernel; the
// page protection bits prevent it from using anything other
// than its memory.
//
// setupkvm() and exec() set up every page table like this:
// 0..640K : user memory (text, data, stack, heap)
// 640K..1M : mapped direct (for IO space)
// 1M..end : mapped direct (for the kernel's text and data)
// end..PHYSTOP : mapped direct (kernel heap and user pages)
// 0xfe000000..0 : mapped direct (devices such as ioapic)
//
// The kernel allocates memory for its heap and for user memory
// between kernend and the end of physical memory (PHYSTOP).
// The virtual address space of each user program includes the kernel
// (which is inaccessible in user mode). The user program addresses
// range from 0 till 640KB (USERTOP), which where the I/O hole starts
// (both in physical memory and in the kernel's virtual address
// space).
static struct kmap {
void *p;
void *e;
int perm;
} kmap[] = {
{(void*)USERTOP, (void*)0x100000, PTE_W}, // I/O space
{(void*)0x100000, data, 0 }, // kernel text, rodata
{data, (void*)PHYSTOP, PTE_W}, // kernel data, memory
{(void*)0xFE000000, 0, PTE_W}, // device mappings
};
// Set up kernel part of a page table. // Set up kernel part of a page table.
pde_t* pde_t*
setupkvm(void) setupkvm(void)
{ {
pde_t *pgdir; pde_t *pgdir;
struct kmap *k;
// Allocate page directory if((pgdir = (pde_t*)kalloc()) == 0)
if (!(pgdir = (pde_t *) kalloc()))
return 0; return 0;
memset(pgdir, 0, PGSIZE); memset(pgdir, 0, PGSIZE);
// Map IO space from 640K to 1Mbyte k = kmap;
if (!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
return 0; if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 0)
// Map kernel text read-only
if (!mappages(pgdir, (void *) kerntext, kerntsz, kerntext, 0))
return 0;
// Map kernel data read/write
if (!mappages(pgdir, (void *) kerndata, kerndsz, kerndata, PTE_W))
return 0;
// Map dynamically-allocated memory read/write (kernel stacks, user mem)
if (!mappages(pgdir, (void *) kernend, freesz, PADDR(kernend), PTE_W))
return 0;
// Map devices such as ioapic, lapic, ...
if (!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W))
return 0;
return pgdir;
}
// return the physical address that a given user address
// maps to. the result is also a kernel logical address,
// since the kernel maps the physical memory allocated to user
// processes directly.
char*
uva2ka(pde_t *pgdir, char *uva)
{
pte_t *pte = walkpgdir(pgdir, uva, 0);
if (pte == 0) return 0;
uint pa = PTE_ADDR(*pte);
return (char *)pa;
}
// allocate sz bytes more memory for a process starting at the
// given user address; allocates physical memory and page
// table entries. addr and sz need not be page-aligned.
// it is a no-op for any parts of the requested memory
// that are already allocated.
int
allocuvm(pde_t *pgdir, char *addr, uint sz)
{
if (addr + sz > (char*)USERTOP)
return 0;
char *first = PGROUNDDOWN(addr);
char *last = PGROUNDDOWN(addr + sz - 1);
char *a;
for(a = first; a <= last; a += PGSIZE){
pte_t *pte = walkpgdir(pgdir, a, 0);
if(pte == 0 || (*pte & PTE_P) == 0){
char *mem = kalloc();
if(mem == 0){
// XXX clean up?
return 0;
}
memset(mem, 0, PGSIZE);
mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U);
}
}
return 1;
}
// deallocate some of the user pages, in response to sbrk()
// with a negative argument. if addr is not page-aligned,
// then only deallocates starting at the next page boundary.
int
deallocuvm(pde_t *pgdir, char *addr, uint sz)
{
if (addr + sz > (char*)USERTOP)
return 0;
char *first = (char*) PGROUNDUP((uint)addr);
char *last = PGROUNDDOWN(addr + sz - 1);
char *a;
for(a = first; a <= last; a += PGSIZE){
pte_t *pte = walkpgdir(pgdir, a, 0);
if(pte && (*pte & PTE_P) != 0){
uint pa = PTE_ADDR(*pte);
if(pa == 0)
panic("deallocuvm");
kfree((void *) pa);
*pte = 0;
}
}
return 1;
}
// free a page table and all the physical memory pages
// in the user part.
void
freevm(pde_t *pgdir)
{
uint i, j, da;
if (!pgdir)
panic("freevm: no pgdir\n");
for (i = 0; i < NPDENTRIES; i++) {
da = PTE_ADDR(pgdir[i]);
if (da != 0) {
pte_t *pgtab = (pte_t*) da;
for (j = 0; j < NPTENTRIES; j++) {
if (pgtab[j] != 0) {
uint pa = PTE_ADDR(pgtab[j]);
uint va = PGADDR(i, j, 0);
if (va < USERTOP) // user memory
kfree((void *) pa);
pgtab[j] = 0;
}
}
kfree((void *) da);
pgdir[i] = 0;
}
}
kfree((void *) pgdir);
}
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\n");
for (i = 0; i < sz; i += PGSIZE) {
if (!(pte = walkpgdir(pgdir, addr+i, 0)))
panic("loaduvm: address should exist\n");
pa = PTE_ADDR(*pte);
if (sz - i < PGSIZE) n = sz - i;
else n = PGSIZE;
if(readi(ip, (char *)pa, offset+i, n) != n)
return 0; return 0;
}
return 1;
}
void return pgdir;
inituvm(pde_t *pgdir, char *addr, char *init, uint sz)
{
uint i, pa, n, off;
pte_t *pte;
for (i = 0; i < sz; i += PGSIZE) {
if (!(pte = walkpgdir(pgdir, (void *)(i+addr), 0)))
panic("inituvm: pte should exist\n");
off = (i+(uint)addr) % PGSIZE;
pa = PTE_ADDR(*pte);
if (sz - i < PGSIZE) n = sz - i;
else n = PGSIZE;
memmove((char *)pa+off, init+i, n);
}
}
// 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 = setupkvm();
pte_t *pte;
uint pa, i;
char *mem;
if (!d) return 0;
for (i = 0; i < sz; i += PGSIZE) {
if (!(pte = walkpgdir(pgdir, (void *)i, 0)))
panic("copyuvm: pte should exist\n");
if(*pte & PTE_P){
pa = PTE_ADDR(*pte);
if (!(mem = kalloc()))
return 0;
memmove(mem, (char *)pa, PGSIZE);
if (!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U))
return 0;
}
}
return d;
}
// Gather information about physical memory layout.
// Called once during boot.
// Really should find out how much physical memory
// there is rather than assuming PHYSTOP.
void
pminit(void)
{
extern char end[];
struct proghdr *ph;
struct elfhdr *elf = (struct elfhdr*)0x10000; // scratch space
if (elf->magic != ELF_MAGIC || elf->phnum != 2)
panic("pminit: need a text and data segment\n");
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
kernend = ((uint)end + PGSIZE) & ~(PGSIZE-1);
kerntext = ph[0].va;
kerndata = ph[1].va;
kerntsz = ph[0].memsz;
kerndsz = ph[1].memsz;
freesz = PHYSTOP - kernend;
kinit((char *)kernend, freesz);
}
// Allocate one page table for the machine for the kernel address
// space for scheduler processes.
void
kvmalloc(void)
{
kpgdir = setupkvm();
} }
// Turn on paging. // Turn on paging.
@ -378,3 +160,208 @@ vmenable(void)
lcr0(cr0); lcr0(cr0);
} }
// Switch h/w page table register to the kernel-only page table,
// for when no process is running.
void
switchkvm(void)
{
lcr3(PADDR(kpgdir)); // switch to the kernel page table
}
// Switch TSS and h/w page table to correspond to process p.
void
switchuvm(struct proc *p)
{
pushcli();
cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
cpu->gdt[SEG_TSS].s = 0;
cpu->ts.ss0 = SEG_KDATA << 3;
cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
ltr(SEG_TSS << 3);
if(p->pgdir == 0)
panic("switchuvm: no pgdir");
lcr3(PADDR(p->pgdir)); // switch to new 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, PADDR(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, (char*)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 > USERTOP)
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);
mappages(pgdir, (char*)a, PGSIZE, PADDR(mem), PTE_W|PTE_U);
}
return newsz;
}
// Deallocate user pages to bring the process size from oldsz to
// newsz. oldsz and newsz need not be page-aligned, nor does newsz
// need to be less than oldsz. oldsz can be larger than the actual
// process size. Returns the new process size.
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 && (*pte & PTE_P) != 0){
pa = PTE_ADDR(*pte);
if(pa == 0)
panic("kfree");
kfree((char*)pa);
*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, USERTOP, 0);
for(i = 0; i < NPDENTRIES; i++){
if(pgdir[i] & PTE_P)
kfree((char*)PTE_ADDR(pgdir[i]));
}
kfree((char*)pgdir);
}
// 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;
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);
if((mem = kalloc()) == 0)
goto bad;
memmove(mem, (char*)pa, PGSIZE);
if(mappages(d, (void*)i, PGSIZE, PADDR(mem), PTE_W|PTE_U) < 0)
goto bad;
}
return d;
bad:
freevm(d);
return 0;
}
//PAGEBREAK!
// Map user virtual address to kernel physical 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*)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;
}

View file

@ -1,3 +0,0 @@
index.html: index.txt mkhtml
./mkhtml index.txt >_$@ && mv _$@ $@

BIN
web/boot.pdf Normal file

Binary file not shown.

BIN
web/disk.pdf Normal file

Binary file not shown.

BIN
web/exec.pdf Normal file

Binary file not shown.

BIN
web/fscall.pdf Normal file

Binary file not shown.

BIN
web/fsdata.pdf Normal file

Binary file not shown.

View file

@ -1,4 +1,3 @@
<!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version -->
<html> <html>
<head> <head>
<title>Xv6, a simple Unix-like teaching operating system</title> <title>Xv6, a simple Unix-like teaching operating system</title>
@ -32,31 +31,36 @@ h2 {
--></style> --></style>
</head> </head>
<body bgcolor=#ffffff> <body bgcolor=#ffffff>
<h1>Xv6, a simple Unix-like teaching operating system</h1> <h1>Xv6, a simple Unix-like teaching operating system</h1>
<br><br>
Xv6 is a teaching operating system developed <h2>Introduction</h2>
in the summer of 2006 for MIT's operating systems course,
&ldquo;6.828: Operating Systems Engineering.&rdquo; Xv6 is a teaching operating system developed in the summer of 2006 for
We used it for 6.828 in Fall 2006 and Fall 2007 MIT's operating systems
and are using it this semester (Fall 2008). course, <a href="http://pdos.csail.mit.edu/6.828">6.828: operating
We hope that xv6 will be useful in other courses too. systems Engineering</a>. We hope that xv6 will be useful in other
This page collects resources to aid the use of xv6 courses too. This page collects resources to aid the use of xv6 in
in other courses. other courses, including a commentary on the source code itself.
<p><font color="red">Status: The xv6 code is in pretty good shape, but
the commentary is rough.</font>
<h2>History and Background</h2> <h2>History and Background</h2>
For many years, MIT had no operating systems course.
In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit <p>For many years, MIT had no operating systems course. In the fall
created a new, experimental course (6.097) of 2002, Frans Kaashoek, Josh Cates, and Emil Sit created a new,
to teach operating systems engineering. experimental course (6.097) to teach operating systems engineering.
In the course lectures, the class worked through Sixth Edition Unix (aka V6) In the course lectures, the class worked through <a href="#v6">Sixth
using John Lions's famous commentary. Edition Unix (aka V6)</a> using John Lions's famous commentary. In
In the lab assignments, students wrote most of an exokernel operating the lab assignments, students wrote most of an exokernel operating
system, eventually named Jos, for the Intel x86. system, eventually named Jos, for the Intel x86. Exposing students to
Exposing students to multiple systems&ndash;V6 and Jos&ndash;helped multiple systems&ndash;V6 and Jos&ndash;helped develop a sense of the
develop a sense of the spectrum of operating system designs. spectrum of operating system designs. In the fall of 2003, the
In the fall of 2003, the experimental 6.097 became the experimental 6.097 became the official course 6.828; the course has
official course 6.828; the course has been offered each fall since then. been offered each fall since then.
<br><br>
<p>
V6 presented pedagogic challenges from the start. V6 presented pedagogic challenges from the start.
Students doubted the relevance of an obsolete 30-year-old operating system Students doubted the relevance of an obsolete 30-year-old operating system
written in an obsolete programming language (pre-K&R C) written in an obsolete programming language (pre-K&R C)
@ -76,13 +80,12 @@ uniprocessors such as
enabling/disabling interrupts) and helps relevance. enabling/disabling interrupts) and helps relevance.
Finally, writing a new system allowed us to write cleaner versions Finally, writing a new system allowed us to write cleaner versions
of the rougher parts of V6, like the scheduler and file system. of the rougher parts of V6, like the scheduler and file system.
<br><br> <p> 6.828 substituted xv6 for V6 in the fall of 2006. Based on
6.828 substituted xv6 for V6 in the fall of 2006. that experience, we cleaned up rough patches of xv6. Since then, xv6
Based on that experience, we cleaned up rough patches has stabilized, so we are making it available in the hopes that others
of xv6 for the course in the fall of 2007. will find it useful too.
Since then, xv6 has stabilized, so we are making it
available in the hopes that others will find it useful too. <p>
<br><br>
6.828 uses both xv6 and Jos. 6.828 uses both xv6 and Jos.
Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua, Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua,
and University Texas (Austin) have used and University Texas (Austin) have used
@ -90,14 +93,16 @@ Jos without xv6; we believe other courses could use
xv6 without Jos, though we are not aware of any that have. xv6 without Jos, though we are not aware of any that have.
<h2>Xv6 sources</h2> <h2>Xv6 sources</h2>
The latest xv6 is <a href="xv6-rev3.tar.gz">xv6-rev3.tar.gz</a>.
The latest xv6 is <a href="xv6-rev5.tar.gz">xv6-rev5.tar.gz</a>.
We distribute the sources in electronic form but also as We distribute the sources in electronic form but also as
a printed booklet with line numbers that keep everyone a printed booklet with line numbers that keep everyone
together during lectures. The booklet is available as together during lectures. The booklet is available as
<a href="xv6-rev3.pdf">xv6-rev3.pdf</a>. <a href="xv6-rev5.pdf">xv6-rev5.pdf</a>.
The xv6 source code is licensed under the traditional <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>; The xv6 source code is licensed under the traditional <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>;
see the LICENSE file in the source distribution. see the LICENSE file in the source distribution.
<br><br>
<p>
xv6 compiles using the GNU C compiler, xv6 compiles using the GNU C compiler,
targeted at the x86 using ELF binaries. targeted at the x86 using ELF binaries.
On BSD and Linux systems, you can use the native compilers; On BSD and Linux systems, you can use the native compilers;
@ -106,239 +111,131 @@ you must use a cross-compiler.
Xv6 does boot on real hardware, but typically Xv6 does boot on real hardware, but typically
we run it using the Bochs emulator. we run it using the Bochs emulator.
Both the GCC cross compiler and Bochs Both the GCC cross compiler and Bochs
can be found on the <a href="../../2007/tools.html">6.828 tools page</a>. can be found on the <a href="../2010/tools.html">6.828 tools page</a>.
<h2>Lectures</h2> <h2>Xv6 lecture material</h2>
In 6.828, the lectures in the first half of the course
introduce the PC hardware, the Intel x86, and then xv6.
The lectures in the second half consider advanced topics
using research papers; for some, xv6 serves as a useful
base for making discussions concrete.
This section describe a typical 6.828 lecture schedule,
linking to lecture notes and homework.
A course using only xv6 (not Jos) will need to adapt
a few of the lectures, but we hope these are a useful
starting point.
<br><br><b><i>Lecture 1. Operating systems</i></b> In 6.828, the lectures in the first half of the course introduce the
<br><br> PC hardware, the Intel x86, and then xv6. The lectures in the second
The first lecture introduces both the general topic of half consider advanced topics using research papers; for some, xv6
operating systems and the specific approach of 6.828. serves as a useful base for making discussions concrete. The lecture
After defining &ldquo;operating system,&rdquo; the lecture notes are available from the 6.828 schedule page, and the chapters of
examines the implementation of a Unix shell the commentary are below.
to look at the details the traditional Unix system call interface.
This is relevant to both xv6 and Jos: in the final
Jos labs, students implement a Unix-like interface
and culminating in a Unix shell.
<br><br>
<a href="l1.html">lecture notes</a>
<a href="os-lab-1.pdf">OS abstractions slides</a>
<br><br><b><i>Lecture 2. PC hardware and x86 programming</i></b> <h2>Xv6 commentary (rough)</h2>
<br><br>
This lecture introduces the PC architecture, the 16- and 32-bit x86,
the stack, and the GCC x86 calling conventions.
It also introduces the pieces of a typical C tool chain&ndash;compiler,
assembler, linker, loader&ndash;and the Bochs emulator.
<br><br>
Reading: PC Assembly Language
<br><br>
Homework: familiarize with Bochs
<br><br>
<a href="l2.html">lecture notes</a>
<a href="os-lab-2.pdf">x86 intro slides</a>
<a href="x86-intro.html">homework</a>
<br><br><b><i>Lecture 3. Operating system organization</i></b> <p>The chapters are rough drafts.
<br><br>
This lecture continues Lecture 1's discussion of what
an operating system does.
An operating system provides a &ldquo;virtual computer&rdquo;
interface to user space programs.
At a high level, the main job of the operating system
is to implement that interface
using the physical computer it runs on.
<br><br>
The lecture discusses four approaches to that job:
monolithic operating systems, microkernels,
virtual machines, and exokernels.
Exokernels might not be worth mentioning
except that the Jos labs are built around one.
<br><br>
Reading: Engler et al., Exokernel: An Operating System Architecture
for Application-Level Resource Management
<br><br>
<a href="l3.html">lecture notes</a>
<br><br><b><i>Lecture 4. Address spaces using segmentation</i></b> <p>Introduction yet to be written.<br>
<br><br> <ul>
This is the first lecture that uses xv6. <li>read with the code side by side
It introduces the idea of address spaces and the <li>code references look like (xxxx) or (xxxx-yyyy) in small text.
details of the x86 segmentation hardware. <li><a href="xv6-rev5.pdf">this pdf</a> is the one with matching line numbers.
It makes the discussion concrete by reading the xv6 <li>each chapter starts with an introduction to the topic,
source code and watching xv6 execute using the Bochs simulator. spends most of the text on code,
<br><br> and then wraps up talking about how xv6
Reading: x86 MMU handout, compares to real-world operating systems.
xv6: bootasm.S, bootother.S, <a href="src/bootmain.c.html">bootmain.c</a>, <a href="src/main.c.html">main.c</a>, <a href="src/init.c.html">init.c</a>, and setupsegs in <a href="src/proc.c.html">proc.c</a>. </ul>
<br><br>
Homework: Bochs stack introduction
<br><br>
<a href="l4.html">lecture notes</a>
<a href="os-lab-3.pdf">x86 virtual memory slides</a>
<a href="xv6-intro.html">homework</a>
<br><br><b><i>Lecture 5. Address spaces using page tables</i></b> <a href="unix.pdf">Chapter 0: Operating system interfaces</a>
<br><br> <blockquote>
This lecture continues the discussion of address spaces, The Unix system call interface. (rev 4)
examining the other x86 virtual memory mechanism: page tables. </blockquote>
Xv6 does not use page tables, so there is no xv6 here.
Instead, the lecture uses Jos as a concrete example.
An xv6-only course might skip or shorten this discussion.
<br><br>
Reading: x86 manual excerpts
<br><br>
Homework: stuff about gdt
XXX not appropriate; should be in Lecture 4
<br><br>
<a href="l5.html">lecture notes</a>
<br><br><b><i>Lecture 6. Interrupts and exceptions</i></b> <a href="boot.pdf">Chapter 1: Bootstrap</a>
<br><br> <blockquote>
How does a user program invoke the operating system kernel? From power on to kernel start. (rev 4)
How does the kernel return to the user program? </blockquote>
What happens when a hardware device needs attention?
This lecture explains the answer to these questions:
interrupt and exception handling.
<br><br>
It explains the x86 trap setup mechanisms and then
examines their use in xv6's SETGATE (<a href="src/mmu.h.html">mmu.h</a>),
tvinit (<a href="src/trap.c.html">trap.c</a>), idtinit (<a href="src/trap.c.html">trap.c</a>), <a href="src/vectors.pl.html">vectors.pl</a>, and vectors.S.
<br><br>
It then traces through a call to the system call open:
<a href="src/init.c.html">init.c</a>, usys.S, vector48 and alltraps (vectors.S), trap (<a href="src/trap.c.html">trap.c</a>),
syscall (<a href="src/syscall.c.html">syscall.c</a>),
sys_open (<a href="src/sysfile.c.html">sysfile.c</a>), fetcharg, fetchint, argint, argptr, argstr (<a href="src/syscall.c.html">syscall.c</a>),
<br><br>
The interrupt controller, briefly:
pic_init and pic_enable (<a href="src/picirq.c.html">picirq.c</a>).
The timer and keyboard, briefly:
timer_init (<a href="src/timer.c.html">timer.c</a>), console_init (<a href="src/console.c.html">console.c</a>).
Enabling and disabling of interrupts.
<br><br>
Reading: x86 manual excerpts,
xv6: trapasm.S, <a href="src/trap.c.html">trap.c</a>, <a href="src/syscall.c.html">syscall.c</a>, and usys.S.
Skim <a href="src/lapic.c.html">lapic.c</a>, <a href="src/ioapic.c.html">ioapic.c</a>, <a href="src/picirq.c.html">picirq.c</a>.
<br><br>
Homework: Explain the 35 words on the top of the
stack at first invocation of <code>syscall</code>.
<br><br>
<a href="l-interrupt.html">lecture notes</a>
<a href="x86-intr.html">homework</a>
<br><br><b><i>Lecture 7. Multiprocessors and locking</i></b> <a href="mem.pdf">Chapter 2: Processes</a>
<br><br> <blockquote>
This lecture introduces the problems of Memory and process allocation, segments, the first user process. (rev 4)
coordination and synchronization on a </blockquote>
multiprocessor
and then the solution of mutual exclusion locks.
Atomic instructions, test-and-set locks,
lock granularity, (the mistake of) recursive locks.
<br><br>
Although xv6 user programs cannot share memory,
the xv6 kernel itself is a program with multiple threads
executing concurrently and sharing memory.
Illustration: the xv6 scheduler's proc_table_lock (<a href="src/proc.c.html">proc.c</a>)
and the spin lock implementation (<a href="src/spinlock.c.html">spinlock.c</a>).
<br><br>
Reading: xv6: <a href="src/spinlock.c.html">spinlock.c</a>. Skim <a href="src/mp.c.html">mp.c</a>.
<br><br>
Homework: Interaction between locking and interrupts.
Try not disabling interrupts in the disk driver and watch xv6 break.
<br><br>
<a href="l-lock.html">lecture notes</a>
<a href="xv6-lock.html">homework</a>
<br><br><b><i>Lecture 8. Threads, processes and context switching</i></b> <a href="trap.pdf">Chapter 3: Traps</a>
<br><br> <blockquote>
The last lecture introduced some of the issues Low-level trap mechanism, trap handler, system call arguments, sbrk, fork.
in writing threaded programs, using xv6's processes </blockquote>
as an example.
This lecture introduces the issues in implementing
threads, continuing to use xv6 as the example.
<br><br>
The lecture defines a thread of computation as a register
set and a stack. A process is an address space plus one
or more threads of computation sharing that address space.
Thus the xv6 kernel can be viewed as a single process
with many threads (each user process) executing concurrently.
<br><br>
Illustrations: thread switching (swtch.S), scheduler (<a href="src/proc.c.html">proc.c</a>), sys_fork (<a href="src/sysproc.c.html">sysproc.c</a>)
<br><br>
Reading: <a href="src/proc.c.html">proc.c</a>, swtch.S, sys_fork (<a href="src/sysproc.c.html">sysproc.c</a>)
<br><br>
Homework: trace through stack switching.
<br><br>
<a href="l-threads.html">lecture notes (need to be updated to use swtch)</a>
<a href="xv6-sched.html">homework</a>
<br><br><b><i>Lecture 9. Processes and coordination</i></b> <a href="lock.pdf">Chapter 4: Locks</a>
<br><br> <blockquote>
This lecture introduces the idea of sequence coordination Locks and interrupts.
and then examines the particular solution illustrated by </blockquote>
sleep and wakeup (<a href="src/proc.c.html">proc.c</a>).
It introduces and refines a simple
producer/consumer queue to illustrate the
need for sleep and wakeup
and then the sleep and wakeup
implementations themselves.
<br><br>
Reading: <a href="src/proc.c.html">proc.c</a>, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (<a href="src/sysproc.c.html">sysproc.c</a>).
<br><br>
Homework: Explain how sleep and wakeup would break
without proc_table_lock. Explain how devices would break
without second lock argument to sleep.
<br><br>
<a href="l-coordination.html">lecture notes</a>
<a href="xv6-sleep.html">homework</a>
<br><br><b><i>Lecture 10. Files and disk I/O</i></b> <a href="sched.pdf">Chapter 5: Scheduling and coordination</a>
<br><br> <blockquote>
This is the first of three file system lectures. Scheduling, sleep and wakeup, pipes, wait and exit.
This lecture introduces the basic file system interface </blockquote>
and then considers the on-disk layout of individual files
and the free block bitmap.
<br><br>
Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and
code related to these calls in <a href="src/fs.c.html">fs.c</a>, <a href="src/bio.c.html">bio.c</a>, <a href="src/ide.c.html">ide.c</a>, and <a href="src/file.c.html">file.c</a>.
<br><br>
Homework: Add print to bwrite to trace every disk write.
Explain the disk writes caused by some simple shell commands.
<br><br>
<a href="l-fs.html">lecture notes</a>
<a href="xv6-disk.html">homework</a>
<br><br><b><i>Lecture 11. Naming</i></b> <a href="disk.pdf">Chapter 6: Buffer cache</a>
<br><br> <blockquote>
The last lecture discussed on-disk file system representation. Buffer cache and IDE disk driver.
This lecture covers the implementation of </blockquote>
file system paths (namei in <a href="src/fs.c.html">fs.c</a>)
and also discusses the security problems of a shared /tmp
and symbolic links.
<br><br>
Understanding exec (<a href="src/exec.c.html">exec.c</a>) is left as an exercise.
<br><br>
Reading: namei in <a href="src/fs.c.html">fs.c</a>, <a href="src/sysfile.c.html">sysfile.c</a>, <a href="src/file.c.html">file.c</a>.
<br><br>
Homework: Explain how to implement symbolic links in xv6.
<br><br>
<a href="l-name.html">lecture notes</a>
<a href="xv6-names.html">homework</a>
<br><br><b><i>Lecture 12. High-performance file systems</i></b> <a href="fsdata.pdf">Chapter 7: File system data</a>
<br><br> <blockquote>
This lecture is the first of the research paper-based lectures. Block in use bitmap, block allocation, inode structure, inode contents,
It discusses the &ldquo;soft updates&rdquo; paper, directories, path names.
using xv6 as a concrete example. </blockquote>
<a href="fscall.pdf">Chapter 8: File system calls</a>
<blockquote>
FIle descriptors, open, close, dup, read, write.
</blockquote>
<a href="exec.pdf">Chapter 9: Exec</a>
<blockquote>
Exec
</blockquote>
Appendix A: Low-level C and inline assembly
<blockquote>
Intro to C and inline assembly for people who only know Java (say).
Examples drawn entirely from xv6 source.
</blockquote>
Appendix B: Additional drivers.
<blockquote>
Keyboard, screen, probably MP hardware.
</blockquote>
<a name="v6"></a>
<h2>Unix Version 6</h2>
<p>6.828's xv6 is inspired by Unix V6 and by:
<ul>
<li>Lions' <i>Commentary on UNIX' 6th Edition</i>, John Lions, Peer to
Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, 2000).
<ul>
<li>An on-line version of the <a
href="http://www.lemis.com/grog/Documentation/Lions/">Lions
commentary</a>, and <a href="http://v6.cuzuco.com/">the source code</a>.
<li>The v6 source code is also available <a
href="http://minnie.tuhs.org/UnixTree/V6/usr/sys/">online</a>
through <a
href="http://minnie.tuhs.org/PUPS/">the PDP Unix Preservation
Society</a>.
</ul>
</ul>
The following are useful to read the original code:
<ul>
<li><i>
The PDP11/40 Processor Handbook</i>, Digital Equipment Corporation, 1972.
<ul>
<li>A <a href="http://pdos.csail.mit.edu/6.828/2005/readings/pdp11-40.pdf">PDF</a> (made from scanned images,
and not text-searchable)
<li>A <a href="http://pdos.csail.mit.edu/6.828/2005/pdp11/">web-based
version</a> that is indexed by instruction name.
</ul>
</ul>
<h2>Feedback</h2> <h2>Feedback</h2>
If you are interested in using xv6 or have used xv6 in a course, If you are interested in using xv6 or have used xv6 in a course,
@ -346,13 +243,10 @@ we would love to hear from you.
If there's anything that we can do to make xv6 easier If there's anything that we can do to make xv6 easier
to adopt, we'd like to hear about it. to adopt, we'd like to hear about it.
We'd also be interested to hear what worked well and what didn't. We'd also be interested to hear what worked well and what didn't.
<br><br> <p>
Russ Cox (rsc@swtch.com)<br> Russ Cox (rsc@swtch.com)<br>
Frans Kaashoek (kaashoek@mit.edu)<br> Frans Kaashoek (kaashoek@mit.edu)<br>
Robert Morris (rtm@mit.edu) Robert Morris (rtm@mit.edu)
<br><br> <p>
You can reach all of us at 6.828-staff@pdos.csail.mit.edu. You can reach all of us at 6.828-staff@pdos.csail.mit.edu.
<br><br>
<br><br>
</body>
</html>

View file

@ -1,339 +0,0 @@
** Xv6, a simple Unix-like teaching operating system
Xv6 is a teaching operating system developed
in the summer of 2006 for MIT's operating systems course,
``6.828: Operating Systems Engineering.''
We used it for 6.828 in Fall 2006 and Fall 2007
and are using it this semester (Fall 2008).
We hope that xv6 will be useful in other courses too.
This page collects resources to aid the use of xv6
in other courses.
* History and Background
For many years, MIT had no operating systems course.
In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit
created a new, experimental course (6.097)
to teach operating systems engineering.
In the course lectures, the class worked through Sixth Edition Unix (aka V6)
using John Lions's famous commentary.
In the lab assignments, students wrote most of an exokernel operating
system, eventually named Jos, for the Intel x86.
Exposing students to multiple systems--V6 and Jos--helped
develop a sense of the spectrum of operating system designs.
In the fall of 2003, the experimental 6.097 became the
official course 6.828; the course has been offered each fall since then.
V6 presented pedagogic challenges from the start.
Students doubted the relevance of an obsolete 30-year-old operating system
written in an obsolete programming language (pre-K&R C)
running on obsolete hardware (the PDP-11).
Students also struggled to learn the low-level details of two different
architectures (the PDP-11 and the Intel x86) at the same time.
By the summer of 2006, we had decided to replace V6
with a new operating system, xv6, modeled on V6
but written in ANSI C and running on multiprocessor
Intel x86 machines.
Xv6's use of the x86 makes it more relevant to
students' experience than V6 was
and unifies the course around a single architecture.
Adding multiprocessor support requires handling concurrency head on with
locks and threads (instead of using special-case solutions for
uniprocessors such as
enabling/disabling interrupts) and helps relevance.
Finally, writing a new system allowed us to write cleaner versions
of the rougher parts of V6, like the scheduler and file system.
6.828 substituted xv6 for V6 in the fall of 2006.
Based on that experience, we cleaned up rough patches
of xv6 for the course in the fall of 2007.
Since then, xv6 has stabilized, so we are making it
available in the hopes that others will find it useful too.
6.828 uses both xv6 and Jos.
Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua,
and University Texas (Austin) have used
Jos without xv6; we believe other courses could use
xv6 without Jos, though we are not aware of any that have.
* Xv6 sources
The latest xv6 is [xv6-rev2.tar.gz].
We distribute the sources in electronic form but also as
a printed booklet with line numbers that keep everyone
together during lectures. The booklet is available as
[xv6-rev2.pdf].
xv6 compiles using the GNU C compiler,
targeted at the x86 using ELF binaries.
On BSD and Linux systems, you can use the native compilers;
On OS X, which doesn't use ELF binaries,
you must use a cross-compiler.
Xv6 does boot on real hardware, but typically
we run it using the Bochs emulator.
Both the GCC cross compiler and Bochs
can be found on the [../../2007/tools.html | 6.828 tools page].
* Lectures
In 6.828, the lectures in the first half of the course
introduce the PC hardware, the Intel x86, and then xv6.
The lectures in the second half consider advanced topics
using research papers; for some, xv6 serves as a useful
base for making discussions concrete.
This section describe a typical 6.828 lecture schedule,
linking to lecture notes and homework.
A course using only xv6 (not Jos) will need to adapt
a few of the lectures, but we hope these are a useful
starting point.
Lecture 1. Operating systems
The first lecture introduces both the general topic of
operating systems and the specific approach of 6.828.
After defining ``operating system,'' the lecture
examines the implementation of a Unix shell
to look at the details the traditional Unix system call interface.
This is relevant to both xv6 and Jos: in the final
Jos labs, students implement a Unix-like interface
and culminating in a Unix shell.
[l1.html | lecture notes]
[os-lab-1.pdf | OS abstractions slides]
Lecture 2. PC hardware and x86 programming
This lecture introduces the PC architecture, the 16- and 32-bit x86,
the stack, and the GCC x86 calling conventions.
It also introduces the pieces of a typical C tool chain--compiler,
assembler, linker, loader--and the Bochs emulator.
Reading: PC Assembly Language
Homework: familiarize with Bochs
[l2.html | lecture notes]
[os-lab-2.pdf | x86 intro slides]
[x86-intro.html | homework]
Lecture 3. Operating system organization
This lecture continues Lecture 1's discussion of what
an operating system does.
An operating system provides a ``virtual computer''
interface to user space programs.
At a high level, the main job of the operating system
is to implement that interface
using the physical computer it runs on.
The lecture discusses four approaches to that job:
monolithic operating systems, microkernels,
virtual machines, and exokernels.
Exokernels might not be worth mentioning
except that the Jos labs are built around one.
Reading: Engler et al., Exokernel: An Operating System Architecture
for Application-Level Resource Management
[l3.html | lecture notes]
Lecture 4. Address spaces using segmentation
This is the first lecture that uses xv6.
It introduces the idea of address spaces and the
details of the x86 segmentation hardware.
It makes the discussion concrete by reading the xv6
source code and watching xv6 execute using the Bochs simulator.
Reading: x86 MMU handout,
xv6: bootasm.S, bootother.S, bootmain.c, main.c, init.c, and setupsegs in proc.c.
Homework: Bochs stack introduction
[l4.html | lecture notes]
[os-lab-3.pdf | x86 virtual memory slides]
[xv6-intro.html | homework]
Lecture 5. Address spaces using page tables
This lecture continues the discussion of address spaces,
examining the other x86 virtual memory mechanism: page tables.
Xv6 does not use page tables, so there is no xv6 here.
Instead, the lecture uses Jos as a concrete example.
An xv6-only course might skip or shorten this discussion.
Reading: x86 manual excerpts
Homework: stuff about gdt
XXX not appropriate; should be in Lecture 4
[l5.html | lecture notes]
Lecture 6. Interrupts and exceptions
How does a user program invoke the operating system kernel?
How does the kernel return to the user program?
What happens when a hardware device needs attention?
This lecture explains the answer to these questions:
interrupt and exception handling.
It explains the x86 trap setup mechanisms and then
examines their use in xv6's SETGATE (mmu.h),
tvinit (trap.c), idtinit (trap.c), vectors.pl, and vectors.S.
It then traces through a call to the system call open:
init.c, usys.S, vector48 and alltraps (vectors.S), trap (trap.c),
syscall (syscall.c),
sys_open (sysfile.c), fetcharg, fetchint, argint, argptr, argstr (syscall.c),
The interrupt controller, briefly:
pic_init and pic_enable (picirq.c).
The timer and keyboard, briefly:
timer_init (timer.c), console_init (console.c).
Enabling and disabling of interrupts.
Reading: x86 manual excerpts,
xv6: trapasm.S, trap.c, syscall.c, and usys.S.
Skim lapic.c, ioapic.c, picirq.c.
Homework: Explain the 35 words on the top of the
stack at first invocation of <code>syscall</code>.
[l-interrupt.html | lecture notes]
[x86-intr.html | homework]
Lecture 7. Multiprocessors and locking
This lecture introduces the problems of
coordination and synchronization on a
multiprocessor
and then the solution of mutual exclusion locks.
Atomic instructions, test-and-set locks,
lock granularity, (the mistake of) recursive locks.
Although xv6 user programs cannot share memory,
the xv6 kernel itself is a program with multiple threads
executing concurrently and sharing memory.
Illustration: the xv6 scheduler's proc_table_lock (proc.c)
and the spin lock implementation (spinlock.c).
Reading: xv6: spinlock.c. Skim mp.c.
Homework: Interaction between locking and interrupts.
Try not disabling interrupts in the disk driver and watch xv6 break.
[l-lock.html | lecture notes]
[xv6-lock.html | homework]
Lecture 8. Threads, processes and context switching
The last lecture introduced some of the issues
in writing threaded programs, using xv6's processes
as an example.
This lecture introduces the issues in implementing
threads, continuing to use xv6 as the example.
The lecture defines a thread of computation as a register
set and a stack. A process is an address space plus one
or more threads of computation sharing that address space.
Thus the xv6 kernel can be viewed as a single process
with many threads (each user process) executing concurrently.
Illustrations: thread switching (swtch.S), scheduler (proc.c), sys_fork (sysproc.c)
Reading: proc.c, swtch.S, sys_fork (sysproc.c)
Homework: trace through stack switching.
[l-threads.html | lecture notes (need to be updated to use swtch)]
[xv6-sched.html | homework]
Lecture 9. Processes and coordination
This lecture introduces the idea of sequence coordination
and then examines the particular solution illustrated by
sleep and wakeup (proc.c).
It introduces and refines a simple
producer/consumer queue to illustrate the
need for sleep and wakeup
and then the sleep and wakeup
implementations themselves.
Reading: proc.c, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (sysproc.c).
Homework: Explain how sleep and wakeup would break
without proc_table_lock. Explain how devices would break
without second lock argument to sleep.
[l-coordination.html | lecture notes]
[xv6-sleep.html | homework]
Lecture 10. Files and disk I/O
This is the first of three file system lectures.
This lecture introduces the basic file system interface
and then considers the on-disk layout of individual files
and the free block bitmap.
Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and
code related to these calls in fs.c, bio.c, ide.c, and file.c.
Homework: Add print to bwrite to trace every disk write.
Explain the disk writes caused by some simple shell commands.
[l-fs.html | lecture notes]
[xv6-disk.html | homework]
Lecture 11. Naming
The last lecture discussed on-disk file system representation.
This lecture covers the implementation of
file system paths (namei in fs.c)
and also discusses the security problems of a shared /tmp
and symbolic links.
Understanding exec (exec.c) is left as an exercise.
Reading: namei in fs.c, sysfile.c, file.c.
Homework: Explain how to implement symbolic links in xv6.
[l-name.html | lecture notes]
[xv6-names.html | homework]
Lecture 12. High-performance file systems
This lecture is the first of the research paper-based lectures.
It discusses the ``soft updates'' paper,
using xv6 as a concrete example.
* Feedback
If you are interested in using xv6 or have used xv6 in a course,
we would love to hear from you.
If there's anything that we can do to make xv6 easier
to adopt, we'd like to hear about it.
We'd also be interested to hear what worked well and what didn't.
Russ Cox (rsc@swtch.com)<br>
Frans Kaashoek (kaashoek@mit.edu)<br>
Robert Morris (rtm@mit.edu)
You can reach all of us at 6.828-staff@pdos.csail.mit.edu.
xv6 and lecture notes are copyright &copy; 2006-present by Russ Cox,
Frans Kaashoek, and Robert Morris.

BIN
web/lock.pdf Normal file

Binary file not shown.

BIN
web/mem.pdf Normal file

Binary file not shown.

View file

@ -1,70 +0,0 @@
#!/usr/bin/perl
my @lines = <>;
my $text = join('', @lines);
my $title;
if($text =~ /^\*\* (.*?)\n/m){
$title = $1;
$text = $` . $';
}else{
$title = "Untitled";
}
$text =~ s/[ \t]+$//mg;
$text =~ s/^$/<br><br>/mg;
$text =~ s!\b([a-z0-9]+\.(c|s|pl|h))\b!<a href="src/$1.html">$1</a>!g;
$text =~ s!^(Lecture [0-9]+\. .*?)$!<b><i>$1</i></b>!mg;
$text =~ s!^\* (.*?)$!<h2>$1</h2>!mg;
$text =~ s!((<br>)+\n)+<h2>!\n<h2>!g;
$text =~ s!</h2>\n?((<br>)+\n)+!</h2>\n!g;
$text =~ s!((<br>)+\n)+<b>!\n<br><br><b>!g;
$text =~ s!\b\s*--\s*\b!\&ndash;!g;
$text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!<a href="$1">$2</a>!g;
$text =~ s!\[([^ \t]+)\]!<a href="$1">$1</a>!g;
$text =~ s!``!\&ldquo;!g;
$text =~ s!''!\&rdquo;!g;
print <<EOF;
<!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version -->
<html>
<head>
<title>$title</title>
<style type="text/css"><!--
body {
background-color: white;
color: black;
font-size: medium;
line-height: 1.2em;
margin-left: 0.5in;
margin-right: 0.5in;
margin-top: 0;
margin-bottom: 0;
}
h1 {
text-indent: 0in;
text-align: left;
margin-top: 2em;
font-weight: bold;
font-size: 1.4em;
}
h2 {
text-indent: 0in;
text-align: left;
margin-top: 2em;
font-weight: bold;
font-size: 1.2em;
}
--></style>
</head>
<body bgcolor=#ffffff>
<h1>$title</h1>
<br><br>
EOF
print $text;
print <<EOF;
</body>
</html>
EOF

BIN
web/sched.pdf Normal file

Binary file not shown.

BIN
web/trap.pdf Normal file

Binary file not shown.

BIN
web/unix.pdf Normal file

Binary file not shown.

87
x86.h
View file

@ -90,25 +90,28 @@ readeflags(void)
return eflags; return eflags;
} }
static inline uint
xchg(volatile uint *addr, uint newval)
{
uint result;
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}
static inline void static inline void
loadgs(ushort v) loadgs(ushort v)
{ {
asm volatile("movw %0, %%gs" : : "r" (v)); asm volatile("movw %0, %%gs" : : "r" (v));
} }
static inline uint
rebp(void)
{
uint val;
asm volatile("movl %%ebp,%0" : "=r" (val));
return val;
}
static inline uint
resp(void)
{
uint val;
asm volatile("movl %%esp,%0" : "=r" (val));
return val;
}
static inline void static inline void
cli(void) cli(void)
{ {
@ -121,66 +124,56 @@ sti(void)
asm volatile("sti"); asm volatile("sti");
} }
static inline void lcr0(uint val) static inline uint
xchg(volatile uint *addr, uint newval)
{
uint result;
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}
//PAGEBREAK!
static inline void
lcr0(uint val)
{ {
asm volatile("movl %0,%%cr0" : : "r" (val)); asm volatile("movl %0,%%cr0" : : "r" (val));
} }
static inline uint rcr0(void) static inline uint
rcr0(void)
{ {
uint val; uint val;
asm volatile("movl %%cr0,%0" : "=r" (val)); asm volatile("movl %%cr0,%0" : "=r" (val));
return val; return val;
} }
static inline uint rcr2(void) static inline uint
rcr2(void)
{ {
uint val; uint val;
asm volatile("movl %%cr2,%0" : "=r" (val)); asm volatile("movl %%cr2,%0" : "=r" (val));
return val; return val;
} }
static inline void lcr3(uint val) static inline void
lcr3(uint val)
{ {
asm volatile("movl %0,%%cr3" : : "r" (val)); asm volatile("movl %0,%%cr3" : : "r" (val));
} }
static inline uint rcr3(void) static inline uint
rcr3(void)
{ {
uint val; uint val;
asm volatile("movl %%cr3,%0" : "=r" (val)); asm volatile("movl %%cr3,%0" : "=r" (val));
return val; return val;
} }
static inline void lebp(uint val)
{
asm volatile("movl %0,%%ebp" : : "r" (val));
}
static inline uint rebp(void)
{
uint val;
asm volatile("movl %%ebp,%0" : "=r" (val));
return val;
}
static inline void lesp(uint val)
{
asm volatile("movl %0,%%esp" : : "r" (val));
}
static inline uint resp(void)
{
uint val;
asm volatile("movl %%esp,%0" : "=r" (val));
return val;
}
static inline void nop_pause(void)
{
asm volatile("pause" : :);
}
//PAGEBREAK: 36 //PAGEBREAK: 36
// Layout of the trap frame built on the stack by the // Layout of the trap frame built on the stack by the
// hardware and by trapasm.S, and passed to trap(). // hardware and by trapasm.S, and passed to trap().

BIN
xv6-rev4.tar.gz Normal file

Binary file not shown.

BIN
xv6-rev5.pdf Normal file

Binary file not shown.

BIN
xv6-rev5.tar.gz Normal file

Binary file not shown.