提交 dccb9152 创建 作者: Frans Kaashoek's avatar Frans Kaashoek

Merge commit 'origin/master' into page

*~
_* _*
*.o *.o
*.d *.d
......
...@@ -26,19 +26,53 @@ OBJS = \ ...@@ -26,19 +26,53 @@ OBJS = \
uart.o\ uart.o\
vectors.o\ vectors.o\
vm.o\ vm.o\
log.o\
# Cross-compiling (e.g., on Mac OS X) # Cross-compiling (e.g., on Mac OS X)
TOOLPREFIX = i386-jos-elf- #TOOLPREFIX = i386-jos-elf-
# Using native tools (e.g., on X86 Linux) # Using native tools (e.g., on X86 Linux)
#TOOLPREFIX = #TOOLPREFIX =
# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# If the makefile can't find QEMU, specify its path here
#QEMU =
# Try to infer the correct QEMU
ifndef QEMU
QEMU = $(shell if which qemu > /dev/null; \
then echo qemu; exit; \
else \
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
echo "***" 1>&2; \
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
echo "***" 1>&2; exit 1)
endif
CC = $(TOOLPREFIX)gcc CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASFLAGS = -m32 -gdwarf-2 ASFLAGS = -m32 -gdwarf-2
# FreeBSD ld wants ``elf_i386_fbsd'' # FreeBSD ld wants ``elf_i386_fbsd''
...@@ -49,6 +83,11 @@ xv6.img: bootblock kernel fs.img ...@@ -49,6 +83,11 @@ xv6.img: bootblock kernel fs.img
dd if=bootblock of=xv6.img conv=notrunc dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc
xv6memfs.img: bootblock kernelmemfs
dd if=/dev/zero of=xv6memfs.img count=10000
dd if=bootblock of=xv6memfs.img conv=notrunc
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
bootblock: bootasm.S bootmain.c bootblock: bootasm.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
...@@ -69,11 +108,23 @@ initcode: initcode.S ...@@ -69,11 +108,23 @@ initcode: initcode.S
$(OBJCOPY) -S -O binary initcode.out initcode $(OBJCOPY) -S -O binary initcode.out initcode
$(OBJDUMP) -S initcode.o > initcode.asm $(OBJDUMP) -S initcode.o > initcode.asm
kernel: $(OBJS) bootother initcode kernel: $(OBJS) multiboot.o data.o bootother initcode
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
$(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
# kernelmemfs is a copy of kernel that maintains the
# disk image in memory instead of writing to a disk.
# This is not so useful for testing persistent storage or
# exploring disk buffering implementations, but it is
# great for testing the kernel on real hardware without
# needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) multiboot.o data.o bootother initcode fs.img
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o data.o $(MEMFSOBJS) -b binary initcode bootother fs.img
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
tags: $(OBJS) bootother.S _init tags: $(OBJS) bootother.S _init
etags *.S *.c etags *.S *.c
...@@ -94,7 +145,7 @@ _forktest: forktest.o $(ULIB) ...@@ -94,7 +145,7 @@ _forktest: forktest.o $(ULIB)
$(OBJDUMP) -S _forktest > forktest.asm $(OBJDUMP) -S _forktest > forktest.asm
mkfs: mkfs.c fs.h mkfs: mkfs.c fs.h
gcc -Wall -o mkfs mkfs.c gcc -m32 -Werror -Wall -o mkfs mkfs.c
UPROGS=\ UPROGS=\
_cat\ _cat\
...@@ -126,7 +177,7 @@ clean: ...@@ -126,7 +177,7 @@ clean:
# make a printout # make a printout
FILES = $(shell grep -v '^\#' runoff.list) FILES = $(shell grep -v '^\#' runoff.list)
PRINT = runoff.list $(FILES) PRINT = runoff.list runoff.spec $(FILES)
xv6.pdf: $(PRINT) xv6.pdf: $(PRINT)
./runoff ./runoff
...@@ -143,27 +194,33 @@ bochs : fs.img xv6.img ...@@ -143,27 +194,33 @@ bochs : fs.img xv6.img
# try to generate a unique GDB port # try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000) GDBPORT = $(shell expr `id -u` % 5000 + 25000)
# QEMU's gdb stub command line changed in 0.11 # QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if qemu -help | grep -q '^-gdb'; \ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \ then echo "-gdb tcp::$(GDBPORT)"; \
else echo "-s -p $(GDBPORT)"; fi) else echo "-s -p $(GDBPORT)"; fi)
QEMUOPTS = -smp 2 -hdb fs.img xv6.img ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS)
qemu: fs.img xv6.img qemu: fs.img xv6.img
qemu -serial mon:stdio $(QEMUOPTS) $(QEMU) -serial mon:stdio $(QEMUOPTS)
qemu-memfs: xv6memfs.img
$(QEMU) xv6memfs.img -smp $(CPUS)
qemu-nox: fs.img xv6.img qemu-nox: fs.img xv6.img
qemu -nographic $(QEMUOPTS) $(QEMU) -nographic $(QEMUOPTS)
.gdbinit: .gdbinit.tmpl .gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
qemu-gdb: fs.img xv6.img .gdbinit qemu-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2 @echo "*** Now run 'gdb'." 1>&2
qemu -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
qemu-nox-gdb: fs.img xv6.img .gdbinit qemu-nox-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2 @echo "*** Now run 'gdb'." 1>&2
qemu -nographic $(QEMUOPTS) -S $(QEMUGDB) $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
# CUT HERE # CUT HERE
# prepare dist for students # prepare dist for students
...@@ -195,14 +252,16 @@ dist-test: ...@@ -195,14 +252,16 @@ dist-test:
rm -rf dist-test rm -rf dist-test
mkdir dist-test mkdir dist-test
cp dist/* dist-test cp dist/* dist-test
cd dist-test; ../m print cd dist-test; $(MAKE) print
cd dist-test; ../m bochs || true cd dist-test; $(MAKE) bochs || true
cd dist-test; ../m qemu cd dist-test; $(MAKE) qemu
# update this rule (change rev1) when it is time to # update this rule (change rev#) when it is time to
# make a new revision. # make a new revision.
tar: tar:
rm -rf /tmp/xv6 rm -rf /tmp/xv6
mkdir -p /tmp/xv6 mkdir -p /tmp/xv6
cp dist/* dist/.gdbinit.tmpl /tmp/xv6 cp dist/* dist/.gdbinit.tmpl /tmp/xv6
(cd /tmp; tar cf - xv6) | gzip >xv6-rev3.tar.gz (cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz
.PHONY: dist-test dist
...@@ -19,6 +19,11 @@ The following people made contributions: ...@@ -19,6 +19,11 @@ The following people made contributions:
Russ Cox (context switching, locking) Russ Cox (context switching, locking)
Cliff Frey (MP) Cliff Frey (MP)
Xiao Yu (MP) Xiao Yu (MP)
Nickolai Zeldovich
Austin Clements
In addition, we are grateful for the patches contributed by Greg
Price, Yandong Mao, and Hitoshi Mitake.
The code in the files that constitute xv6 is The code in the files that constitute xv6 is
Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox.
...@@ -39,9 +44,7 @@ Then run "make TOOLPREFIX=i386-jos-elf-". ...@@ -39,9 +44,7 @@ Then run "make TOOLPREFIX=i386-jos-elf-".
To run xv6, you can use Bochs or QEMU, both PC simulators. To run xv6, you can use Bochs or QEMU, both PC simulators.
Bochs makes debugging easier, but QEMU is much faster. Bochs makes debugging easier, but QEMU is much faster.
To run in Bochs, run "make bochs" and then type "c" at the bochs prompt. To run in Bochs, run "make bochs" and then type "c" at the bochs prompt.
To run in QEMU, run "make qemu". Both log the xv6 screen output to To run in QEMU, run "make qemu".
standard output.
To create a typeset version of the code, run "make xv6.pdf". To create a typeset version of the code, run "make xv6.pdf". This
This requires the "mpage" text formatting utility. requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/.
See http://www.mesa.nl/pub/mpage/.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
.code16 # Assemble for 16-bit mode .code16 # Assemble for 16-bit mode
.globl start .globl start
start: start:
cli # Disable interrupts cli # BIOS enabled interrupts; disable
# Set up the important data segment registers (DS, ES, SS). # Set up the important data segment registers (DS, ES, SS).
xorw %ax,%ax # Segment number zero xorw %ax,%ax # Segment number zero
...@@ -21,10 +21,8 @@ start: ...@@ -21,10 +21,8 @@ start:
movw %ax,%es # -> Extra Segment movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment movw %ax,%ss # -> Stack Segment
# Enable A20: # Physical address line A20 is tied to zero so that the first PCs
# For backwards compatibility with the earliest PCs, physical # with 2 MB would run software that assumed 1 MB. Undo that.
# address line 20 is tied low, so that addresses higher than
# 1MB wrap around to zero by default. This code undoes this.
seta20.1: seta20.1:
inb $0x64,%al # Wait for not busy inb $0x64,%al # Wait for not busy
testb $0x2,%al testb $0x2,%al
...@@ -41,23 +39,21 @@ seta20.2: ...@@ -41,23 +39,21 @@ seta20.2:
movb $0xdf,%al # 0xdf -> port 0x60 movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60 outb %al,$0x60
//PAGEBREAK! # Switch from real to protected mode. Use a bootstrap GDT that makes
# Switch from real to protected mode, using a bootstrap GDT # virtual addresses map dierctly to physical addresses so that the
# and segment translation that makes virtual addresses # effective memory map doesn't change during the transition.
# identical to physical addresses, so that the
# effective memory map does not change during the switch.
lgdt gdtdesc lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
# This ljmp is how you load the CS (Code Segment) register. //PAGEBREAK!
# SEG_ASM produces segment descriptors with the 32-bit mode # Complete transition to 32-bit protected mode by using long jmp
# flag set (the D flag), so addresses and word operands will # to reload %cs and %eip. The segment registers are set up with no
# default to 32 bits after this jump. # translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
.code32 # Assemble for 32-bit mode .code32 # Tell assembler to generate 32-bit code now.
start32: start32:
# Set up the protected-mode data segment registers # Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector movw $(SEG_KDATA<<3), %ax # Our data segment selector
......
...@@ -33,8 +33,8 @@ bootmain(void) ...@@ -33,8 +33,8 @@ bootmain(void)
// Load each program segment (ignores ph flags). // Load each program segment (ignores ph flags).
ph = (struct proghdr*)((uchar*)elf + elf->phoff); ph = (struct proghdr*)((uchar*)elf + elf->phoff);
eph = ph + elf->phnum; eph = ph + elf->phnum;
for(; ph < eph; ph++) { for(; ph < eph; ph++){
va = (uchar*)(ph->va & 0xFFFFFF); va = (uchar*)ph->va;
readseg(va, ph->filesz, ph->offset); readseg(va, ph->filesz, ph->offset);
if(ph->memsz > ph->filesz) if(ph->memsz > ph->filesz)
stosb(va + ph->filesz, 0, ph->memsz - ph->filesz); stosb(va + ph->filesz, 0, ph->memsz - ph->filesz);
...@@ -42,7 +42,7 @@ bootmain(void) ...@@ -42,7 +42,7 @@ bootmain(void)
// Call the entry point from the ELF header. // Call the entry point from the ELF header.
// Does not return! // Does not return!
entry = (void(*)(void))(elf->entry & 0xFFFFFF); entry = (void(*)(void))(elf->entry);
entry(); entry();
} }
......
...@@ -9,80 +9,69 @@ ...@@ -9,80 +9,69 @@
# Because this code sets DS to zero, it must sit # Because this code sets DS to zero, it must sit
# at an address in the low 2^16 bytes. # at an address in the low 2^16 bytes.
# #
# Bootothers (in main.c) sends the STARTUPs, one at a time. # Bootothers (in main.c) sends the STARTUPs one at a time.
# It puts this code (start) at 0x7000. # It copies this code (start) at 0x7000.
# It puts the correct %esp in start-4, # It puts the address of a newly allocated per-core stack in start-4,
# and the place to jump to in start-8. # and the address of the place to jump to (mpmain) in start-8.
# #
# This code is identical to bootasm.S except: # This code is identical to bootasm.S except:
# - it does not need to enable A20 # - it does not need to enable A20
# - it uses the address at start-4 for the %esp # - it uses the address at start-4 for the %esp
# - it jumps to the address at start-8 instead of calling bootmain # - it jumps to the address at start-8 instead of calling bootmain
#define SEG_KCODE 1 // kernel code #define SEG_KCODE 1
#define SEG_KDATA 2 // kernel data+stack #define SEG_KDATA 2
#define CR0_PE 1 // protected mode enable bit #define CR0_PE 1
.code16 # Assemble for 16-bit mode .code16
.globl start .globl start
start: start:
cli # Disable interrupts cli
# Set up the important data segment registers (DS, ES, SS). xorw %ax,%ax
xorw %ax,%ax # Segment number zero movw %ax,%ds
movw %ax,%ds # -> Data Segment movw %ax,%es
movw %ax,%es # -> Extra Segment movw %ax,%ss
movw %ax,%ss # -> Stack Segment
//PAGEBREAK!
# Switch from real to protected mode, using a bootstrap GDT
# and segment translation that makes virtual addresses
# identical to physical addresses, so that the
# effective memory map does not change during the switch.
lgdt gdtdesc lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
# This ljmp is how you load the CS (Code Segment) register. //PAGEBREAK!
# SEG_ASM produces segment descriptors with the 32-bit mode
# flag set (the D flag), so addresses and word operands will
# default to 32 bits after this jump.
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
.code32 # Assemble for 32-bit mode .code32
start32: start32:
# Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax
movw $(SEG_KDATA<<3), %ax # Our data segment selector movw %ax, %ds
movw %ax, %ds # -> DS: Data Segment movw %ax, %es
movw %ax, %es # -> ES: Extra Segment movw %ax, %ss
movw %ax, %ss # -> SS: Stack Segment movw $0, %ax
movw $0, %ax # Zero segments not ready for use movw %ax, %fs
movw %ax, %fs # -> FS movw %ax, %gs
movw %ax, %gs # -> GS
# Set up the stack pointer and call into C. # switch to the stack allocated by bootothers()
movl start-4, %esp movl start-4, %esp
# call mpmain()
call *(start-8) call *(start-8)
# If the call returns (it shouldn't), trigger a Bochs movw $0x8a00, %ax
# breakpoint if running under Bochs, then loop.
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
movw %ax, %dx movw %ax, %dx
outw %ax, %dx outw %ax, %dx
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 movw $0x8ae0, %ax
outw %ax, %dx outw %ax, %dx
spin: spin:
jmp spin jmp spin
# Bootstrap GDT .p2align 2
.p2align 2 # force 4 byte alignment
gdt: gdt:
SEG_NULLASM # null seg SEG_NULLASM
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg SEG_ASM(STA_W, 0x0, 0xffffffff)
gdtdesc: gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 .word (gdtdesc - gdt - 1)
.long gdt # address gdt .long gdt
...@@ -23,23 +23,24 @@ static struct { ...@@ -23,23 +23,24 @@ static struct {
} cons; } cons;
static void static void
printint(int xx, int base, int sgn) printint(int xx, int base, int sign)
{ {
static char digits[] = "0123456789abcdef"; static char digits[] = "0123456789abcdef";
char buf[16]; char buf[16];
int i = 0, neg = 0; int i;
uint x; uint x;
if(sgn && xx < 0){ if(sign && (sign = xx < 0))
neg = 1;
x = -xx; x = -xx;
} else else
x = xx; x = xx;
i = 0;
do{ do{
buf[i++] = digits[x % base]; buf[i++] = digits[x % base];
}while((x /= base) != 0); }while((x /= base) != 0);
if(neg)
if(sign)
buf[i++] = '-'; buf[i++] = '-';
while(--i >= 0) while(--i >= 0)
...@@ -136,8 +137,7 @@ cgaputc(int c) ...@@ -136,8 +137,7 @@ cgaputc(int c)
if(c == '\n') if(c == '\n')
pos += 80 - pos%80; pos += 80 - pos%80;
else if(c == BACKSPACE){ else if(c == BACKSPACE){
if(pos > 0) if(pos > 0) --pos;
crt[--pos] = ' ' | 0x0700;
} else } else
crt[pos++] = (c&0xff) | 0x0700; // black on white crt[pos++] = (c&0xff) | 0x0700; // black on white
...@@ -163,16 +163,13 @@ consputc(int c) ...@@ -163,16 +163,13 @@ consputc(int c)
; ;
} }
if (c == BACKSPACE) { if(c == BACKSPACE){
uartputc('\b'); uartputc('\b'); uartputc(' '); uartputc('\b');
uartputc(' ');
uartputc('\b');
} else } else
uartputc(c); uartputc(c);
cgaputc(c); cgaputc(c);
} }
//PAGEBREAK: 50
#define INPUT_BUF 128 #define INPUT_BUF 128
struct { struct {
struct spinlock lock; struct spinlock lock;
...@@ -202,8 +199,7 @@ consoleintr(int (*getc)(void)) ...@@ -202,8 +199,7 @@ consoleintr(int (*getc)(void))
consputc(BACKSPACE); consputc(BACKSPACE);
} }
break; break;
case C('H'): // Backspace case C('H'): case '\x7f': // Backspace
case '\x7f':
if(input.e != input.w){ if(input.e != input.w){
input.e--; input.e--;
consputc(BACKSPACE); consputc(BACKSPACE);
...@@ -211,9 +207,7 @@ consoleintr(int (*getc)(void)) ...@@ -211,9 +207,7 @@ consoleintr(int (*getc)(void))
break; break;
default: default:
if(c != 0 && input.e-input.r < INPUT_BUF){ if(c != 0 && input.e-input.r < INPUT_BUF){
// The serial port produces 0x13, not 0x10 c = (c == '\r') ? '\n' : c;
if(c == '\r')
c = '\n';
input.buf[input.e++ % INPUT_BUF] = c; input.buf[input.e++ % INPUT_BUF] = c;
consputc(c); consputc(c);
if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
......
// 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
...@@ -6,6 +6,7 @@ struct pipe; ...@@ -6,6 +6,7 @@ struct pipe;
struct proc; struct proc;
struct spinlock; struct spinlock;
struct stat; struct stat;
struct superblock;
// bio.c // bio.c
void binit(void); void binit(void);
...@@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*); ...@@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*);
int filewrite(struct file*, char*, int n); int filewrite(struct file*, char*, int n);
// fs.c // fs.c
void readsb(int dev, struct superblock *sb);
int dirlink(struct inode*, char*, uint); int dirlink(struct inode*, char*, uint);
struct inode* dirlookup(struct inode*, char*, uint*); struct inode* dirlookup(struct inode*, char*, uint*);
struct inode* ialloc(uint, short); struct inode* ialloc(uint, short);
...@@ -62,7 +64,7 @@ void ioapicinit(void); ...@@ -62,7 +64,7 @@ void ioapicinit(void);
// kalloc.c // kalloc.c
char* kalloc(void); char* kalloc(void);
void kfree(char*); void kfree(char*);
void kinit(char*,uint); void kinit(void);
// kbd.c // kbd.c
void kbdintr(void); void kbdintr(void);
...@@ -75,6 +77,12 @@ void lapicinit(int); ...@@ -75,6 +77,12 @@ void lapicinit(int);
void lapicstartap(uchar, uint); void lapicstartap(uchar, uint);
void microdelay(int); void microdelay(int);
// log.c
void initlog(void);
void log_write(struct buf*);
void begin_trans();
void commit_trans();
// mp.c // mp.c
extern int ismp; extern int ismp;
int mpbcpu(void); int mpbcpu(void);
...@@ -101,6 +109,7 @@ int kill(int); ...@@ -101,6 +109,7 @@ int kill(int);
void pinit(void); void pinit(void);
void procdump(void); void procdump(void);
void scheduler(void) __attribute__((noreturn)); void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep(void*, struct spinlock*); void sleep(void*, struct spinlock*);
void userinit(void); void userinit(void);
int wait(void); int wait(void);
...@@ -116,8 +125,8 @@ void getcallerpcs(void*, uint*); ...@@ -116,8 +125,8 @@ void getcallerpcs(void*, uint*);
int holding(struct spinlock*); int holding(struct spinlock*);
void initlock(struct spinlock*, char*); void initlock(struct spinlock*, char*);
void release(struct spinlock*); void release(struct spinlock*);
void pushcli(); void pushcli(void);
void popcli(); void popcli(void);
// string.c // string.c
int memcmp(const void*, const void*, uint); int memcmp(const void*, const void*, uint);
...@@ -151,20 +160,20 @@ void uartintr(void); ...@@ -151,20 +160,20 @@ void uartintr(void);
void uartputc(int); void uartputc(int);
// vm.c // vm.c
void pminit(void); void seginit(void);
void ksegment(void);
void kvmalloc(void); void kvmalloc(void);
void vmenable(void); void vmenable(void);
pde_t* setupkvm(void); pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*); char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, char*, uint); int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t *pgdir, char *addr, uint sz); int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*); void freevm(pde_t*);
void inituvm(pde_t*, char*, char*, uint); void inituvm(pde_t*, char*, uint);
int loaduvm(pde_t*, char*, struct inode *ip, uint, uint); int loaduvm(pde_t*, char*, struct inode*, uint, uint);
pde_t* copyuvm(pde_t*,uint); pde_t* copyuvm(pde_t*, uint);
void switchuvm(struct proc*); void switchuvm(struct proc*);
void switchkvm(); void switchkvm(void);
int copyout(pde_t*, uint, void*, uint);
// number of elements in fixed-size array // number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0])) #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
...@@ -9,20 +9,18 @@ ...@@ -9,20 +9,18 @@
int int
exec(char *path, char **argv) exec(char *path, char **argv)
{ {
char *mem, *s, *last; char *s, *last;
int i, argc, arglen, len, off; int i, off;
uint sz, sp, spoffset, argp; uint argc, sz, sp, ustack[3+MAXARG+1];
struct elfhdr elf; struct elfhdr elf;
struct inode *ip; struct inode *ip;
struct proghdr ph; struct proghdr ph;
pde_t *pgdir, *oldpgdir; pde_t *pgdir, *oldpgdir;
pgdir = 0;
sz = 0;
if((ip = namei(path)) == 0) if((ip = namei(path)) == 0)
return -1; return -1;
ilock(ip); ilock(ip);
pgdir = 0;
// Check ELF header // Check ELF header
if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))
...@@ -30,10 +28,11 @@ exec(char *path, char **argv) ...@@ -30,10 +28,11 @@ exec(char *path, char **argv)
if(elf.magic != ELF_MAGIC) if(elf.magic != ELF_MAGIC)
goto bad; goto bad;
if (!(pgdir = setupkvm())) if((pgdir = setupkvm()) == 0)
goto bad; goto bad;
// Load program into memory. // Load program into memory.
sz = 0;
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad; goto bad;
...@@ -41,49 +40,39 @@ exec(char *path, char **argv) ...@@ -41,49 +40,39 @@ exec(char *path, char **argv)
continue; continue;
if(ph.memsz < ph.filesz) if(ph.memsz < ph.filesz)
goto bad; goto bad;
if (!allocuvm(pgdir, (char *)ph.va, ph.memsz)) if((sz = allocuvm(pgdir, sz, ph.va + ph.memsz)) == 0)
goto bad; goto bad;
if(ph.va + ph.memsz > sz) if(loaduvm(pgdir, (char*)ph.va, ip, ph.offset, ph.filesz) < 0)
sz = ph.va + ph.memsz;
if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz))
goto bad; goto bad;
} }
iunlockput(ip); iunlockput(ip);
ip = 0;
// Allocate and initialize stack at sz // Allocate a one-page stack at the next page boundary
sz = PGROUNDUP(sz); sz = PGROUNDUP(sz);
sz += PGSIZE; // leave an invalid page if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0)
if (!allocuvm(pgdir, (char *)sz, PGSIZE))
goto bad; goto bad;
mem = uva2ka(pgdir, (char *)sz);
spoffset = sz;
sz += PGSIZE;
arglen = 0;
for(argc=0; argv[argc]; argc++)
arglen += strlen(argv[argc]) + 1;
arglen = (arglen+3) & ~3;
// Push argument strings, prepare rest of stack in ustack.
sp = sz; sp = sz;
argp = sz - arglen - 4*(argc+1); for(argc = 0; argv[argc]; argc++) {
if(argc >= MAXARG)
// Copy argv strings and pointers to stack. goto bad;
*(uint*)(mem+argp-spoffset + 4*argc) = 0; // argv[argc] sp -= strlen(argv[argc]) + 1;
for(i=argc-1; i>=0; i--){ sp &= ~3;
len = strlen(argv[i]) + 1; if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
sp -= len; goto bad;
memmove(mem+sp-spoffset, argv[i], len); ustack[3+argc] = sp;
*(uint*)(mem+argp-spoffset + 4*i) = sp; // argv[i]
} }
ustack[3+argc] = 0;
ustack[0] = 0xffffffff; // fake return PC
ustack[1] = argc;
ustack[2] = sp - (argc+1)*4; // argv pointer
// Stack frame for main(argc, argv), below arguments. sp -= (3+argc+1) * 4;
sp = argp; if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
sp -= 4; goto bad;
*(uint*)(mem+sp-spoffset) = argp;
sp -= 4;
*(uint*)(mem+sp-spoffset) = argc;
sp -= 4;
*(uint*)(mem+sp-spoffset) = 0xffffffff; // fake return pc
// Save program name for debugging. // Save program name for debugging.
for(last=s=path; *s; s++) for(last=s=path; *s; s++)
...@@ -97,15 +86,15 @@ exec(char *path, char **argv) ...@@ -97,15 +86,15 @@ exec(char *path, char **argv)
proc->sz = sz; proc->sz = sz;
proc->tf->eip = elf.entry; // main proc->tf->eip = elf.entry; // main
proc->tf->esp = sp; proc->tf->esp = sp;
switchuvm(proc); switchuvm(proc);
freevm(oldpgdir); freevm(oldpgdir);
return 0; return 0;
bad: bad:
if (pgdir) freevm(pgdir); if(pgdir)
freevm(pgdir);
if(ip)
iunlockput(ip); iunlockput(ip);
return -1; return -1;
} }
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
static void itrunc(struct inode*); static void itrunc(struct inode*);
// Read the super block. // Read the super block.
static void void
readsb(int dev, struct superblock *sb) readsb(int dev, struct superblock *sb)
{ {
struct buf *bp; struct buf *bp;
...@@ -61,11 +61,11 @@ balloc(uint dev) ...@@ -61,11 +61,11 @@ balloc(uint dev)
readsb(dev, &sb); readsb(dev, &sb);
for(b = 0; b < sb.size; b += BPB){ for(b = 0; b < sb.size; b += BPB){
bp = bread(dev, BBLOCK(b, sb.ninodes)); bp = bread(dev, BBLOCK(b, sb.ninodes));
for(bi = 0; bi < BPB; bi++){ for(bi = 0; bi < BPB && bi < (sb.size - b); bi++){
m = 1 << (bi % 8); m = 1 << (bi % 8);
if((bp->data[bi/8] & m) == 0){ // Is block free? if((bp->data[bi/8] & m) == 0){ // Is block free?
bp->data[bi/8] |= m; // Mark block in use on disk. bp->data[bi/8] |= m; // Mark block in use on disk.
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
return b + bi; return b + bi;
} }
...@@ -92,7 +92,7 @@ bfree(int dev, uint b) ...@@ -92,7 +92,7 @@ bfree(int dev, uint b)
if((bp->data[bi/8] & m) == 0) if((bp->data[bi/8] & m) == 0)
panic("freeing free block"); panic("freeing free block");
bp->data[bi/8] &= ~m; // Mark block free on disk. bp->data[bi/8] &= ~m; // Mark block free on disk.
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
...@@ -159,7 +159,7 @@ ialloc(uint dev, short type) ...@@ -159,7 +159,7 @@ ialloc(uint dev, short type)
if(dip->type == 0){ // a free inode if(dip->type == 0){ // a free inode
memset(dip, 0, sizeof(*dip)); memset(dip, 0, sizeof(*dip));
dip->type = type; dip->type = type;
bwrite(bp); // mark it allocated on the disk log_write(bp); // mark it allocated on the disk
brelse(bp); brelse(bp);
return iget(dev, inum); return iget(dev, inum);
} }
...@@ -183,7 +183,7 @@ iupdate(struct inode *ip) ...@@ -183,7 +183,7 @@ iupdate(struct inode *ip)
dip->nlink = ip->nlink; dip->nlink = ip->nlink;
dip->size = ip->size; dip->size = ip->size;
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
...@@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn) ...@@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn)
a = (uint*)bp->data; a = (uint*)bp->data;
if((addr = a[bn]) == 0){ if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev); a[bn] = addr = balloc(ip->dev);
bwrite(bp); log_write(bp);
} }
brelse(bp); brelse(bp);
return addr; return addr;
......
...@@ -13,6 +13,7 @@ struct superblock { ...@@ -13,6 +13,7 @@ struct superblock {
uint size; // Size of file system image (blocks) uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes. uint ninodes; // Number of inodes.
uint nlog; // Number of log blocks
}; };
#define NDIRECT 12 #define NDIRECT 12
...@@ -41,7 +42,6 @@ struct dinode { ...@@ -41,7 +42,6 @@ struct dinode {
// Block containing bit for block b // Block containing bit for block b
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)
// PAGEBREAK: 10
// Directory is a file containing a sequence of dirent structures. // Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14 #define DIRSIZ 14
......
...@@ -96,7 +96,7 @@ ideintr(void) ...@@ -96,7 +96,7 @@ ideintr(void)
acquire(&idelock); acquire(&idelock);
if((b = idequeue) == 0){ if((b = idequeue) == 0){
release(&idelock); release(&idelock);
cprintf("Spurious IDE interrupt.\n"); // cprintf("spurious IDE interrupt\n");
return; return;
} }
idequeue = b->qnext; idequeue = b->qnext;
...@@ -131,7 +131,7 @@ iderw(struct buf *b) ...@@ -131,7 +131,7 @@ iderw(struct buf *b)
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
panic("iderw: nothing to do"); panic("iderw: nothing to do");
if(b->dev != 0 && !havedisk1) if(b->dev != 0 && !havedisk1)
panic("idrw: ide disk 1 not present"); panic("iderw: ide disk 1 not present");
acquire(&idelock); acquire(&idelock);
...@@ -147,7 +147,7 @@ iderw(struct buf *b) ...@@ -147,7 +147,7 @@ iderw(struct buf *b)
// Wait for request to finish. // Wait for request to finish.
// Assuming will not sleep too long: ignore proc->killed. // Assuming will not sleep too long: ignore proc->killed.
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) { while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
sleep(b, &idelock); sleep(b, &idelock);
} }
......
...@@ -3,9 +3,12 @@ ...@@ -3,9 +3,12 @@
#include "syscall.h" #include "syscall.h"
#include "traps.h" #include "traps.h"
# exec(init, argv) # exec(init, argv)
.globl start .globl start
start: start:
movl $SYS_init, %eax
int $T_SYSCALL
pushl $argv pushl $argv
pushl $init pushl $init
pushl $0 // where caller pc would be pushl $0 // where caller pc would be
......
...@@ -17,17 +17,21 @@ struct { ...@@ -17,17 +17,21 @@ struct {
struct run *freelist; struct run *freelist;
} kmem; } kmem;
extern char end[]; // first address after kernel loaded from ELF file
// Initialize free list of physical pages. // Initialize free list of physical pages.
void void
kinit(char *p, uint len) kinit(void)
{ {
char *p;
initlock(&kmem.lock, "kmem"); initlock(&kmem.lock, "kmem");
char *p1 = (char*)PGROUNDUP((uint)p); p = (char*)PGROUNDUP((uint)end);
char *p2 = PGROUNDDOWN(p + len); for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE)
for( ; p1 < p2; p1 += 4096) kfree(p);
kfree(p1);
} }
//PAGEBREAK: 21
// Free the page of physical memory pointed at by v, // Free the page of physical memory pointed at by v,
// which normally should have been returned by a // which normally should have been returned by a
// call to kalloc(). (The exception is when // call to kalloc(). (The exception is when
...@@ -37,14 +41,14 @@ kfree(char *v) ...@@ -37,14 +41,14 @@ kfree(char *v)
{ {
struct run *r; struct run *r;
if(((uint) v) % PGSIZE || (uint)v < 1024*1024 || (uint)v >= PHYSTOP) if((uint)v % PGSIZE || v < end || (uint)v >= PHYSTOP)
panic("kfree"); panic("kfree");
// Fill with junk to catch dangling refs. // Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE); memset(v, 1, PGSIZE);
acquire(&kmem.lock); acquire(&kmem.lock);
r = (struct run *) v; r = (struct run*)v;
r->next = kmem.freelist; r->next = kmem.freelist;
kmem.freelist = r; kmem.freelist = r;
release(&kmem.lock); release(&kmem.lock);
...@@ -54,7 +58,7 @@ kfree(char *v) ...@@ -54,7 +58,7 @@ kfree(char *v)
// Returns a pointer that the kernel can use. // Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated. // Returns 0 if the memory cannot be allocated.
char* char*
kalloc() kalloc(void)
{ {
struct run *r; struct run *r;
...@@ -63,6 +67,6 @@ kalloc() ...@@ -63,6 +67,6 @@ kalloc()
if(r) if(r)
kmem.freelist = r->next; kmem.freelist = r->next;
release(&kmem.lock); release(&kmem.lock);
return (char*) r; return (char*)r;
} }
#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++;
}
...@@ -7,40 +7,45 @@ ...@@ -7,40 +7,45 @@
static void bootothers(void); static void bootothers(void);
static void mpmain(void); static void mpmain(void);
void jkstack(void) __attribute__((noreturn)); void jmpkstack(void) __attribute__((noreturn));
void mainc(void); void mainc(void);
// Bootstrap processor starts running C code here. // Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int int
main(void) main(void)
{ {
mpinit(); // collect info about this machine mpinit(); // collect info about this machine
lapicinit(mpbcpu()); lapicinit(mpbcpu());
ksegment(); // set up segments seginit(); // set up segments
picinit(); // interrupt controller kinit(); // initialize memory allocator
ioapicinit(); // another interrupt controller jmpkstack(); // call mainc() on a properly-allocated stack
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
pminit(); // discover how much memory there is
jkstack(); // call mainc() on a properly-allocated stack
} }
void void
jkstack(void) jmpkstack(void)
{ {
char *kstack = kalloc(); char *kstack, *top;
if (!kstack)
panic("jkstack\n"); kstack = kalloc();
char *top = kstack + PGSIZE; if(kstack == 0)
asm volatile("movl %0,%%esp" : : "r" (top)); panic("jmpkstack kalloc");
asm volatile("call mainc"); top = kstack + PGSIZE;
panic("jkstack"); asm volatile("movl %0,%%esp; call mainc" : : "r" (top));
panic("jmpkstack");
} }
// Set up hardware and software.
// Runs only on the boostrap processor.
void void
mainc(void) mainc(void)
{ {
cprintf("\ncpu%d: starting xv6\n\n", cpu->id); cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
picinit(); // interrupt controller
ioapicinit(); // another interrupt controller
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
kvmalloc(); // initialize the kernel page table kvmalloc(); // initialize the kernel page table
pinit(); // process table pinit(); // process table
tvinit(); // trap vectors tvinit(); // trap vectors
...@@ -63,17 +68,18 @@ mainc(void) ...@@ -63,17 +68,18 @@ mainc(void)
static void static void
mpmain(void) mpmain(void)
{ {
if(cpunum() != mpbcpu()) { if(cpunum() != mpbcpu()){
ksegment(); seginit();
lapicinit(cpunum()); lapicinit(cpunum());
} }
vmenable(); // turn on paging vmenable(); // turn on paging
cprintf("cpu%d: starting\n", cpu->id); cprintf("cpu%d: starting\n", cpu->id);
idtinit(); // load idt register idtinit(); // load idt register
xchg(&cpu->booted, 1); xchg(&cpu->booted, 1); // tell bootothers() we're up
scheduler(); // start running processes scheduler(); // start running processes
} }
// Start the non-boot processors.
static void static void
bootothers(void) bootothers(void)
{ {
...@@ -82,19 +88,23 @@ bootothers(void) ...@@ -82,19 +88,23 @@ bootothers(void)
struct cpu *c; struct cpu *c;
char *stack; char *stack;
// Write bootstrap code to unused memory at 0x7000. The linker has // Write bootstrap code to unused memory at 0x7000.
// placed the start of bootother.S there. // The linker has placed the image of bootother.S in
code = (uchar *) 0x7000; // _binary_bootother_start.
code = (uchar*)0x7000;
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
for(c = cpus; c < cpus+ncpu; c++){ for(c = cpus; c < cpus+ncpu; c++){
if(c == cpus+cpunum()) // We've started already. if(c == cpus+cpunum()) // We've started already.
continue; continue;
// Fill in %esp, %eip and start code on cpu. // Tell bootother.S what stack to use and the address of mpmain;
// it expects to find these two addresses stored just before
// its first instruction.
stack = kalloc(); stack = kalloc();
*(void**)(code-4) = stack + KSTACKSIZE; *(void**)(code-4) = stack + KSTACKSIZE;
*(void**)(code-8) = mpmain; *(void**)(code-8) = mpmain;
lapicstartap(c->id, (uint)code); lapicstartap(c->id, (uint)code);
// Wait for cpu to finish mpmain() // Wait for cpu to finish mpmain()
...@@ -103,3 +113,6 @@ bootothers(void) ...@@ -103,3 +113,6 @@ bootothers(void)
} }
} }
//PAGEBREAK!
// Blank page.
// 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;
}
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <assert.h> #include <assert.h>
#define stat xv6_stat // avoid clash with host struct stat
#include "types.h" #include "types.h"
#include "fs.h" #include "fs.h"
#include "stat.h" #include "stat.h"
#include "param.h"
int nblocks = 995; int nblocks = 985;
int nlog = LOGSIZE;
int ninodes = 200; int ninodes = 200;
int size = 1024; int size = 1024;
...@@ -33,7 +37,7 @@ ushort ...@@ -33,7 +37,7 @@ ushort
xshort(ushort x) xshort(ushort x)
{ {
ushort y; ushort y;
uchar *a = (uchar*) &y; uchar *a = (uchar*)&y;
a[0] = x; a[0] = x;
a[1] = x >> 8; a[1] = x >> 8;
return y; return y;
...@@ -43,7 +47,7 @@ uint ...@@ -43,7 +47,7 @@ uint
xint(uint x) xint(uint x)
{ {
uint y; uint y;
uchar *a = (uchar*) &y; uchar *a = (uchar*)&y;
a[0] = x; a[0] = x;
a[1] = x >> 8; a[1] = x >> 8;
a[2] = x >> 16; a[2] = x >> 16;
...@@ -77,20 +81,23 @@ main(int argc, char *argv[]) ...@@ -77,20 +81,23 @@ main(int argc, char *argv[])
sb.size = xint(size); sb.size = xint(size);
sb.nblocks = xint(nblocks); // so whole disk is size sectors sb.nblocks = xint(nblocks); // so whole disk is size sectors
sb.ninodes = xint(ninodes); sb.ninodes = xint(ninodes);
sb.nlog = xint(nlog);
bitblocks = size/(512*8) + 1; bitblocks = size/(512*8) + 1;
usedblocks = ninodes / IPB + 3 + bitblocks; usedblocks = ninodes / IPB + 3 + bitblocks;
freeblock = usedblocks; freeblock = usedblocks;
printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks,
bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog);
assert(nblocks + usedblocks == size); assert(nblocks + usedblocks + nlog == size);
for(i = 0; i < nblocks + usedblocks; i++) for(i = 0; i < nblocks + usedblocks + nlog; i++)
wsect(i, zeroes); wsect(i, zeroes);
wsect(1, &sb); memset(buf, 0, sizeof(buf));
memmove(buf, &sb, sizeof(sb));
wsect(1, buf);
rootino = ialloc(T_DIR); rootino = ialloc(T_DIR);
assert(rootino == ROOTINO); assert(rootino == ROOTINO);
...@@ -173,7 +180,7 @@ winode(uint inum, struct dinode *ip) ...@@ -173,7 +180,7 @@ winode(uint inum, struct dinode *ip)
bn = i2b(inum); bn = i2b(inum);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode*) buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*dip = *ip; *dip = *ip;
wsect(bn, buf); wsect(bn, buf);
} }
...@@ -187,7 +194,7 @@ rinode(uint inum, struct dinode *ip) ...@@ -187,7 +194,7 @@ rinode(uint inum, struct dinode *ip)
bn = i2b(inum); bn = i2b(inum);
rsect(bn, buf); rsect(bn, buf);
dip = ((struct dinode*) buf) + (inum % IPB); dip = ((struct dinode*)buf) + (inum % IPB);
*ip = *dip; *ip = *dip;
} }
...@@ -225,12 +232,12 @@ balloc(int used) ...@@ -225,12 +232,12 @@ balloc(int used)
int i; int i;
printf("balloc: first %d blocks have been allocated\n", used); printf("balloc: first %d blocks have been allocated\n", used);
assert(used < 512); assert(used < 512*8);
bzero(buf, 512); bzero(buf, 512);
for(i = 0; i < used; i++) { for(i = 0; i < used; i++){
buf[i/8] = buf[i/8] | (0x1 << (i%8)); buf[i/8] = buf[i/8] | (0x1 << (i%8));
} }
printf("balloc: write bitmap block at sector %lu\n", ninodes/IPB + 3); printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3);
wsect(ninodes / IPB + 3, buf); wsect(ninodes / IPB + 3, buf);
} }
...@@ -239,7 +246,7 @@ balloc(int used) ...@@ -239,7 +246,7 @@ balloc(int used)
void void
iappend(uint inum, void *xp, int n) iappend(uint inum, void *xp, int n)
{ {
char *p = (char*) xp; char *p = (char*)xp;
uint fbn, off, n1; uint fbn, off, n1;
struct dinode din; struct dinode din;
char buf[512]; char buf[512];
...@@ -252,24 +259,24 @@ iappend(uint inum, void *xp, int n) ...@@ -252,24 +259,24 @@ iappend(uint inum, void *xp, int n)
while(n > 0){ while(n > 0){
fbn = off / 512; fbn = off / 512;
assert(fbn < MAXFILE); assert(fbn < MAXFILE);
if(fbn < NDIRECT) { if(fbn < NDIRECT){
if(xint(din.addrs[fbn]) == 0) { if(xint(din.addrs[fbn]) == 0){
din.addrs[fbn] = xint(freeblock++); din.addrs[fbn] = xint(freeblock++);
usedblocks++; usedblocks++;
} }
x = xint(din.addrs[fbn]); x = xint(din.addrs[fbn]);
} else { } else {
if(xint(din.addrs[NDIRECT]) == 0) { if(xint(din.addrs[NDIRECT]) == 0){
// printf("allocate indirect block\n"); // printf("allocate indirect block\n");
din.addrs[NDIRECT] = xint(freeblock++); din.addrs[NDIRECT] = xint(freeblock++);
usedblocks++; usedblocks++;
} }
// printf("read indirect block\n"); // printf("read indirect block\n");
rsect(xint(din.addrs[NDIRECT]), (char*) indirect); rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
if(indirect[fbn - NDIRECT] == 0) { if(indirect[fbn - NDIRECT] == 0){
indirect[fbn - NDIRECT] = xint(freeblock++); indirect[fbn - NDIRECT] = xint(freeblock++);
usedblocks++; usedblocks++;
wsect(xint(din.addrs[NDIRECT]), (char*) indirect); wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
} }
x = xint(indirect[fbn-NDIRECT]); x = xint(indirect[fbn-NDIRECT]);
} }
......
...@@ -24,6 +24,20 @@ ...@@ -24,6 +24,20 @@
#define FL_VIP 0x00100000 // Virtual Interrupt Pending #define FL_VIP 0x00100000 // Virtual Interrupt Pending
#define FL_ID 0x00200000 // ID flag #define FL_ID 0x00200000 // ID flag
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
//PAGEBREAK!
// Segment Descriptor // Segment Descriptor
struct segdesc { struct segdesc {
uint lim_15_0 : 16; // Low bits of segment limit uint lim_15_0 : 16; // Low bits of segment limit
...@@ -46,7 +60,6 @@ struct segdesc { ...@@ -46,7 +60,6 @@ struct segdesc {
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (struct segdesc) \ #define SEG16(type, base, lim, dpl) (struct segdesc) \
{ (lim) & 0xffff, (uint)(base) & 0xffff, \ { (lim) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
...@@ -62,8 +75,6 @@ struct segdesc { ...@@ -62,8 +75,6 @@ struct segdesc {
#define STA_R 0x2 // Readable (executable segments) #define STA_R 0x2 // Readable (executable segments)
#define STA_A 0x1 // Accessed #define STA_A 0x1 // Accessed
//
// System segment type bits // System segment type bits
#define STS_T16A 0x1 // Available 16-bit TSS #define STS_T16A 0x1 // Available 16-bit TSS
#define STS_LDT 0x2 // Local Descriptor Table #define STS_LDT 0x2 // Local Descriptor Table
...@@ -78,7 +89,6 @@ struct segdesc { ...@@ -78,7 +89,6 @@ struct segdesc {
#define STS_IG32 0xE // 32-bit Interrupt Gate #define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate #define STS_TG32 0xF // 32-bit Trap Gate
// A linear address 'la' has a three-part structure as follows: // A linear address 'la' has a three-part structure as follows:
// //
// +--------10------+-------10-------+---------12----------+ // +--------10------+-------10-------+---------12----------+
...@@ -88,18 +98,18 @@ struct segdesc { ...@@ -88,18 +98,18 @@ struct segdesc {
// \--- PDX(la) --/ \--- PTX(la) --/ // \--- PDX(la) --/ \--- PTX(la) --/
// page directory index // page directory index
#define PDX(la) ((((uint) (la)) >> PDXSHIFT) & 0x3FF) #define PDX(la) (((uint)(la) >> PDXSHIFT) & 0x3FF)
// page table index // page table index
#define PTX(la) ((((uint) (la)) >> PTXSHIFT) & 0x3FF) #define PTX(la) (((uint)(la) >> PTXSHIFT) & 0x3FF)
// construct linear address from indexes and offset // construct linear address from indexes and offset
#define PGADDR(d, t, o) ((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) #define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
// turn a kernel linear address into a physical address. // turn a kernel linear address into a physical address.
// all of the kernel data structures have linear and // all of the kernel data structures have linear and
// physical addresses that are equal. // physical addresses that are equal.
#define PADDR(a) ((uint) a) #define PADDR(a) ((uint)(a))
// Page directory and page table constants. // Page directory and page table constants.
#define NPDENTRIES 1024 // page directory entries per page directory #define NPDENTRIES 1024 // page directory entries per page directory
...@@ -126,25 +136,10 @@ struct segdesc { ...@@ -126,25 +136,10 @@ struct segdesc {
#define PTE_MBZ 0x180 // Bits must be zero #define PTE_MBZ 0x180 // Bits must be zero
// Address in page table or page directory entry // Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint) (pte) & ~0xFFF) #define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
typedef uint pte_t; typedef uint pte_t;
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
// PAGEBREAK: 40
// Task state segment format // Task state segment format
struct taskstate { struct taskstate {
uint link; // Old ts selector uint link; // Old ts selector
...@@ -210,7 +205,7 @@ struct gatedesc { ...@@ -210,7 +205,7 @@ struct gatedesc {
// this interrupt/trap gate explicitly using an int instruction. // this interrupt/trap gate explicitly using an int instruction.
#define SETGATE(gate, istrap, sel, off, d) \ #define SETGATE(gate, istrap, sel, off, d) \
{ \ { \
(gate).off_15_0 = (uint) (off) & 0xffff; \ (gate).off_15_0 = (uint)(off) & 0xffff; \
(gate).cs = (sel); \ (gate).cs = (sel); \
(gate).args = 0; \ (gate).args = 0; \
(gate).rsv1 = 0; \ (gate).rsv1 = 0; \
...@@ -218,6 +213,6 @@ struct gatedesc { ...@@ -218,6 +213,6 @@ struct gatedesc {
(gate).s = 0; \ (gate).s = 0; \
(gate).dpl = (d); \ (gate).dpl = (d); \
(gate).p = 1; \ (gate).p = 1; \
(gate).off_31_16 = (uint) (off) >> 16; \ (gate).off_31_16 = (uint)(off) >> 16; \
} }
...@@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len) ...@@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len)
{ {
uchar *e, *p; uchar *e, *p;
cprintf("mpsearch1 0x%x %d\n", addr, len);
e = addr+len; e = addr+len;
for(p = addr; p < e; p += sizeof(struct mp)) for(p = addr; p < e; p += sizeof(struct mp))
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
...@@ -113,9 +112,9 @@ mpinit(void) ...@@ -113,9 +112,9 @@ mpinit(void)
switch(*p){ switch(*p){
case MPPROC: case MPPROC:
proc = (struct mpproc*)p; proc = (struct mpproc*)p;
if(ncpu != proc->apicid) { if(ncpu != proc->apicid){
cprintf("mpinit: ncpu=%d apicpid=%d", ncpu, proc->apicid); cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
panic("mpinit"); ismp = 0;
} }
if(proc->flags & MPBOOT) if(proc->flags & MPBOOT)
bcpu = &cpus[ncpu]; bcpu = &cpus[ncpu];
...@@ -135,9 +134,17 @@ mpinit(void) ...@@ -135,9 +134,17 @@ mpinit(void)
continue; continue;
default: default:
cprintf("mpinit: unknown config type %x\n", *p); cprintf("mpinit: unknown config type %x\n", *p);
panic("mpinit"); ismp = 0;
} }
} }
if(!ismp){
// Didn't like what we found; fall back to no MP.
ncpu = 1;
lapic = 0;
ioapicid = 0;
return;
}
if(mp->imcrp){ if(mp->imcrp){
// Bochs doesn't support IMCR, so this doesn't run on Bochs. // Bochs doesn't support IMCR, so this doesn't run on Bochs.
// But it would on real hardware. // But it would on real hardware.
......
# 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
...@@ -7,4 +7,8 @@ ...@@ -7,4 +7,8 @@
#define NINODE 50 // maximum number of active i-nodes #define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number #define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk #define ROOTDEV 1 // device number of file system root disk
#define USERTOP 0xA0000 // end of user address space
#define PHYSTOP 0x1000000 // use phys mem up to here as free pool #define PHYSTOP 0x1000000 // use phys mem up to here as free pool
#define MAXARG 32 // max exec arguments
#define LOGSIZE 10 // size of log
...@@ -82,32 +82,3 @@ picinit(void) ...@@ -82,32 +82,3 @@ picinit(void)
if(irqmask != 0xFFFF) if(irqmask != 0xFFFF)
picsetmask(irqmask); picsetmask(irqmask);
} }
// Blank page.
...@@ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable) ...@@ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable)
p->readopen = 0; p->readopen = 0;
wakeup(&p->nwrite); wakeup(&p->nwrite);
} }
if(p->readopen == 0 && p->writeopen == 0) { if(p->readopen == 0 && p->writeopen == 0){
release(&p->lock); release(&p->lock);
kfree((char*)p); kfree((char*)p);
} else } else
...@@ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n) ...@@ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n)
acquire(&p->lock); acquire(&p->lock);
for(i = 0; i < n; i++){ for(i = 0; i < n; i++){
while(p->nwrite == p->nread + PIPESIZE) { //DOC: pipewrite-full while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full
if(p->readopen == 0 || proc->killed){ if(p->readopen == 0 || proc->killed){
release(&p->lock); release(&p->lock);
return -1; return -1;
......
...@@ -17,53 +17,18 @@ int nextpid = 1; ...@@ -17,53 +17,18 @@ int nextpid = 1;
extern void forkret(void); extern void forkret(void);
extern void trapret(void); extern void trapret(void);
static void wakeup1(void *chan);
void void
pinit(void) pinit(void)
{ {
initlock(&ptable.lock, "ptable"); initlock(&ptable.lock, "ptable");
} }
//PAGEBREAK: 36
// Print a process listing to console. For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
int i;
struct proc *p;
char *state;
uint pc[10];
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
state = states[p->state];
else
state = "???";
cprintf("%d %s %s", p->pid, state, p->name);
if(p->state == SLEEPING){
getcallerpcs((uint*)p->context->ebp+2, pc);
for(i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]);
}
cprintf("\n");
}
}
//PAGEBREAK: 32 //PAGEBREAK: 32
// Look in the process table for an UNUSED proc. // Look in the process table for an UNUSED proc.
// If found, change state to EMBRYO and return it. // If found, change state to EMBRYO and initialize
// state required to run in the kernel.
// Otherwise return 0. // Otherwise return 0.
static struct proc* static struct proc*
allocproc(void) allocproc(void)
...@@ -95,7 +60,7 @@ found: ...@@ -95,7 +60,7 @@ found:
p->tf = (struct trapframe*)sp; p->tf = (struct trapframe*)sp;
// Set up new context to start executing at forkret, // Set up new context to start executing at forkret,
// which returns to trapret (see below). // which returns to trapret.
sp -= 4; sp -= 4;
*(uint*)sp = (uint)trapret; *(uint*)sp = (uint)trapret;
...@@ -103,6 +68,7 @@ found: ...@@ -103,6 +68,7 @@ found:
p->context = (struct context*)sp; p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context); memset(p->context, 0, sizeof *p->context);
p->context->eip = (uint)forkret; p->context->eip = (uint)forkret;
return p; return p;
} }
...@@ -116,12 +82,10 @@ userinit(void) ...@@ -116,12 +82,10 @@ userinit(void)
p = allocproc(); p = allocproc();
initproc = p; initproc = p;
if (!(p->pgdir = setupkvm())) if((p->pgdir = setupkvm()) == 0)
panic("userinit: out of memory?");
if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size))
panic("userinit: out of memory?"); panic("userinit: out of memory?");
inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size); inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
p->sz = PGROUNDUP((int)_binary_initcode_size); p->sz = PGSIZE;
memset(p->tf, 0, sizeof(*p->tf)); memset(p->tf, 0, sizeof(*p->tf));
p->tf->cs = (SEG_UCODE << 3) | DPL_USER; p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
p->tf->ds = (SEG_UDATA << 3) | DPL_USER; p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
...@@ -142,14 +106,17 @@ userinit(void) ...@@ -142,14 +106,17 @@ userinit(void)
int int
growproc(int n) growproc(int n)
{ {
uint sz;
sz = proc->sz;
if(n > 0){ if(n > 0){
if (!allocuvm(proc->pgdir, (char *)proc->sz, n)) if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)
return -1; return -1;
} else if(n < 0){ } else if(n < 0){
if (!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0)
return -1; return -1;
} }
proc->sz += n; proc->sz = sz;
switchuvm(proc); switchuvm(proc);
return 0; return 0;
} }
...@@ -168,7 +135,7 @@ fork(void) ...@@ -168,7 +135,7 @@ fork(void)
return -1; return -1;
// Copy process state from p. // Copy process state from p.
if (!(np->pgdir = copyuvm(proc->pgdir, proc->sz))) { if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){
kfree(np->kstack); kfree(np->kstack);
np->kstack = 0; np->kstack = 0;
np->state = UNUSED; np->state = UNUSED;
...@@ -192,6 +159,92 @@ fork(void) ...@@ -192,6 +159,92 @@ fork(void)
return pid; return pid;
} }
// Exit the current process. Does not return.
// An exited process remains in the zombie state
// until its parent calls wait() to find out it exited.
void
exit(void)
{
struct proc *p;
int fd;
if(proc == initproc)
panic("init exiting");
// Close all open files.
for(fd = 0; fd < NOFILE; fd++){
if(proc->ofile[fd]){
fileclose(proc->ofile[fd]);
proc->ofile[fd] = 0;
}
}
iput(proc->cwd);
proc->cwd = 0;
acquire(&ptable.lock);
// Parent might be sleeping in wait().
wakeup1(proc->parent);
// Pass abandoned children to init.
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent == proc){
p->parent = initproc;
if(p->state == ZOMBIE)
wakeup1(initproc);
}
}
// Jump into the scheduler, never to return.
proc->state = ZOMBIE;
sched();
panic("zombie exit");
}
// Wait for a child process to exit and return its pid.
// Return -1 if this process has no children.
int
wait(void)
{
struct proc *p;
int havekids, pid;
acquire(&ptable.lock);
for(;;){
// Scan through table looking for zombie children.
havekids = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent != proc)
continue;
havekids = 1;
if(p->state == ZOMBIE){
// Found one.
pid = p->pid;
kfree(p->kstack);
p->kstack = 0;
freevm(p->pgdir);
p->state = UNUSED;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
release(&ptable.lock);
return pid;
}
}
// No point waiting if we don't have any children.
if(!havekids || proc->killed){
release(&ptable.lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(proc, &ptable.lock); //DOC: wait-sleep
}
}
//PAGEBREAK: 42 //PAGEBREAK: 42
// Per-CPU process scheduler. // Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up. // Each CPU calls scheduler() after setting itself up.
...@@ -356,89 +409,41 @@ kill(int pid) ...@@ -356,89 +409,41 @@ kill(int pid)
return -1; return -1;
} }
// Exit the current process. Does not return. //PAGEBREAK: 36
// An exited process remains in the zombie state // Print a process listing to console. For debugging.
// until its parent calls wait() to find out it exited. // Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void void
exit(void) procdump(void)
{
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)
{ {
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
int i;
struct proc *p; struct proc *p;
int havekids, pid; char *state;
uint pc[10];
acquire(&ptable.lock);
for(;;){
// Scan through table looking for zombie children.
havekids = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->parent != proc) if(p->state == UNUSED)
continue; continue;
havekids = 1; if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
if(p->state == ZOMBIE){ state = states[p->state];
// Found one. else
pid = p->pid; state = "???";
kfree(p->kstack); cprintf("%d %s %s", p->pid, state, p->name);
p->kstack = 0; if(p->state == SLEEPING){
freevm(p->pgdir); getcallerpcs((uint*)p->context->ebp+2, pc);
p->state = UNUSED; for(i=0; i<10 && pc[i] != 0; i++)
p->pid = 0; cprintf(" %p", pc[i]);
p->parent = 0;
p->name[0] = 0;
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;
} }
cprintf("\n");
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(proc, &ptable.lock); //DOC: wait-sleep
} }
} }
...@@ -8,6 +8,36 @@ ...@@ -8,6 +8,36 @@
#define SEG_TSS 6 // this process's task state #define SEG_TSS 6 // this process's task state
#define NSEGS 7 #define NSEGS 7
// Per-CPU state
struct cpu {
uchar id; // Local APIC ID; index into cpus[] below
struct context *scheduler; // swtch() here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table
volatile uint booted; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
// Cpu-local storage variables; see below
struct cpu *cpu;
struct proc *proc; // The currently-running process.
};
extern struct cpu cpus[NCPU];
extern int ncpu;
// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// The asm suffix tells gcc to use "%gs:0" to refer to cpu
// and "%gs:4" to refer to proc. seginit sets up the
// %gs segment register so that %gs refers to the memory
// holding those two variables in the local cpu's struct cpu.
// This is similar to how thread-local variables are implemented
// in thread libraries such as Linux pthreads.
extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()]
extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc
//PAGEBREAK: 17
// Saved registers for kernel context switches. // Saved registers for kernel context switches.
// Don't need to save all the segment registers (%cs, etc), // Don't need to save all the segment registers (%cs, etc),
// because they are constant across kernel contexts. // because they are constant across kernel contexts.
...@@ -31,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; ...@@ -31,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state // Per-process state
struct proc { struct proc {
uint sz; // Size of process memory (bytes) uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Linear address of proc's pgdir pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state enum procstate state; // Process state
volatile int pid; // Process ID volatile int pid; // Process ID
struct proc *parent; // Parent process struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall struct trapframe *tf; // Trap frame for current syscall
struct context *context; // Switch here to run process struct context *context; // swtch() here to run process
void *chan; // If non-zero, sleeping on chan void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files struct file *ofile[NOFILE]; // Open files
...@@ -48,35 +78,5 @@ struct proc { ...@@ -48,35 +78,5 @@ struct proc {
// Process memory is laid out contiguously, low addresses first: // Process memory is laid out contiguously, low addresses first:
// text // text
// original data and bss // original data and bss
// invalid page
// fixed-size stack // fixed-size stack
// expandable heap // expandable heap
// Per-CPU state
struct cpu {
uchar id; // Local APIC ID; index into cpus[] below
struct context *scheduler; // Switch here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table
volatile uint booted; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
// Cpu-local storage variables; see below
struct cpu *cpu;
struct proc *proc;
};
extern struct cpu cpus[NCPU];
extern int ncpu;
// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// The asm suffix tells gcc to use "%gs:0" to refer to cpu
// and "%gs:4" to refer to proc. ksegment sets up the
// %gs segment register so that %gs refers to the memory
// holding those two variables in the local cpu's struct cpu.
// This is similar to how thread-local variables are implemented
// in thread libraries such as Linux pthreads.
extern struct cpu *cpu asm("%gs:0"); // This cpu.
extern struct proc *proc asm("%gs:4"); // Current proc on this cpu.
...@@ -58,6 +58,13 @@ perl -e ' ...@@ -58,6 +58,13 @@ perl -e '
next; next;
} }
if(/sheet1: (left|right)$/){
print STDERR "assuming that sheet 1 is a $1 page. double-check!\n";
$left = $1 eq "left" ? "13579" : "02468";
$right = $1 eq "left" ? "02468" : "13579";
next;
}
if(/even: (.*)/){ if(/even: (.*)/){
$file = $1; $file = $1;
if(!defined($toc{$file})){ if(!defined($toc{$file})){
...@@ -89,18 +96,13 @@ perl -e ' ...@@ -89,18 +96,13 @@ perl -e '
print STDERR "Have no toc for $file\n"; print STDERR "Have no toc for $file\n";
next; next;
} }
# this assumes that sheet 1 of code is a left page if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
# double-check the PDF print STDERR "$file does not start on a left page [$toc{$file}]\n";
if(!$leftwarn++) {
print STDERR "assuming that sheet 1 is a left page. double-check!\n";
}
if($what eq "left" && !($toc{$file} =~ /^\d[13579]0/)){
print STDERR "$file does not start on a fresh left page [$toc{$file}]\n";
} }
# why does this not work if I inline $x in the if? # why does this not work if I inline $x in the if?
$x = ($toc{$file} =~ /^\d[02468]0/); $x = ($toc{$file} =~ /^\d[$right][05]/);
if($what eq "right" && !$x){ if($what eq "right" && !$x){
print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n";
} }
next; next;
} }
...@@ -189,7 +191,9 @@ do ...@@ -189,7 +191,9 @@ do
uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
if [ "x$defs" != "x$uses" ]; then if [ "x$defs" != "x$uses" ]; then
echo $i $defs echo $i $defs
echo $uses |fmt -24 | sed 's/^/ /' echo $uses |fmt -29 | sed 's/^/ /'
# else
# echo $i defined but not used >&2
fi fi
done done
) >refs ) >refs
......
...@@ -22,8 +22,8 @@ proc.h ...@@ -22,8 +22,8 @@ proc.h
proc.c proc.c
swtch.S swtch.S
kalloc.c kalloc.c
data.S
vm.c vm.c
# system calls # system calls
traps.h traps.h
vectors.pl vectors.pl
...@@ -46,11 +46,10 @@ file.c ...@@ -46,11 +46,10 @@ file.c
sysfile.c sysfile.c
exec.c exec.c
# pipes # pipes
pipe.c pipe.c
# string operations # string operations
string.c string.c
...@@ -65,6 +64,7 @@ kbd.c ...@@ -65,6 +64,7 @@ kbd.c
console.c console.c
timer.c timer.c
uart.c uart.c
multiboot.S
# user-level # user-level
initcode.S initcode.S
...@@ -73,3 +73,6 @@ init.c ...@@ -73,3 +73,6 @@ init.c
sh.c sh.c
sheet1: left
# "left" and "right" specify which page of a two-page spread a file
# must start on. "left" means that a file must start on the first of
# the two pages. "right" means it must start on the second of the two
# pages. The file may start in either column.
#
# "even" and "odd" specify which column a file must start on. "even"
# means it must start in the left of the two columns (00). "odd" means it
# must start in the right of the two columns (50).
#
# You'd think these would be the other way around.
# types.h either # types.h either
# param.h either # param.h either
# defs.h either # defs.h either
...@@ -9,25 +22,36 @@ ...@@ -9,25 +22,36 @@
even: bootasm.S # mild preference even: bootasm.S # mild preference
even: bootother.S # mild preference even: bootother.S # mild preference
# bootmain.c either even: bootmain.c # mild preference
even: main.c even: main.c
# mp.c don't care at all # mp.c don't care at all
# even: initcode.S # even: initcode.S
# odd: init.c # odd: init.c
# spinlock.h either # spinlock.h either
# spinlock.c either left: spinlock.h # mild preference
even: proc.h # mild preference even: spinlock.h # mild preference
# This gets struct proc and allocproc on the same spread
left: proc.h
even: proc.h
# goal is to have two action-packed 2-page spreads, # goal is to have two action-packed 2-page spreads,
# one with # one with
# ksegment usegment allocproc userinit growproc fork # userinit growproc fork exit wait
# and another with # and another with
# scheduler sched yield forkret sleep wakeup1 wakeup # scheduler sched yield forkret sleep wakeup1 wakeup
right: proc.c # VERY important right: proc.c # VERY important
even: proc.c # VERY important
# A few more action packed spreads
# page table creation and process loading
# walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm
# process memory management
# allocuvm deallocuvm freevm
left: vm.c
odd: vm.c
# setjmp.S either
# vm.c either
# kalloc.c either # kalloc.c either
# syscall.h either # syscall.h either
...@@ -45,15 +69,25 @@ right: proc.c # VERY important ...@@ -45,15 +69,25 @@ right: proc.c # VERY important
# file.h either # file.h either
# fs.h either # fs.h either
# fsvar.h either # fsvar.h either
left: ide.c # left: ide.c # mild preference
even: ide.c
# odd: bio.c # odd: bio.c
# with fs.c starting on 2nd column of a left page, we get these 2-page spreads:
# ialloc iupdate iget idup ilock iunlock iput iunlockput
# bmap itrunc stati readi writei
# namecmp dirlookup dirlink skipelem namex namei
# fielinit filealloc filedup fileclose filestat fileread filewrite
# starting on 2nd column of a right page is not terrible either
odd: fs.c # VERY important odd: fs.c # VERY important
left: fs.c # mild preference
# file.c either # file.c either
# exec.c either # exec.c either
# sysfile.c either # sysfile.c either
# even: pipe.c # mild preference # even: pipe.c # mild preference
# string.c either # string.c either
left: kbd.h # left: kbd.h # mild preference
even: kbd.h
even: console.c even: console.c
odd: sh.c odd: sh.c
...@@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){ ...@@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){
last if $i>=@lines; last if $i>=@lines;
# If the rest of the file fits, use the whole thing. # If the rest of the file fits, use the whole thing.
if(@lines <= $i+50){ if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
$breakbefore = @lines; $breakbefore = @lines;
}else{ }else{
# Find a good next page break; # Find a good next page break;
......
...@@ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name) ...@@ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name)
void void
acquire(struct spinlock *lk) acquire(struct spinlock *lk)
{ {
pushcli(); pushcli(); // disable interrupts to avoid deadlock.
if(holding(lk)) if(holding(lk))
panic("acquire"); panic("acquire");
...@@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[]) ...@@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[])
ebp = (uint*)v - 2; ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){ for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint *) 0x100000 || ebp == (uint*)0xffffffff) if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff)
break; break;
pcs[i] = ebp[1]; // saved %eip pcs[i] = ebp[1]; // saved %eip
ebp = (uint*)ebp[0]; // saved %ebp ebp = (uint*)ebp[0]; // saved %ebp
......
...@@ -14,21 +14,20 @@ ...@@ -14,21 +14,20 @@
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int i; int fd, i;
char path[] = "stressfs0";
printf(1, "stressfs starting\n"); printf(1, "stressfs starting\n");
for (i = 0; i < 4; i++) { for(i = 0; i < 4; i++)
if (fork() > 0) { if(fork() > 0)
break; break;
}
}
printf(1, "%d\n", i); printf(1, "%d\n", i);
char path[] = "stressfs0";
path[8] += i; path[8] += i;
int fd = open(path, O_CREATE | O_RDWR); fd = open(path, O_CREATE | O_RDWR);
for (i = 0; i < 100; i++) for(i = 0; i < 100; i++)
printf(fd, "%d\n", i); printf(fd, "%d\n", i);
close(fd); close(fd);
......
...@@ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip) ...@@ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip)
return 0; return 0;
} }
// XXX should we copy the string?
// Fetch the nul-terminated string at addr from process p. // Fetch the nul-terminated string at addr from process p.
// Doesn't actually copy the string - just sets *pp to point at it. // Doesn't actually copy the string - just sets *pp to point at it.
// Returns length of string, not including nul. // Returns length of string, not including nul.
...@@ -34,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp) ...@@ -34,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp)
if(addr >= p->sz) if(addr >= p->sz)
return -1; return -1;
*pp = (char *) addr; *pp = (char*)addr;
ep = (char *) p->sz; ep = (char*)p->sz;
for(s = *pp; s < ep; s++) for(s = *pp; s < ep; s++)
if(*s == 0) if(*s == 0)
return s - *pp; return s - *pp;
...@@ -46,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp) ...@@ -46,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp)
int int
argint(int n, int *ip) argint(int n, int *ip)
{ {
int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip); return fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
return x;
} }
// Fetch the nth word-sized system call argument as a pointer // Fetch the nth word-sized system call argument as a pointer
...@@ -60,10 +57,9 @@ argptr(int n, char **pp, int size) ...@@ -60,10 +57,9 @@ argptr(int n, char **pp, int size)
if(argint(n, &i) < 0) if(argint(n, &i) < 0)
return -1; return -1;
if((uint)i >= proc->sz || (uint)i+size >= proc->sz) if((uint)i >= proc->sz || (uint)i+size > proc->sz)
return -1; return -1;
// *pp = proc->mem + i; // XXXXX *pp = (char*)i;
*pp = (char *) i; // XXXXX
return 0; return 0;
} }
...@@ -102,28 +98,37 @@ extern int sys_wait(void); ...@@ -102,28 +98,37 @@ extern int sys_wait(void);
extern int sys_write(void); extern int sys_write(void);
extern int sys_uptime(void); extern int sys_uptime(void);
int
sys_init(void)
{
initlog();
return 0;
}
static int (*syscalls[])(void) = { static int (*syscalls[])(void) = {
[SYS_chdir] sys_chdir, [SYS_init] sys_init,
[SYS_close] sys_close,
[SYS_dup] sys_dup,
[SYS_exec] sys_exec,
[SYS_exit] sys_exit,
[SYS_fork] sys_fork, [SYS_fork] sys_fork,
[SYS_fstat] sys_fstat, [SYS_exit] sys_exit,
[SYS_getpid] sys_getpid, [SYS_wait] sys_wait,
[SYS_kill] sys_kill,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_mknod] sys_mknod,
[SYS_open] sys_open,
[SYS_pipe] sys_pipe, [SYS_pipe] sys_pipe,
[SYS_read] sys_read, [SYS_read] sys_read,
[SYS_kill] sys_kill,
[SYS_exec] sys_exec,
[SYS_fstat] sys_fstat,
[SYS_chdir] sys_chdir,
[SYS_dup] sys_dup,
[SYS_getpid] sys_getpid,
[SYS_sbrk] sys_sbrk, [SYS_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep, [SYS_sleep] sys_sleep,
[SYS_unlink] sys_unlink,
[SYS_wait] sys_wait,
[SYS_write] sys_write,
[SYS_uptime] sys_uptime, [SYS_uptime] sys_uptime,
// File system calls that are run in a transaction:
[SYS_open] sys_open,
[SYS_write] sys_write,
[SYS_mknod] sys_mknod,
[SYS_unlink] sys_unlink,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
}; };
void void
...@@ -132,9 +137,13 @@ syscall(void) ...@@ -132,9 +137,13 @@ syscall(void)
int num; int num;
num = proc->tf->eax; num = proc->tf->eax;
if(num >= 0 && num < NELEM(syscalls) && syscalls[num]) if(num >= 0 && num < SYS_open && syscalls[num]) {
proc->tf->eax = syscalls[num]();
} else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
begin_trans();
proc->tf->eax = syscalls[num](); proc->tf->eax = syscalls[num]();
else { commit_trans();
} else {
cprintf("%d %s: unknown sys call %d\n", cprintf("%d %s: unknown sys call %d\n",
proc->pid, proc->name, num); proc->pid, proc->name, num);
proc->tf->eax = -1; proc->tf->eax = -1;
......
// System call numbers // System call numbers
#define SYS_init 0
#define SYS_fork 1 #define SYS_fork 1
#define SYS_exit 2 #define SYS_exit 2
#define SYS_wait 3 #define SYS_wait 3
#define SYS_pipe 4 #define SYS_pipe 4
#define SYS_write 5 #define SYS_read 5
#define SYS_read 6 #define SYS_kill 6
#define SYS_close 7 #define SYS_exec 7
#define SYS_kill 8 #define SYS_fstat 8
#define SYS_exec 9 #define SYS_chdir 9
#define SYS_open 10 #define SYS_dup 10
#define SYS_mknod 11 #define SYS_getpid 11
#define SYS_unlink 12 #define SYS_sbrk 12
#define SYS_fstat 13 #define SYS_sleep 13
#define SYS_link 14 #define SYS_uptime 14
#define SYS_mkdir 15
#define SYS_chdir 16 #define SYS_open 15
#define SYS_dup 17 #define SYS_write 16
#define SYS_getpid 18 #define SYS_mknod 17
#define SYS_sbrk 19 #define SYS_unlink 18
#define SYS_sleep 20 #define SYS_link 19
#define SYS_uptime 21 #define SYS_mkdir 20
#define SYS_close 21
...@@ -344,11 +344,11 @@ sys_chdir(void) ...@@ -344,11 +344,11 @@ sys_chdir(void)
int int
sys_exec(void) sys_exec(void)
{ {
char *path, *argv[20]; char *path, *argv[MAXARG];
int i; int i;
uint uargv, uarg; uint uargv, uarg;
if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) { if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
return -1; return -1;
} }
memset(argv, 0, sizeof(argv)); memset(argv, 0, sizeof(argv));
......
...@@ -6,9 +6,8 @@ on the same line as the name, the line number (or, in a few cases, numbers) ...@@ -6,9 +6,8 @@ on the same line as the name, the line number (or, in a few cases, numbers)
where the name is defined. Successive lines in an entry list the line where the name is defined. Successive lines in an entry list the line
numbers where the name is used. For example, this entry: numbers where the name is used. For example, this entry:
swtch 2208 swtch 2358
0318 1928 1967 2207 0317 2128 2166 2357 2358
2208
indicates that swtch is defined on line 2208 and is mentioned on five lines indicates that swtch is defined on line 2358 and is mentioned on five lines
on sheets 03, 19, and 22. on sheets 03, 21, and 23.
...@@ -59,6 +59,9 @@ trap(struct trapframe *tf) ...@@ -59,6 +59,9 @@ trap(struct trapframe *tf)
ideintr(); ideintr();
lapiceoi(); lapiceoi();
break; break;
case T_IRQ0 + IRQ_IDE+1:
// Bochs generates spurious IDE1 interrupts.
break;
case T_IRQ0 + IRQ_KBD: case T_IRQ0 + IRQ_KBD:
kbdintr(); kbdintr();
lapiceoi(); lapiceoi();
...@@ -83,7 +86,8 @@ trap(struct trapframe *tf) ...@@ -83,7 +86,8 @@ trap(struct trapframe *tf)
panic("trap"); panic("trap");
} }
// In user space, assume process misbehaved. // In user space, assume process misbehaved.
cprintf("pid %d %s: trap %d err %d on cpu %d eip 0x%x addr 0x%x--kill proc\n", cprintf("pid %d %s: trap %d err %d on cpu %d "
"eip 0x%x addr 0x%x--kill proc\n",
proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,
rcr2()); rcr2());
proc->killed = 1; proc->killed = 1;
......
...@@ -45,7 +45,7 @@ strchr(const char *s, char c) ...@@ -45,7 +45,7 @@ strchr(const char *s, char c)
{ {
for(; *s; s++) for(; *s; s++)
if(*s == c) if(*s == c)
return (char*) s; return (char*)s;
return 0; return 0;
} }
......
...@@ -26,7 +26,7 @@ free(void *ap) ...@@ -26,7 +26,7 @@ free(void *ap)
{ {
Header *bp, *p; Header *bp, *p;
bp = (Header*) ap - 1; bp = (Header*)ap - 1;
for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; break;
...@@ -52,7 +52,7 @@ morecore(uint nu) ...@@ -52,7 +52,7 @@ morecore(uint nu)
if(nu < 4096) if(nu < 4096)
nu = 4096; nu = 4096;
p = sbrk(nu * sizeof(Header)); p = sbrk(nu * sizeof(Header));
if(p == (char*) -1) if(p == (char*)-1)
return 0; return 0;
hp = (Header*)p; hp = (Header*)p;
hp->s.size = nu; hp->s.size = nu;
...@@ -81,7 +81,7 @@ malloc(uint nbytes) ...@@ -81,7 +81,7 @@ malloc(uint nbytes)
p->s.size = nunits; p->s.size = nunits;
} }
freep = prevp; freep = prevp;
return (void*) (p + 1); return (void*)(p + 1);
} }
if(p == freep) if(p == freep)
if((p = morecore(nunits)) == 0) if((p = morecore(nunits)) == 0)
......
...@@ -18,10 +18,10 @@ int link(char*, char*); ...@@ -18,10 +18,10 @@ int link(char*, char*);
int mkdir(char*); int mkdir(char*);
int chdir(char*); int chdir(char*);
int dup(int); int dup(int);
int getpid(); int getpid(void);
char* sbrk(int); char* sbrk(int);
int sleep(int); int sleep(int);
int uptime(); int uptime(void);
// ulib.c // ulib.c
int stat(char*, struct stat*); int stat(char*, struct stat*);
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#include "user.h" #include "user.h"
#include "fs.h" #include "fs.h"
#include "fcntl.h" #include "fcntl.h"
#include "syscall.h"
#include "traps.h"
char buf[2048]; char buf[2048];
char name[3]; char name[3];
...@@ -45,12 +47,12 @@ writetest(void) ...@@ -45,12 +47,12 @@ writetest(void)
printf(stdout, "error: creat small failed!\n"); printf(stdout, "error: creat small failed!\n");
exit(); exit();
} }
for(i = 0; i < 100; i++) { for(i = 0; i < 100; i++){
if(write(fd, "aaaaaaaaaa", 10) != 10) { if(write(fd, "aaaaaaaaaa", 10) != 10){
printf(stdout, "error: write aa %d new file failed\n", i); printf(stdout, "error: write aa %d new file failed\n", i);
exit(); exit();
} }
if(write(fd, "bbbbbbbbbb", 10) != 10) { if(write(fd, "bbbbbbbbbb", 10) != 10){
printf(stdout, "error: write bb %d new file failed\n", i); printf(stdout, "error: write bb %d new file failed\n", i);
exit(); exit();
} }
...@@ -65,7 +67,7 @@ writetest(void) ...@@ -65,7 +67,7 @@ writetest(void)
exit(); exit();
} }
i = read(fd, buf, 2000); i = read(fd, buf, 2000);
if(i == 2000) { if(i == 2000){
printf(stdout, "read succeeded ok\n"); printf(stdout, "read succeeded ok\n");
} else { } else {
printf(stdout, "read failed\n"); printf(stdout, "read failed\n");
...@@ -73,7 +75,7 @@ writetest(void) ...@@ -73,7 +75,7 @@ writetest(void)
} }
close(fd); close(fd);
if(unlink("small") < 0) { if(unlink("small") < 0){
printf(stdout, "unlink small failed\n"); printf(stdout, "unlink small failed\n");
exit(); exit();
} }
...@@ -93,9 +95,9 @@ writetest1(void) ...@@ -93,9 +95,9 @@ writetest1(void)
exit(); exit();
} }
for(i = 0; i < MAXFILE; i++) { for(i = 0; i < MAXFILE; i++){
((int*) buf)[0] = i; ((int*)buf)[0] = i;
if(write(fd, buf, 512) != 512) { if(write(fd, buf, 512) != 512){
printf(stdout, "error: write big file failed\n", i); printf(stdout, "error: write big file failed\n", i);
exit(); exit();
} }
...@@ -110,19 +112,19 @@ writetest1(void) ...@@ -110,19 +112,19 @@ writetest1(void)
} }
n = 0; n = 0;
for(;;) { for(;;){
i = read(fd, buf, 512); i = read(fd, buf, 512);
if(i == 0) { if(i == 0){
if(n == MAXFILE - 1) { if(n == MAXFILE - 1){
printf(stdout, "read only %d blocks from big", n); printf(stdout, "read only %d blocks from big", n);
exit(); exit();
} }
break; break;
} else if(i != 512) { } else if(i != 512){
printf(stdout, "read failed %d\n", i); printf(stdout, "read failed %d\n", i);
exit(); exit();
} }
if(((int*)buf)[0] != n) { if(((int*)buf)[0] != n){
printf(stdout, "read content of block %d is %d\n", printf(stdout, "read content of block %d is %d\n",
n, ((int*)buf)[0]); n, ((int*)buf)[0]);
exit(); exit();
...@@ -130,7 +132,7 @@ writetest1(void) ...@@ -130,7 +132,7 @@ writetest1(void)
n++; n++;
} }
close(fd); close(fd);
if(unlink("big") < 0) { if(unlink("big") < 0){
printf(stdout, "unlink big failed\n"); printf(stdout, "unlink big failed\n");
exit(); exit();
} }
...@@ -146,14 +148,14 @@ createtest(void) ...@@ -146,14 +148,14 @@ createtest(void)
name[0] = 'a'; name[0] = 'a';
name[2] = '\0'; name[2] = '\0';
for(i = 0; i < 52; i++) { for(i = 0; i < 52; i++){
name[1] = '0' + i; name[1] = '0' + i;
fd = open(name, O_CREATE|O_RDWR); fd = open(name, O_CREATE|O_RDWR);
close(fd); close(fd);
} }
name[0] = 'a'; name[0] = 'a';
name[2] = '\0'; name[2] = '\0';
for(i = 0; i < 52; i++) { for(i = 0; i < 52; i++){
name[1] = '0' + i; name[1] = '0' + i;
unlink(name); unlink(name);
} }
...@@ -164,22 +166,22 @@ void dirtest(void) ...@@ -164,22 +166,22 @@ void dirtest(void)
{ {
printf(stdout, "mkdir test\n"); printf(stdout, "mkdir test\n");
if(mkdir("dir0") < 0) { if(mkdir("dir0") < 0){
printf(stdout, "mkdir failed\n"); printf(stdout, "mkdir failed\n");
exit(); exit();
} }
if(chdir("dir0") < 0) { if(chdir("dir0") < 0){
printf(stdout, "chdir dir0 failed\n"); printf(stdout, "chdir dir0 failed\n");
exit(); exit();
} }
if(chdir("..") < 0) { if(chdir("..") < 0){
printf(stdout, "chdir .. failed\n"); printf(stdout, "chdir .. failed\n");
exit(); exit();
} }
if(unlink("dir0") < 0) { if(unlink("dir0") < 0){
printf(stdout, "unlink dir0 failed\n"); printf(stdout, "unlink dir0 failed\n");
exit(); exit();
} }
...@@ -190,7 +192,7 @@ void ...@@ -190,7 +192,7 @@ void
exectest(void) exectest(void)
{ {
printf(stdout, "exec test\n"); printf(stdout, "exec test\n");
if(exec("echo", echoargv) < 0) { if(exec("echo", echoargv) < 0){
printf(stdout, "exec echo failed\n"); printf(stdout, "exec echo failed\n");
exit(); exit();
} }
...@@ -324,20 +326,21 @@ mem(void) ...@@ -324,20 +326,21 @@ mem(void)
void *m1, *m2; void *m1, *m2;
int pid, ppid; int pid, ppid;
printf(1, "mem test\n");
ppid = getpid(); ppid = getpid();
if((pid = fork()) == 0){ if((pid = fork()) == 0){
m1 = 0; m1 = 0;
while((m2 = malloc(10001)) != 0) { while((m2 = malloc(10001)) != 0){
*(char**) m2 = m1; *(char**)m2 = m1;
m1 = m2; m1 = m2;
} }
while(m1) { while(m1){
m2 = *(char**)m1; m2 = *(char**)m1;
free(m1); free(m1);
m1 = m2; m1 = m2;
} }
m1 = malloc(1024*20); m1 = malloc(1024*20);
if(m1 == 0) { if(m1 == 0){
printf(1, "couldn't allocate mem?!!\n"); printf(1, "couldn't allocate mem?!!\n");
kill(ppid); kill(ppid);
exit(); exit();
...@@ -1234,16 +1237,18 @@ forktest(void) ...@@ -1234,16 +1237,18 @@ forktest(void)
void void
sbrktest(void) sbrktest(void)
{ {
int pid; int fds[2], pid, pids[32], ppid;
char *oldbrk = sbrk(0); char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch;
uint amt;
printf(stdout, "sbrk test\n"); printf(stdout, "sbrk test\n");
oldbrk = sbrk(0);
// can one sbrk() less than a page? // can one sbrk() less than a page?
char *a = sbrk(0); a = sbrk(0);
int i; int i;
for(i = 0; i < 5000; i++){ for(i = 0; i < 5000; i++){
char *b = sbrk(1); b = sbrk(1);
if(b != a){ if(b != a){
printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); printf(stdout, "sbrk test failed %d %x %x\n", i, a, b);
exit(); exit();
...@@ -1256,7 +1261,7 @@ sbrktest(void) ...@@ -1256,7 +1261,7 @@ sbrktest(void)
printf(stdout, "sbrk test fork failed\n"); printf(stdout, "sbrk test fork failed\n");
exit(); exit();
} }
char *c = sbrk(1); c = sbrk(1);
c = sbrk(1); c = sbrk(1);
if(c != a + 1){ if(c != a + 1){
printf(stdout, "sbrk test failed post-fork\n"); printf(stdout, "sbrk test failed post-fork\n");
...@@ -1268,18 +1273,18 @@ sbrktest(void) ...@@ -1268,18 +1273,18 @@ sbrktest(void)
// can one allocate the full 640K? // can one allocate the full 640K?
a = sbrk(0); a = sbrk(0);
uint amt = (640 * 1024) - (uint) a; amt = (640 * 1024) - (uint)a;
char *p = sbrk(amt); p = sbrk(amt);
if(p != a){ if(p != a){
printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a); printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a);
exit(); exit();
} }
char *lastaddr = (char *)(640 * 1024 - 1); lastaddr = (char*)(640 * 1024 - 1);
*lastaddr = 99; *lastaddr = 99;
// is one forbidden from allocating more than 640K? // is one forbidden from allocating more than 640K?
c = sbrk(4096); c = sbrk(4096);
if(c != (char *) 0xffffffff){ if(c != (char*)0xffffffff){
printf(stdout, "sbrk allocated more than 640K, c %x\n", c); printf(stdout, "sbrk allocated more than 640K, c %x\n", c);
exit(); exit();
} }
...@@ -1287,7 +1292,7 @@ sbrktest(void) ...@@ -1287,7 +1292,7 @@ sbrktest(void)
// can one de-allocate? // can one de-allocate?
a = sbrk(0); a = sbrk(0);
c = sbrk(-4096); c = sbrk(-4096);
if(c == (char *) 0xffffffff){ if(c == (char*)0xffffffff){
printf(stdout, "sbrk could not deallocate\n"); printf(stdout, "sbrk could not deallocate\n");
exit(); exit();
} }
...@@ -1311,15 +1316,15 @@ sbrktest(void) ...@@ -1311,15 +1316,15 @@ sbrktest(void)
} }
c = sbrk(4096); c = sbrk(4096);
if(c != (char *) 0xffffffff){ if(c != (char*)0xffffffff){
printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c); printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c);
exit(); exit();
} }
// can we read the kernel's memory? // can we read the kernel's memory?
for(a = (char*)(640*1024); a < (char *)2000000; a += 50000){ for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){
int ppid = getpid(); ppid = getpid();
int pid = fork(); pid = fork();
if(pid < 0){ if(pid < 0){
printf(stdout, "fork failed\n"); printf(stdout, "fork failed\n");
exit(); exit();
...@@ -1332,6 +1337,38 @@ sbrktest(void) ...@@ -1332,6 +1337,38 @@ sbrktest(void)
wait(); wait();
} }
// if we run the system out of memory, does it clean up the last
// failed allocation?
sbrk(-(sbrk(0) - oldbrk));
if(pipe(fds) != 0){
printf(1, "pipe() failed\n");
exit();
}
for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){
if((pids[i] = fork()) == 0){
// allocate the full 640K
sbrk((640 * 1024) - (uint)sbrk(0));
write(fds[1], "x", 1);
// sit around until killed
for(;;) sleep(1000);
}
if(pids[i] != -1)
read(fds[0], &scratch, 1);
}
// if those failed allocations freed up the pages they did allocate,
// we'll be able to allocate here
c = sbrk(4096);
for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){
if(pids[i] == -1)
continue;
kill(pids[i]);
wait();
}
if(c == (char*)0xffffffff){
printf(stdout, "failed sbrk leaked memory\n");
exit();
}
if(sbrk(0) > oldbrk) if(sbrk(0) > oldbrk)
sbrk(-(sbrk(0) - oldbrk)); sbrk(-(sbrk(0) - oldbrk));
...@@ -1339,26 +1376,89 @@ sbrktest(void) ...@@ -1339,26 +1376,89 @@ sbrktest(void)
} }
void void
stacktest(void) validateint(int *p)
{ {
printf(stdout, "stack test\n"); int res;
char dummy = 1; asm("mov %%esp, %%ebx\n\t"
char *p = &dummy; "mov %3, %%esp\n\t"
int ppid = getpid(); "int %2\n\t"
int pid = fork(); "mov %%ebx, %%esp" :
if(pid < 0){ "=a" (res) :
printf(stdout, "fork failed\n"); "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) :
"ebx");
}
void
validatetest(void)
{
int hi, pid;
uint p;
printf(stdout, "validate test\n");
hi = 1100*1024;
for(p = 0; p <= (uint)hi; p += 4096){
if((pid = fork()) == 0){
// try to crash the kernel by passing in a badly placed integer
validateint((int*)p);
exit(); exit();
} }
sleep(0);
sleep(0);
kill(pid);
wait();
// try to crash the kernel by passing in a bad string pointer
if(link("nosuchfile", (char*)p) != -1){
printf(stdout, "link should not succeed\n");
exit();
}
}
printf(stdout, "validate ok\n");
}
// does unintialized data start out zero?
char uninit[10000];
void
bsstest(void)
{
int i;
printf(stdout, "bss test\n");
for(i = 0; i < sizeof(uninit); i++){
if(uninit[i] != '\0'){
printf(stdout, "bss test failed\n");
exit();
}
}
printf(stdout, "bss test ok\n");
}
// does exec do something sensible if the arguments
// are larger than a page?
void
bigargtest(void)
{
int pid, ppid;
ppid = getpid();
pid = fork();
if(pid == 0){ if(pid == 0){
// should cause a trap: char *args[32+1];
p[-4096] = 'z'; int i;
kill(ppid); for(i = 0; i < 32; i++)
printf(stdout, "stack test failed: page before stack was writeable\n"); args[i] = "bigargs test: failed\n ";
args[32] = 0;
printf(stdout, "bigarg test\n");
exec("echo", args);
printf(stdout, "bigarg test ok\n");
exit();
} else if(pid < 0){
printf(stdout, "bigargtest: fork failed\n");
exit(); exit();
} }
wait(); wait();
printf(stdout, "stack test OK\n");
} }
int int
...@@ -1372,8 +1472,10 @@ main(int argc, char *argv[]) ...@@ -1372,8 +1472,10 @@ main(int argc, char *argv[])
} }
close(open("usertests.ran", O_CREATE)); close(open("usertests.ran", O_CREATE));
stacktest(); bigargtest();
bsstest();
sbrktest(); sbrktest();
validatetest();
opentest(); opentest();
writetest(); writetest();
......
差异被折叠。
index.html: index.txt mkhtml
./mkhtml index.txt >_$@ && mv _$@ $@
添加文件
添加文件
添加文件
添加文件
添加文件
差异被折叠。
差异被折叠。
添加文件
添加文件
#!/usr/bin/perl
my @lines = <>;
my $text = join('', @lines);
my $title;
if($text =~ /^\*\* (.*?)\n/m){
$title = $1;
$text = $` . $';
}else{
$title = "Untitled";
}
$text =~ s/[ \t]+$//mg;
$text =~ s/^$/<br><br>/mg;
$text =~ s!\b([a-z0-9]+\.(c|s|pl|h))\b!<a href="src/$1.html">$1</a>!g;
$text =~ s!^(Lecture [0-9]+\. .*?)$!<b><i>$1</i></b>!mg;
$text =~ s!^\* (.*?)$!<h2>$1</h2>!mg;
$text =~ s!((<br>)+\n)+<h2>!\n<h2>!g;
$text =~ s!</h2>\n?((<br>)+\n)+!</h2>\n!g;
$text =~ s!((<br>)+\n)+<b>!\n<br><br><b>!g;
$text =~ s!\b\s*--\s*\b!\&ndash;!g;
$text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!<a href="$1">$2</a>!g;
$text =~ s!\[([^ \t]+)\]!<a href="$1">$1</a>!g;
$text =~ s!``!\&ldquo;!g;
$text =~ s!''!\&rdquo;!g;
print <<EOF;
<!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version -->
<html>
<head>
<title>$title</title>
<style type="text/css"><!--
body {
background-color: white;
color: black;
font-size: medium;
line-height: 1.2em;
margin-left: 0.5in;
margin-right: 0.5in;
margin-top: 0;
margin-bottom: 0;
}
h1 {
text-indent: 0in;
text-align: left;
margin-top: 2em;
font-weight: bold;
font-size: 1.4em;
}
h2 {
text-indent: 0in;
text-align: left;
margin-top: 2em;
font-weight: bold;
font-size: 1.2em;
}
--></style>
</head>
<body bgcolor=#ffffff>
<h1>$title</h1>
<br><br>
EOF
print $text;
print <<EOF;
</body>
</html>
EOF
添加文件
添加文件
添加文件
...@@ -90,23 +90,26 @@ readeflags(void) ...@@ -90,23 +90,26 @@ readeflags(void)
return eflags; return eflags;
} }
static inline uint static inline void
xchg(volatile uint *addr, uint newval) loadgs(ushort v)
{ {
uint result; asm volatile("movw %0, %%gs" : : "r" (v));
}
// The + in "+m" denotes a read-modify-write operand. static inline uint
asm volatile("lock; xchgl %0, %1" : rebp(void)
"+m" (*addr), "=a" (result) : {
"1" (newval) : uint val;
"cc"); asm volatile("movl %%ebp,%0" : "=r" (val));
return result; return val;
} }
static inline void static inline uint
loadgs(ushort v) resp(void)
{ {
asm volatile("movw %0, %%gs" : : "r" (v)); uint val;
asm volatile("movl %%esp,%0" : "=r" (val));
return val;
} }
static inline void static inline void
...@@ -121,66 +124,56 @@ sti(void) ...@@ -121,66 +124,56 @@ sti(void)
asm volatile("sti"); asm volatile("sti");
} }
static inline void lcr0(uint val) static inline uint
xchg(volatile uint *addr, uint newval)
{
uint result;
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}
//PAGEBREAK!
static inline void
lcr0(uint val)
{ {
asm volatile("movl %0,%%cr0" : : "r" (val)); asm volatile("movl %0,%%cr0" : : "r" (val));
} }
static inline uint rcr0(void) static inline uint
rcr0(void)
{ {
uint val; uint val;
asm volatile("movl %%cr0,%0" : "=r" (val)); asm volatile("movl %%cr0,%0" : "=r" (val));
return val; return val;
} }
static inline uint rcr2(void) static inline uint
rcr2(void)
{ {
uint val; uint val;
asm volatile("movl %%cr2,%0" : "=r" (val)); asm volatile("movl %%cr2,%0" : "=r" (val));
return val; return val;
} }
static inline void lcr3(uint val) static inline void
lcr3(uint val)
{ {
asm volatile("movl %0,%%cr3" : : "r" (val)); asm volatile("movl %0,%%cr3" : : "r" (val));
} }
static inline uint rcr3(void) static inline uint
rcr3(void)
{ {
uint val; uint val;
asm volatile("movl %%cr3,%0" : "=r" (val)); asm volatile("movl %%cr3,%0" : "=r" (val));
return val; return val;
} }
static inline void lebp(uint val)
{
asm volatile("movl %0,%%ebp" : : "r" (val));
}
static inline uint rebp(void)
{
uint val;
asm volatile("movl %%ebp,%0" : "=r" (val));
return val;
}
static inline void lesp(uint val)
{
asm volatile("movl %0,%%esp" : : "r" (val));
}
static inline uint resp(void)
{
uint val;
asm volatile("movl %%esp,%0" : "=r" (val));
return val;
}
static inline void nop_pause(void)
{
asm volatile("pause" : :);
}
//PAGEBREAK: 36 //PAGEBREAK: 36
// Layout of the trap frame built on the stack by the // Layout of the trap frame built on the stack by the
// hardware and by trapasm.S, and passed to trap(). // hardware and by trapasm.S, and passed to trap().
......
添加文件
添加文件
添加文件
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论