Merge commit 'origin/master' into page
This commit is contained in:
commit
dccb915282
61 changed files with 1606 additions and 1616 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
*~
|
||||||
_*
|
_*
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
|
|
93
Makefile
93
Makefile
|
@ -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
13
README
|
@ -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/.
|
|
||||||
|
|
26
bootasm.S
26
bootasm.S
|
@ -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
|
||||||
|
|
|
@ -34,7 +34,7 @@ bootmain(void)
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
77
bootother.S
77
bootother.S
|
@ -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
|
||||||
|
|
28
console.c
28
console.c
|
@ -23,23 +23,24 @@ static struct {
|
||||||
} 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
|
||||||
|
|
||||||
|
@ -164,15 +164,12 @@ 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
26
data.S
Normal 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
|
29
defs.h
29
defs.h
|
@ -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]))
|
||||||
|
|
73
exec.c
73
exec.c
|
@ -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)
|
||||||
|
freevm(pgdir);
|
||||||
|
if(ip)
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
14
fs.c
14
fs.c
|
@ -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
2
fs.h
|
@ -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
|
||||||
|
|
||||||
|
|
4
ide.c
4
ide.c
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
18
kalloc.c
18
kalloc.c
|
@ -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,7 +41,7 @@ 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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
164
log.c
Normal file
164
log.c
Normal 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++;
|
||||||
|
}
|
55
main.c
55
main.c
|
@ -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
|
||||||
|
@ -64,16 +69,17 @@ 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,8 +88,9 @@ 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
|
||||||
|
// _binary_bootother_start.
|
||||||
code = (uchar*)0x7000;
|
code = (uchar*)0x7000;
|
||||||
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
|
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
|
||||||
|
|
||||||
|
@ -91,10 +98,13 @@ bootothers(void)
|
||||||
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
58
memide.c
Normal 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;
|
||||||
|
}
|
23
mkfs.c
23
mkfs.c
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
mmu.h
39
mmu.h
|
@ -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,10 +98,10 @@ 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)))
|
||||||
|
@ -99,7 +109,7 @@ struct segdesc {
|
||||||
// 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
|
||||||
|
@ -130,21 +140,6 @@ struct segdesc {
|
||||||
|
|
||||||
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
|
||||||
|
|
15
mp.c
15
mp.c
|
@ -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)
|
||||||
|
@ -114,8 +113,8 @@ mpinit(void)
|
||||||
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
75
multiboot.S
Normal 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
|
4
param.h
4
param.h
|
@ -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
|
||||||
|
|
||||||
|
|
29
picirq.c
29
picirq.c
|
@ -82,32 +82,3 @@ picinit(void)
|
||||||
if(irqmask != 0xFFFF)
|
if(irqmask != 0xFFFF)
|
||||||
picsetmask(irqmask);
|
picsetmask(irqmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Blank page.
|
|
||||||
|
|
255
proc.c
255
proc.c
|
@ -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;
|
|
||||||
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;
|
continue;
|
||||||
havekids = 1;
|
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
|
||||||
if(p->state == ZOMBIE){
|
state = states[p->state];
|
||||||
// Found one.
|
else
|
||||||
pid = p->pid;
|
state = "???";
|
||||||
kfree(p->kstack);
|
cprintf("%d %s %s", p->pid, state, p->name);
|
||||||
p->kstack = 0;
|
if(p->state == SLEEPING){
|
||||||
freevm(p->pgdir);
|
getcallerpcs((uint*)p->context->ebp+2, pc);
|
||||||
p->state = UNUSED;
|
for(i=0; i<10 && pc[i] != 0; i++)
|
||||||
p->pid = 0;
|
cprintf(" %p", pc[i]);
|
||||||
p->parent = 0;
|
}
|
||||||
p->name[0] = 0;
|
cprintf("\n");
|
||||||
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
64
proc.h
|
@ -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
24
runoff
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
50
runoff.spec
50
runoff.spec
|
@ -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
|
||||||
|
|
2
runoff1
2
runoff1
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
13
stressfs.c
13
stressfs.c
|
@ -14,20 +14,19 @@
|
||||||
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);
|
||||||
|
|
57
syscall.c
57
syscall.c
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
36
syscall.h
36
syscall.h
|
@ -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
|
||||||
|
|
|
@ -344,7 +344,7 @@ 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;
|
||||||
|
|
||||||
|
|
9
toc.ftr
9
toc.ftr
|
@ -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.
|
||||||
|
|
6
trap.c
6
trap.c
|
@ -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,7 +86,8 @@ 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;
|
||||||
|
|
4
user.h
4
user.h
|
@ -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*);
|
||||||
|
|
150
usertests.c
150
usertests.c
|
@ -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];
|
||||||
|
@ -324,6 +326,7 @@ 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;
|
||||||
|
@ -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,13 +1273,13 @@ 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?
|
||||||
|
@ -1318,8 +1323,8 @@ sbrktest(void)
|
||||||
|
|
||||||
// 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) :
|
||||||
|
"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();
|
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
605
vm.c
|
@ -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,252 +46,108 @@ 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 0;
|
||||||
|
|
||||||
return pgdir;
|
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 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
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.
|
||||||
void
|
void
|
||||||
vmenable(void)
|
vmenable(void)
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
index.html: index.txt mkhtml
|
|
||||||
./mkhtml index.txt >_$@ && mv _$@ $@
|
|
||||||
|
|
BIN
web/boot.pdf
Normal file
BIN
web/boot.pdf
Normal file
Binary file not shown.
BIN
web/disk.pdf
Normal file
BIN
web/disk.pdf
Normal file
Binary file not shown.
BIN
web/exec.pdf
Normal file
BIN
web/exec.pdf
Normal file
Binary file not shown.
BIN
web/fscall.pdf
Normal file
BIN
web/fscall.pdf
Normal file
Binary file not shown.
BIN
web/fsdata.pdf
Normal file
BIN
web/fsdata.pdf
Normal file
Binary file not shown.
412
web/index.html
412
web/index.html
|
@ -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,
|
|
||||||
“6.828: Operating Systems Engineering.”
|
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–V6 and Jos–helped
|
multiple systems–V6 and Jos–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 “operating system,” 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–compiler,
|
|
||||||
assembler, linker, loader–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 “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.
|
|
||||||
<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 “soft updates” 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>
|
|
||||||
|
|
339
web/index.txt
339
web/index.txt
|
@ -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 © 2006-present by Russ Cox,
|
|
||||||
Frans Kaashoek, and Robert Morris.
|
|
BIN
web/lock.pdf
Normal file
BIN
web/lock.pdf
Normal file
Binary file not shown.
BIN
web/mem.pdf
Normal file
BIN
web/mem.pdf
Normal file
Binary file not shown.
70
web/mkhtml
70
web/mkhtml
|
@ -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!\–!g;
|
|
||||||
$text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!<a href="$1">$2</a>!g;
|
|
||||||
$text =~ s!\[([^ \t]+)\]!<a href="$1">$1</a>!g;
|
|
||||||
|
|
||||||
$text =~ s!``!\“!g;
|
|
||||||
$text =~ s!''!\”!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
BIN
web/sched.pdf
Normal file
Binary file not shown.
BIN
web/trap.pdf
Normal file
BIN
web/trap.pdf
Normal file
Binary file not shown.
BIN
web/unix.pdf
Normal file
BIN
web/unix.pdf
Normal file
Binary file not shown.
87
x86.h
87
x86.h
|
@ -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
BIN
xv6-rev4.tar.gz
Normal file
Binary file not shown.
BIN
xv6-rev5.pdf
Normal file
BIN
xv6-rev5.pdf
Normal file
Binary file not shown.
BIN
xv6-rev5.tar.gz
Normal file
BIN
xv6-rev5.tar.gz
Normal file
Binary file not shown.
Loading…
Reference in a new issue