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
|
||||
*.d
|
||||
|
|
93
Makefile
93
Makefile
|
@ -26,19 +26,53 @@ OBJS = \
|
|||
uart.o\
|
||||
vectors.o\
|
||||
vm.o\
|
||||
log.o\
|
||||
|
||||
# Cross-compiling (e.g., on Mac OS X)
|
||||
TOOLPREFIX = i386-jos-elf-
|
||||
#TOOLPREFIX = i386-jos-elf-
|
||||
|
||||
# Using native tools (e.g., on X86 Linux)
|
||||
#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
|
||||
AS = $(TOOLPREFIX)gas
|
||||
LD = $(TOOLPREFIX)ld
|
||||
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||
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)
|
||||
ASFLAGS = -m32 -gdwarf-2
|
||||
# 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=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
|
||||
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
|
||||
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
|
||||
|
@ -69,11 +108,23 @@ initcode: initcode.S
|
|||
$(OBJCOPY) -S -O binary initcode.out initcode
|
||||
$(OBJDUMP) -S initcode.o > initcode.asm
|
||||
|
||||
kernel: $(OBJS) bootother initcode
|
||||
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother
|
||||
kernel: $(OBJS) multiboot.o data.o bootother initcode
|
||||
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
|
||||
$(OBJDUMP) -S kernel > kernel.asm
|
||||
$(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
|
||||
etags *.S *.c
|
||||
|
||||
|
@ -94,7 +145,7 @@ _forktest: forktest.o $(ULIB)
|
|||
$(OBJDUMP) -S _forktest > forktest.asm
|
||||
|
||||
mkfs: mkfs.c fs.h
|
||||
gcc -Wall -o mkfs mkfs.c
|
||||
gcc -m32 -Werror -Wall -o mkfs mkfs.c
|
||||
|
||||
UPROGS=\
|
||||
_cat\
|
||||
|
@ -126,7 +177,7 @@ clean:
|
|||
|
||||
# make a printout
|
||||
FILES = $(shell grep -v '^\#' runoff.list)
|
||||
PRINT = runoff.list $(FILES)
|
||||
PRINT = runoff.list runoff.spec $(FILES)
|
||||
|
||||
xv6.pdf: $(PRINT)
|
||||
./runoff
|
||||
|
@ -143,27 +194,33 @@ bochs : fs.img xv6.img
|
|||
# try to generate a unique GDB port
|
||||
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
|
||||
# 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)"; \
|
||||
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 -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 -nographic $(QEMUOPTS)
|
||||
$(QEMU) -nographic $(QEMUOPTS)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl
|
||||
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||
|
||||
qemu-gdb: fs.img xv6.img .gdbinit
|
||||
@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
|
||||
@echo "*** Now run 'gdb'." 1>&2
|
||||
qemu -nographic $(QEMUOPTS) -S $(QEMUGDB)
|
||||
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
|
||||
|
||||
# CUT HERE
|
||||
# prepare dist for students
|
||||
|
@ -195,14 +252,16 @@ dist-test:
|
|||
rm -rf dist-test
|
||||
mkdir dist-test
|
||||
cp dist/* dist-test
|
||||
cd dist-test; ../m print
|
||||
cd dist-test; ../m bochs || true
|
||||
cd dist-test; ../m qemu
|
||||
cd dist-test; $(MAKE) print
|
||||
cd dist-test; $(MAKE) bochs || true
|
||||
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.
|
||||
tar:
|
||||
rm -rf /tmp/xv6
|
||||
mkdir -p /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)
|
||||
Cliff Frey (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
|
||||
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.
|
||||
Bochs makes debugging easier, but QEMU is much faster.
|
||||
To run in Bochs, run "make bochs" and then type "c" at the bochs prompt.
|
||||
To run in QEMU, run "make qemu". Both log the xv6 screen output to
|
||||
standard output.
|
||||
To run in QEMU, run "make qemu".
|
||||
|
||||
To create a typeset version of the code, run "make xv6.pdf".
|
||||
This requires the "mpage" text formatting utility.
|
||||
See http://www.mesa.nl/pub/mpage/.
|
||||
To create a typeset version of the code, run "make xv6.pdf". This
|
||||
requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/.
|
||||
|
|
26
bootasm.S
26
bootasm.S
|
@ -13,7 +13,7 @@
|
|||
.code16 # Assemble for 16-bit mode
|
||||
.globl start
|
||||
start:
|
||||
cli # Disable interrupts
|
||||
cli # BIOS enabled interrupts; disable
|
||||
|
||||
# Set up the important data segment registers (DS, ES, SS).
|
||||
xorw %ax,%ax # Segment number zero
|
||||
|
@ -21,10 +21,8 @@ start:
|
|||
movw %ax,%es # -> Extra Segment
|
||||
movw %ax,%ss # -> Stack Segment
|
||||
|
||||
# Enable A20:
|
||||
# For backwards compatibility with the earliest PCs, physical
|
||||
# address line 20 is tied low, so that addresses higher than
|
||||
# 1MB wrap around to zero by default. This code undoes this.
|
||||
# Physical address line A20 is tied to zero so that the first PCs
|
||||
# with 2 MB would run software that assumed 1 MB. Undo that.
|
||||
seta20.1:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
|
@ -41,23 +39,21 @@ seta20.2:
|
|||
movb $0xdf,%al # 0xdf -> port 0x60
|
||||
outb %al,$0x60
|
||||
|
||||
//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.
|
||||
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||
# virtual addresses map dierctly to physical addresses so that the
|
||||
# effective memory map doesn't change during the transition.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# This ljmp is how you load the CS (Code Segment) register.
|
||||
# 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.
|
||||
//PAGEBREAK!
|
||||
# Complete transition to 32-bit protected mode by using long jmp
|
||||
# to reload %cs and %eip. The segment registers are set up with no
|
||||
# translation, so that the mapping is still the identity mapping.
|
||||
ljmp $(SEG_KCODE<<3), $start32
|
||||
|
||||
.code32 # Assemble for 32-bit mode
|
||||
.code32 # Tell assembler to generate 32-bit code now.
|
||||
start32:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||
|
|
|
@ -33,8 +33,8 @@ bootmain(void)
|
|||
// Load each program segment (ignores ph flags).
|
||||
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
|
||||
eph = ph + elf->phnum;
|
||||
for(; ph < eph; ph++) {
|
||||
va = (uchar*)(ph->va & 0xFFFFFF);
|
||||
for(; ph < eph; ph++){
|
||||
va = (uchar*)ph->va;
|
||||
readseg(va, ph->filesz, ph->offset);
|
||||
if(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.
|
||||
// Does not return!
|
||||
entry = (void(*)(void))(elf->entry & 0xFFFFFF);
|
||||
entry = (void(*)(void))(elf->entry);
|
||||
entry();
|
||||
}
|
||||
|
||||
|
|
77
bootother.S
77
bootother.S
|
@ -9,80 +9,69 @@
|
|||
# Because this code sets DS to zero, it must sit
|
||||
# at an address in the low 2^16 bytes.
|
||||
#
|
||||
# Bootothers (in main.c) sends the STARTUPs, one at a time.
|
||||
# It puts this code (start) at 0x7000.
|
||||
# It puts the correct %esp in start-4,
|
||||
# and the place to jump to in start-8.
|
||||
# Bootothers (in main.c) sends the STARTUPs one at a time.
|
||||
# It copies this code (start) at 0x7000.
|
||||
# It puts the address of a newly allocated per-core stack in start-4,
|
||||
# and the address of the place to jump to (mpmain) in start-8.
|
||||
#
|
||||
# This code is identical to bootasm.S except:
|
||||
# - it does not need to enable A20
|
||||
# - it uses the address at start-4 for the %esp
|
||||
# - it jumps to the address at start-8 instead of calling bootmain
|
||||
|
||||
#define SEG_KCODE 1 // kernel code
|
||||
#define SEG_KDATA 2 // kernel data+stack
|
||||
#define SEG_KCODE 1
|
||||
#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
|
||||
start:
|
||||
cli # Disable interrupts
|
||||
cli
|
||||
|
||||
# Set up the important data segment registers (DS, ES, SS).
|
||||
xorw %ax,%ax # Segment number zero
|
||||
movw %ax,%ds # -> Data Segment
|
||||
movw %ax,%es # -> Extra Segment
|
||||
movw %ax,%ss # -> Stack Segment
|
||||
xorw %ax,%ax
|
||||
movw %ax,%ds
|
||||
movw %ax,%es
|
||||
movw %ax,%ss
|
||||
|
||||
//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
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# This ljmp is how you load the CS (Code Segment) register.
|
||||
# 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.
|
||||
//PAGEBREAK!
|
||||
ljmp $(SEG_KCODE<<3), $start32
|
||||
|
||||
.code32 # Assemble for 32-bit mode
|
||||
.code32
|
||||
start32:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||
movw %ax, %ds # -> DS: Data Segment
|
||||
movw %ax, %es # -> ES: Extra Segment
|
||||
movw %ax, %ss # -> SS: Stack Segment
|
||||
movw $0, %ax # Zero segments not ready for use
|
||||
movw %ax, %fs # -> FS
|
||||
movw %ax, %gs # -> GS
|
||||
movw $(SEG_KDATA<<3), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
movw $0, %ax
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
# Set up the stack pointer and call into C.
|
||||
# switch to the stack allocated by bootothers()
|
||||
movl start-4, %esp
|
||||
|
||||
# call mpmain()
|
||||
call *(start-8)
|
||||
|
||||
# If the call returns (it shouldn't), trigger a Bochs
|
||||
# breakpoint if running under Bochs, then loop.
|
||||
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
|
||||
movw $0x8a00, %ax
|
||||
movw %ax, %dx
|
||||
outw %ax, %dx
|
||||
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
|
||||
movw $0x8ae0, %ax
|
||||
outw %ax, %dx
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
.p2align 2
|
||||
gdt:
|
||||
SEG_NULLASM # null seg
|
||||
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
|
||||
SEG_NULLASM
|
||||
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)
|
||||
SEG_ASM(STA_W, 0x0, 0xffffffff)
|
||||
|
||||
gdtdesc:
|
||||
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
|
||||
.long gdt # address gdt
|
||||
.word (gdtdesc - gdt - 1)
|
||||
.long gdt
|
||||
|
|
30
console.c
30
console.c
|
@ -23,23 +23,24 @@ static struct {
|
|||
} cons;
|
||||
|
||||
static void
|
||||
printint(int xx, int base, int sgn)
|
||||
printint(int xx, int base, int sign)
|
||||
{
|
||||
static char digits[] = "0123456789abcdef";
|
||||
char buf[16];
|
||||
int i = 0, neg = 0;
|
||||
int i;
|
||||
uint x;
|
||||
|
||||
if(sgn && xx < 0){
|
||||
neg = 1;
|
||||
if(sign && (sign = xx < 0))
|
||||
x = -xx;
|
||||
} else
|
||||
else
|
||||
x = xx;
|
||||
|
||||
i = 0;
|
||||
do{
|
||||
buf[i++] = digits[x % base];
|
||||
}while((x /= base) != 0);
|
||||
if(neg)
|
||||
|
||||
if(sign)
|
||||
buf[i++] = '-';
|
||||
|
||||
while(--i >= 0)
|
||||
|
@ -136,8 +137,7 @@ cgaputc(int c)
|
|||
if(c == '\n')
|
||||
pos += 80 - pos%80;
|
||||
else if(c == BACKSPACE){
|
||||
if(pos > 0)
|
||||
crt[--pos] = ' ' | 0x0700;
|
||||
if(pos > 0) --pos;
|
||||
} else
|
||||
crt[pos++] = (c&0xff) | 0x0700; // black on white
|
||||
|
||||
|
@ -163,16 +163,13 @@ consputc(int c)
|
|||
;
|
||||
}
|
||||
|
||||
if (c == BACKSPACE) {
|
||||
uartputc('\b');
|
||||
uartputc(' ');
|
||||
uartputc('\b');
|
||||
if(c == BACKSPACE){
|
||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
||||
} else
|
||||
uartputc(c);
|
||||
cgaputc(c);
|
||||
}
|
||||
|
||||
//PAGEBREAK: 50
|
||||
#define INPUT_BUF 128
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
|
@ -202,8 +199,7 @@ consoleintr(int (*getc)(void))
|
|||
consputc(BACKSPACE);
|
||||
}
|
||||
break;
|
||||
case C('H'): // Backspace
|
||||
case '\x7f':
|
||||
case C('H'): case '\x7f': // Backspace
|
||||
if(input.e != input.w){
|
||||
input.e--;
|
||||
consputc(BACKSPACE);
|
||||
|
@ -211,9 +207,7 @@ consoleintr(int (*getc)(void))
|
|||
break;
|
||||
default:
|
||||
if(c != 0 && input.e-input.r < INPUT_BUF){
|
||||
// The serial port produces 0x13, not 0x10
|
||||
if(c == '\r')
|
||||
c = '\n';
|
||||
c = (c == '\r') ? '\n' : c;
|
||||
input.buf[input.e++ % INPUT_BUF] = c;
|
||||
consputc(c);
|
||||
if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
|
||||
|
|
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
|
31
defs.h
31
defs.h
|
@ -6,6 +6,7 @@ struct pipe;
|
|||
struct proc;
|
||||
struct spinlock;
|
||||
struct stat;
|
||||
struct superblock;
|
||||
|
||||
// bio.c
|
||||
void binit(void);
|
||||
|
@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*);
|
|||
int filewrite(struct file*, char*, int n);
|
||||
|
||||
// fs.c
|
||||
void readsb(int dev, struct superblock *sb);
|
||||
int dirlink(struct inode*, char*, uint);
|
||||
struct inode* dirlookup(struct inode*, char*, uint*);
|
||||
struct inode* ialloc(uint, short);
|
||||
|
@ -62,7 +64,7 @@ void ioapicinit(void);
|
|||
// kalloc.c
|
||||
char* kalloc(void);
|
||||
void kfree(char*);
|
||||
void kinit(char*,uint);
|
||||
void kinit(void);
|
||||
|
||||
// kbd.c
|
||||
void kbdintr(void);
|
||||
|
@ -75,6 +77,12 @@ void lapicinit(int);
|
|||
void lapicstartap(uchar, uint);
|
||||
void microdelay(int);
|
||||
|
||||
// log.c
|
||||
void initlog(void);
|
||||
void log_write(struct buf*);
|
||||
void begin_trans();
|
||||
void commit_trans();
|
||||
|
||||
// mp.c
|
||||
extern int ismp;
|
||||
int mpbcpu(void);
|
||||
|
@ -101,6 +109,7 @@ int kill(int);
|
|||
void pinit(void);
|
||||
void procdump(void);
|
||||
void scheduler(void) __attribute__((noreturn));
|
||||
void sched(void);
|
||||
void sleep(void*, struct spinlock*);
|
||||
void userinit(void);
|
||||
int wait(void);
|
||||
|
@ -116,8 +125,8 @@ void getcallerpcs(void*, uint*);
|
|||
int holding(struct spinlock*);
|
||||
void initlock(struct spinlock*, char*);
|
||||
void release(struct spinlock*);
|
||||
void pushcli();
|
||||
void popcli();
|
||||
void pushcli(void);
|
||||
void popcli(void);
|
||||
|
||||
// string.c
|
||||
int memcmp(const void*, const void*, uint);
|
||||
|
@ -151,20 +160,20 @@ void uartintr(void);
|
|||
void uartputc(int);
|
||||
|
||||
// vm.c
|
||||
void pminit(void);
|
||||
void ksegment(void);
|
||||
void seginit(void);
|
||||
void kvmalloc(void);
|
||||
void vmenable(void);
|
||||
pde_t* setupkvm(void);
|
||||
char* uva2ka(pde_t*, char*);
|
||||
int allocuvm(pde_t*, char*, uint);
|
||||
int deallocuvm(pde_t *pgdir, char *addr, uint sz);
|
||||
int allocuvm(pde_t*, uint, uint);
|
||||
int deallocuvm(pde_t*, uint, uint);
|
||||
void freevm(pde_t*);
|
||||
void inituvm(pde_t*, char*, char*, uint);
|
||||
int loaduvm(pde_t*, char*, struct inode *ip, uint, uint);
|
||||
pde_t* copyuvm(pde_t*,uint);
|
||||
void inituvm(pde_t*, char*, uint);
|
||||
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
|
||||
pde_t* copyuvm(pde_t*, uint);
|
||||
void switchuvm(struct proc*);
|
||||
void switchkvm();
|
||||
void switchkvm(void);
|
||||
int copyout(pde_t*, uint, void*, uint);
|
||||
|
||||
// number of elements in fixed-size array
|
||||
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
|
73
exec.c
73
exec.c
|
@ -9,20 +9,18 @@
|
|||
int
|
||||
exec(char *path, char **argv)
|
||||
{
|
||||
char *mem, *s, *last;
|
||||
int i, argc, arglen, len, off;
|
||||
uint sz, sp, spoffset, argp;
|
||||
char *s, *last;
|
||||
int i, off;
|
||||
uint argc, sz, sp, ustack[3+MAXARG+1];
|
||||
struct elfhdr elf;
|
||||
struct inode *ip;
|
||||
struct proghdr ph;
|
||||
pde_t *pgdir, *oldpgdir;
|
||||
|
||||
pgdir = 0;
|
||||
sz = 0;
|
||||
|
||||
if((ip = namei(path)) == 0)
|
||||
return -1;
|
||||
ilock(ip);
|
||||
pgdir = 0;
|
||||
|
||||
// Check ELF header
|
||||
if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))
|
||||
|
@ -30,10 +28,11 @@ exec(char *path, char **argv)
|
|||
if(elf.magic != ELF_MAGIC)
|
||||
goto bad;
|
||||
|
||||
if (!(pgdir = setupkvm()))
|
||||
if((pgdir = setupkvm()) == 0)
|
||||
goto bad;
|
||||
|
||||
// Load program into memory.
|
||||
sz = 0;
|
||||
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
|
||||
if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
|
||||
goto bad;
|
||||
|
@ -41,49 +40,39 @@ exec(char *path, char **argv)
|
|||
continue;
|
||||
if(ph.memsz < ph.filesz)
|
||||
goto bad;
|
||||
if (!allocuvm(pgdir, (char *)ph.va, ph.memsz))
|
||||
if((sz = allocuvm(pgdir, sz, ph.va + ph.memsz)) == 0)
|
||||
goto bad;
|
||||
if(ph.va + ph.memsz > sz)
|
||||
sz = ph.va + ph.memsz;
|
||||
if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz))
|
||||
if(loaduvm(pgdir, (char*)ph.va, ip, ph.offset, ph.filesz) < 0)
|
||||
goto bad;
|
||||
}
|
||||
iunlockput(ip);
|
||||
ip = 0;
|
||||
|
||||
// Allocate and initialize stack at sz
|
||||
// Allocate a one-page stack at the next page boundary
|
||||
sz = PGROUNDUP(sz);
|
||||
sz += PGSIZE; // leave an invalid page
|
||||
if (!allocuvm(pgdir, (char *)sz, PGSIZE))
|
||||
if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0)
|
||||
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;
|
||||
argp = sz - arglen - 4*(argc+1);
|
||||
|
||||
// Copy argv strings and pointers to stack.
|
||||
*(uint*)(mem+argp-spoffset + 4*argc) = 0; // argv[argc]
|
||||
for(i=argc-1; i>=0; i--){
|
||||
len = strlen(argv[i]) + 1;
|
||||
sp -= len;
|
||||
memmove(mem+sp-spoffset, argv[i], len);
|
||||
*(uint*)(mem+argp-spoffset + 4*i) = sp; // argv[i]
|
||||
for(argc = 0; argv[argc]; argc++) {
|
||||
if(argc >= MAXARG)
|
||||
goto bad;
|
||||
sp -= strlen(argv[argc]) + 1;
|
||||
sp &= ~3;
|
||||
if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
|
||||
goto bad;
|
||||
ustack[3+argc] = sp;
|
||||
}
|
||||
ustack[3+argc] = 0;
|
||||
|
||||
// Stack frame for main(argc, argv), below arguments.
|
||||
sp = argp;
|
||||
sp -= 4;
|
||||
*(uint*)(mem+sp-spoffset) = argp;
|
||||
sp -= 4;
|
||||
*(uint*)(mem+sp-spoffset) = argc;
|
||||
sp -= 4;
|
||||
*(uint*)(mem+sp-spoffset) = 0xffffffff; // fake return pc
|
||||
ustack[0] = 0xffffffff; // fake return PC
|
||||
ustack[1] = argc;
|
||||
ustack[2] = sp - (argc+1)*4; // argv pointer
|
||||
|
||||
sp -= (3+argc+1) * 4;
|
||||
if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
|
||||
goto bad;
|
||||
|
||||
// Save program name for debugging.
|
||||
for(last=s=path; *s; s++)
|
||||
|
@ -97,15 +86,15 @@ exec(char *path, char **argv)
|
|||
proc->sz = sz;
|
||||
proc->tf->eip = elf.entry; // main
|
||||
proc->tf->esp = sp;
|
||||
|
||||
switchuvm(proc);
|
||||
|
||||
freevm(oldpgdir);
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
if (pgdir) freevm(pgdir);
|
||||
if(pgdir)
|
||||
freevm(pgdir);
|
||||
if(ip)
|
||||
iunlockput(ip);
|
||||
return -1;
|
||||
}
|
||||
|
|
14
fs.c
14
fs.c
|
@ -25,7 +25,7 @@
|
|||
static void itrunc(struct inode*);
|
||||
|
||||
// Read the super block.
|
||||
static void
|
||||
void
|
||||
readsb(int dev, struct superblock *sb)
|
||||
{
|
||||
struct buf *bp;
|
||||
|
@ -61,11 +61,11 @@ balloc(uint dev)
|
|||
readsb(dev, &sb);
|
||||
for(b = 0; b < sb.size; b += BPB){
|
||||
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);
|
||||
if((bp->data[bi/8] & m) == 0){ // Is block free?
|
||||
bp->data[bi/8] |= m; // Mark block in use on disk.
|
||||
bwrite(bp);
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
return b + bi;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ bfree(int dev, uint b)
|
|||
if((bp->data[bi/8] & m) == 0)
|
||||
panic("freeing free block");
|
||||
bp->data[bi/8] &= ~m; // Mark block free on disk.
|
||||
bwrite(bp);
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ ialloc(uint dev, short type)
|
|||
if(dip->type == 0){ // a free inode
|
||||
memset(dip, 0, sizeof(*dip));
|
||||
dip->type = type;
|
||||
bwrite(bp); // mark it allocated on the disk
|
||||
log_write(bp); // mark it allocated on the disk
|
||||
brelse(bp);
|
||||
return iget(dev, inum);
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ iupdate(struct inode *ip)
|
|||
dip->nlink = ip->nlink;
|
||||
dip->size = ip->size;
|
||||
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
||||
bwrite(bp);
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn)
|
|||
a = (uint*)bp->data;
|
||||
if((addr = a[bn]) == 0){
|
||||
a[bn] = addr = balloc(ip->dev);
|
||||
bwrite(bp);
|
||||
log_write(bp);
|
||||
}
|
||||
brelse(bp);
|
||||
return addr;
|
||||
|
|
2
fs.h
2
fs.h
|
@ -13,6 +13,7 @@ struct superblock {
|
|||
uint size; // Size of file system image (blocks)
|
||||
uint nblocks; // Number of data blocks
|
||||
uint ninodes; // Number of inodes.
|
||||
uint nlog; // Number of log blocks
|
||||
};
|
||||
|
||||
#define NDIRECT 12
|
||||
|
@ -41,7 +42,6 @@ struct dinode {
|
|||
// Block containing bit for block b
|
||||
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)
|
||||
|
||||
// PAGEBREAK: 10
|
||||
// Directory is a file containing a sequence of dirent structures.
|
||||
#define DIRSIZ 14
|
||||
|
||||
|
|
6
ide.c
6
ide.c
|
@ -96,7 +96,7 @@ ideintr(void)
|
|||
acquire(&idelock);
|
||||
if((b = idequeue) == 0){
|
||||
release(&idelock);
|
||||
cprintf("Spurious IDE interrupt.\n");
|
||||
// cprintf("spurious IDE interrupt\n");
|
||||
return;
|
||||
}
|
||||
idequeue = b->qnext;
|
||||
|
@ -131,7 +131,7 @@ iderw(struct buf *b)
|
|||
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
|
||||
panic("iderw: nothing to do");
|
||||
if(b->dev != 0 && !havedisk1)
|
||||
panic("idrw: ide disk 1 not present");
|
||||
panic("iderw: ide disk 1 not present");
|
||||
|
||||
acquire(&idelock);
|
||||
|
||||
|
@ -147,7 +147,7 @@ iderw(struct buf *b)
|
|||
|
||||
// Wait for request to finish.
|
||||
// Assuming will not sleep too long: ignore proc->killed.
|
||||
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) {
|
||||
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
|
||||
sleep(b, &idelock);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
#include "syscall.h"
|
||||
#include "traps.h"
|
||||
|
||||
|
||||
# exec(init, argv)
|
||||
.globl start
|
||||
start:
|
||||
movl $SYS_init, %eax
|
||||
int $T_SYSCALL
|
||||
pushl $argv
|
||||
pushl $init
|
||||
pushl $0 // where caller pc would be
|
||||
|
|
22
kalloc.c
22
kalloc.c
|
@ -17,17 +17,21 @@ struct {
|
|||
struct run *freelist;
|
||||
} kmem;
|
||||
|
||||
extern char end[]; // first address after kernel loaded from ELF file
|
||||
|
||||
// Initialize free list of physical pages.
|
||||
void
|
||||
kinit(char *p, uint len)
|
||||
kinit(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
initlock(&kmem.lock, "kmem");
|
||||
char *p1 = (char*)PGROUNDUP((uint)p);
|
||||
char *p2 = PGROUNDDOWN(p + len);
|
||||
for( ; p1 < p2; p1 += 4096)
|
||||
kfree(p1);
|
||||
p = (char*)PGROUNDUP((uint)end);
|
||||
for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE)
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
//PAGEBREAK: 21
|
||||
// Free the page of physical memory pointed at by v,
|
||||
// which normally should have been returned by a
|
||||
// call to kalloc(). (The exception is when
|
||||
|
@ -37,14 +41,14 @@ kfree(char *v)
|
|||
{
|
||||
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");
|
||||
|
||||
// Fill with junk to catch dangling refs.
|
||||
memset(v, 1, PGSIZE);
|
||||
|
||||
acquire(&kmem.lock);
|
||||
r = (struct run *) v;
|
||||
r = (struct run*)v;
|
||||
r->next = kmem.freelist;
|
||||
kmem.freelist = r;
|
||||
release(&kmem.lock);
|
||||
|
@ -54,7 +58,7 @@ kfree(char *v)
|
|||
// Returns a pointer that the kernel can use.
|
||||
// Returns 0 if the memory cannot be allocated.
|
||||
char*
|
||||
kalloc()
|
||||
kalloc(void)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
|
@ -63,6 +67,6 @@ kalloc()
|
|||
if(r)
|
||||
kmem.freelist = r->next;
|
||||
release(&kmem.lock);
|
||||
return (char*) r;
|
||||
return (char*)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++;
|
||||
}
|
59
main.c
59
main.c
|
@ -7,40 +7,45 @@
|
|||
|
||||
static void bootothers(void);
|
||||
static void mpmain(void);
|
||||
void jkstack(void) __attribute__((noreturn));
|
||||
void jmpkstack(void) __attribute__((noreturn));
|
||||
void mainc(void);
|
||||
|
||||
// Bootstrap processor starts running C code here.
|
||||
// Allocate a real stack and switch to it, first
|
||||
// doing some setup required for memory allocator to work.
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
mpinit(); // collect info about this machine
|
||||
lapicinit(mpbcpu());
|
||||
ksegment(); // set up segments
|
||||
picinit(); // interrupt controller
|
||||
ioapicinit(); // another interrupt controller
|
||||
consoleinit(); // I/O devices & their interrupts
|
||||
uartinit(); // serial port
|
||||
pminit(); // discover how much memory there is
|
||||
jkstack(); // call mainc() on a properly-allocated stack
|
||||
seginit(); // set up segments
|
||||
kinit(); // initialize memory allocator
|
||||
jmpkstack(); // call mainc() on a properly-allocated stack
|
||||
}
|
||||
|
||||
void
|
||||
jkstack(void)
|
||||
jmpkstack(void)
|
||||
{
|
||||
char *kstack = kalloc();
|
||||
if (!kstack)
|
||||
panic("jkstack\n");
|
||||
char *top = kstack + PGSIZE;
|
||||
asm volatile("movl %0,%%esp" : : "r" (top));
|
||||
asm volatile("call mainc");
|
||||
panic("jkstack");
|
||||
char *kstack, *top;
|
||||
|
||||
kstack = kalloc();
|
||||
if(kstack == 0)
|
||||
panic("jmpkstack kalloc");
|
||||
top = kstack + PGSIZE;
|
||||
asm volatile("movl %0,%%esp; call mainc" : : "r" (top));
|
||||
panic("jmpkstack");
|
||||
}
|
||||
|
||||
// Set up hardware and software.
|
||||
// Runs only on the boostrap processor.
|
||||
void
|
||||
mainc(void)
|
||||
{
|
||||
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
|
||||
pinit(); // process table
|
||||
tvinit(); // trap vectors
|
||||
|
@ -63,17 +68,18 @@ mainc(void)
|
|||
static void
|
||||
mpmain(void)
|
||||
{
|
||||
if(cpunum() != mpbcpu()) {
|
||||
ksegment();
|
||||
if(cpunum() != mpbcpu()){
|
||||
seginit();
|
||||
lapicinit(cpunum());
|
||||
}
|
||||
vmenable(); // turn on paging
|
||||
cprintf("cpu%d: starting\n", cpu->id);
|
||||
idtinit(); // load idt register
|
||||
xchg(&cpu->booted, 1);
|
||||
xchg(&cpu->booted, 1); // tell bootothers() we're up
|
||||
scheduler(); // start running processes
|
||||
}
|
||||
|
||||
// Start the non-boot processors.
|
||||
static void
|
||||
bootothers(void)
|
||||
{
|
||||
|
@ -82,19 +88,23 @@ bootothers(void)
|
|||
struct cpu *c;
|
||||
char *stack;
|
||||
|
||||
// Write bootstrap code to unused memory at 0x7000. The linker has
|
||||
// placed the start of bootother.S there.
|
||||
code = (uchar *) 0x7000;
|
||||
// Write bootstrap code to unused memory at 0x7000.
|
||||
// The linker has placed the image of bootother.S in
|
||||
// _binary_bootother_start.
|
||||
code = (uchar*)0x7000;
|
||||
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
|
||||
|
||||
for(c = cpus; c < cpus+ncpu; c++){
|
||||
if(c == cpus+cpunum()) // We've started already.
|
||||
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();
|
||||
*(void**)(code-4) = stack + KSTACKSIZE;
|
||||
*(void**)(code-8) = mpmain;
|
||||
|
||||
lapicstartap(c->id, (uint)code);
|
||||
|
||||
// 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;
|
||||
}
|
47
mkfs.c
47
mkfs.c
|
@ -4,11 +4,15 @@
|
|||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define stat xv6_stat // avoid clash with host struct stat
|
||||
#include "types.h"
|
||||
#include "fs.h"
|
||||
#include "stat.h"
|
||||
#include "param.h"
|
||||
|
||||
int nblocks = 995;
|
||||
int nblocks = 985;
|
||||
int nlog = LOGSIZE;
|
||||
int ninodes = 200;
|
||||
int size = 1024;
|
||||
|
||||
|
@ -33,7 +37,7 @@ ushort
|
|||
xshort(ushort x)
|
||||
{
|
||||
ushort y;
|
||||
uchar *a = (uchar*) &y;
|
||||
uchar *a = (uchar*)&y;
|
||||
a[0] = x;
|
||||
a[1] = x >> 8;
|
||||
return y;
|
||||
|
@ -43,7 +47,7 @@ uint
|
|||
xint(uint x)
|
||||
{
|
||||
uint y;
|
||||
uchar *a = (uchar*) &y;
|
||||
uchar *a = (uchar*)&y;
|
||||
a[0] = x;
|
||||
a[1] = x >> 8;
|
||||
a[2] = x >> 16;
|
||||
|
@ -77,20 +81,23 @@ main(int argc, char *argv[])
|
|||
sb.size = xint(size);
|
||||
sb.nblocks = xint(nblocks); // so whole disk is size sectors
|
||||
sb.ninodes = xint(ninodes);
|
||||
sb.nlog = xint(nlog);
|
||||
|
||||
bitblocks = size/(512*8) + 1;
|
||||
usedblocks = ninodes / IPB + 3 + bitblocks;
|
||||
freeblock = usedblocks;
|
||||
|
||||
printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks,
|
||||
bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks);
|
||||
printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", 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(1, &sb);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memmove(buf, &sb, sizeof(sb));
|
||||
wsect(1, buf);
|
||||
|
||||
rootino = ialloc(T_DIR);
|
||||
assert(rootino == ROOTINO);
|
||||
|
@ -173,7 +180,7 @@ winode(uint inum, struct dinode *ip)
|
|||
|
||||
bn = i2b(inum);
|
||||
rsect(bn, buf);
|
||||
dip = ((struct dinode*) buf) + (inum % IPB);
|
||||
dip = ((struct dinode*)buf) + (inum % IPB);
|
||||
*dip = *ip;
|
||||
wsect(bn, buf);
|
||||
}
|
||||
|
@ -187,7 +194,7 @@ rinode(uint inum, struct dinode *ip)
|
|||
|
||||
bn = i2b(inum);
|
||||
rsect(bn, buf);
|
||||
dip = ((struct dinode*) buf) + (inum % IPB);
|
||||
dip = ((struct dinode*)buf) + (inum % IPB);
|
||||
*ip = *dip;
|
||||
}
|
||||
|
||||
|
@ -225,12 +232,12 @@ balloc(int used)
|
|||
int i;
|
||||
|
||||
printf("balloc: first %d blocks have been allocated\n", used);
|
||||
assert(used < 512);
|
||||
assert(used < 512*8);
|
||||
bzero(buf, 512);
|
||||
for(i = 0; i < used; i++) {
|
||||
for(i = 0; i < used; i++){
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -239,7 +246,7 @@ balloc(int used)
|
|||
void
|
||||
iappend(uint inum, void *xp, int n)
|
||||
{
|
||||
char *p = (char*) xp;
|
||||
char *p = (char*)xp;
|
||||
uint fbn, off, n1;
|
||||
struct dinode din;
|
||||
char buf[512];
|
||||
|
@ -252,24 +259,24 @@ iappend(uint inum, void *xp, int n)
|
|||
while(n > 0){
|
||||
fbn = off / 512;
|
||||
assert(fbn < MAXFILE);
|
||||
if(fbn < NDIRECT) {
|
||||
if(xint(din.addrs[fbn]) == 0) {
|
||||
if(fbn < NDIRECT){
|
||||
if(xint(din.addrs[fbn]) == 0){
|
||||
din.addrs[fbn] = xint(freeblock++);
|
||||
usedblocks++;
|
||||
}
|
||||
x = xint(din.addrs[fbn]);
|
||||
} else {
|
||||
if(xint(din.addrs[NDIRECT]) == 0) {
|
||||
if(xint(din.addrs[NDIRECT]) == 0){
|
||||
// printf("allocate indirect block\n");
|
||||
din.addrs[NDIRECT] = xint(freeblock++);
|
||||
usedblocks++;
|
||||
}
|
||||
// printf("read indirect block\n");
|
||||
rsect(xint(din.addrs[NDIRECT]), (char*) indirect);
|
||||
if(indirect[fbn - NDIRECT] == 0) {
|
||||
rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
|
||||
if(indirect[fbn - NDIRECT] == 0){
|
||||
indirect[fbn - NDIRECT] = xint(freeblock++);
|
||||
usedblocks++;
|
||||
wsect(xint(din.addrs[NDIRECT]), (char*) indirect);
|
||||
wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
|
||||
}
|
||||
x = xint(indirect[fbn-NDIRECT]);
|
||||
}
|
||||
|
|
47
mmu.h
47
mmu.h
|
@ -24,6 +24,20 @@
|
|||
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
|
||||
#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
|
||||
struct segdesc {
|
||||
uint lim_15_0 : 16; // Low bits of segment limit
|
||||
|
@ -46,7 +60,6 @@ struct segdesc {
|
|||
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
|
||||
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
|
||||
|
||||
#define SEG16(type, base, lim, dpl) (struct segdesc) \
|
||||
{ (lim) & 0xffff, (uint)(base) & 0xffff, \
|
||||
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
|
@ -62,8 +75,6 @@ struct segdesc {
|
|||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
//
|
||||
|
||||
// System segment type bits
|
||||
#define STS_T16A 0x1 // Available 16-bit TSS
|
||||
#define STS_LDT 0x2 // Local Descriptor Table
|
||||
|
@ -78,7 +89,6 @@ struct segdesc {
|
|||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
|
||||
// A linear address 'la' has a three-part structure as follows:
|
||||
//
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
|
@ -88,18 +98,18 @@ struct segdesc {
|
|||
// \--- PDX(la) --/ \--- PTX(la) --/
|
||||
|
||||
// page directory index
|
||||
#define PDX(la) ((((uint) (la)) >> PDXSHIFT) & 0x3FF)
|
||||
#define PDX(la) (((uint)(la) >> PDXSHIFT) & 0x3FF)
|
||||
|
||||
// page table index
|
||||
#define PTX(la) ((((uint) (la)) >> PTXSHIFT) & 0x3FF)
|
||||
#define PTX(la) (((uint)(la) >> PTXSHIFT) & 0x3FF)
|
||||
|
||||
// construct linear address from indexes and offset
|
||||
#define PGADDR(d, t, o) ((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
|
||||
// turn a kernel linear address into a physical address.
|
||||
// all of the kernel data structures have linear and
|
||||
// physical addresses that are equal.
|
||||
#define PADDR(a) ((uint) a)
|
||||
#define PADDR(a) ((uint)(a))
|
||||
|
||||
// Page directory and page table constants.
|
||||
#define NPDENTRIES 1024 // page directory entries per page directory
|
||||
|
@ -126,25 +136,10 @@ struct segdesc {
|
|||
#define PTE_MBZ 0x180 // Bits must be zero
|
||||
|
||||
// Address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((uint) (pte) & ~0xFFF)
|
||||
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
|
||||
|
||||
typedef uint pte_t;
|
||||
|
||||
// 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
|
||||
struct taskstate {
|
||||
uint link; // Old ts selector
|
||||
|
@ -210,7 +205,7 @@ struct gatedesc {
|
|||
// this interrupt/trap gate explicitly using an int instruction.
|
||||
#define SETGATE(gate, istrap, sel, off, d) \
|
||||
{ \
|
||||
(gate).off_15_0 = (uint) (off) & 0xffff; \
|
||||
(gate).off_15_0 = (uint)(off) & 0xffff; \
|
||||
(gate).cs = (sel); \
|
||||
(gate).args = 0; \
|
||||
(gate).rsv1 = 0; \
|
||||
|
@ -218,6 +213,6 @@ struct gatedesc {
|
|||
(gate).s = 0; \
|
||||
(gate).dpl = (d); \
|
||||
(gate).p = 1; \
|
||||
(gate).off_31_16 = (uint) (off) >> 16; \
|
||||
(gate).off_31_16 = (uint)(off) >> 16; \
|
||||
}
|
||||
|
||||
|
|
17
mp.c
17
mp.c
|
@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len)
|
|||
{
|
||||
uchar *e, *p;
|
||||
|
||||
cprintf("mpsearch1 0x%x %d\n", addr, len);
|
||||
e = addr+len;
|
||||
for(p = addr; p < e; p += sizeof(struct mp))
|
||||
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
|
||||
|
@ -113,9 +112,9 @@ mpinit(void)
|
|||
switch(*p){
|
||||
case MPPROC:
|
||||
proc = (struct mpproc*)p;
|
||||
if(ncpu != proc->apicid) {
|
||||
cprintf("mpinit: ncpu=%d apicpid=%d", ncpu, proc->apicid);
|
||||
panic("mpinit");
|
||||
if(ncpu != proc->apicid){
|
||||
cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
|
||||
ismp = 0;
|
||||
}
|
||||
if(proc->flags & MPBOOT)
|
||||
bcpu = &cpus[ncpu];
|
||||
|
@ -135,9 +134,17 @@ mpinit(void)
|
|||
continue;
|
||||
default:
|
||||
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){
|
||||
// Bochs doesn't support IMCR, so this doesn't run on Bochs.
|
||||
// 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 NDEV 10 // maximum major device number
|
||||
#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 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)
|
||||
picsetmask(irqmask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Blank page.
|
||||
|
|
4
pipe.c
4
pipe.c
|
@ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable)
|
|||
p->readopen = 0;
|
||||
wakeup(&p->nwrite);
|
||||
}
|
||||
if(p->readopen == 0 && p->writeopen == 0) {
|
||||
if(p->readopen == 0 && p->writeopen == 0){
|
||||
release(&p->lock);
|
||||
kfree((char*)p);
|
||||
} else
|
||||
|
@ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n)
|
|||
|
||||
acquire(&p->lock);
|
||||
for(i = 0; i < n; i++){
|
||||
while(p->nwrite == p->nread + PIPESIZE) { //DOC: pipewrite-full
|
||||
while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full
|
||||
if(p->readopen == 0 || proc->killed){
|
||||
release(&p->lock);
|
||||
return -1;
|
||||
|
|
255
proc.c
255
proc.c
|
@ -17,53 +17,18 @@ int nextpid = 1;
|
|||
extern void forkret(void);
|
||||
extern void trapret(void);
|
||||
|
||||
static void wakeup1(void *chan);
|
||||
|
||||
void
|
||||
pinit(void)
|
||||
{
|
||||
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
|
||||
// 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.
|
||||
static struct proc*
|
||||
allocproc(void)
|
||||
|
@ -95,7 +60,7 @@ found:
|
|||
p->tf = (struct trapframe*)sp;
|
||||
|
||||
// Set up new context to start executing at forkret,
|
||||
// which returns to trapret (see below).
|
||||
// which returns to trapret.
|
||||
sp -= 4;
|
||||
*(uint*)sp = (uint)trapret;
|
||||
|
||||
|
@ -103,6 +68,7 @@ found:
|
|||
p->context = (struct context*)sp;
|
||||
memset(p->context, 0, sizeof *p->context);
|
||||
p->context->eip = (uint)forkret;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -116,12 +82,10 @@ userinit(void)
|
|||
|
||||
p = allocproc();
|
||||
initproc = p;
|
||||
if (!(p->pgdir = setupkvm()))
|
||||
if((p->pgdir = setupkvm()) == 0)
|
||||
panic("userinit: out of memory?");
|
||||
if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size))
|
||||
panic("userinit: out of memory?");
|
||||
inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size);
|
||||
p->sz = PGROUNDUP((int)_binary_initcode_size);
|
||||
inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
|
||||
p->sz = PGSIZE;
|
||||
memset(p->tf, 0, sizeof(*p->tf));
|
||||
p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
|
||||
p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
|
||||
|
@ -142,14 +106,17 @@ userinit(void)
|
|||
int
|
||||
growproc(int n)
|
||||
{
|
||||
uint sz;
|
||||
|
||||
sz = proc->sz;
|
||||
if(n > 0){
|
||||
if (!allocuvm(proc->pgdir, (char *)proc->sz, n))
|
||||
if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)
|
||||
return -1;
|
||||
} 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;
|
||||
}
|
||||
proc->sz += n;
|
||||
proc->sz = sz;
|
||||
switchuvm(proc);
|
||||
return 0;
|
||||
}
|
||||
|
@ -168,7 +135,7 @@ fork(void)
|
|||
return -1;
|
||||
|
||||
// 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);
|
||||
np->kstack = 0;
|
||||
np->state = UNUSED;
|
||||
|
@ -192,6 +159,92 @@ fork(void)
|
|||
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
|
||||
// Per-CPU process scheduler.
|
||||
// Each CPU calls scheduler() after setting itself up.
|
||||
|
@ -356,89 +409,41 @@ kill(int pid)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// 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.
|
||||
//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
|
||||
exit(void)
|
||||
procdump(void)
|
||||
{
|
||||
static char *states[] = {
|
||||
[UNUSED] "unused",
|
||||
[EMBRYO] "embryo",
|
||||
[SLEEPING] "sleep ",
|
||||
[RUNNABLE] "runble",
|
||||
[RUNNING] "run ",
|
||||
[ZOMBIE] "zombie"
|
||||
};
|
||||
int i;
|
||||
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++){
|
||||
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)
|
||||
if(p->state == UNUSED)
|
||||
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;
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
cprintf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
64
proc.h
64
proc.h
|
@ -8,6 +8,36 @@
|
|||
#define SEG_TSS 6 // this process's task state
|
||||
#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.
|
||||
// Don't need to save all the segment registers (%cs, etc),
|
||||
// because they are constant across kernel contexts.
|
||||
|
@ -31,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
|
|||
// Per-process state
|
||||
struct proc {
|
||||
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
|
||||
enum procstate state; // Process state
|
||||
volatile int pid; // Process ID
|
||||
struct proc *parent; // Parent process
|
||||
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
|
||||
int killed; // If non-zero, have been killed
|
||||
struct file *ofile[NOFILE]; // Open files
|
||||
|
@ -48,35 +78,5 @@ struct proc {
|
|||
// Process memory is laid out contiguously, low addresses first:
|
||||
// text
|
||||
// original data and bss
|
||||
// invalid page
|
||||
// fixed-size stack
|
||||
// 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;
|
||||
}
|
||||
|
||||
if(/sheet1: (left|right)$/){
|
||||
print STDERR "assuming that sheet 1 is a $1 page. double-check!\n";
|
||||
$left = $1 eq "left" ? "13579" : "02468";
|
||||
$right = $1 eq "left" ? "02468" : "13579";
|
||||
next;
|
||||
}
|
||||
|
||||
if(/even: (.*)/){
|
||||
$file = $1;
|
||||
if(!defined($toc{$file})){
|
||||
|
@ -89,18 +96,13 @@ perl -e '
|
|||
print STDERR "Have no toc for $file\n";
|
||||
next;
|
||||
}
|
||||
# this assumes that sheet 1 of code is a left page
|
||||
# double-check the PDF
|
||||
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";
|
||||
if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
|
||||
print STDERR "$file does not start on a left page [$toc{$file}]\n";
|
||||
}
|
||||
# why does this not work if I inline $x in the if?
|
||||
$x = ($toc{$file} =~ /^\d[02468]0/);
|
||||
$x = ($toc{$file} =~ /^\d[$right][05]/);
|
||||
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;
|
||||
}
|
||||
|
@ -189,7 +191,9 @@ do
|
|||
uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
|
||||
if [ "x$defs" != "x$uses" ]; then
|
||||
echo $i $defs
|
||||
echo $uses |fmt -24 | sed 's/^/ /'
|
||||
echo $uses |fmt -29 | sed 's/^/ /'
|
||||
# else
|
||||
# echo $i defined but not used >&2
|
||||
fi
|
||||
done
|
||||
) >refs
|
||||
|
|
|
@ -22,8 +22,8 @@ proc.h
|
|||
proc.c
|
||||
swtch.S
|
||||
kalloc.c
|
||||
data.S
|
||||
vm.c
|
||||
|
||||
# system calls
|
||||
traps.h
|
||||
vectors.pl
|
||||
|
@ -46,11 +46,10 @@ file.c
|
|||
sysfile.c
|
||||
exec.c
|
||||
|
||||
|
||||
|
||||
# pipes
|
||||
pipe.c
|
||||
|
||||
|
||||
# string operations
|
||||
string.c
|
||||
|
||||
|
@ -65,6 +64,7 @@ kbd.c
|
|||
console.c
|
||||
timer.c
|
||||
uart.c
|
||||
multiboot.S
|
||||
|
||||
# user-level
|
||||
initcode.S
|
||||
|
@ -73,3 +73,6 @@ init.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
|
||||
# param.h either
|
||||
# defs.h either
|
||||
|
@ -9,25 +22,36 @@
|
|||
|
||||
even: bootasm.S # mild preference
|
||||
even: bootother.S # mild preference
|
||||
# bootmain.c either
|
||||
even: bootmain.c # mild preference
|
||||
even: main.c
|
||||
# mp.c don't care at all
|
||||
# even: initcode.S
|
||||
# odd: init.c
|
||||
|
||||
# spinlock.h either
|
||||
# spinlock.c either
|
||||
even: proc.h # mild preference
|
||||
left: spinlock.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,
|
||||
# one with
|
||||
# ksegment usegment allocproc userinit growproc fork
|
||||
# userinit growproc fork exit wait
|
||||
# and another with
|
||||
# scheduler sched yield forkret sleep wakeup1 wakeup
|
||||
right: proc.c # VERY important
|
||||
even: proc.c # VERY important
|
||||
|
||||
# A few more action packed spreads
|
||||
# page table creation and process loading
|
||||
# walkpgdir mappages setupkvm 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
|
||||
|
||||
# syscall.h either
|
||||
|
@ -45,15 +69,25 @@ right: proc.c # VERY important
|
|||
# file.h either
|
||||
# fs.h either
|
||||
# fsvar.h either
|
||||
left: ide.c
|
||||
# left: ide.c # mild preference
|
||||
even: ide.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
|
||||
left: fs.c # mild preference
|
||||
# file.c either
|
||||
# exec.c either
|
||||
# sysfile.c either
|
||||
|
||||
# even: pipe.c # mild preference
|
||||
# string.c either
|
||||
left: kbd.h
|
||||
# left: kbd.h # mild preference
|
||||
even: kbd.h
|
||||
even: console.c
|
||||
odd: sh.c
|
||||
|
|
2
runoff1
2
runoff1
|
@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){
|
|||
last if $i>=@lines;
|
||||
|
||||
# If the rest of the file fits, use the whole thing.
|
||||
if(@lines <= $i+50){
|
||||
if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
|
||||
$breakbefore = @lines;
|
||||
}else{
|
||||
# Find a good next page break;
|
||||
|
|
|
@ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name)
|
|||
void
|
||||
acquire(struct spinlock *lk)
|
||||
{
|
||||
pushcli();
|
||||
pushcli(); // disable interrupts to avoid deadlock.
|
||||
if(holding(lk))
|
||||
panic("acquire");
|
||||
|
||||
|
@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[])
|
|||
|
||||
ebp = (uint*)v - 2;
|
||||
for(i = 0; i < 10; i++){
|
||||
if(ebp == 0 || ebp < (uint *) 0x100000 || ebp == (uint*)0xffffffff)
|
||||
if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff)
|
||||
break;
|
||||
pcs[i] = ebp[1]; // saved %eip
|
||||
ebp = (uint*)ebp[0]; // saved %ebp
|
||||
|
|
15
stressfs.c
15
stressfs.c
|
@ -14,21 +14,20 @@
|
|||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int fd, i;
|
||||
char path[] = "stressfs0";
|
||||
|
||||
printf(1, "stressfs starting\n");
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fork() > 0) {
|
||||
for(i = 0; i < 4; i++)
|
||||
if(fork() > 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf(1, "%d\n", i);
|
||||
|
||||
char path[] = "stressfs0";
|
||||
path[8] += i;
|
||||
int fd = open(path, O_CREATE | O_RDWR);
|
||||
for (i = 0; i < 100; i++)
|
||||
fd = open(path, O_CREATE | O_RDWR);
|
||||
for(i = 0; i < 100; i++)
|
||||
printf(fd, "%d\n", i);
|
||||
close(fd);
|
||||
|
||||
|
|
61
syscall.c
61
syscall.c
|
@ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// XXX should we copy the string?
|
||||
|
||||
// Fetch the nul-terminated string at addr from process p.
|
||||
// Doesn't actually copy the string - just sets *pp to point at it.
|
||||
// Returns length of string, not including nul.
|
||||
|
@ -34,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp)
|
|||
|
||||
if(addr >= p->sz)
|
||||
return -1;
|
||||
*pp = (char *) addr;
|
||||
ep = (char *) p->sz;
|
||||
*pp = (char*)addr;
|
||||
ep = (char*)p->sz;
|
||||
for(s = *pp; s < ep; s++)
|
||||
if(*s == 0)
|
||||
return s - *pp;
|
||||
|
@ -46,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp)
|
|||
int
|
||||
argint(int n, int *ip)
|
||||
{
|
||||
int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
|
||||
return x;
|
||||
return fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
|
||||
}
|
||||
|
||||
// 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)
|
||||
return -1;
|
||||
if((uint)i >= proc->sz || (uint)i+size >= proc->sz)
|
||||
if((uint)i >= proc->sz || (uint)i+size > proc->sz)
|
||||
return -1;
|
||||
// *pp = proc->mem + i; // XXXXX
|
||||
*pp = (char *) i; // XXXXX
|
||||
*pp = (char*)i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -102,28 +98,37 @@ extern int sys_wait(void);
|
|||
extern int sys_write(void);
|
||||
extern int sys_uptime(void);
|
||||
|
||||
int
|
||||
sys_init(void)
|
||||
{
|
||||
initlog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*syscalls[])(void) = {
|
||||
[SYS_chdir] sys_chdir,
|
||||
[SYS_close] sys_close,
|
||||
[SYS_dup] sys_dup,
|
||||
[SYS_exec] sys_exec,
|
||||
[SYS_exit] sys_exit,
|
||||
[SYS_init] sys_init,
|
||||
[SYS_fork] sys_fork,
|
||||
[SYS_fstat] sys_fstat,
|
||||
[SYS_getpid] sys_getpid,
|
||||
[SYS_kill] sys_kill,
|
||||
[SYS_link] sys_link,
|
||||
[SYS_mkdir] sys_mkdir,
|
||||
[SYS_mknod] sys_mknod,
|
||||
[SYS_open] sys_open,
|
||||
[SYS_exit] sys_exit,
|
||||
[SYS_wait] sys_wait,
|
||||
[SYS_pipe] sys_pipe,
|
||||
[SYS_read] sys_read,
|
||||
[SYS_kill] sys_kill,
|
||||
[SYS_exec] sys_exec,
|
||||
[SYS_fstat] sys_fstat,
|
||||
[SYS_chdir] sys_chdir,
|
||||
[SYS_dup] sys_dup,
|
||||
[SYS_getpid] sys_getpid,
|
||||
[SYS_sbrk] sys_sbrk,
|
||||
[SYS_sleep] sys_sleep,
|
||||
[SYS_unlink] sys_unlink,
|
||||
[SYS_wait] sys_wait,
|
||||
[SYS_write] sys_write,
|
||||
[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
|
||||
|
@ -132,9 +137,13 @@ syscall(void)
|
|||
int num;
|
||||
|
||||
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]();
|
||||
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",
|
||||
proc->pid, proc->name, num);
|
||||
proc->tf->eax = -1;
|
||||
|
|
36
syscall.h
36
syscall.h
|
@ -1,22 +1,24 @@
|
|||
// System call numbers
|
||||
#define SYS_init 0
|
||||
#define SYS_fork 1
|
||||
#define SYS_exit 2
|
||||
#define SYS_wait 3
|
||||
#define SYS_pipe 4
|
||||
#define SYS_write 5
|
||||
#define SYS_read 6
|
||||
#define SYS_close 7
|
||||
#define SYS_kill 8
|
||||
#define SYS_exec 9
|
||||
#define SYS_open 10
|
||||
#define SYS_mknod 11
|
||||
#define SYS_unlink 12
|
||||
#define SYS_fstat 13
|
||||
#define SYS_link 14
|
||||
#define SYS_mkdir 15
|
||||
#define SYS_chdir 16
|
||||
#define SYS_dup 17
|
||||
#define SYS_getpid 18
|
||||
#define SYS_sbrk 19
|
||||
#define SYS_sleep 20
|
||||
#define SYS_uptime 21
|
||||
#define SYS_read 5
|
||||
#define SYS_kill 6
|
||||
#define SYS_exec 7
|
||||
#define SYS_fstat 8
|
||||
#define SYS_chdir 9
|
||||
#define SYS_dup 10
|
||||
#define SYS_getpid 11
|
||||
#define SYS_sbrk 12
|
||||
#define SYS_sleep 13
|
||||
#define SYS_uptime 14
|
||||
|
||||
#define SYS_open 15
|
||||
#define SYS_write 16
|
||||
#define SYS_mknod 17
|
||||
#define SYS_unlink 18
|
||||
#define SYS_link 19
|
||||
#define SYS_mkdir 20
|
||||
#define SYS_close 21
|
||||
|
|
|
@ -344,11 +344,11 @@ sys_chdir(void)
|
|||
int
|
||||
sys_exec(void)
|
||||
{
|
||||
char *path, *argv[20];
|
||||
char *path, *argv[MAXARG];
|
||||
int i;
|
||||
uint uargv, uarg;
|
||||
|
||||
if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) {
|
||||
if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
|
||||
return -1;
|
||||
}
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
|
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
|
||||
numbers where the name is used. For example, this entry:
|
||||
|
||||
swtch 2208
|
||||
0318 1928 1967 2207
|
||||
2208
|
||||
swtch 2358
|
||||
0317 2128 2166 2357 2358
|
||||
|
||||
indicates that swtch is defined on line 2208 and is mentioned on five lines
|
||||
on sheets 03, 19, and 22.
|
||||
indicates that swtch is defined on line 2358 and is mentioned on five lines
|
||||
on sheets 03, 21, and 23.
|
||||
|
|
6
trap.c
6
trap.c
|
@ -59,6 +59,9 @@ trap(struct trapframe *tf)
|
|||
ideintr();
|
||||
lapiceoi();
|
||||
break;
|
||||
case T_IRQ0 + IRQ_IDE+1:
|
||||
// Bochs generates spurious IDE1 interrupts.
|
||||
break;
|
||||
case T_IRQ0 + IRQ_KBD:
|
||||
kbdintr();
|
||||
lapiceoi();
|
||||
|
@ -83,7 +86,8 @@ trap(struct trapframe *tf)
|
|||
panic("trap");
|
||||
}
|
||||
// In user space, assume process misbehaved.
|
||||
cprintf("pid %d %s: trap %d err %d on cpu %d eip 0x%x addr 0x%x--kill proc\n",
|
||||
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,
|
||||
rcr2());
|
||||
proc->killed = 1;
|
||||
|
|
2
ulib.c
2
ulib.c
|
@ -45,7 +45,7 @@ strchr(const char *s, char c)
|
|||
{
|
||||
for(; *s; s++)
|
||||
if(*s == c)
|
||||
return (char*) s;
|
||||
return (char*)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ free(void *ap)
|
|||
{
|
||||
Header *bp, *p;
|
||||
|
||||
bp = (Header*) ap - 1;
|
||||
bp = (Header*)ap - 1;
|
||||
for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
|
||||
if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
|
||||
break;
|
||||
|
@ -52,7 +52,7 @@ morecore(uint nu)
|
|||
if(nu < 4096)
|
||||
nu = 4096;
|
||||
p = sbrk(nu * sizeof(Header));
|
||||
if(p == (char*) -1)
|
||||
if(p == (char*)-1)
|
||||
return 0;
|
||||
hp = (Header*)p;
|
||||
hp->s.size = nu;
|
||||
|
@ -81,7 +81,7 @@ malloc(uint nbytes)
|
|||
p->s.size = nunits;
|
||||
}
|
||||
freep = prevp;
|
||||
return (void*) (p + 1);
|
||||
return (void*)(p + 1);
|
||||
}
|
||||
if(p == freep)
|
||||
if((p = morecore(nunits)) == 0)
|
||||
|
|
4
user.h
4
user.h
|
@ -18,10 +18,10 @@ int link(char*, char*);
|
|||
int mkdir(char*);
|
||||
int chdir(char*);
|
||||
int dup(int);
|
||||
int getpid();
|
||||
int getpid(void);
|
||||
char* sbrk(int);
|
||||
int sleep(int);
|
||||
int uptime();
|
||||
int uptime(void);
|
||||
|
||||
// ulib.c
|
||||
int stat(char*, struct stat*);
|
||||
|
|
208
usertests.c
208
usertests.c
|
@ -3,6 +3,8 @@
|
|||
#include "user.h"
|
||||
#include "fs.h"
|
||||
#include "fcntl.h"
|
||||
#include "syscall.h"
|
||||
#include "traps.h"
|
||||
|
||||
char buf[2048];
|
||||
char name[3];
|
||||
|
@ -45,12 +47,12 @@ writetest(void)
|
|||
printf(stdout, "error: creat small failed!\n");
|
||||
exit();
|
||||
}
|
||||
for(i = 0; i < 100; i++) {
|
||||
if(write(fd, "aaaaaaaaaa", 10) != 10) {
|
||||
for(i = 0; i < 100; i++){
|
||||
if(write(fd, "aaaaaaaaaa", 10) != 10){
|
||||
printf(stdout, "error: write aa %d new file failed\n", i);
|
||||
exit();
|
||||
}
|
||||
if(write(fd, "bbbbbbbbbb", 10) != 10) {
|
||||
if(write(fd, "bbbbbbbbbb", 10) != 10){
|
||||
printf(stdout, "error: write bb %d new file failed\n", i);
|
||||
exit();
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ writetest(void)
|
|||
exit();
|
||||
}
|
||||
i = read(fd, buf, 2000);
|
||||
if(i == 2000) {
|
||||
if(i == 2000){
|
||||
printf(stdout, "read succeeded ok\n");
|
||||
} else {
|
||||
printf(stdout, "read failed\n");
|
||||
|
@ -73,7 +75,7 @@ writetest(void)
|
|||
}
|
||||
close(fd);
|
||||
|
||||
if(unlink("small") < 0) {
|
||||
if(unlink("small") < 0){
|
||||
printf(stdout, "unlink small failed\n");
|
||||
exit();
|
||||
}
|
||||
|
@ -93,9 +95,9 @@ writetest1(void)
|
|||
exit();
|
||||
}
|
||||
|
||||
for(i = 0; i < MAXFILE; i++) {
|
||||
((int*) buf)[0] = i;
|
||||
if(write(fd, buf, 512) != 512) {
|
||||
for(i = 0; i < MAXFILE; i++){
|
||||
((int*)buf)[0] = i;
|
||||
if(write(fd, buf, 512) != 512){
|
||||
printf(stdout, "error: write big file failed\n", i);
|
||||
exit();
|
||||
}
|
||||
|
@ -110,19 +112,19 @@ writetest1(void)
|
|||
}
|
||||
|
||||
n = 0;
|
||||
for(;;) {
|
||||
for(;;){
|
||||
i = read(fd, buf, 512);
|
||||
if(i == 0) {
|
||||
if(n == MAXFILE - 1) {
|
||||
if(i == 0){
|
||||
if(n == MAXFILE - 1){
|
||||
printf(stdout, "read only %d blocks from big", n);
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
} else if(i != 512) {
|
||||
} else if(i != 512){
|
||||
printf(stdout, "read failed %d\n", i);
|
||||
exit();
|
||||
}
|
||||
if(((int*)buf)[0] != n) {
|
||||
if(((int*)buf)[0] != n){
|
||||
printf(stdout, "read content of block %d is %d\n",
|
||||
n, ((int*)buf)[0]);
|
||||
exit();
|
||||
|
@ -130,7 +132,7 @@ writetest1(void)
|
|||
n++;
|
||||
}
|
||||
close(fd);
|
||||
if(unlink("big") < 0) {
|
||||
if(unlink("big") < 0){
|
||||
printf(stdout, "unlink big failed\n");
|
||||
exit();
|
||||
}
|
||||
|
@ -146,14 +148,14 @@ createtest(void)
|
|||
|
||||
name[0] = 'a';
|
||||
name[2] = '\0';
|
||||
for(i = 0; i < 52; i++) {
|
||||
for(i = 0; i < 52; i++){
|
||||
name[1] = '0' + i;
|
||||
fd = open(name, O_CREATE|O_RDWR);
|
||||
close(fd);
|
||||
}
|
||||
name[0] = 'a';
|
||||
name[2] = '\0';
|
||||
for(i = 0; i < 52; i++) {
|
||||
for(i = 0; i < 52; i++){
|
||||
name[1] = '0' + i;
|
||||
unlink(name);
|
||||
}
|
||||
|
@ -164,22 +166,22 @@ void dirtest(void)
|
|||
{
|
||||
printf(stdout, "mkdir test\n");
|
||||
|
||||
if(mkdir("dir0") < 0) {
|
||||
if(mkdir("dir0") < 0){
|
||||
printf(stdout, "mkdir failed\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
if(chdir("dir0") < 0) {
|
||||
if(chdir("dir0") < 0){
|
||||
printf(stdout, "chdir dir0 failed\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
if(chdir("..") < 0) {
|
||||
if(chdir("..") < 0){
|
||||
printf(stdout, "chdir .. failed\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
if(unlink("dir0") < 0) {
|
||||
if(unlink("dir0") < 0){
|
||||
printf(stdout, "unlink dir0 failed\n");
|
||||
exit();
|
||||
}
|
||||
|
@ -190,7 +192,7 @@ void
|
|||
exectest(void)
|
||||
{
|
||||
printf(stdout, "exec test\n");
|
||||
if(exec("echo", echoargv) < 0) {
|
||||
if(exec("echo", echoargv) < 0){
|
||||
printf(stdout, "exec echo failed\n");
|
||||
exit();
|
||||
}
|
||||
|
@ -324,20 +326,21 @@ mem(void)
|
|||
void *m1, *m2;
|
||||
int pid, ppid;
|
||||
|
||||
printf(1, "mem test\n");
|
||||
ppid = getpid();
|
||||
if((pid = fork()) == 0){
|
||||
m1 = 0;
|
||||
while((m2 = malloc(10001)) != 0) {
|
||||
*(char**) m2 = m1;
|
||||
while((m2 = malloc(10001)) != 0){
|
||||
*(char**)m2 = m1;
|
||||
m1 = m2;
|
||||
}
|
||||
while(m1) {
|
||||
while(m1){
|
||||
m2 = *(char**)m1;
|
||||
free(m1);
|
||||
m1 = m2;
|
||||
}
|
||||
m1 = malloc(1024*20);
|
||||
if(m1 == 0) {
|
||||
if(m1 == 0){
|
||||
printf(1, "couldn't allocate mem?!!\n");
|
||||
kill(ppid);
|
||||
exit();
|
||||
|
@ -1234,16 +1237,18 @@ forktest(void)
|
|||
void
|
||||
sbrktest(void)
|
||||
{
|
||||
int pid;
|
||||
char *oldbrk = sbrk(0);
|
||||
int fds[2], pid, pids[32], ppid;
|
||||
char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch;
|
||||
uint amt;
|
||||
|
||||
printf(stdout, "sbrk test\n");
|
||||
oldbrk = sbrk(0);
|
||||
|
||||
// can one sbrk() less than a page?
|
||||
char *a = sbrk(0);
|
||||
a = sbrk(0);
|
||||
int i;
|
||||
for(i = 0; i < 5000; i++){
|
||||
char *b = sbrk(1);
|
||||
b = sbrk(1);
|
||||
if(b != a){
|
||||
printf(stdout, "sbrk test failed %d %x %x\n", i, a, b);
|
||||
exit();
|
||||
|
@ -1256,7 +1261,7 @@ sbrktest(void)
|
|||
printf(stdout, "sbrk test fork failed\n");
|
||||
exit();
|
||||
}
|
||||
char *c = sbrk(1);
|
||||
c = sbrk(1);
|
||||
c = sbrk(1);
|
||||
if(c != a + 1){
|
||||
printf(stdout, "sbrk test failed post-fork\n");
|
||||
|
@ -1268,18 +1273,18 @@ sbrktest(void)
|
|||
|
||||
// can one allocate the full 640K?
|
||||
a = sbrk(0);
|
||||
uint amt = (640 * 1024) - (uint) a;
|
||||
char *p = sbrk(amt);
|
||||
amt = (640 * 1024) - (uint)a;
|
||||
p = sbrk(amt);
|
||||
if(p != a){
|
||||
printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a);
|
||||
exit();
|
||||
}
|
||||
char *lastaddr = (char *)(640 * 1024 - 1);
|
||||
lastaddr = (char*)(640 * 1024 - 1);
|
||||
*lastaddr = 99;
|
||||
|
||||
// is one forbidden from allocating more than 640K?
|
||||
c = sbrk(4096);
|
||||
if(c != (char *) 0xffffffff){
|
||||
if(c != (char*)0xffffffff){
|
||||
printf(stdout, "sbrk allocated more than 640K, c %x\n", c);
|
||||
exit();
|
||||
}
|
||||
|
@ -1287,7 +1292,7 @@ sbrktest(void)
|
|||
// can one de-allocate?
|
||||
a = sbrk(0);
|
||||
c = sbrk(-4096);
|
||||
if(c == (char *) 0xffffffff){
|
||||
if(c == (char*)0xffffffff){
|
||||
printf(stdout, "sbrk could not deallocate\n");
|
||||
exit();
|
||||
}
|
||||
|
@ -1311,15 +1316,15 @@ sbrktest(void)
|
|||
}
|
||||
|
||||
c = sbrk(4096);
|
||||
if(c != (char *) 0xffffffff){
|
||||
if(c != (char*)0xffffffff){
|
||||
printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c);
|
||||
exit();
|
||||
}
|
||||
|
||||
// can we read the kernel's memory?
|
||||
for(a = (char*)(640*1024); a < (char *)2000000; a += 50000){
|
||||
int ppid = getpid();
|
||||
int pid = fork();
|
||||
for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){
|
||||
ppid = getpid();
|
||||
pid = fork();
|
||||
if(pid < 0){
|
||||
printf(stdout, "fork failed\n");
|
||||
exit();
|
||||
|
@ -1332,6 +1337,38 @@ sbrktest(void)
|
|||
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)
|
||||
sbrk(-(sbrk(0) - oldbrk));
|
||||
|
||||
|
@ -1339,26 +1376,89 @@ sbrktest(void)
|
|||
}
|
||||
|
||||
void
|
||||
stacktest(void)
|
||||
validateint(int *p)
|
||||
{
|
||||
printf(stdout, "stack test\n");
|
||||
char dummy = 1;
|
||||
char *p = &dummy;
|
||||
int ppid = getpid();
|
||||
int pid = fork();
|
||||
if(pid < 0){
|
||||
printf(stdout, "fork failed\n");
|
||||
int res;
|
||||
asm("mov %%esp, %%ebx\n\t"
|
||||
"mov %3, %%esp\n\t"
|
||||
"int %2\n\t"
|
||||
"mov %%ebx, %%esp" :
|
||||
"=a" (res) :
|
||||
"a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) :
|
||||
"ebx");
|
||||
}
|
||||
|
||||
void
|
||||
validatetest(void)
|
||||
{
|
||||
int hi, pid;
|
||||
uint p;
|
||||
|
||||
printf(stdout, "validate test\n");
|
||||
hi = 1100*1024;
|
||||
|
||||
for(p = 0; p <= (uint)hi; p += 4096){
|
||||
if((pid = fork()) == 0){
|
||||
// try to crash the kernel by passing in a badly placed integer
|
||||
validateint((int*)p);
|
||||
exit();
|
||||
}
|
||||
sleep(0);
|
||||
sleep(0);
|
||||
kill(pid);
|
||||
wait();
|
||||
|
||||
// try to crash the kernel by passing in a bad string pointer
|
||||
if(link("nosuchfile", (char*)p) != -1){
|
||||
printf(stdout, "link should not succeed\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
printf(stdout, "validate ok\n");
|
||||
}
|
||||
|
||||
// does unintialized data start out zero?
|
||||
char uninit[10000];
|
||||
void
|
||||
bsstest(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(stdout, "bss test\n");
|
||||
for(i = 0; i < sizeof(uninit); i++){
|
||||
if(uninit[i] != '\0'){
|
||||
printf(stdout, "bss test failed\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
printf(stdout, "bss test ok\n");
|
||||
}
|
||||
|
||||
// does exec do something sensible if the arguments
|
||||
// are larger than a page?
|
||||
void
|
||||
bigargtest(void)
|
||||
{
|
||||
int pid, ppid;
|
||||
|
||||
ppid = getpid();
|
||||
pid = fork();
|
||||
if(pid == 0){
|
||||
// should cause a trap:
|
||||
p[-4096] = 'z';
|
||||
kill(ppid);
|
||||
printf(stdout, "stack test failed: page before stack was writeable\n");
|
||||
char *args[32+1];
|
||||
int i;
|
||||
for(i = 0; i < 32; i++)
|
||||
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();
|
||||
}
|
||||
wait();
|
||||
printf(stdout, "stack test OK\n");
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1372,8 +1472,10 @@ main(int argc, char *argv[])
|
|||
}
|
||||
close(open("usertests.ran", O_CREATE));
|
||||
|
||||
stacktest();
|
||||
bigargtest();
|
||||
bsstest();
|
||||
sbrktest();
|
||||
validatetest();
|
||||
|
||||
opentest();
|
||||
writetest();
|
||||
|
|
605
vm.c
605
vm.c
|
@ -6,96 +6,22 @@
|
|||
#include "proc.h"
|
||||
#include "elf.h"
|
||||
|
||||
// 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..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).
|
||||
extern char data[]; // defined in data.S
|
||||
|
||||
#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()
|
||||
|
||||
// return the address of the PTE in page table pgdir
|
||||
// that corresponds to linear address va. if create!=0,
|
||||
// create any required page table pages.
|
||||
static pte_t *
|
||||
walkpgdir(pde_t *pgdir, const void *va, int create)
|
||||
// Allocate one page table for the machine for the kernel address
|
||||
// space for scheduler processes.
|
||||
void
|
||||
kvmalloc(void)
|
||||
{
|
||||
uint r;
|
||||
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;
|
||||
kpgdir = setupkvm();
|
||||
}
|
||||
|
||||
// Set up CPU's kernel segment descriptors.
|
||||
// Run once at boot time on each CPU.
|
||||
void
|
||||
ksegment(void)
|
||||
seginit(void)
|
||||
{
|
||||
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_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);
|
||||
|
||||
lgdt(c->gdt, sizeof(c->gdt));
|
||||
|
@ -120,252 +46,108 @@ ksegment(void)
|
|||
proc = 0;
|
||||
}
|
||||
|
||||
// Switch h/w page table and TSS registers to point to process p.
|
||||
void
|
||||
switchuvm(struct proc *p)
|
||||
// Return the address of the PTE in page table pgdir
|
||||
// that corresponds to linear address va. If create!=0,
|
||||
// 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
|
||||
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\n");
|
||||
|
||||
lcr3(PADDR(p->pgdir)); // switch to new address space
|
||||
popcli();
|
||||
pde = &pgdir[PDX(va)];
|
||||
if(*pde & PTE_P){
|
||||
pgtab = (pte_t*)PTE_ADDR(*pde);
|
||||
} else {
|
||||
if(!create || (pgtab = (pte_t*)kalloc()) == 0)
|
||||
return 0;
|
||||
// Make sure all those PTE_P bits are zero.
|
||||
memset(pgtab, 0, PGSIZE);
|
||||
// The permissions here are overly generous, but they can
|
||||
// be further restricted by the permissions in the page table
|
||||
// entries, if necessary.
|
||||
*pde = 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
|
||||
// no process is running.
|
||||
void
|
||||
switchkvm()
|
||||
// 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)
|
||||
{
|
||||
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.
|
||||
pde_t*
|
||||
setupkvm(void)
|
||||
{
|
||||
pde_t *pgdir;
|
||||
struct kmap *k;
|
||||
|
||||
// Allocate page directory
|
||||
if (!(pgdir = (pde_t *) kalloc()))
|
||||
if((pgdir = (pde_t*)kalloc()) == 0)
|
||||
return 0;
|
||||
memset(pgdir, 0, PGSIZE);
|
||||
// Map IO space from 640K to 1Mbyte
|
||||
if (!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W))
|
||||
return 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))
|
||||
k = kmap;
|
||||
for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
|
||||
if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 0)
|
||||
return 0;
|
||||
|
||||
return pgdir;
|
||||
}
|
||||
|
||||
// return the physical address that a given user address
|
||||
// maps to. the result is also a kernel logical address,
|
||||
// since the kernel maps the physical memory allocated to user
|
||||
// processes directly.
|
||||
char*
|
||||
uva2ka(pde_t *pgdir, char *uva)
|
||||
{
|
||||
pte_t *pte = walkpgdir(pgdir, uva, 0);
|
||||
if (pte == 0) return 0;
|
||||
uint pa = PTE_ADDR(*pte);
|
||||
return (char *)pa;
|
||||
}
|
||||
|
||||
// allocate sz bytes more memory for a process starting at the
|
||||
// given user address; allocates physical memory and page
|
||||
// table entries. addr and sz need not be page-aligned.
|
||||
// it is a no-op for any parts of the requested memory
|
||||
// that are already allocated.
|
||||
int
|
||||
allocuvm(pde_t *pgdir, char *addr, uint sz)
|
||||
{
|
||||
if (addr + sz > (char*)USERTOP)
|
||||
return 0;
|
||||
char *first = PGROUNDDOWN(addr);
|
||||
char *last = PGROUNDDOWN(addr + sz - 1);
|
||||
char *a;
|
||||
for(a = first; a <= last; a += PGSIZE){
|
||||
pte_t *pte = walkpgdir(pgdir, a, 0);
|
||||
if(pte == 0 || (*pte & PTE_P) == 0){
|
||||
char *mem = kalloc();
|
||||
if(mem == 0){
|
||||
// XXX clean up?
|
||||
return 0;
|
||||
}
|
||||
memset(mem, 0, PGSIZE);
|
||||
mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// deallocate some of the user pages, in response to sbrk()
|
||||
// with a negative argument. if addr is not page-aligned,
|
||||
// then only deallocates starting at the next page boundary.
|
||||
int
|
||||
deallocuvm(pde_t *pgdir, char *addr, uint sz)
|
||||
{
|
||||
if (addr + sz > (char*)USERTOP)
|
||||
return 0;
|
||||
char *first = (char*) PGROUNDUP((uint)addr);
|
||||
char *last = PGROUNDDOWN(addr + sz - 1);
|
||||
char *a;
|
||||
for(a = first; a <= last; a += PGSIZE){
|
||||
pte_t *pte = walkpgdir(pgdir, a, 0);
|
||||
if(pte && (*pte & PTE_P) != 0){
|
||||
uint pa = PTE_ADDR(*pte);
|
||||
if(pa == 0)
|
||||
panic("deallocuvm");
|
||||
kfree((void *) pa);
|
||||
*pte = 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// free a page table and all the physical memory pages
|
||||
// in the user part.
|
||||
void
|
||||
freevm(pde_t *pgdir)
|
||||
{
|
||||
uint i, j, da;
|
||||
|
||||
if (!pgdir)
|
||||
panic("freevm: no pgdir\n");
|
||||
for (i = 0; i < NPDENTRIES; i++) {
|
||||
da = PTE_ADDR(pgdir[i]);
|
||||
if (da != 0) {
|
||||
pte_t *pgtab = (pte_t*) da;
|
||||
for (j = 0; j < NPTENTRIES; j++) {
|
||||
if (pgtab[j] != 0) {
|
||||
uint pa = PTE_ADDR(pgtab[j]);
|
||||
uint va = PGADDR(i, j, 0);
|
||||
if (va < USERTOP) // user memory
|
||||
kfree((void *) pa);
|
||||
pgtab[j] = 0;
|
||||
}
|
||||
}
|
||||
kfree((void *) da);
|
||||
pgdir[i] = 0;
|
||||
}
|
||||
}
|
||||
kfree((void *) pgdir);
|
||||
}
|
||||
|
||||
int
|
||||
loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
|
||||
{
|
||||
uint i, pa, n;
|
||||
pte_t *pte;
|
||||
|
||||
if ((uint)addr % PGSIZE != 0)
|
||||
panic("loaduvm: addr must be page aligned\n");
|
||||
for (i = 0; i < sz; i += PGSIZE) {
|
||||
if (!(pte = walkpgdir(pgdir, addr+i, 0)))
|
||||
panic("loaduvm: address should exist\n");
|
||||
pa = PTE_ADDR(*pte);
|
||||
if (sz - i < PGSIZE) n = sz - i;
|
||||
else n = PGSIZE;
|
||||
if(readi(ip, (char *)pa, offset+i, n) != n)
|
||||
return 0;
|
||||
}
|
||||
return 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.
|
||||
void
|
||||
vmenable(void)
|
||||
|
@ -378,3 +160,208 @@ vmenable(void)
|
|||
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>
|
||||
<head>
|
||||
<title>Xv6, a simple Unix-like teaching operating system</title>
|
||||
|
@ -32,31 +31,36 @@ h2 {
|
|||
--></style>
|
||||
</head>
|
||||
<body bgcolor=#ffffff>
|
||||
|
||||
<h1>Xv6, a simple Unix-like teaching operating system</h1>
|
||||
<br><br>
|
||||
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.
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
Xv6 is a teaching operating system developed in the summer of 2006 for
|
||||
MIT's operating systems
|
||||
course, <a href="http://pdos.csail.mit.edu/6.828">6.828: operating
|
||||
systems Engineering</a>. We hope that xv6 will be useful in other
|
||||
courses too. This page collects resources to aid the use of xv6 in
|
||||
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>
|
||||
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.
|
||||
<br><br>
|
||||
|
||||
<p>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 <a href="#v6">Sixth
|
||||
Edition Unix (aka V6)</a> 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.
|
||||
|
||||
<p>
|
||||
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)
|
||||
|
@ -76,13 +80,12 @@ 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.
|
||||
<br><br>
|
||||
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.
|
||||
<br><br>
|
||||
<p> 6.828 substituted xv6 for V6 in the fall of 2006. Based on
|
||||
that experience, we cleaned up rough patches of xv6. Since then, xv6
|
||||
has stabilized, so we are making it available in the hopes that others
|
||||
will find it useful too.
|
||||
|
||||
<p>
|
||||
6.828 uses both xv6 and Jos.
|
||||
Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua,
|
||||
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.
|
||||
|
||||
<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
|
||||
a printed booklet with line numbers that keep everyone
|
||||
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>;
|
||||
see the LICENSE file in the source distribution.
|
||||
<br><br>
|
||||
|
||||
<p>
|
||||
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;
|
||||
|
@ -106,239 +111,131 @@ 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 <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>
|
||||
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.
|
||||
<h2>Xv6 lecture material</h2>
|
||||
|
||||
<br><br><b><i>Lecture 1. Operating systems</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<br><br>
|
||||
<a href="l1.html">lecture notes</a>
|
||||
<a href="os-lab-1.pdf">OS abstractions slides</a>
|
||||
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. The lecture
|
||||
notes are available from the 6.828 schedule page, and the chapters of
|
||||
the commentary are below.
|
||||
|
||||
<br><br><b><i>Lecture 2. PC hardware and x86 programming</i></b>
|
||||
<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>
|
||||
<h2>Xv6 commentary (rough)</h2>
|
||||
|
||||
<br><br><b><i>Lecture 3. Operating system organization</i></b>
|
||||
<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>
|
||||
<p>The chapters are rough drafts.
|
||||
|
||||
<br><br><b><i>Lecture 4. Address spaces using segmentation</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<br><br>
|
||||
Reading: x86 MMU handout,
|
||||
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>.
|
||||
<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>
|
||||
<p>Introduction yet to be written.<br>
|
||||
<ul>
|
||||
<li>read with the code side by side
|
||||
<li>code references look like (xxxx) or (xxxx-yyyy) in small text.
|
||||
<li><a href="xv6-rev5.pdf">this pdf</a> is the one with matching line numbers.
|
||||
<li>each chapter starts with an introduction to the topic,
|
||||
spends most of the text on code,
|
||||
and then wraps up talking about how xv6
|
||||
compares to real-world operating systems.
|
||||
</ul>
|
||||
|
||||
<br><br><b><i>Lecture 5. Address spaces using page tables</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<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>
|
||||
<a href="unix.pdf">Chapter 0: Operating system interfaces</a>
|
||||
<blockquote>
|
||||
The Unix system call interface. (rev 4)
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 6. Interrupts and exceptions</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<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>
|
||||
<a href="boot.pdf">Chapter 1: Bootstrap</a>
|
||||
<blockquote>
|
||||
From power on to kernel start. (rev 4)
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 7. Multiprocessors and locking</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<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>
|
||||
<a href="mem.pdf">Chapter 2: Processes</a>
|
||||
<blockquote>
|
||||
Memory and process allocation, segments, the first user process. (rev 4)
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 8. Threads, processes and context switching</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<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>
|
||||
<a href="trap.pdf">Chapter 3: Traps</a>
|
||||
<blockquote>
|
||||
Low-level trap mechanism, trap handler, system call arguments, sbrk, fork.
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 9. Processes and coordination</i></b>
|
||||
<br><br>
|
||||
This lecture introduces the idea of sequence coordination
|
||||
and then examines the particular solution illustrated by
|
||||
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>
|
||||
<a href="lock.pdf">Chapter 4: Locks</a>
|
||||
<blockquote>
|
||||
Locks and interrupts.
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 10. Files and disk I/O</i></b>
|
||||
<br><br>
|
||||
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.
|
||||
<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>
|
||||
<a href="sched.pdf">Chapter 5: Scheduling and coordination</a>
|
||||
<blockquote>
|
||||
Scheduling, sleep and wakeup, pipes, wait and exit.
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 11. Naming</i></b>
|
||||
<br><br>
|
||||
The last lecture discussed on-disk file system representation.
|
||||
This lecture covers the implementation of
|
||||
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>
|
||||
<a href="disk.pdf">Chapter 6: Buffer cache</a>
|
||||
<blockquote>
|
||||
Buffer cache and IDE disk driver.
|
||||
</blockquote>
|
||||
|
||||
<br><br><b><i>Lecture 12. High-performance file systems</i></b>
|
||||
<br><br>
|
||||
This lecture is the first of the research paper-based lectures.
|
||||
It discusses the “soft updates” paper,
|
||||
using xv6 as a concrete example.
|
||||
<a href="fsdata.pdf">Chapter 7: File system data</a>
|
||||
<blockquote>
|
||||
Block in use bitmap, block allocation, inode structure, inode contents,
|
||||
directories, path names.
|
||||
</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>
|
||||
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
|
||||
to adopt, we'd like to hear about it.
|
||||
We'd also be interested to hear what worked well and what didn't.
|
||||
<br><br>
|
||||
<p>
|
||||
Russ Cox (rsc@swtch.com)<br>
|
||||
Frans Kaashoek (kaashoek@mit.edu)<br>
|
||||
Robert Morris (rtm@mit.edu)
|
||||
<br><br>
|
||||
<p>
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
loadgs(ushort 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
|
||||
cli(void)
|
||||
{
|
||||
|
@ -121,66 +124,56 @@ sti(void)
|
|||
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));
|
||||
}
|
||||
|
||||
static inline uint rcr0(void)
|
||||
static inline uint
|
||||
rcr0(void)
|
||||
{
|
||||
uint val;
|
||||
asm volatile("movl %%cr0,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint rcr2(void)
|
||||
static inline uint
|
||||
rcr2(void)
|
||||
{
|
||||
uint val;
|
||||
asm volatile("movl %%cr2,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void lcr3(uint val)
|
||||
static inline void
|
||||
lcr3(uint val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr3" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint rcr3(void)
|
||||
static inline uint
|
||||
rcr3(void)
|
||||
{
|
||||
uint val;
|
||||
asm volatile("movl %%cr3,%0" : "=r" (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
|
||||
// Layout of the trap frame built on the stack by the
|
||||
// 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