mirror of
https://github.com/SmallPond/MIT6.828_OS.git
synced 2026-04-03 02:27:39 +08:00
HomeWork 8 is done
This commit is contained in:
16
xv6-public/.cvsignore
Normal file
16
xv6-public/.cvsignore
Normal file
@@ -0,0 +1,16 @@
|
||||
*.asm
|
||||
*.d
|
||||
*.sym
|
||||
_*
|
||||
kernel
|
||||
user1
|
||||
userfs
|
||||
usertests
|
||||
xv6.img
|
||||
vectors.S
|
||||
bochsout.txt
|
||||
bootblock
|
||||
bootother
|
||||
bootother.out
|
||||
parport.out
|
||||
fmt
|
||||
4
xv6-public/.dir-locals.el
Normal file
4
xv6-public/.dir-locals.el
Normal file
@@ -0,0 +1,4 @@
|
||||
((c-mode
|
||||
(indent-tabs-mode . nil)
|
||||
(c-file-style . "bsd")
|
||||
(c-basic-offset . 2)))
|
||||
27
xv6-public/.gdbinit.tmpl
Normal file
27
xv6-public/.gdbinit.tmpl
Normal file
@@ -0,0 +1,27 @@
|
||||
set $lastcs = -1
|
||||
|
||||
define hook-stop
|
||||
# There doesn't seem to be a good way to detect if we're in 16- or
|
||||
# 32-bit mode, but in 32-bit mode we always run with CS == 8 in the
|
||||
# kernel and CS == 35 in user space
|
||||
if $cs == 8 || $cs == 35
|
||||
if $lastcs != 8 && $lastcs != 35
|
||||
set architecture i386
|
||||
end
|
||||
x/i $pc
|
||||
else
|
||||
if $lastcs == -1 || $lastcs == 8 || $lastcs == 35
|
||||
set architecture i8086
|
||||
end
|
||||
# Translate the segment:offset into a physical address
|
||||
printf "[%4x:%4x] ", $cs, $eip
|
||||
x/i $cs*16+$eip
|
||||
end
|
||||
set $lastcs = $cs
|
||||
end
|
||||
|
||||
echo + target remote localhost:1234\n
|
||||
target remote localhost:1234
|
||||
|
||||
echo + symbol-file kernel\n
|
||||
symbol-file kernel
|
||||
16
xv6-public/.gitignore
vendored
Normal file
16
xv6-public/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
*~
|
||||
_*
|
||||
*.o
|
||||
*.d
|
||||
*.asm
|
||||
*.sym
|
||||
*.img
|
||||
vectors.S
|
||||
bootblock
|
||||
entryother
|
||||
initcode
|
||||
initcode.out
|
||||
kernel
|
||||
kernelmemfs
|
||||
mkfs
|
||||
.gdbinit
|
||||
7
xv6-public/BUGS
Normal file
7
xv6-public/BUGS
Normal file
@@ -0,0 +1,7 @@
|
||||
formatting:
|
||||
need to fix PAGEBREAK mechanism
|
||||
|
||||
sh:
|
||||
can't always runcmd in child -- breaks cd.
|
||||
maybe should hard-code PATH=/ ?
|
||||
|
||||
24
xv6-public/LICENSE
Normal file
24
xv6-public/LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
The xv6 software is:
|
||||
|
||||
Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox,
|
||||
Massachusetts Institute of Technology
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
296
xv6-public/Makefile
Normal file
296
xv6-public/Makefile
Normal file
@@ -0,0 +1,296 @@
|
||||
OBJS = \
|
||||
bio.o\
|
||||
console.o\
|
||||
exec.o\
|
||||
file.o\
|
||||
fs.o\
|
||||
ide.o\
|
||||
ioapic.o\
|
||||
kalloc.o\
|
||||
kbd.o\
|
||||
lapic.o\
|
||||
log.o\
|
||||
main.o\
|
||||
mp.o\
|
||||
picirq.o\
|
||||
pipe.o\
|
||||
proc.o\
|
||||
sleeplock.o\
|
||||
spinlock.o\
|
||||
string.o\
|
||||
swtch.o\
|
||||
syscall.o\
|
||||
sysfile.o\
|
||||
sysproc.o\
|
||||
trapasm.o\
|
||||
trap.o\
|
||||
uart.o\
|
||||
vectors.o\
|
||||
vm.o\
|
||||
|
||||
# Cross-compiling (e.g., on Mac OS X)
|
||||
# TOOLPREFIX = i386-jos-elf
|
||||
|
||||
# Using native tools (e.g., on X86 Linux)
|
||||
#TOOLPREFIX =
|
||||
|
||||
# Try to infer the correct TOOLPREFIX if not set
|
||||
ifndef TOOLPREFIX
|
||||
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
|
||||
then echo 'i386-jos-elf-'; \
|
||||
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
|
||||
then echo ''; \
|
||||
else echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
|
||||
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
|
||||
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
|
||||
echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \
|
||||
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
|
||||
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
|
||||
echo "***" 1>&2; exit 1; fi)
|
||||
endif
|
||||
|
||||
# If the makefile can't find QEMU, specify its path here
|
||||
# QEMU = qemu-system-i386
|
||||
|
||||
# Try to infer the correct QEMU
|
||||
ifndef QEMU
|
||||
QEMU = $(shell if which qemu > /dev/null; \
|
||||
then echo qemu; exit; \
|
||||
elif which qemu-system-i386 > /dev/null; \
|
||||
then echo qemu-system-i386; exit; \
|
||||
elif which qemu-system-x86_64 > /dev/null; \
|
||||
then echo qemu-system-x86_64; exit; \
|
||||
else \
|
||||
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
|
||||
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
|
||||
echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
|
||||
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
|
||||
echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
|
||||
echo "***" 1>&2; exit 1)
|
||||
endif
|
||||
|
||||
CC = $(TOOLPREFIX)gcc
|
||||
AS = $(TOOLPREFIX)gas
|
||||
LD = $(TOOLPREFIX)ld
|
||||
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||
OBJDUMP = $(TOOLPREFIX)objdump
|
||||
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
|
||||
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||
ASFLAGS = -m32 -gdwarf-2 -Wa,-divide
|
||||
# FreeBSD ld wants ``elf_i386_fbsd''
|
||||
LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1)
|
||||
|
||||
# Disable PIE when possible (for Ubuntu 16.10 toolchain)
|
||||
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
||||
CFLAGS += -fno-pie -no-pie
|
||||
endif
|
||||
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),)
|
||||
CFLAGS += -fno-pie -nopie
|
||||
endif
|
||||
|
||||
xv6.img: bootblock kernel
|
||||
dd if=/dev/zero of=xv6.img count=10000
|
||||
dd if=bootblock of=xv6.img conv=notrunc
|
||||
dd if=kernel of=xv6.img seek=1 conv=notrunc
|
||||
|
||||
xv6memfs.img: bootblock kernelmemfs
|
||||
dd if=/dev/zero of=xv6memfs.img count=10000
|
||||
dd if=bootblock of=xv6memfs.img conv=notrunc
|
||||
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
|
||||
|
||||
bootblock: bootasm.S bootmain.c
|
||||
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
|
||||
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
|
||||
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
|
||||
$(OBJDUMP) -S bootblock.o > bootblock.asm
|
||||
$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
|
||||
./sign.pl bootblock
|
||||
|
||||
entryother: entryother.S
|
||||
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S
|
||||
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o
|
||||
$(OBJCOPY) -S -O binary -j .text bootblockother.o entryother
|
||||
$(OBJDUMP) -S bootblockother.o > entryother.asm
|
||||
|
||||
initcode: initcode.S
|
||||
$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
|
||||
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
|
||||
$(OBJCOPY) -S -O binary initcode.out initcode
|
||||
$(OBJDUMP) -S initcode.o > initcode.asm
|
||||
|
||||
kernel: $(OBJS) entry.o entryother initcode kernel.ld
|
||||
$(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
|
||||
$(OBJDUMP) -S kernel > kernel.asm
|
||||
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
|
||||
|
||||
_uthread: uthread.o uthread_switch.o
|
||||
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _uthread uthread.o uthread_switch.o $(ULIB)
|
||||
$(OBJDUMP) -S _uthread > uthread.asm
|
||||
|
||||
# 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) entry.o entryother initcode kernel.ld fs.img
|
||||
$(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o $(MEMFSOBJS) -b binary initcode entryother fs.img
|
||||
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
|
||||
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
|
||||
|
||||
tags: $(OBJS) entryother.S _init
|
||||
etags *.S *.c
|
||||
|
||||
vectors.S: vectors.pl
|
||||
./vectors.pl > vectors.S
|
||||
|
||||
ULIB = ulib.o usys.o printf.o umalloc.o
|
||||
|
||||
_%: %.o $(ULIB)
|
||||
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
|
||||
$(OBJDUMP) -S $@ > $*.asm
|
||||
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
|
||||
|
||||
_forktest: forktest.o $(ULIB)
|
||||
# forktest has less library code linked in - needs to be small
|
||||
# in order to be able to max out the proc table.
|
||||
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
|
||||
$(OBJDUMP) -S _forktest > forktest.asm
|
||||
|
||||
mkfs: mkfs.c fs.h
|
||||
gcc -Werror -Wall -o mkfs mkfs.c
|
||||
|
||||
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
|
||||
# that disk image changes after first build are persistent until clean. More
|
||||
# details:
|
||||
# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
|
||||
.PRECIOUS: %.o
|
||||
|
||||
UPROGS=\
|
||||
_cat\
|
||||
_echo\
|
||||
_forktest\
|
||||
_grep\
|
||||
_init\
|
||||
_kill\
|
||||
_ln\
|
||||
_ls\
|
||||
_mkdir\
|
||||
_rm\
|
||||
_sh\
|
||||
_stressfs\
|
||||
_usertests\
|
||||
_wc\
|
||||
_zombie\
|
||||
_date\
|
||||
_alarmtest\
|
||||
_uthread\
|
||||
|
||||
fs.img: mkfs README $(UPROGS)
|
||||
./mkfs fs.img README $(UPROGS)
|
||||
|
||||
-include *.d
|
||||
|
||||
clean:
|
||||
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
|
||||
*.o *.d *.asm *.sym vectors.S bootblock entryother \
|
||||
initcode initcode.out kernel xv6.img fs.img kernelmemfs \
|
||||
xv6memfs.img mkfs .gdbinit \
|
||||
$(UPROGS)
|
||||
|
||||
# make a printout
|
||||
FILES = $(shell grep -v '^\#' runoff.list)
|
||||
PRINT = runoff.list runoff.spec README toc.hdr toc.ftr $(FILES)
|
||||
|
||||
xv6.pdf: $(PRINT)
|
||||
./runoff
|
||||
ls -l xv6.pdf
|
||||
|
||||
print: xv6.pdf
|
||||
|
||||
# run in emulators
|
||||
|
||||
bochs : fs.img xv6.img
|
||||
if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi
|
||||
bochs -q
|
||||
|
||||
# try to generate a unique GDB port
|
||||
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
|
||||
# QEMU's gdb stub command line changed in 0.11
|
||||
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
|
||||
then echo "-gdb tcp::$(GDBPORT)"; \
|
||||
else echo "-s -p $(GDBPORT)"; fi)
|
||||
ifndef CPUS
|
||||
CPUS := 2
|
||||
endif
|
||||
QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)
|
||||
|
||||
gdb:
|
||||
gdb -x .gdbinit
|
||||
|
||||
qemu: fs.img xv6.img
|
||||
$(QEMU) -serial mon:stdio $(QEMUOPTS)
|
||||
|
||||
qemu-memfs: xv6memfs.img
|
||||
$(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256
|
||||
|
||||
qemu-nox: fs.img xv6.img
|
||||
$(QEMU) -nographic $(QEMUOPTS)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl
|
||||
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||
|
||||
qemu-gdb: fs.img xv6.img .gdbinit
|
||||
@echo "*** Now run 'gdb'." 1>&2
|
||||
$(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
|
||||
|
||||
qemu-nox-gdb: fs.img xv6.img .gdbinit
|
||||
@echo "*** Now run 'gdb'." 1>&2
|
||||
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
|
||||
|
||||
# CUT HERE
|
||||
# prepare dist for students
|
||||
# after running make dist, probably want to
|
||||
# rename it to rev0 or rev1 or so on and then
|
||||
# check in that version.
|
||||
|
||||
EXTRA=\
|
||||
mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
|
||||
ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\
|
||||
printf.c umalloc.c\
|
||||
README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
|
||||
.gdbinit.tmpl gdbutil\
|
||||
|
||||
dist:
|
||||
rm -rf dist
|
||||
mkdir dist
|
||||
for i in $(FILES); \
|
||||
do \
|
||||
grep -v PAGEBREAK $$i >dist/$$i; \
|
||||
done
|
||||
sed '/CUT HERE/,$$d' Makefile >dist/Makefile
|
||||
echo >dist/runoff.spec
|
||||
cp $(EXTRA) dist
|
||||
|
||||
dist-test:
|
||||
rm -rf dist
|
||||
make dist
|
||||
rm -rf dist-test
|
||||
mkdir dist-test
|
||||
cp dist/* dist-test
|
||||
cd dist-test; $(MAKE) print
|
||||
cd dist-test; $(MAKE) bochs || true
|
||||
cd dist-test; $(MAKE) qemu
|
||||
|
||||
# update this rule (change rev#) when it is time to
|
||||
# make a new revision.
|
||||
tar:
|
||||
rm -rf /tmp/xv6
|
||||
mkdir -p /tmp/xv6
|
||||
cp dist/* dist/.gdbinit.tmpl /tmp/xv6
|
||||
(cd /tmp; tar cf - xv6) | gzip >xv6-rev10.tar.gz # the next one will be 10 (9/17)
|
||||
|
||||
.PHONY: dist-test dist
|
||||
123
xv6-public/Notes
Normal file
123
xv6-public/Notes
Normal file
@@ -0,0 +1,123 @@
|
||||
bochs 2.2.6:
|
||||
./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae --disable-reset-on-triple-fault
|
||||
bochs CVS after 2.2.6:
|
||||
./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae
|
||||
|
||||
bootmain.c doesn't work right if the ELF sections aren't
|
||||
sector-aligned. so you can't use ld -N. and the sections may also need
|
||||
to be non-zero length, only really matters for tiny "kernels".
|
||||
|
||||
kernel loaded at 1 megabyte. stack same place that bootasm.S left it.
|
||||
|
||||
kinit() should find real mem size
|
||||
and rescue useable memory below 1 meg
|
||||
|
||||
no paging, no use of page table hardware, just segments
|
||||
|
||||
no user area: no magic kernel stack mapping
|
||||
so no copying of kernel stack during fork
|
||||
though there is a kernel stack page for each process
|
||||
|
||||
no kernel malloc(), just kalloc() for user core
|
||||
|
||||
user pointers aren't valid in the kernel
|
||||
|
||||
are interrupts turned on in the kernel? yes.
|
||||
|
||||
pass curproc explicitly, or implicit from cpu #?
|
||||
e.g. argument to newproc()?
|
||||
hmm, you need a global curproc[cpu] for trap() &c
|
||||
|
||||
no stack expansion
|
||||
|
||||
test running out of memory, process slots
|
||||
|
||||
we can't really use a separate stack segment, since stack addresses
|
||||
need to work correctly as ordinary pointers. the same may be true of
|
||||
data vs text. how can we have a gap between data and stack, so that
|
||||
both can grow, without committing 4GB of physical memory? does this
|
||||
mean we need paging?
|
||||
|
||||
perhaps have fixed-size stack, put it in the data segment?
|
||||
|
||||
oops, if kernel stack is in contiguous user phys mem, then moving
|
||||
users' memory (e.g. to expand it) will wreck any pointers into the
|
||||
kernel stack.
|
||||
|
||||
do we need to set fs and gs? so user processes can't abuse them?
|
||||
|
||||
setupsegs() may modify current segment table, is that legal?
|
||||
|
||||
trap() ought to lgdt on return, since currently only done in swtch()
|
||||
|
||||
protect hardware interrupt vectors from user INT instructions?
|
||||
|
||||
test out-of-fd cases for creating pipe.
|
||||
test pipe reader closes then write
|
||||
test two readers, two writers.
|
||||
test children being inherited by grandparent &c
|
||||
|
||||
some sleep()s should be interruptible by kill()
|
||||
|
||||
locks
|
||||
init_lock
|
||||
sequences CPU startup
|
||||
proc_table_lock
|
||||
also protects next_pid
|
||||
per-fd lock *just* protects count read-modify-write
|
||||
also maybe freeness?
|
||||
memory allocator
|
||||
printf
|
||||
|
||||
in general, the table locks protect both free-ness and
|
||||
public variables of table elements
|
||||
in many cases you can use table elements w/o a lock
|
||||
e.g. if you are the process, or you are using an fd
|
||||
|
||||
lock order
|
||||
per-pipe lock
|
||||
proc_table_lock fd_table_lock kalloc_lock
|
||||
console_lock
|
||||
|
||||
do you have to be holding the mutex in order to call wakeup()? yes
|
||||
|
||||
device interrupts don't clear FL_IF
|
||||
so a recursive timer interrupt is possible
|
||||
|
||||
what does inode->busy mean?
|
||||
might be held across disk reads
|
||||
no-one is allowed to do anything to the inode
|
||||
protected by inode_table_lock
|
||||
inode->count counts in-memory pointers to the struct
|
||||
prevents inode[] element from being re-used
|
||||
protected by inode_table_lock
|
||||
|
||||
blocks and inodes have ad-hoc sleep-locks
|
||||
provide a single mechanism?
|
||||
|
||||
kalloc() can return 0; do callers handle this right?
|
||||
|
||||
test: one process unlinks a file while another links to it
|
||||
test: one process opens a file while another deletes it
|
||||
test: deadlock d/.. vs ../d, two processes.
|
||||
test: dup() shared fd->off
|
||||
test: does echo foo > x truncate x?
|
||||
|
||||
sh: ioredirection incorrect now we have pipes
|
||||
sh: chain of pipes won't work, also ugly that parent closes fdarray entries too
|
||||
sh: dynamic memory allocation?
|
||||
sh: should sh support ; () &
|
||||
sh: stop stdin on ctrl-d (for cat > y)
|
||||
|
||||
really should have bdwrite() for file content
|
||||
and make some inode updates async
|
||||
so soft updates make sense
|
||||
|
||||
disk scheduling
|
||||
echo foo > bar should truncate bar
|
||||
so O_CREATE should not truncate
|
||||
but O_TRUNC should
|
||||
|
||||
make it work on a real machine
|
||||
release before acquire at end of sleep?
|
||||
check 2nd disk (i.e. if not in .bochsrc)
|
||||
50
xv6-public/README
Normal file
50
xv6-public/README
Normal file
@@ -0,0 +1,50 @@
|
||||
xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
|
||||
Version 6 (v6). xv6 loosely follows the structure and style of v6,
|
||||
but is implemented for a modern x86-based multiprocessor using ANSI C.
|
||||
|
||||
ACKNOWLEDGMENTS
|
||||
|
||||
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
|
||||
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
|
||||
2000)). See also https://pdos.csail.mit.edu/6.828/, which
|
||||
provides pointers to on-line resources for v6.
|
||||
|
||||
xv6 borrows code from the following sources:
|
||||
JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others)
|
||||
Plan 9 (entryother.S, mp.h, mp.c, lapic.c)
|
||||
FreeBSD (ioapic.c)
|
||||
NetBSD (console.c)
|
||||
|
||||
The following people have made contributions: Russ Cox (context switching,
|
||||
locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
|
||||
Clements.
|
||||
|
||||
We are also grateful for the bug reports and patches contributed by Silas
|
||||
Boyd-Wickizer, Anton Burtsev, Cody Cutler, Mike CAT, Tej Chajed, eyalz800,
|
||||
Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, Peter
|
||||
Froehlich, Yakir Goaron,Shivam Handa, Bryan Henry, Jim Huang, Alexander
|
||||
Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller, Eddie Kohler, Austin
|
||||
Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi
|
||||
Merimovich, Mark Morrissey, mtasm, Joel Nider, Greg Price, Ayan Shafqat,
|
||||
Eldar Sehayek, Yongming Shen, Cam Tenny, tyfkda, Rafael Ubal, Warren
|
||||
Toomey, Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas
|
||||
Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang Wei.
|
||||
|
||||
The code in the files that constitute xv6 is
|
||||
Copyright 2006-2018 Frans Kaashoek, Robert Morris, and Russ Cox.
|
||||
|
||||
ERROR REPORTS
|
||||
|
||||
Please send errors and suggestions to Frans Kaashoek and Robert Morris
|
||||
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
|
||||
operating system for MIT's 6.828, so we are more interested in
|
||||
simplifications and clarifications than new features.
|
||||
|
||||
BUILDING AND RUNNING XV6
|
||||
|
||||
To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run
|
||||
"make". On non-x86 or non-ELF machines (like OS X, even on x86), you
|
||||
will need to install a cross-compiler gcc suite capable of producing
|
||||
x86 ELF binaries (see https://pdos.csail.mit.edu/6.828/).
|
||||
Then run "make TOOLPREFIX=i386-jos-elf-". Now install the QEMU PC
|
||||
simulator and run "make qemu".
|
||||
140
xv6-public/TRICKS
Normal file
140
xv6-public/TRICKS
Normal file
@@ -0,0 +1,140 @@
|
||||
This file lists subtle things that might not be commented
|
||||
as well as they should be in the source code and that
|
||||
might be worth pointing out in a longer explanation or in class.
|
||||
|
||||
---
|
||||
|
||||
[2009/07/12: No longer relevant; forkret1 changed
|
||||
and this is now cleaner.]
|
||||
|
||||
forkret1 in trapasm.S is called with a tf argument.
|
||||
In order to use it, forkret1 copies the tf pointer into
|
||||
%esp and then jumps to trapret, which pops the
|
||||
register state out of the trap frame. If an interrupt
|
||||
came in between the mov tf, %esp and the iret that
|
||||
goes back out to user space, the interrupt stack frame
|
||||
would end up scribbling over the tf and whatever memory
|
||||
lay under it.
|
||||
|
||||
Why is this safe? Because forkret1 is only called
|
||||
the first time a process returns to user space, and
|
||||
at that point, cp->tf is set to point to a trap frame
|
||||
constructed at the top of cp's kernel stack. So tf
|
||||
*is* a valid %esp that can hold interrupt state.
|
||||
|
||||
If other tf's were used in forkret1, we could add
|
||||
a cli before the mov tf, %esp.
|
||||
|
||||
---
|
||||
|
||||
In pushcli, must cli() no matter what. It is not safe to do
|
||||
|
||||
if(cpus[cpu()].ncli == 0)
|
||||
cli();
|
||||
cpus[cpu()].ncli++;
|
||||
|
||||
because if interrupts are off then we might call cpu(), get
|
||||
rescheduled to a different cpu, look at cpus[oldcpu].ncli,
|
||||
and wrongly decide not to disable interrupts on the new cpu.
|
||||
|
||||
Instead do
|
||||
|
||||
cli();
|
||||
cpus[cpu()].ncli++;
|
||||
|
||||
always.
|
||||
|
||||
---
|
||||
|
||||
There is a (harmless) race in pushcli, which does
|
||||
|
||||
eflags = readeflags();
|
||||
cli();
|
||||
if(c->ncli++ == 0)
|
||||
c->intena = eflags & FL_IF;
|
||||
|
||||
Consider a bottom-level pushcli.
|
||||
If interrupts are disabled already, then the right thing
|
||||
happens: read_eflags finds that FL_IF is not set,
|
||||
and intena = 0. If interrupts are enabled, then
|
||||
it is less clear that the right thing happens:
|
||||
the readeflags can execute, then the process
|
||||
can get preempted and rescheduled on another cpu,
|
||||
and then once it starts running, perhaps with
|
||||
interrupts disabled (can happen since the scheduler
|
||||
only enables interrupts once per scheduling loop,
|
||||
not every time it schedules a process), it will
|
||||
incorrectly record that interrupts *were* enabled.
|
||||
This doesn't matter, because if it was safe to be
|
||||
running with interrupts enabled before the context
|
||||
switch, it is still safe (and arguably more correct)
|
||||
to run with them enabled after the context switch too.
|
||||
|
||||
In fact it would be safe if scheduler always set
|
||||
c->intena = 1;
|
||||
before calling swtch, and perhaps it should.
|
||||
|
||||
---
|
||||
|
||||
The x86's processor-ordering memory model
|
||||
matches spin locks well, so no explicit memory
|
||||
synchronization instructions are required in
|
||||
acquire and release.
|
||||
|
||||
Consider two sequences of code on different CPUs:
|
||||
|
||||
CPU0
|
||||
A;
|
||||
release(lk);
|
||||
|
||||
and
|
||||
|
||||
CPU1
|
||||
acquire(lk);
|
||||
B;
|
||||
|
||||
We want to make sure that:
|
||||
- all reads in B see the effects of writes in A.
|
||||
- all reads in A do *not* see the effects of writes in B.
|
||||
|
||||
The x86 guarantees that writes in A will go out
|
||||
to memory before the write of lk->locked = 0 in
|
||||
release(lk). It further guarantees that CPU1
|
||||
will observe CPU0's write of lk->locked = 0 only
|
||||
after observing the earlier writes by CPU0.
|
||||
So any reads in B are guaranteed to observe the
|
||||
effects of writes in A.
|
||||
|
||||
According to the Intel manual behavior spec, the
|
||||
second condition requires a serialization instruction
|
||||
in release, to avoid reads in A happening after giving
|
||||
up lk. No Intel SMP processor in existence actually
|
||||
moves reads down after writes, but the language in
|
||||
the spec allows it. There is no telling whether future
|
||||
processors will need it.
|
||||
|
||||
---
|
||||
|
||||
The code in fork needs to read np->pid before
|
||||
setting np->state to RUNNABLE. The following
|
||||
is not a correct way to do this:
|
||||
|
||||
int
|
||||
fork(void)
|
||||
{
|
||||
...
|
||||
np->state = RUNNABLE;
|
||||
return np->pid; // oops
|
||||
}
|
||||
|
||||
After setting np->state to RUNNABLE, some other CPU
|
||||
might run the process, it might exit, and then it might
|
||||
get reused for a different process (with a new pid), all
|
||||
before the return statement. So it's not safe to just
|
||||
"return np->pid". Even saving a copy of np->pid before
|
||||
setting np->state isn't safe, since the compiler is
|
||||
allowed to re-order statements.
|
||||
|
||||
The real code saves a copy of np->pid, then acquires a lock
|
||||
around the write to np->state. The acquire() prevents the
|
||||
compiler from re-ordering.
|
||||
25
xv6-public/alarmtest.c
Normal file
25
xv6-public/alarmtest.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
void periodic();
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
printf(1, "alarmtest starting\n");
|
||||
alarm(10, periodic);
|
||||
for(i = 0; i < 25*5000000; i++){
|
||||
if((i % 250000) == 0)
|
||||
write(2, ".", 1);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
periodic()
|
||||
{
|
||||
printf(1, "alarm!\n");
|
||||
}
|
||||
|
||||
18
xv6-public/asm.h
Normal file
18
xv6-public/asm.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// assembler macros to create x86 segments
|
||||
//
|
||||
|
||||
#define SEG_NULLASM \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
|
||||
// The 0xC0 means the limit is in 4096-byte units
|
||||
// and (for executable segments) 32-bit mode.
|
||||
#define SEG_ASM(type,base,lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
144
xv6-public/bio.c
Normal file
144
xv6-public/bio.c
Normal file
@@ -0,0 +1,144 @@
|
||||
// Buffer cache.
|
||||
//
|
||||
// The buffer cache is a linked list of buf structures holding
|
||||
// cached copies of disk block contents. Caching disk blocks
|
||||
// in memory reduces the number of disk reads and also provides
|
||||
// a synchronization point for disk blocks used by multiple processes.
|
||||
//
|
||||
// Interface:
|
||||
// * To get a buffer for a particular disk block, call bread.
|
||||
// * After changing buffer data, call bwrite to write it to disk.
|
||||
// * When done with the buffer, call brelse.
|
||||
// * Do not use the buffer after calling brelse.
|
||||
// * Only one process at a time can use a buffer,
|
||||
// so do not keep them longer than necessary.
|
||||
//
|
||||
// The implementation uses two state flags internally:
|
||||
// * B_VALID: the buffer data has been read from the disk.
|
||||
// * B_DIRTY: the buffer data has been modified
|
||||
// and needs to be written to disk.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "buf.h"
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct buf buf[NBUF];
|
||||
|
||||
// Linked list of all buffers, through prev/next.
|
||||
// head.next is most recently used.
|
||||
struct buf head;
|
||||
} bcache;
|
||||
|
||||
void
|
||||
binit(void)
|
||||
{
|
||||
struct buf *b;
|
||||
|
||||
initlock(&bcache.lock, "bcache");
|
||||
|
||||
//PAGEBREAK!
|
||||
// Create linked list of buffers
|
||||
bcache.head.prev = &bcache.head;
|
||||
bcache.head.next = &bcache.head;
|
||||
for(b = bcache.buf; b < bcache.buf+NBUF; b++){
|
||||
b->next = bcache.head.next;
|
||||
b->prev = &bcache.head;
|
||||
initsleeplock(&b->lock, "buffer");
|
||||
bcache.head.next->prev = b;
|
||||
bcache.head.next = b;
|
||||
}
|
||||
}
|
||||
|
||||
// Look through buffer cache for block on device dev.
|
||||
// If not found, allocate a buffer.
|
||||
// In either case, return locked buffer.
|
||||
static struct buf*
|
||||
bget(uint dev, uint blockno)
|
||||
{
|
||||
struct buf *b;
|
||||
|
||||
acquire(&bcache.lock);
|
||||
|
||||
// Is the block already cached?
|
||||
for(b = bcache.head.next; b != &bcache.head; b = b->next){
|
||||
if(b->dev == dev && b->blockno == blockno){
|
||||
b->refcnt++;
|
||||
release(&bcache.lock);
|
||||
acquiresleep(&b->lock);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// Not cached; recycle an unused buffer.
|
||||
// Even if refcnt==0, B_DIRTY indicates a buffer is in use
|
||||
// because log.c has modified it but not yet committed it.
|
||||
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
|
||||
if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) {
|
||||
b->dev = dev;
|
||||
b->blockno = blockno;
|
||||
b->flags = 0;
|
||||
b->refcnt = 1;
|
||||
release(&bcache.lock);
|
||||
acquiresleep(&b->lock);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
panic("bget: no buffers");
|
||||
}
|
||||
|
||||
// Return a locked buf with the contents of the indicated block.
|
||||
struct buf*
|
||||
bread(uint dev, uint blockno)
|
||||
{
|
||||
struct buf *b;
|
||||
|
||||
b = bget(dev, blockno);
|
||||
if((b->flags & B_VALID) == 0) {
|
||||
iderw(b);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Write b's contents to disk. Must be locked.
|
||||
void
|
||||
bwrite(struct buf *b)
|
||||
{
|
||||
if(!holdingsleep(&b->lock))
|
||||
panic("bwrite");
|
||||
b->flags |= B_DIRTY;
|
||||
iderw(b);
|
||||
}
|
||||
|
||||
// Release a locked buffer.
|
||||
// Move to the head of the MRU list.
|
||||
void
|
||||
brelse(struct buf *b)
|
||||
{
|
||||
if(!holdingsleep(&b->lock))
|
||||
panic("brelse");
|
||||
|
||||
releasesleep(&b->lock);
|
||||
|
||||
acquire(&bcache.lock);
|
||||
b->refcnt--;
|
||||
if (b->refcnt == 0) {
|
||||
// no one is waiting for it.
|
||||
b->next->prev = b->prev;
|
||||
b->prev->next = b->next;
|
||||
b->next = bcache.head.next;
|
||||
b->prev = &bcache.head;
|
||||
bcache.head.next->prev = b;
|
||||
bcache.head.next = b;
|
||||
}
|
||||
|
||||
release(&bcache.lock);
|
||||
}
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
|
||||
88
xv6-public/bootasm.S
Normal file
88
xv6-public/bootasm.S
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "asm.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
|
||||
# Start the first CPU: switch to 32-bit protected mode, jump into C.
|
||||
# The BIOS loads this code from the first sector of the hard disk into
|
||||
# memory at physical address 0x7c00 and starts executing in real mode
|
||||
# with %cs=0 %ip=7c00.
|
||||
|
||||
.code16 # Assemble for 16-bit mode
|
||||
.globl start
|
||||
start:
|
||||
cli # BIOS enabled interrupts; disable
|
||||
|
||||
# Zero data segment registers DS, ES, and SS.
|
||||
xorw %ax,%ax # Set %ax to zero
|
||||
movw %ax,%ds # -> Data Segment
|
||||
movw %ax,%es # -> Extra Segment
|
||||
movw %ax,%ss # -> Stack Segment
|
||||
|
||||
# Physical address line A20 is tied to zero so that the first PCs
|
||||
# with 2 MB would run software that assumed 1 MB. Undo that.
|
||||
seta20.1:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.1
|
||||
|
||||
movb $0xd1,%al # 0xd1 -> port 0x64
|
||||
outb %al,$0x64
|
||||
|
||||
seta20.2:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.2
|
||||
|
||||
movb $0xdf,%al # 0xdf -> port 0x60
|
||||
outb %al,$0x60
|
||||
|
||||
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||
# virtual addresses map directly to physical addresses so that the
|
||||
# effective memory map doesn't change during the transition.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
//PAGEBREAK!
|
||||
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||
# translation, so that the mapping is still the identity mapping.
|
||||
ljmp $(SEG_KCODE<<3), $start32
|
||||
|
||||
.code32 # Tell assembler to generate 32-bit code now.
|
||||
start32:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||
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 $start, %esp
|
||||
call bootmain
|
||||
|
||||
# If bootmain returns (it shouldn't), trigger a Bochs
|
||||
# breakpoint if running under Bochs, then loop.
|
||||
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
|
||||
movw %ax, %dx
|
||||
outw %ax, %dx
|
||||
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
|
||||
outw %ax, %dx
|
||||
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
|
||||
|
||||
96
xv6-public/bootmain.c
Normal file
96
xv6-public/bootmain.c
Normal file
@@ -0,0 +1,96 @@
|
||||
// Boot loader.
|
||||
//
|
||||
// Part of the boot block, along with bootasm.S, which calls bootmain().
|
||||
// bootasm.S has put the processor into protected 32-bit mode.
|
||||
// bootmain() loads an ELF kernel image from the disk starting at
|
||||
// sector 1 and then jumps to the kernel entry routine.
|
||||
|
||||
#include "types.h"
|
||||
#include "elf.h"
|
||||
#include "x86.h"
|
||||
#include "memlayout.h"
|
||||
|
||||
#define SECTSIZE 512
|
||||
|
||||
void readseg(uchar*, uint, uint);
|
||||
|
||||
void
|
||||
bootmain(void)
|
||||
{
|
||||
struct elfhdr *elf;
|
||||
struct proghdr *ph, *eph;
|
||||
void (*entry)(void);
|
||||
uchar* pa;
|
||||
|
||||
elf = (struct elfhdr*)0x10000; // scratch space
|
||||
|
||||
// Read 1st page off disk
|
||||
readseg((uchar*)elf, 4096, 0);
|
||||
|
||||
// Is this an ELF executable?
|
||||
if(elf->magic != ELF_MAGIC)
|
||||
return; // let bootasm.S handle error
|
||||
|
||||
// Load each program segment (ignores ph flags).
|
||||
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
|
||||
eph = ph + elf->phnum;
|
||||
for(; ph < eph; ph++){
|
||||
pa = (uchar*)ph->paddr;
|
||||
readseg(pa, ph->filesz, ph->off);
|
||||
if(ph->memsz > ph->filesz)
|
||||
stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
|
||||
}
|
||||
|
||||
// Call the entry point from the ELF header.
|
||||
// Does not return!
|
||||
entry = (void(*)(void))(elf->entry);
|
||||
entry();
|
||||
}
|
||||
|
||||
void
|
||||
waitdisk(void)
|
||||
{
|
||||
// Wait for disk ready.
|
||||
while((inb(0x1F7) & 0xC0) != 0x40)
|
||||
;
|
||||
}
|
||||
|
||||
// Read a single sector at offset into dst.
|
||||
void
|
||||
readsect(void *dst, uint offset)
|
||||
{
|
||||
// Issue command.
|
||||
waitdisk();
|
||||
outb(0x1F2, 1); // count = 1
|
||||
outb(0x1F3, offset);
|
||||
outb(0x1F4, offset >> 8);
|
||||
outb(0x1F5, offset >> 16);
|
||||
outb(0x1F6, (offset >> 24) | 0xE0);
|
||||
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
||||
|
||||
// Read data.
|
||||
waitdisk();
|
||||
insl(0x1F0, dst, SECTSIZE/4);
|
||||
}
|
||||
|
||||
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
|
||||
// Might copy more than asked.
|
||||
void
|
||||
readseg(uchar* pa, uint count, uint offset)
|
||||
{
|
||||
uchar* epa;
|
||||
|
||||
epa = pa + count;
|
||||
|
||||
// Round down to sector boundary.
|
||||
pa -= offset % SECTSIZE;
|
||||
|
||||
// Translate from bytes to sectors; kernel starts at sector 1.
|
||||
offset = (offset / SECTSIZE) + 1;
|
||||
|
||||
// If this is too slow, we could read lots of sectors at a time.
|
||||
// We'd write more to memory than asked, but it doesn't matter --
|
||||
// we load in increasing order.
|
||||
for(; pa < epa; pa += SECTSIZE, offset++)
|
||||
readsect(pa, offset);
|
||||
}
|
||||
14
xv6-public/buf.h
Normal file
14
xv6-public/buf.h
Normal file
@@ -0,0 +1,14 @@
|
||||
struct buf {
|
||||
int flags;
|
||||
uint dev;
|
||||
uint blockno;
|
||||
struct sleeplock lock;
|
||||
uint refcnt;
|
||||
struct buf *prev; // LRU cache list
|
||||
struct buf *next;
|
||||
struct buf *qnext; // disk queue
|
||||
uchar data[BSIZE];
|
||||
};
|
||||
#define B_VALID 0x2 // buffer has been read from disk
|
||||
#define B_DIRTY 0x4 // buffer needs to be written to disk
|
||||
|
||||
43
xv6-public/cat.c
Normal file
43
xv6-public/cat.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
char buf[512];
|
||||
|
||||
void
|
||||
cat(int fd)
|
||||
{
|
||||
int n;
|
||||
|
||||
while((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||
if (write(1, buf, n) != n) {
|
||||
printf(1, "cat: write error\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
if(n < 0){
|
||||
printf(1, "cat: read error\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, i;
|
||||
|
||||
if(argc <= 1){
|
||||
cat(0);
|
||||
exit();
|
||||
}
|
||||
|
||||
for(i = 1; i < argc; i++){
|
||||
if((fd = open(argv[i], 0)) < 0){
|
||||
printf(1, "cat: cannot open %s\n", argv[i]);
|
||||
exit();
|
||||
}
|
||||
cat(fd);
|
||||
close(fd);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
299
xv6-public/console.c
Normal file
299
xv6-public/console.c
Normal file
@@ -0,0 +1,299 @@
|
||||
// Console input and output.
|
||||
// Input is from the keyboard or serial port.
|
||||
// Output is written to the screen and serial port.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "traps.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
|
||||
static void consputc(int);
|
||||
|
||||
static int panicked = 0;
|
||||
|
||||
static struct {
|
||||
struct spinlock lock;
|
||||
int locking;
|
||||
} cons;
|
||||
|
||||
static void
|
||||
printint(int xx, int base, int sign)
|
||||
{
|
||||
static char digits[] = "0123456789abcdef";
|
||||
char buf[16];
|
||||
int i;
|
||||
uint x;
|
||||
|
||||
if(sign && (sign = xx < 0))
|
||||
x = -xx;
|
||||
else
|
||||
x = xx;
|
||||
|
||||
i = 0;
|
||||
do{
|
||||
buf[i++] = digits[x % base];
|
||||
}while((x /= base) != 0);
|
||||
|
||||
if(sign)
|
||||
buf[i++] = '-';
|
||||
|
||||
while(--i >= 0)
|
||||
consputc(buf[i]);
|
||||
}
|
||||
//PAGEBREAK: 50
|
||||
|
||||
// Print to the console. only understands %d, %x, %p, %s.
|
||||
void
|
||||
cprintf(char *fmt, ...)
|
||||
{
|
||||
int i, c, locking;
|
||||
uint *argp;
|
||||
char *s;
|
||||
|
||||
locking = cons.locking;
|
||||
if(locking)
|
||||
acquire(&cons.lock);
|
||||
|
||||
if (fmt == 0)
|
||||
panic("null fmt");
|
||||
|
||||
argp = (uint*)(void*)(&fmt + 1);
|
||||
for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
|
||||
if(c != '%'){
|
||||
consputc(c);
|
||||
continue;
|
||||
}
|
||||
c = fmt[++i] & 0xff;
|
||||
if(c == 0)
|
||||
break;
|
||||
switch(c){
|
||||
case 'd':
|
||||
printint(*argp++, 10, 1);
|
||||
break;
|
||||
case 'x':
|
||||
case 'p':
|
||||
printint(*argp++, 16, 0);
|
||||
break;
|
||||
case 's':
|
||||
if((s = (char*)*argp++) == 0)
|
||||
s = "(null)";
|
||||
for(; *s; s++)
|
||||
consputc(*s);
|
||||
break;
|
||||
case '%':
|
||||
consputc('%');
|
||||
break;
|
||||
default:
|
||||
// Print unknown % sequence to draw attention.
|
||||
consputc('%');
|
||||
consputc(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(locking)
|
||||
release(&cons.lock);
|
||||
}
|
||||
|
||||
void
|
||||
panic(char *s)
|
||||
{
|
||||
int i;
|
||||
uint pcs[10];
|
||||
|
||||
cli();
|
||||
cons.locking = 0;
|
||||
// use lapiccpunum so that we can call panic from mycpu()
|
||||
cprintf("lapicid %d: panic: ", lapicid());
|
||||
cprintf(s);
|
||||
cprintf("\n");
|
||||
getcallerpcs(&s, pcs);
|
||||
for(i=0; i<10; i++)
|
||||
cprintf(" %p", pcs[i]);
|
||||
panicked = 1; // freeze other CPU
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
|
||||
//PAGEBREAK: 50
|
||||
#define BACKSPACE 0x100
|
||||
#define CRTPORT 0x3d4
|
||||
static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory
|
||||
|
||||
static void
|
||||
cgaputc(int c)
|
||||
{
|
||||
int pos;
|
||||
|
||||
// Cursor position: col + 80*row.
|
||||
outb(CRTPORT, 14);
|
||||
pos = inb(CRTPORT+1) << 8;
|
||||
outb(CRTPORT, 15);
|
||||
pos |= inb(CRTPORT+1);
|
||||
|
||||
if(c == '\n')
|
||||
pos += 80 - pos%80;
|
||||
else if(c == BACKSPACE){
|
||||
if(pos > 0) --pos;
|
||||
} else
|
||||
crt[pos++] = (c&0xff) | 0x0700; // black on white
|
||||
|
||||
if(pos < 0 || pos > 25*80)
|
||||
panic("pos under/overflow");
|
||||
|
||||
if((pos/80) >= 24){ // Scroll up.
|
||||
memmove(crt, crt+80, sizeof(crt[0])*23*80);
|
||||
pos -= 80;
|
||||
memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos));
|
||||
}
|
||||
|
||||
outb(CRTPORT, 14);
|
||||
outb(CRTPORT+1, pos>>8);
|
||||
outb(CRTPORT, 15);
|
||||
outb(CRTPORT+1, pos);
|
||||
crt[pos] = ' ' | 0x0700;
|
||||
}
|
||||
|
||||
void
|
||||
consputc(int c)
|
||||
{
|
||||
if(panicked){
|
||||
cli();
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
|
||||
if(c == BACKSPACE){
|
||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
||||
} else
|
||||
uartputc(c);
|
||||
cgaputc(c);
|
||||
}
|
||||
|
||||
#define INPUT_BUF 128
|
||||
struct {
|
||||
char buf[INPUT_BUF];
|
||||
uint r; // Read index
|
||||
uint w; // Write index
|
||||
uint e; // Edit index
|
||||
} input;
|
||||
|
||||
#define C(x) ((x)-'@') // Control-x
|
||||
|
||||
void
|
||||
consoleintr(int (*getc)(void))
|
||||
{
|
||||
int c, doprocdump = 0;
|
||||
|
||||
acquire(&cons.lock);
|
||||
while((c = getc()) >= 0){
|
||||
switch(c){
|
||||
case C('P'): // Process listing.
|
||||
// procdump() locks cons.lock indirectly; invoke later
|
||||
doprocdump = 1;
|
||||
break;
|
||||
case C('U'): // Kill line.
|
||||
while(input.e != input.w &&
|
||||
input.buf[(input.e-1) % INPUT_BUF] != '\n'){
|
||||
input.e--;
|
||||
consputc(BACKSPACE);
|
||||
}
|
||||
break;
|
||||
case C('H'): case '\x7f': // Backspace
|
||||
if(input.e != input.w){
|
||||
input.e--;
|
||||
consputc(BACKSPACE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(c != 0 && input.e-input.r < INPUT_BUF){
|
||||
c = (c == '\r') ? '\n' : c;
|
||||
input.buf[input.e++ % INPUT_BUF] = c;
|
||||
consputc(c);
|
||||
if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
|
||||
input.w = input.e;
|
||||
wakeup(&input.r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
release(&cons.lock);
|
||||
if(doprocdump) {
|
||||
procdump(); // now call procdump() wo. cons.lock held
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
consoleread(struct inode *ip, char *dst, int n)
|
||||
{
|
||||
uint target;
|
||||
int c;
|
||||
|
||||
iunlock(ip);
|
||||
target = n;
|
||||
acquire(&cons.lock);
|
||||
while(n > 0){
|
||||
while(input.r == input.w){
|
||||
if(myproc()->killed){
|
||||
release(&cons.lock);
|
||||
ilock(ip);
|
||||
return -1;
|
||||
}
|
||||
sleep(&input.r, &cons.lock);
|
||||
}
|
||||
c = input.buf[input.r++ % INPUT_BUF];
|
||||
if(c == C('D')){ // EOF
|
||||
if(n < target){
|
||||
// Save ^D for next time, to make sure
|
||||
// caller gets a 0-byte result.
|
||||
input.r--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*dst++ = c;
|
||||
--n;
|
||||
if(c == '\n')
|
||||
break;
|
||||
}
|
||||
release(&cons.lock);
|
||||
ilock(ip);
|
||||
|
||||
return target - n;
|
||||
}
|
||||
|
||||
int
|
||||
consolewrite(struct inode *ip, char *buf, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
iunlock(ip);
|
||||
acquire(&cons.lock);
|
||||
for(i = 0; i < n; i++)
|
||||
consputc(buf[i] & 0xff);
|
||||
release(&cons.lock);
|
||||
ilock(ip);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
consoleinit(void)
|
||||
{
|
||||
initlock(&cons.lock, "console");
|
||||
|
||||
devsw[CONSOLE].write = consolewrite;
|
||||
devsw[CONSOLE].read = consoleread;
|
||||
cons.locking = 1;
|
||||
|
||||
ioapicenable(IRQ_KBD, 0);
|
||||
}
|
||||
|
||||
48
xv6-public/cuth
Normal file
48
xv6-public/cuth
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
$| = 1;
|
||||
|
||||
sub writefile($@){
|
||||
my ($file, @lines) = @_;
|
||||
|
||||
sleep(1);
|
||||
open(F, ">$file") || die "open >$file: $!";
|
||||
print F @lines;
|
||||
close(F);
|
||||
}
|
||||
|
||||
# Cut out #include lines that don't contribute anything.
|
||||
for($i=0; $i<@ARGV; $i++){
|
||||
$file = $ARGV[$i];
|
||||
if(!open(F, $file)){
|
||||
print STDERR "open $file: $!\n";
|
||||
next;
|
||||
}
|
||||
@lines = <F>;
|
||||
close(F);
|
||||
|
||||
$obj = "$file.o";
|
||||
$obj =~ s/\.c\.o$/.o/;
|
||||
system("touch $file");
|
||||
|
||||
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||
print STDERR "make $obj failed: $rv\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("cp $file =$file");
|
||||
for($j=@lines-1; $j>=0; $j--){
|
||||
if($lines[$j] =~ /^#include/){
|
||||
$old = $lines[$j];
|
||||
$lines[$j] = "/* CUT-H */\n";
|
||||
writefile($file, @lines);
|
||||
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||
$lines[$j] = $old;
|
||||
}else{
|
||||
print STDERR "$file $old";
|
||||
}
|
||||
}
|
||||
}
|
||||
writefile($file, grep {!/CUT-H/} @lines);
|
||||
system("rm =$file");
|
||||
}
|
||||
20
xv6-public/date.c
Normal file
20
xv6-public/date.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "types.h"
|
||||
#include "user.h"
|
||||
#include "date.h"
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct rtcdate r;
|
||||
|
||||
if (date(&r)) {
|
||||
printf(2, "date failed\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
// your code to print the time in any format you like...
|
||||
printf(1, "%d-%d-%d %d:%d\n",r.year, r.month, r.day, r.hour, r.minute);
|
||||
exit();
|
||||
}
|
||||
8
xv6-public/date.h
Normal file
8
xv6-public/date.h
Normal file
@@ -0,0 +1,8 @@
|
||||
struct rtcdate {
|
||||
uint second;
|
||||
uint minute;
|
||||
uint hour;
|
||||
uint day;
|
||||
uint month;
|
||||
uint year;
|
||||
};
|
||||
190
xv6-public/defs.h
Normal file
190
xv6-public/defs.h
Normal file
@@ -0,0 +1,190 @@
|
||||
struct buf;
|
||||
struct context;
|
||||
struct file;
|
||||
struct inode;
|
||||
struct pipe;
|
||||
struct proc;
|
||||
struct rtcdate;
|
||||
struct spinlock;
|
||||
struct sleeplock;
|
||||
struct stat;
|
||||
struct superblock;
|
||||
|
||||
// bio.c
|
||||
void binit(void);
|
||||
struct buf* bread(uint, uint);
|
||||
void brelse(struct buf*);
|
||||
void bwrite(struct buf*);
|
||||
|
||||
// console.c
|
||||
void consoleinit(void);
|
||||
void cprintf(char*, ...);
|
||||
void consoleintr(int(*)(void));
|
||||
void panic(char*) __attribute__((noreturn));
|
||||
|
||||
// exec.c
|
||||
int exec(char*, char**);
|
||||
|
||||
// file.c
|
||||
struct file* filealloc(void);
|
||||
void fileclose(struct file*);
|
||||
struct file* filedup(struct file*);
|
||||
void fileinit(void);
|
||||
int fileread(struct file*, char*, int n);
|
||||
int filestat(struct file*, struct stat*);
|
||||
int filewrite(struct file*, char*, int n);
|
||||
|
||||
// fs.c
|
||||
void readsb(int dev, struct superblock *sb);
|
||||
int dirlink(struct inode*, char*, uint);
|
||||
struct inode* dirlookup(struct inode*, char*, uint*);
|
||||
struct inode* ialloc(uint, short);
|
||||
struct inode* idup(struct inode*);
|
||||
void iinit(int dev);
|
||||
void ilock(struct inode*);
|
||||
void iput(struct inode*);
|
||||
void iunlock(struct inode*);
|
||||
void iunlockput(struct inode*);
|
||||
void iupdate(struct inode*);
|
||||
int namecmp(const char*, const char*);
|
||||
struct inode* namei(char*);
|
||||
struct inode* nameiparent(char*, char*);
|
||||
int readi(struct inode*, char*, uint, uint);
|
||||
void stati(struct inode*, struct stat*);
|
||||
int writei(struct inode*, char*, uint, uint);
|
||||
|
||||
// ide.c
|
||||
void ideinit(void);
|
||||
void ideintr(void);
|
||||
void iderw(struct buf*);
|
||||
|
||||
// ioapic.c
|
||||
void ioapicenable(int irq, int cpu);
|
||||
extern uchar ioapicid;
|
||||
void ioapicinit(void);
|
||||
|
||||
// kalloc.c
|
||||
char* kalloc(void);
|
||||
void kfree(char*);
|
||||
void kinit1(void*, void*);
|
||||
void kinit2(void*, void*);
|
||||
|
||||
// kbd.c
|
||||
void kbdintr(void);
|
||||
|
||||
// lapic.c
|
||||
void cmostime(struct rtcdate *r);
|
||||
int lapicid(void);
|
||||
extern volatile uint* lapic;
|
||||
void lapiceoi(void);
|
||||
void lapicinit(void);
|
||||
void lapicstartap(uchar, uint);
|
||||
void microdelay(int);
|
||||
|
||||
// log.c
|
||||
void initlog(int dev);
|
||||
void log_write(struct buf*);
|
||||
void begin_op();
|
||||
void end_op();
|
||||
|
||||
// mp.c
|
||||
extern int ismp;
|
||||
void mpinit(void);
|
||||
|
||||
// picirq.c
|
||||
void picenable(int);
|
||||
void picinit(void);
|
||||
|
||||
// pipe.c
|
||||
int pipealloc(struct file**, struct file**);
|
||||
void pipeclose(struct pipe*, int);
|
||||
int piperead(struct pipe*, char*, int);
|
||||
int pipewrite(struct pipe*, char*, int);
|
||||
|
||||
//PAGEBREAK: 16
|
||||
// proc.c
|
||||
int cpuid(void);
|
||||
void exit(void);
|
||||
int fork(void);
|
||||
int growproc(int);
|
||||
int kill(int);
|
||||
struct cpu* mycpu(void);
|
||||
struct proc* myproc();
|
||||
void pinit(void);
|
||||
void procdump(void);
|
||||
void scheduler(void) __attribute__((noreturn));
|
||||
void sched(void);
|
||||
void setproc(struct proc*);
|
||||
void sleep(void*, struct spinlock*);
|
||||
void userinit(void);
|
||||
int wait(void);
|
||||
void wakeup(void*);
|
||||
void yield(void);
|
||||
|
||||
// swtch.S
|
||||
void swtch(struct context**, struct context*);
|
||||
|
||||
// spinlock.c
|
||||
void acquire(struct spinlock*);
|
||||
void getcallerpcs(void*, uint*);
|
||||
int holding(struct spinlock*);
|
||||
void initlock(struct spinlock*, char*);
|
||||
void release(struct spinlock*);
|
||||
void pushcli(void);
|
||||
void popcli(void);
|
||||
|
||||
// sleeplock.c
|
||||
void acquiresleep(struct sleeplock*);
|
||||
void releasesleep(struct sleeplock*);
|
||||
int holdingsleep(struct sleeplock*);
|
||||
void initsleeplock(struct sleeplock*, char*);
|
||||
|
||||
// string.c
|
||||
int memcmp(const void*, const void*, uint);
|
||||
void* memmove(void*, const void*, uint);
|
||||
void* memset(void*, int, uint);
|
||||
char* safestrcpy(char*, const char*, int);
|
||||
int strlen(const char*);
|
||||
int strncmp(const char*, const char*, uint);
|
||||
char* strncpy(char*, const char*, int);
|
||||
|
||||
// syscall.c
|
||||
int argint(int, int*);
|
||||
int argptr(int, char**, int);
|
||||
int argstr(int, char**);
|
||||
int fetchint(uint, int*);
|
||||
int fetchstr(uint, char**);
|
||||
void syscall(void);
|
||||
|
||||
// timer.c
|
||||
void timerinit(void);
|
||||
|
||||
// trap.c
|
||||
void idtinit(void);
|
||||
extern uint ticks;
|
||||
void tvinit(void);
|
||||
extern struct spinlock tickslock;
|
||||
|
||||
// uart.c
|
||||
void uartinit(void);
|
||||
void uartintr(void);
|
||||
void uartputc(int);
|
||||
|
||||
// vm.c
|
||||
void seginit(void);
|
||||
void kvmalloc(void);
|
||||
pde_t* setupkvm(void);
|
||||
char* uva2ka(pde_t*, char*);
|
||||
int allocuvm(pde_t*, uint, uint);
|
||||
int deallocuvm(pde_t*, uint, uint);
|
||||
void freevm(pde_t*);
|
||||
void inituvm(pde_t*, char*, uint);
|
||||
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
|
||||
pde_t* copyuvm(pde_t*, uint);
|
||||
void switchuvm(struct proc*);
|
||||
void switchkvm(void);
|
||||
int copyout(pde_t*, uint, void*, uint);
|
||||
void clearpteu(pde_t *pgdir, char *uva);
|
||||
|
||||
// number of elements in fixed-size array
|
||||
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
|
||||
738
xv6-public/dot-bochsrc
Normal file
738
xv6-public/dot-bochsrc
Normal file
@@ -0,0 +1,738 @@
|
||||
# You may now use double quotes around pathnames, in case
|
||||
# your pathname includes spaces.
|
||||
|
||||
#=======================================================================
|
||||
# CONFIG_INTERFACE
|
||||
#
|
||||
# The configuration interface is a series of menus or dialog boxes that
|
||||
# allows you to change all the settings that control Bochs's behavior.
|
||||
# There are two choices of configuration interface: a text mode version
|
||||
# called "textconfig" and a graphical version called "wx". The text
|
||||
# mode version uses stdin/stdout and is always compiled in. The graphical
|
||||
# version is only available when you use "--with-wx" on the configure
|
||||
# command. If you do not write a config_interface line, Bochs will
|
||||
# choose a default for you.
|
||||
#
|
||||
# NOTE: if you use the "wx" configuration interface, you must also use
|
||||
# the "wx" display library.
|
||||
#=======================================================================
|
||||
#config_interface: textconfig
|
||||
#config_interface: wx
|
||||
|
||||
#=======================================================================
|
||||
# DISPLAY_LIBRARY
|
||||
#
|
||||
# The display library is the code that displays the Bochs VGA screen. Bochs
|
||||
# has a selection of about 10 different display library implementations for
|
||||
# different platforms. If you run configure with multiple --with-* options,
|
||||
# the display_library command lets you choose which one you want to run with.
|
||||
# If you do not write a display_library line, Bochs will choose a default for
|
||||
# you.
|
||||
#
|
||||
# The choices are:
|
||||
# x use X windows interface, cross platform
|
||||
# win32 use native win32 libraries
|
||||
# carbon use Carbon library (for MacOS X)
|
||||
# beos use native BeOS libraries
|
||||
# macintosh use MacOS pre-10
|
||||
# amigaos use native AmigaOS libraries
|
||||
# sdl use SDL library, cross platform
|
||||
# svga use SVGALIB library for Linux, allows graphics without X11
|
||||
# term text only, uses curses/ncurses library, cross platform
|
||||
# rfb provides an interface to AT&T's VNC viewer, cross platform
|
||||
# wx use wxWidgets library, cross platform
|
||||
# nogui no display at all
|
||||
#
|
||||
# NOTE: if you use the "wx" configuration interface, you must also use
|
||||
# the "wx" display library.
|
||||
#
|
||||
# Specific options:
|
||||
# Some display libraries now support specific option to control their
|
||||
# behaviour. See the examples below for currently supported options.
|
||||
#=======================================================================
|
||||
#display_library: amigaos
|
||||
#display_library: beos
|
||||
#display_library: carbon
|
||||
#display_library: macintosh
|
||||
#display_library: nogui
|
||||
#display_library: rfb, options="timeout=60" # time to wait for client
|
||||
#display_library: sdl, options="fullscreen" # startup in fullscreen mode
|
||||
#display_library: term
|
||||
#display_library: win32, options="legacyF12" # use F12 to toggle mouse
|
||||
#display_library: wx
|
||||
#display_library: x
|
||||
|
||||
#=======================================================================
|
||||
# ROMIMAGE:
|
||||
# The ROM BIOS controls what the PC does when it first powers on.
|
||||
# Normally, you can use a precompiled BIOS in the source or binary
|
||||
# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded
|
||||
# starting at address 0xf0000, and it is exactly 64k long.
|
||||
# You can also use the environment variable $BXSHARE to specify the
|
||||
# location of the BIOS.
|
||||
# The usage of external large BIOS images (up to 512k) at memory top is
|
||||
# now supported, but we still recommend to use the BIOS distributed with
|
||||
# Bochs. Now the start address can be calculated from image size.
|
||||
#=======================================================================
|
||||
romimage: file=$BXSHARE/BIOS-bochs-latest
|
||||
#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top
|
||||
#romimage: file=mybios.bin # calculate start address from image size
|
||||
|
||||
#=======================================================================
|
||||
# CPU:
|
||||
# This defines cpu-related parameters inside Bochs:
|
||||
#
|
||||
# COUNT:
|
||||
# Set the number of processors when Bochs is compiled for SMP emulation.
|
||||
# Bochs currently supports up to 8 processors. If Bochs is compiled
|
||||
# without SMP support, it won't accept values different from 1.
|
||||
#
|
||||
# IPS:
|
||||
# Emulated Instructions Per Second. This is the number of IPS that bochs
|
||||
# is capable of running on your machine. You can recompile Bochs with
|
||||
# --enable-show-ips option enabled, to find your workstation's capability.
|
||||
# Measured IPS value will then be logged into your log file or status bar
|
||||
# (if supported by the gui).
|
||||
#
|
||||
# IPS is used to calibrate many time-dependent events within the bochs
|
||||
# simulation. For example, changing IPS affects the frequency of VGA
|
||||
# updates, the duration of time before a key starts to autorepeat, and
|
||||
# the measurement of BogoMips and other benchmarks.
|
||||
#
|
||||
# Examples:
|
||||
# Machine Mips
|
||||
# ________________________________________________________________
|
||||
# 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips
|
||||
# 1.6Ghz Intel P4 with Win2000/g++ 3.3 5 to 7 Mips
|
||||
# 650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66 2 to 2.5 Mips
|
||||
# 400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3 1 to 1.8 Mips
|
||||
#=======================================================================
|
||||
cpu: count=2, ips=10000000
|
||||
|
||||
#=======================================================================
|
||||
# MEGS
|
||||
# Set the number of Megabytes of physical memory you want to emulate.
|
||||
# The default is 32MB, most OS's won't need more than that.
|
||||
# The maximum amount of memory supported is 2048Mb.
|
||||
#=======================================================================
|
||||
#megs: 256
|
||||
#megs: 128
|
||||
#megs: 64
|
||||
megs: 32
|
||||
#megs: 16
|
||||
#megs: 8
|
||||
|
||||
#=======================================================================
|
||||
# OPTROMIMAGE[1-4]:
|
||||
# You may now load up to 4 optional ROM images. Be sure to use a
|
||||
# read-only area, typically between C8000 and EFFFF. These optional
|
||||
# ROM images should not overwrite the rombios (located at
|
||||
# F0000-FFFFF) and the videobios (located at C0000-C7FFF).
|
||||
# Those ROM images will be initialized by the bios if they contain
|
||||
# the right signature (0x55AA) and a valid checksum.
|
||||
# It can also be a convenient way to upload some arbitrary code/data
|
||||
# in the simulation, that can be retrieved by the boot loader
|
||||
#=======================================================================
|
||||
#optromimage1: file=optionalrom.bin, address=0xd0000
|
||||
#optromimage2: file=optionalrom.bin, address=0xd1000
|
||||
#optromimage3: file=optionalrom.bin, address=0xd2000
|
||||
#optromimage4: file=optionalrom.bin, address=0xd3000
|
||||
|
||||
#optramimage1: file=/path/file1.img, address=0x0010000
|
||||
#optramimage2: file=/path/file2.img, address=0x0020000
|
||||
#optramimage3: file=/path/file3.img, address=0x0030000
|
||||
#optramimage4: file=/path/file4.img, address=0x0040000
|
||||
|
||||
#=======================================================================
|
||||
# VGAROMIMAGE
|
||||
# You now need to load a VGA ROM BIOS into C0000.
|
||||
#=======================================================================
|
||||
#vgaromimage: file=bios/VGABIOS-elpin-2.40
|
||||
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
|
||||
#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus
|
||||
|
||||
#=======================================================================
|
||||
# VGA:
|
||||
# Here you can specify the display extension to be used. With the value
|
||||
# 'none' you can use standard VGA with no extension. Other supported
|
||||
# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support.
|
||||
#=======================================================================
|
||||
#vga: extension=cirrus
|
||||
#vga: extension=vbe
|
||||
vga: extension=none
|
||||
|
||||
#=======================================================================
|
||||
# FLOPPYA:
|
||||
# Point this to pathname of floppy image file or device
|
||||
# This should be of a bootable floppy(image/device) if you're
|
||||
# booting from 'a' (or 'floppy').
|
||||
#
|
||||
# You can set the initial status of the media to 'ejected' or 'inserted'.
|
||||
# floppya: 2_88=path, status=ejected (2.88M 3.5" floppy)
|
||||
# floppya: 1_44=path, status=inserted (1.44M 3.5" floppy)
|
||||
# floppya: 1_2=path, status=ejected (1.2M 5.25" floppy)
|
||||
# floppya: 720k=path, status=inserted (720K 3.5" floppy)
|
||||
# floppya: 360k=path, status=inserted (360K 5.25" floppy)
|
||||
# floppya: 320k=path, status=inserted (320K 5.25" floppy)
|
||||
# floppya: 180k=path, status=inserted (180K 5.25" floppy)
|
||||
# floppya: 160k=path, status=inserted (160K 5.25" floppy)
|
||||
# floppya: image=path, status=inserted (guess type from image size)
|
||||
#
|
||||
# The path should be the name of a disk image file. On Unix, you can use a raw
|
||||
# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters
|
||||
# such as a: or b: as the path. The parameter 'image' works with image files
|
||||
# only. In that case the size must match one of the supported types.
|
||||
#=======================================================================
|
||||
floppya: 1_44=/dev/fd0, status=inserted
|
||||
#floppya: image=../1.44, status=inserted
|
||||
#floppya: 1_44=/dev/fd0H1440, status=inserted
|
||||
#floppya: 1_2=../1_2, status=inserted
|
||||
#floppya: 1_44=a:, status=inserted
|
||||
#floppya: 1_44=a.img, status=inserted
|
||||
#floppya: 1_44=/dev/rfd0a, status=inserted
|
||||
|
||||
#=======================================================================
|
||||
# FLOPPYB:
|
||||
# See FLOPPYA above for syntax
|
||||
#=======================================================================
|
||||
#floppyb: 1_44=b:, status=inserted
|
||||
floppyb: 1_44=b.img, status=inserted
|
||||
|
||||
#=======================================================================
|
||||
# ATA0, ATA1, ATA2, ATA3
|
||||
# ATA controller for hard disks and cdroms
|
||||
#
|
||||
# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number
|
||||
#
|
||||
# These options enables up to 4 ata channels. For each channel
|
||||
# the two base io addresses and the irq must be specified.
|
||||
#
|
||||
# ata0 and ata1 are enabled by default with the values shown below
|
||||
#
|
||||
# Examples:
|
||||
# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
|
||||
# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9
|
||||
#=======================================================================
|
||||
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
|
||||
ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
|
||||
|
||||
#=======================================================================
|
||||
# ATA[0-3]-MASTER, ATA[0-3]-SLAVE
|
||||
#
|
||||
# This defines the type and characteristics of all attached ata devices:
|
||||
# type= type of attached device [disk|cdrom]
|
||||
# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3]
|
||||
# mode= only valid for disks [undoable|growing|volatile]
|
||||
# path= path of the image
|
||||
# cylinders= only valid for disks
|
||||
# heads= only valid for disks
|
||||
# spt= only valid for disks
|
||||
# status= only valid for cdroms [inserted|ejected]
|
||||
# biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos]
|
||||
# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto]
|
||||
# model= string returned by identify device command
|
||||
# journal= optional filename of the redolog for undoable and volatile disks
|
||||
#
|
||||
# Point this at a hard disk image file, cdrom iso file, or physical cdrom
|
||||
# device. To create a hard disk image, try running bximage. It will help you
|
||||
# choose the size and then suggest a line that works with it.
|
||||
#
|
||||
# In UNIX it may be possible to use a raw device as a Bochs hard disk,
|
||||
# but WE DON'T RECOMMEND IT. In Windows there is no easy way.
|
||||
#
|
||||
# In windows, the drive letter + colon notation should be used for cdroms.
|
||||
# Depending on versions of windows and drivers, you may only be able to
|
||||
# access the "first" cdrom in the system. On MacOSX, use path="drive"
|
||||
# to access the physical drive.
|
||||
#
|
||||
# The path is always mandatory. For flat hard disk images created with
|
||||
# bximage geometry autodetection can be used (cylinders=0 -> cylinders are
|
||||
# calculated using heads=16 and spt=63). For other hard disk images and modes
|
||||
# the cylinders, heads, and spt are mandatory.
|
||||
#
|
||||
# Default values are:
|
||||
# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234"
|
||||
#
|
||||
# The biosdetect option has currently no effect on the bios
|
||||
#
|
||||
# Examples:
|
||||
# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17
|
||||
# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17
|
||||
# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17
|
||||
# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17
|
||||
# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17
|
||||
# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17
|
||||
# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63
|
||||
# ata3-slave: type=cdrom, path=iso.sample, status=inserted
|
||||
#=======================================================================
|
||||
ata0-master: type=disk, mode=flat, path="xv6.img", cylinders=100, heads=10, spt=10
|
||||
ata0-slave: type=disk, mode=flat, path="fs.img", cylinders=1024, heads=1, spt=1
|
||||
#ata0-slave: type=cdrom, path=D:, status=inserted
|
||||
#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted
|
||||
#ata0-slave: type=cdrom, path="drive", status=inserted
|
||||
#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted
|
||||
|
||||
#=======================================================================
|
||||
# BOOT:
|
||||
# This defines the boot sequence. Now you can specify up to 3 boot drives.
|
||||
# You can either boot from 'floppy', 'disk' or 'cdrom'
|
||||
# legacy 'a' and 'c' are also supported
|
||||
# Examples:
|
||||
# boot: floppy
|
||||
# boot: disk
|
||||
# boot: cdrom
|
||||
# boot: c
|
||||
# boot: a
|
||||
# boot: cdrom, floppy, disk
|
||||
#=======================================================================
|
||||
#boot: floppy
|
||||
boot: disk
|
||||
|
||||
#=======================================================================
|
||||
# CLOCK:
|
||||
# This defines the parameters of the clock inside Bochs:
|
||||
#
|
||||
# SYNC:
|
||||
# TO BE COMPLETED (see Greg explanation in feature request #536329)
|
||||
#
|
||||
# TIME0:
|
||||
# Specifies the start (boot) time of the virtual machine. Use a time
|
||||
# value as returned by the time(2) system call. If no time0 value is
|
||||
# set or if time0 equal to 1 (special case) or if time0 equal 'local',
|
||||
# the simulation will be started at the current local host time.
|
||||
# If time0 equal to 2 (special case) or if time0 equal 'utc',
|
||||
# the simulation will be started at the current utc time.
|
||||
#
|
||||
# Syntax:
|
||||
# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc]
|
||||
#
|
||||
# Example:
|
||||
# clock: sync=none, time0=local # Now (localtime)
|
||||
# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980
|
||||
# clock: sync=none, time0=631148400 # Mon Jan 1 00:00:00 1990
|
||||
# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999
|
||||
# clock: sync=realtime, time0=946681200 # Sat Jan 1 00:00:00 2000
|
||||
# clock: sync=none, time0=1 # Now (localtime)
|
||||
# clock: sync=none, time0=utc # Now (utc/gmt)
|
||||
#
|
||||
# Default value are sync=none, time0=local
|
||||
#=======================================================================
|
||||
#clock: sync=none, time0=local
|
||||
|
||||
|
||||
#=======================================================================
|
||||
# FLOPPY_BOOTSIG_CHECK: disabled=[0|1]
|
||||
# Enables or disables the 0xaa55 signature check on boot floppies
|
||||
# Defaults to disabled=0
|
||||
# Examples:
|
||||
# floppy_bootsig_check: disabled=0
|
||||
# floppy_bootsig_check: disabled=1
|
||||
#=======================================================================
|
||||
#floppy_bootsig_check: disabled=1
|
||||
floppy_bootsig_check: disabled=0
|
||||
|
||||
#=======================================================================
|
||||
# LOG:
|
||||
# Give the path of the log file you'd like Bochs debug and misc. verbiage
|
||||
# to be written to. If you don't use this option or set the filename to
|
||||
# '-' the output is written to the console. If you really don't want it,
|
||||
# make it "/dev/null" (Unix) or "nul" (win32). :^(
|
||||
#
|
||||
# Examples:
|
||||
# log: ./bochs.out
|
||||
# log: /dev/tty
|
||||
#=======================================================================
|
||||
#log: /dev/null
|
||||
log: bochsout.txt
|
||||
|
||||
#=======================================================================
|
||||
# LOGPREFIX:
|
||||
# This handles the format of the string prepended to each log line.
|
||||
# You may use those special tokens :
|
||||
# %t : 11 decimal digits timer tick
|
||||
# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration)
|
||||
# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror)
|
||||
# %d : 5 characters string of the device, between brackets
|
||||
#
|
||||
# Default : %t%e%d
|
||||
# Examples:
|
||||
# logprefix: %t-%e-@%i-%d
|
||||
# logprefix: %i%e%d
|
||||
#=======================================================================
|
||||
#logprefix: %t%e%d
|
||||
|
||||
#=======================================================================
|
||||
# LOG CONTROLS
|
||||
#
|
||||
# Bochs now has four severity levels for event logging.
|
||||
# panic: cannot proceed. If you choose to continue after a panic,
|
||||
# don't be surprised if you get strange behavior or crashes.
|
||||
# error: something went wrong, but it is probably safe to continue the
|
||||
# simulation.
|
||||
# info: interesting or useful messages.
|
||||
# debug: messages useful only when debugging the code. This may
|
||||
# spit out thousands per second.
|
||||
#
|
||||
# For events of each level, you can choose to crash, report, or ignore.
|
||||
# TODO: allow choice based on the facility: e.g. crash on panics from
|
||||
# everything except the cdrom, and only report those.
|
||||
#
|
||||
# If you are experiencing many panics, it can be helpful to change
|
||||
# the panic action to report instead of fatal. However, be aware
|
||||
# that anything executed after a panic is uncharted territory and can
|
||||
# cause bochs to become unstable. The panic is a "graceful exit," so
|
||||
# if you disable it you may get a spectacular disaster instead.
|
||||
#=======================================================================
|
||||
panic: action=ask
|
||||
error: action=report
|
||||
info: action=report
|
||||
debug: action=ignore
|
||||
#pass: action=fatal
|
||||
|
||||
#=======================================================================
|
||||
# DEBUGGER_LOG:
|
||||
# Give the path of the log file you'd like Bochs to log debugger output.
|
||||
# If you really don't want it, make it /dev/null or '-'. :^(
|
||||
#
|
||||
# Examples:
|
||||
# debugger_log: ./debugger.out
|
||||
#=======================================================================
|
||||
#debugger_log: /dev/null
|
||||
#debugger_log: debugger.out
|
||||
debugger_log: -
|
||||
|
||||
#=======================================================================
|
||||
# COM1, COM2, COM3, COM4:
|
||||
# This defines a serial port (UART type 16550A). In the 'term' you can specify
|
||||
# a device to use as com1. This can be a real serial line, or a pty. To use
|
||||
# a pty (under X/Unix), create two windows (xterms, usually). One of them will
|
||||
# run bochs, and the other will act as com1. Find out the tty the com1
|
||||
# window using the `tty' command, and use that as the `dev' parameter.
|
||||
# Then do `sleep 1000000' in the com1 window to keep the shell from
|
||||
# messing with things, and run bochs in the other window. Serial I/O to
|
||||
# com1 (port 0x3f8) will all go to the other window.
|
||||
# Other serial modes are 'null' (no input/output), 'file' (output to a file
|
||||
# specified as the 'dev' parameter), 'raw' (use the real serial port - under
|
||||
# construction for win32), 'mouse' (standard serial mouse - requires
|
||||
# mouse option setting 'type=serial' or 'type=serial_wheel') and 'socket'
|
||||
# (connect a networking socket).
|
||||
#
|
||||
# Examples:
|
||||
# com1: enabled=1, mode=null
|
||||
# com1: enabled=1, mode=mouse
|
||||
# com2: enabled=1, mode=file, dev=serial.out
|
||||
# com3: enabled=1, mode=raw, dev=com1
|
||||
# com3: enabled=1, mode=socket, dev=localhost:8888
|
||||
#=======================================================================
|
||||
#com1: enabled=1, mode=term, dev=/dev/ttyp9
|
||||
|
||||
|
||||
#=======================================================================
|
||||
# PARPORT1, PARPORT2:
|
||||
# This defines a parallel (printer) port. When turned on and an output file is
|
||||
# defined the emulated printer port sends characters printed by the guest OS
|
||||
# into the output file. On some platforms a device filename can be used to
|
||||
# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on
|
||||
# win32 platforms).
|
||||
#
|
||||
# Examples:
|
||||
# parport1: enabled=1, file="parport.out"
|
||||
# parport2: enabled=1, file="/dev/lp0"
|
||||
# parport1: enabled=0
|
||||
#=======================================================================
|
||||
parport1: enabled=1, file="/dev/stdout"
|
||||
|
||||
#=======================================================================
|
||||
# SB16:
|
||||
# This defines the SB16 sound emulation. It can have several of the
|
||||
# following properties.
|
||||
# All properties are in the format sb16: property=value
|
||||
# midi: The filename is where the midi data is sent. This can be a
|
||||
# device or just a file if you want to record the midi data.
|
||||
# midimode:
|
||||
# 0=no data
|
||||
# 1=output to device (system dependent. midi denotes the device driver)
|
||||
# 2=SMF file output, including headers
|
||||
# 3=output the midi data stream to the file (no midi headers and no
|
||||
# delta times, just command and data bytes)
|
||||
# wave: This is the device/file where wave output is stored
|
||||
# wavemode:
|
||||
# 0=no data
|
||||
# 1=output to device (system dependent. wave denotes the device driver)
|
||||
# 2=VOC file output, incl. headers
|
||||
# 3=output the raw wave stream to the file
|
||||
# log: The file to write the sb16 emulator messages to.
|
||||
# loglevel:
|
||||
# 0=no log
|
||||
# 1=resource changes, midi program and bank changes
|
||||
# 2=severe errors
|
||||
# 3=all errors
|
||||
# 4=all errors plus all port accesses
|
||||
# 5=all errors and port accesses plus a lot of extra info
|
||||
# dmatimer:
|
||||
# microseconds per second for a DMA cycle. Make it smaller to fix
|
||||
# non-continuous sound. 750000 is usually a good value. This needs a
|
||||
# reasonably correct setting for the IPS parameter of the CPU option.
|
||||
#
|
||||
# For an example look at the next line:
|
||||
#=======================================================================
|
||||
|
||||
#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000
|
||||
|
||||
#=======================================================================
|
||||
# VGA_UPDATE_INTERVAL:
|
||||
# Video memory is scanned for updates and screen updated every so many
|
||||
# virtual seconds. The default is 40000, about 25Hz. Keep in mind that
|
||||
# you must tweak the 'cpu: ips=N' directive to be as close to the number
|
||||
# of emulated instructions-per-second your workstation can do, for this
|
||||
# to be accurate.
|
||||
#
|
||||
# Examples:
|
||||
# vga_update_interval: 250000
|
||||
#=======================================================================
|
||||
vga_update_interval: 300000
|
||||
|
||||
# using for Winstone '98 tests
|
||||
#vga_update_interval: 100000
|
||||
|
||||
#=======================================================================
|
||||
# KEYBOARD_SERIAL_DELAY:
|
||||
# Approximate time in microseconds that it takes one character to
|
||||
# be transfered from the keyboard to controller over the serial path.
|
||||
# Examples:
|
||||
# keyboard_serial_delay: 200
|
||||
#=======================================================================
|
||||
keyboard_serial_delay: 250
|
||||
|
||||
#=======================================================================
|
||||
# KEYBOARD_PASTE_DELAY:
|
||||
# Approximate time in microseconds between attempts to paste
|
||||
# characters to the keyboard controller. This leaves time for the
|
||||
# guest os to deal with the flow of characters. The ideal setting
|
||||
# depends on how your operating system processes characters. The
|
||||
# default of 100000 usec (.1 seconds) was chosen because it works
|
||||
# consistently in Windows.
|
||||
#
|
||||
# If your OS is losing characters during a paste, increase the paste
|
||||
# delay until it stops losing characters.
|
||||
#
|
||||
# Examples:
|
||||
# keyboard_paste_delay: 100000
|
||||
#=======================================================================
|
||||
keyboard_paste_delay: 100000
|
||||
|
||||
#=======================================================================
|
||||
# MOUSE:
|
||||
# This option prevents Bochs from creating mouse "events" unless a mouse
|
||||
# is enabled. The hardware emulation itself is not disabled by this.
|
||||
# You can turn the mouse on by setting enabled to 1, or turn it off by
|
||||
# setting enabled to 0. Unless you have a particular reason for enabling
|
||||
# the mouse by default, it is recommended that you leave it off.
|
||||
# You can also toggle the mouse usage at runtime (control key + middle
|
||||
# mouse button on X11, SDL, wxWidgets and Win32).
|
||||
# With the mouse type option you can select the type of mouse to emulate.
|
||||
# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse
|
||||
# on PS/2), 'serial', 'serial_wheel' (one com port requires setting
|
||||
# 'mode=mouse') and 'usb' (3-button mouse - one of the USB ports must be
|
||||
# connected with the 'mouse' device - requires PCI and USB support).
|
||||
#
|
||||
# Examples:
|
||||
# mouse: enabled=1
|
||||
# mouse: enabled=1, type=imps2
|
||||
# mouse: enabled=1, type=serial
|
||||
# mouse: enabled=0
|
||||
#=======================================================================
|
||||
mouse: enabled=0
|
||||
|
||||
#=======================================================================
|
||||
# private_colormap: Request that the GUI create and use it's own
|
||||
# non-shared colormap. This colormap will be used
|
||||
# when in the bochs window. If not enabled, a
|
||||
# shared colormap scheme may be used. Not implemented
|
||||
# on all GUI's.
|
||||
#
|
||||
# Examples:
|
||||
# private_colormap: enabled=1
|
||||
# private_colormap: enabled=0
|
||||
#=======================================================================
|
||||
private_colormap: enabled=0
|
||||
|
||||
#=======================================================================
|
||||
# fullscreen: ONLY IMPLEMENTED ON AMIGA
|
||||
# Request that Bochs occupy the entire screen instead of a
|
||||
# window.
|
||||
#
|
||||
# Examples:
|
||||
# fullscreen: enabled=0
|
||||
# fullscreen: enabled=1
|
||||
#=======================================================================
|
||||
#fullscreen: enabled=0
|
||||
#screenmode: name="sample"
|
||||
|
||||
#=======================================================================
|
||||
# ne2k: NE2000 compatible ethernet adapter
|
||||
#
|
||||
# Examples:
|
||||
# ne2k: ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT
|
||||
#
|
||||
# ioaddr, irq: You probably won't need to change ioaddr and irq, unless there
|
||||
# are IRQ conflicts.
|
||||
#
|
||||
# mac: The MAC address MUST NOT match the address of any machine on the net.
|
||||
# Also, the first byte must be an even number (bit 0 set means a multicast
|
||||
# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast
|
||||
# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may
|
||||
# be other restrictions too. To be safe, just use the b0:c4... address.
|
||||
#
|
||||
# ethdev: The ethdev value is the name of the network interface on your host
|
||||
# platform. On UNIX machines, you can get the name by running ifconfig. On
|
||||
# Windows machines, you must run niclist to get the name of the ethdev.
|
||||
# Niclist source code is in misc/niclist.c and it is included in Windows
|
||||
# binary releases.
|
||||
#
|
||||
# script: The script value is optional, and is the name of a script that
|
||||
# is executed after bochs initialize the network interface. You can use
|
||||
# this script to configure this network interface, or enable masquerading.
|
||||
# This is mainly useful for the tun/tap devices that only exist during
|
||||
# Bochs execution. The network interface name is supplied to the script
|
||||
# as first parameter
|
||||
#
|
||||
# If you don't want to make connections to any physical networks,
|
||||
# you can use the following 'ethmod's to simulate a virtual network.
|
||||
# null: All packets are discarded, but logged to a few files.
|
||||
# arpback: ARP is simulated. Disabled by default.
|
||||
# vde: Virtual Distributed Ethernet
|
||||
# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated.
|
||||
# The virtual host uses 192.168.10.1.
|
||||
# DHCP assigns 192.168.10.2 to the guest.
|
||||
# TFTP uses the ethdev value for the root directory and doesn't
|
||||
# overwrite files.
|
||||
#
|
||||
#=======================================================================
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl"
|
||||
# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp"
|
||||
|
||||
#=======================================================================
|
||||
# KEYBOARD_MAPPING:
|
||||
# This enables a remap of a physical localized keyboard to a
|
||||
# virtualized us keyboard, as the PC architecture expects.
|
||||
# If enabled, the keymap file must be specified.
|
||||
#
|
||||
# Examples:
|
||||
# keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-de.map
|
||||
#=======================================================================
|
||||
keyboard_mapping: enabled=0, map=
|
||||
|
||||
#=======================================================================
|
||||
# KEYBOARD_TYPE:
|
||||
# Type of keyboard return by a "identify keyboard" command to the
|
||||
# keyboard controler. It must be one of "xt", "at" or "mf".
|
||||
# Defaults to "mf". It should be ok for almost everybody. A known
|
||||
# exception is french macs, that do have a "at"-like keyboard.
|
||||
#
|
||||
# Examples:
|
||||
# keyboard_type: mf
|
||||
#=======================================================================
|
||||
#keyboard_type: mf
|
||||
|
||||
#=======================================================================
|
||||
# USER_SHORTCUT:
|
||||
# This defines the keyboard shortcut to be sent when you press the "user"
|
||||
# button in the headerbar. The shortcut string is a combination of maximum
|
||||
# 3 key names (listed below) separated with a '-' character. The old-style
|
||||
# syntax (without the '-') still works for the key combinations supported
|
||||
# in Bochs 2.2.1.
|
||||
# Valid key names:
|
||||
# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc",
|
||||
# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup",
|
||||
# "plus", "right", "shift", "space", "tab", "up", and "win".
|
||||
#
|
||||
# Example:
|
||||
# user_shortcut: keys=ctrl-alt-del
|
||||
#=======================================================================
|
||||
#user_shortcut: keys=ctrl-alt-del
|
||||
|
||||
#=======================================================================
|
||||
# I440FXSUPPORT:
|
||||
# This option controls the presence of the i440FX PCI chipset. You can
|
||||
# also specify the devices connected to PCI slots. Up to 5 slots are
|
||||
# available now. These devices are currently supported: ne2k, pcivga,
|
||||
# pcidev and pcipnic. If Bochs is compiled with Cirrus SVGA support
|
||||
# you'll have the additional choice 'cirrus'.
|
||||
#
|
||||
# Example:
|
||||
# i440fxsupport: enabled=1, slot1=pcivga, slot2=ne2k
|
||||
#=======================================================================
|
||||
#i440fxsupport: enabled=1
|
||||
|
||||
#=======================================================================
|
||||
# USB1:
|
||||
# This option controls the presence of the USB root hub which is a part
|
||||
# of the i440FX PCI chipset. With the portX option you can connect devices
|
||||
# to the hub (currently supported: 'mouse' and 'keypad'). If you connect
|
||||
# the mouse to one of the ports and use the mouse option 'type=usb' you'll
|
||||
# have a 3-button USB mouse.
|
||||
#
|
||||
# Example:
|
||||
# usb1: enabled=1, port1=mouse, port2=keypad
|
||||
#=======================================================================
|
||||
#usb1: enabled=1
|
||||
|
||||
#=======================================================================
|
||||
# CMOSIMAGE:
|
||||
# This defines image file that can be loaded into the CMOS RAM at startup.
|
||||
# The rtc_init parameter controls whether initialize the RTC with values stored
|
||||
# in the image. By default the time0 argument given to the clock option is used.
|
||||
# With 'rtc_init=image' the image is the source for the initial time.
|
||||
#
|
||||
# Example:
|
||||
# cmosimage: file=cmos.img, rtc_init=image
|
||||
#=======================================================================
|
||||
#cmosimage: file=cmos.img, rtc_init=time0
|
||||
|
||||
#=======================================================================
|
||||
# other stuff
|
||||
#=======================================================================
|
||||
#magic_break: enabled=1
|
||||
#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
|
||||
#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
|
||||
#text_snapshot_check: enable
|
||||
|
||||
#-------------------------
|
||||
# PCI host device mapping
|
||||
#-------------------------
|
||||
#pcidev: vendor=0x1234, device=0x5678
|
||||
|
||||
#=======================================================================
|
||||
# GDBSTUB:
|
||||
# Enable GDB stub. See user documentation for details.
|
||||
# Default value is enabled=0.
|
||||
#=======================================================================
|
||||
#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0
|
||||
|
||||
#=======================================================================
|
||||
# IPS:
|
||||
# The IPS directive is DEPRECATED. Use the parameter IPS of the CPU
|
||||
# directive instead.
|
||||
#=======================================================================
|
||||
#ips: 10000000
|
||||
|
||||
#=======================================================================
|
||||
# for Macintosh, use the style of pathnames in the following
|
||||
# examples.
|
||||
#
|
||||
# vgaromimage: :bios:VGABIOS-elpin-2.40
|
||||
# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000
|
||||
# floppya: 1_44=[fd:], status=inserted
|
||||
#=======================================================================
|
||||
13
xv6-public/echo.c
Normal file
13
xv6-public/echo.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 1; i < argc; i++)
|
||||
printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n");
|
||||
exit();
|
||||
}
|
||||
42
xv6-public/elf.h
Normal file
42
xv6-public/elf.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Format of an ELF executable file
|
||||
|
||||
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
|
||||
|
||||
// File header
|
||||
struct elfhdr {
|
||||
uint magic; // must equal ELF_MAGIC
|
||||
uchar elf[12];
|
||||
ushort type;
|
||||
ushort machine;
|
||||
uint version;
|
||||
uint entry;
|
||||
uint phoff;
|
||||
uint shoff;
|
||||
uint flags;
|
||||
ushort ehsize;
|
||||
ushort phentsize;
|
||||
ushort phnum;
|
||||
ushort shentsize;
|
||||
ushort shnum;
|
||||
ushort shstrndx;
|
||||
};
|
||||
|
||||
// Program section header
|
||||
struct proghdr {
|
||||
uint type;
|
||||
uint off;
|
||||
uint vaddr;
|
||||
uint paddr;
|
||||
uint filesz;
|
||||
uint memsz;
|
||||
uint flags;
|
||||
uint align;
|
||||
};
|
||||
|
||||
// Values for Proghdr type
|
||||
#define ELF_PROG_LOAD 1
|
||||
|
||||
// Flag bits for Proghdr flags
|
||||
#define ELF_PROG_FLAG_EXEC 1
|
||||
#define ELF_PROG_FLAG_WRITE 2
|
||||
#define ELF_PROG_FLAG_READ 4
|
||||
68
xv6-public/entry.S
Normal file
68
xv6-public/entry.S
Normal file
@@ -0,0 +1,68 @@
|
||||
# The xv6 kernel starts executing in this file. This file is linked with
|
||||
# the kernel C code, so it can refer to kernel symbols such as main().
|
||||
# The boot block (bootasm.S and bootmain.c) jumps to entry below.
|
||||
|
||||
# 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"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "param.h"
|
||||
|
||||
# Multiboot header. Data to direct multiboot loader.
|
||||
.p2align 2
|
||||
.text
|
||||
.globl multiboot_header
|
||||
multiboot_header:
|
||||
#define magic 0x1badb002
|
||||
#define flags 0
|
||||
.long magic
|
||||
.long flags
|
||||
.long (-magic-flags)
|
||||
|
||||
# By convention, the _start symbol specifies the ELF entry point.
|
||||
# Since we haven't set up virtual memory yet, our entry point is
|
||||
# the physical address of 'entry'.
|
||||
.globl _start
|
||||
_start = V2P_WO(entry)
|
||||
|
||||
# Entering xv6 on boot processor, with paging off.
|
||||
.globl entry
|
||||
entry:
|
||||
# Turn on page size extension for 4Mbyte pages
|
||||
movl %cr4, %eax
|
||||
orl $(CR4_PSE), %eax
|
||||
movl %eax, %cr4
|
||||
# Set page directory
|
||||
movl $(V2P_WO(entrypgdir)), %eax
|
||||
movl %eax, %cr3
|
||||
# Turn on paging.
|
||||
movl %cr0, %eax
|
||||
orl $(CR0_PG|CR0_WP), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Set up the stack pointer.
|
||||
movl $(stack + KSTACKSIZE), %esp
|
||||
|
||||
# Jump to main(), and switch to executing at
|
||||
# high addresses. The indirect call is needed because
|
||||
# the assembler produces a PC-relative instruction
|
||||
# for a direct jump.
|
||||
mov $main, %eax
|
||||
jmp *%eax
|
||||
|
||||
.comm stack, KSTACKSIZE
|
||||
93
xv6-public/entryother.S
Normal file
93
xv6-public/entryother.S
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "asm.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
|
||||
# Each non-boot CPU ("AP") is started up in response to a STARTUP
|
||||
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
|
||||
# Specification says that the AP will start in real mode with CS:IP
|
||||
# set to XY00:0000, where XY is an 8-bit value sent with the
|
||||
# STARTUP. Thus this code must start at a 4096-byte boundary.
|
||||
#
|
||||
# Because this code sets DS to zero, it must sit
|
||||
# at an address in the low 2^16 bytes.
|
||||
#
|
||||
# Startothers (in main.c) sends the STARTUPs one at a time.
|
||||
# It copies this code (start) at 0x7000. It puts the address of
|
||||
# a newly allocated per-core stack in start-4,the address of the
|
||||
# place to jump to (mpenter) in start-8, and the physical address
|
||||
# of entrypgdir in start-12.
|
||||
#
|
||||
# This code combines elements of bootasm.S and entry.S.
|
||||
|
||||
.code16
|
||||
.globl start
|
||||
start:
|
||||
cli
|
||||
|
||||
# Zero data segment registers DS, ES, and SS.
|
||||
xorw %ax,%ax
|
||||
movw %ax,%ds
|
||||
movw %ax,%es
|
||||
movw %ax,%ss
|
||||
|
||||
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||
# virtual addresses map directly to physical addresses so that the
|
||||
# effective memory map doesn't change during the transition.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||
# translation, so that the mapping is still the identity mapping.
|
||||
ljmpl $(SEG_KCODE<<3), $(start32)
|
||||
|
||||
//PAGEBREAK!
|
||||
.code32 # Tell assembler to generate 32-bit code now.
|
||||
start32:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||
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
|
||||
|
||||
# Turn on page size extension for 4Mbyte pages
|
||||
movl %cr4, %eax
|
||||
orl $(CR4_PSE), %eax
|
||||
movl %eax, %cr4
|
||||
# Use entrypgdir as our initial page table
|
||||
movl (start-12), %eax
|
||||
movl %eax, %cr3
|
||||
# Turn on paging.
|
||||
movl %cr0, %eax
|
||||
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Switch to the stack allocated by startothers()
|
||||
movl (start-4), %esp
|
||||
# Call mpenter()
|
||||
call *(start-8)
|
||||
|
||||
movw $0x8a00, %ax
|
||||
movw %ax, %dx
|
||||
outw %ax, %dx
|
||||
movw $0x8ae0, %ax
|
||||
outw %ax, %dx
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
.p2align 2
|
||||
gdt:
|
||||
SEG_NULLASM
|
||||
SEG_ASM(STA_X|STA_R, 0, 0xffffffff)
|
||||
SEG_ASM(STA_W, 0, 0xffffffff)
|
||||
|
||||
|
||||
gdtdesc:
|
||||
.word (gdtdesc - gdt - 1)
|
||||
.long gdt
|
||||
|
||||
114
xv6-public/exec.c
Normal file
114
xv6-public/exec.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "defs.h"
|
||||
#include "x86.h"
|
||||
#include "elf.h"
|
||||
|
||||
int
|
||||
exec(char *path, char **argv)
|
||||
{
|
||||
char *s, *last;
|
||||
int i, off;
|
||||
uint argc, sz, sp, ustack[3+MAXARG+1];
|
||||
struct elfhdr elf;
|
||||
struct inode *ip;
|
||||
struct proghdr ph;
|
||||
pde_t *pgdir, *oldpgdir;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
begin_op();
|
||||
|
||||
if((ip = namei(path)) == 0){
|
||||
end_op();
|
||||
cprintf("exec: fail\n");
|
||||
return -1;
|
||||
}
|
||||
ilock(ip);
|
||||
pgdir = 0;
|
||||
|
||||
// Check ELF header
|
||||
if(readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf))
|
||||
goto bad;
|
||||
if(elf.magic != ELF_MAGIC)
|
||||
goto bad;
|
||||
|
||||
if((pgdir = setupkvm()) == 0)
|
||||
goto bad;
|
||||
|
||||
// Load program into memory.
|
||||
sz = 0;
|
||||
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
|
||||
if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
|
||||
goto bad;
|
||||
if(ph.type != ELF_PROG_LOAD)
|
||||
continue;
|
||||
if(ph.memsz < ph.filesz)
|
||||
goto bad;
|
||||
if(ph.vaddr + ph.memsz < ph.vaddr)
|
||||
goto bad;
|
||||
if((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0)
|
||||
goto bad;
|
||||
if(ph.vaddr % PGSIZE != 0)
|
||||
goto bad;
|
||||
if(loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0)
|
||||
goto bad;
|
||||
}
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
ip = 0;
|
||||
|
||||
// Allocate two pages at the next page boundary.
|
||||
// Make the first inaccessible. Use the second as the user stack.
|
||||
sz = PGROUNDUP(sz);
|
||||
if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0)
|
||||
goto bad;
|
||||
clearpteu(pgdir, (char*)(sz - 2*PGSIZE));
|
||||
sp = sz;
|
||||
|
||||
// Push argument strings, prepare rest of stack in ustack.
|
||||
for(argc = 0; argv[argc]; argc++) {
|
||||
if(argc >= MAXARG)
|
||||
goto bad;
|
||||
sp = (sp - (strlen(argv[argc]) + 1)) & ~3;
|
||||
if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
|
||||
goto bad;
|
||||
ustack[3+argc] = sp;
|
||||
}
|
||||
ustack[3+argc] = 0;
|
||||
|
||||
ustack[0] = 0xffffffff; // fake return PC
|
||||
ustack[1] = argc;
|
||||
ustack[2] = sp - (argc+1)*4; // argv pointer
|
||||
|
||||
sp -= (3+argc+1) * 4;
|
||||
if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
|
||||
goto bad;
|
||||
|
||||
// Save program name for debugging.
|
||||
for(last=s=path; *s; s++)
|
||||
if(*s == '/')
|
||||
last = s+1;
|
||||
safestrcpy(curproc->name, last, sizeof(curproc->name));
|
||||
|
||||
// Commit to the user image.
|
||||
oldpgdir = curproc->pgdir;
|
||||
curproc->pgdir = pgdir;
|
||||
curproc->sz = sz;
|
||||
curproc->tf->eip = elf.entry; // main
|
||||
curproc->tf->esp = sp;
|
||||
switchuvm(curproc);
|
||||
freevm(oldpgdir);
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
if(pgdir)
|
||||
freevm(pgdir);
|
||||
if(ip){
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
4
xv6-public/fcntl.h
Normal file
4
xv6-public/fcntl.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#define O_RDONLY 0x000
|
||||
#define O_WRONLY 0x001
|
||||
#define O_RDWR 0x002
|
||||
#define O_CREATE 0x200
|
||||
157
xv6-public/file.c
Normal file
157
xv6-public/file.c
Normal file
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// File descriptors
|
||||
//
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "fs.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "file.h"
|
||||
|
||||
struct devsw devsw[NDEV];
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct file file[NFILE];
|
||||
} ftable;
|
||||
|
||||
void
|
||||
fileinit(void)
|
||||
{
|
||||
initlock(&ftable.lock, "ftable");
|
||||
}
|
||||
|
||||
// Allocate a file structure.
|
||||
struct file*
|
||||
filealloc(void)
|
||||
{
|
||||
struct file *f;
|
||||
|
||||
acquire(&ftable.lock);
|
||||
for(f = ftable.file; f < ftable.file + NFILE; f++){
|
||||
if(f->ref == 0){
|
||||
f->ref = 1;
|
||||
release(&ftable.lock);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
release(&ftable.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Increment ref count for file f.
|
||||
struct file*
|
||||
filedup(struct file *f)
|
||||
{
|
||||
acquire(&ftable.lock);
|
||||
if(f->ref < 1)
|
||||
panic("filedup");
|
||||
f->ref++;
|
||||
release(&ftable.lock);
|
||||
return f;
|
||||
}
|
||||
|
||||
// Close file f. (Decrement ref count, close when reaches 0.)
|
||||
void
|
||||
fileclose(struct file *f)
|
||||
{
|
||||
struct file ff;
|
||||
|
||||
acquire(&ftable.lock);
|
||||
if(f->ref < 1)
|
||||
panic("fileclose");
|
||||
if(--f->ref > 0){
|
||||
release(&ftable.lock);
|
||||
return;
|
||||
}
|
||||
ff = *f;
|
||||
f->ref = 0;
|
||||
f->type = FD_NONE;
|
||||
release(&ftable.lock);
|
||||
|
||||
if(ff.type == FD_PIPE)
|
||||
pipeclose(ff.pipe, ff.writable);
|
||||
else if(ff.type == FD_INODE){
|
||||
begin_op();
|
||||
iput(ff.ip);
|
||||
end_op();
|
||||
}
|
||||
}
|
||||
|
||||
// Get metadata about file f.
|
||||
int
|
||||
filestat(struct file *f, struct stat *st)
|
||||
{
|
||||
if(f->type == FD_INODE){
|
||||
ilock(f->ip);
|
||||
stati(f->ip, st);
|
||||
iunlock(f->ip);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read from file f.
|
||||
int
|
||||
fileread(struct file *f, char *addr, int n)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(f->readable == 0)
|
||||
return -1;
|
||||
if(f->type == FD_PIPE)
|
||||
return piperead(f->pipe, addr, n);
|
||||
if(f->type == FD_INODE){
|
||||
ilock(f->ip);
|
||||
if((r = readi(f->ip, addr, f->off, n)) > 0)
|
||||
f->off += r;
|
||||
iunlock(f->ip);
|
||||
return r;
|
||||
}
|
||||
panic("fileread");
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Write to file f.
|
||||
int
|
||||
filewrite(struct file *f, char *addr, int n)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(f->writable == 0)
|
||||
return -1;
|
||||
if(f->type == FD_PIPE)
|
||||
return pipewrite(f->pipe, addr, n);
|
||||
if(f->type == FD_INODE){
|
||||
// write a few blocks at a time to avoid exceeding
|
||||
// the maximum log transaction size, including
|
||||
// i-node, indirect block, allocation blocks,
|
||||
// and 2 blocks of slop for non-aligned writes.
|
||||
// this really belongs lower down, since writei()
|
||||
// might be writing a device like the console.
|
||||
int max = ((MAXOPBLOCKS-1-1-2) / 2) * 512;
|
||||
int i = 0;
|
||||
while(i < n){
|
||||
int n1 = n - i;
|
||||
if(n1 > max)
|
||||
n1 = max;
|
||||
|
||||
begin_op();
|
||||
ilock(f->ip);
|
||||
if ((r = writei(f->ip, addr + i, f->off, n1)) > 0)
|
||||
f->off += r;
|
||||
iunlock(f->ip);
|
||||
end_op();
|
||||
|
||||
if(r < 0)
|
||||
break;
|
||||
if(r != n1)
|
||||
panic("short filewrite");
|
||||
i += r;
|
||||
}
|
||||
return i == n ? n : -1;
|
||||
}
|
||||
panic("filewrite");
|
||||
}
|
||||
|
||||
37
xv6-public/file.h
Normal file
37
xv6-public/file.h
Normal file
@@ -0,0 +1,37 @@
|
||||
struct file {
|
||||
enum { FD_NONE, FD_PIPE, FD_INODE } type;
|
||||
int ref; // reference count
|
||||
char readable;
|
||||
char writable;
|
||||
struct pipe *pipe;
|
||||
struct inode *ip;
|
||||
uint off;
|
||||
};
|
||||
|
||||
|
||||
// in-memory copy of an inode
|
||||
struct inode {
|
||||
uint dev; // Device number
|
||||
uint inum; // Inode number
|
||||
int ref; // Reference count
|
||||
struct sleeplock lock; // protects everything below here
|
||||
int valid; // inode has been read from disk?
|
||||
|
||||
short type; // copy of disk inode
|
||||
short major;
|
||||
short minor;
|
||||
short nlink;
|
||||
uint size;
|
||||
uint addrs[NDIRECT+1];
|
||||
};
|
||||
|
||||
// table mapping major device number to
|
||||
// device functions
|
||||
struct devsw {
|
||||
int (*read)(struct inode*, char*, int);
|
||||
int (*write)(struct inode*, char*, int);
|
||||
};
|
||||
|
||||
extern struct devsw devsw[];
|
||||
|
||||
#define CONSOLE 1
|
||||
56
xv6-public/forktest.c
Normal file
56
xv6-public/forktest.c
Normal file
@@ -0,0 +1,56 @@
|
||||
// Test that fork fails gracefully.
|
||||
// Tiny executable so that the limit can be filling the proc table.
|
||||
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
#define N 1000
|
||||
|
||||
void
|
||||
printf(int fd, const char *s, ...)
|
||||
{
|
||||
write(fd, s, strlen(s));
|
||||
}
|
||||
|
||||
void
|
||||
forktest(void)
|
||||
{
|
||||
int n, pid;
|
||||
|
||||
printf(1, "fork test\n");
|
||||
|
||||
for(n=0; n<N; n++){
|
||||
pid = fork();
|
||||
if(pid < 0)
|
||||
break;
|
||||
if(pid == 0)
|
||||
exit();
|
||||
}
|
||||
|
||||
if(n == N){
|
||||
printf(1, "fork claimed to work N times!\n", N);
|
||||
exit();
|
||||
}
|
||||
|
||||
for(; n > 0; n--){
|
||||
if(wait() < 0){
|
||||
printf(1, "wait stopped early\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if(wait() != -1){
|
||||
printf(1, "wait got too many\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
printf(1, "fork test OK\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
forktest();
|
||||
exit();
|
||||
}
|
||||
671
xv6-public/fs.c
Normal file
671
xv6-public/fs.c
Normal file
@@ -0,0 +1,671 @@
|
||||
// File system implementation. Five layers:
|
||||
// + Blocks: allocator for raw disk blocks.
|
||||
// + Log: crash recovery for multi-step updates.
|
||||
// + Files: inode allocator, reading, writing, metadata.
|
||||
// + Directories: inode with special contents (list of other inodes!)
|
||||
// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
|
||||
//
|
||||
// This file contains the low-level file system manipulation
|
||||
// routines. The (higher-level) system call implementations
|
||||
// are in sysfile.c.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "stat.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "buf.h"
|
||||
#include "file.h"
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
static void itrunc(struct inode*);
|
||||
// there should be one superblock per disk device, but we run with
|
||||
// only one device
|
||||
struct superblock sb;
|
||||
|
||||
// Read the super block.
|
||||
void
|
||||
readsb(int dev, struct superblock *sb)
|
||||
{
|
||||
struct buf *bp;
|
||||
|
||||
bp = bread(dev, 1);
|
||||
memmove(sb, bp->data, sizeof(*sb));
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
// Zero a block.
|
||||
static void
|
||||
bzero(int dev, int bno)
|
||||
{
|
||||
struct buf *bp;
|
||||
|
||||
bp = bread(dev, bno);
|
||||
memset(bp->data, 0, BSIZE);
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
// Blocks.
|
||||
|
||||
// Allocate a zeroed disk block.
|
||||
static uint
|
||||
balloc(uint dev)
|
||||
{
|
||||
int b, bi, m;
|
||||
struct buf *bp;
|
||||
|
||||
bp = 0;
|
||||
for(b = 0; b < sb.size; b += BPB){
|
||||
bp = bread(dev, BBLOCK(b, sb));
|
||||
for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
|
||||
m = 1 << (bi % 8);
|
||||
if((bp->data[bi/8] & m) == 0){ // Is block free?
|
||||
bp->data[bi/8] |= m; // Mark block in use.
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
bzero(dev, b + bi);
|
||||
return b + bi;
|
||||
}
|
||||
}
|
||||
brelse(bp);
|
||||
}
|
||||
panic("balloc: out of blocks");
|
||||
}
|
||||
|
||||
// Free a disk block.
|
||||
static void
|
||||
bfree(int dev, uint b)
|
||||
{
|
||||
struct buf *bp;
|
||||
int bi, m;
|
||||
|
||||
readsb(dev, &sb);
|
||||
bp = bread(dev, BBLOCK(b, sb));
|
||||
bi = b % BPB;
|
||||
m = 1 << (bi % 8);
|
||||
if((bp->data[bi/8] & m) == 0)
|
||||
panic("freeing free block");
|
||||
bp->data[bi/8] &= ~m;
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
// Inodes.
|
||||
//
|
||||
// An inode describes a single unnamed file.
|
||||
// The inode disk structure holds metadata: the file's type,
|
||||
// its size, the number of links referring to it, and the
|
||||
// list of blocks holding the file's content.
|
||||
//
|
||||
// The inodes are laid out sequentially on disk at
|
||||
// sb.startinode. Each inode has a number, indicating its
|
||||
// position on the disk.
|
||||
//
|
||||
// The kernel keeps a cache of in-use inodes in memory
|
||||
// to provide a place for synchronizing access
|
||||
// to inodes used by multiple processes. The cached
|
||||
// inodes include book-keeping information that is
|
||||
// not stored on disk: ip->ref and ip->valid.
|
||||
//
|
||||
// An inode and its in-memory representation go through a
|
||||
// sequence of states before they can be used by the
|
||||
// rest of the file system code.
|
||||
//
|
||||
// * Allocation: an inode is allocated if its type (on disk)
|
||||
// is non-zero. ialloc() allocates, and iput() frees if
|
||||
// the reference and link counts have fallen to zero.
|
||||
//
|
||||
// * Referencing in cache: an entry in the inode cache
|
||||
// is free if ip->ref is zero. Otherwise ip->ref tracks
|
||||
// the number of in-memory pointers to the entry (open
|
||||
// files and current directories). iget() finds or
|
||||
// creates a cache entry and increments its ref; iput()
|
||||
// decrements ref.
|
||||
//
|
||||
// * Valid: the information (type, size, &c) in an inode
|
||||
// cache entry is only correct when ip->valid is 1.
|
||||
// ilock() reads the inode from
|
||||
// the disk and sets ip->valid, while iput() clears
|
||||
// ip->valid if ip->ref has fallen to zero.
|
||||
//
|
||||
// * Locked: file system code may only examine and modify
|
||||
// the information in an inode and its content if it
|
||||
// has first locked the inode.
|
||||
//
|
||||
// Thus a typical sequence is:
|
||||
// ip = iget(dev, inum)
|
||||
// ilock(ip)
|
||||
// ... examine and modify ip->xxx ...
|
||||
// iunlock(ip)
|
||||
// iput(ip)
|
||||
//
|
||||
// ilock() is separate from iget() so that system calls can
|
||||
// get a long-term reference to an inode (as for an open file)
|
||||
// and only lock it for short periods (e.g., in read()).
|
||||
// The separation also helps avoid deadlock and races during
|
||||
// pathname lookup. iget() increments ip->ref so that the inode
|
||||
// stays cached and pointers to it remain valid.
|
||||
//
|
||||
// Many internal file system functions expect the caller to
|
||||
// have locked the inodes involved; this lets callers create
|
||||
// multi-step atomic operations.
|
||||
//
|
||||
// The icache.lock spin-lock protects the allocation of icache
|
||||
// entries. Since ip->ref indicates whether an entry is free,
|
||||
// and ip->dev and ip->inum indicate which i-node an entry
|
||||
// holds, one must hold icache.lock while using any of those fields.
|
||||
//
|
||||
// An ip->lock sleep-lock protects all ip-> fields other than ref,
|
||||
// dev, and inum. One must hold ip->lock in order to
|
||||
// read or write that inode's ip->valid, ip->size, ip->type, &c.
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct inode inode[NINODE];
|
||||
} icache;
|
||||
|
||||
void
|
||||
iinit(int dev)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
initlock(&icache.lock, "icache");
|
||||
for(i = 0; i < NINODE; i++) {
|
||||
initsleeplock(&icache.inode[i].lock, "inode");
|
||||
}
|
||||
|
||||
readsb(dev, &sb);
|
||||
cprintf("sb: size %d nblocks %d ninodes %d nlog %d logstart %d\
|
||||
inodestart %d bmap start %d\n", sb.size, sb.nblocks,
|
||||
sb.ninodes, sb.nlog, sb.logstart, sb.inodestart,
|
||||
sb.bmapstart);
|
||||
}
|
||||
|
||||
static struct inode* iget(uint dev, uint inum);
|
||||
|
||||
//PAGEBREAK!
|
||||
// Allocate an inode on device dev.
|
||||
// Mark it as allocated by giving it type type.
|
||||
// Returns an unlocked but allocated and referenced inode.
|
||||
struct inode*
|
||||
ialloc(uint dev, short type)
|
||||
{
|
||||
int inum;
|
||||
struct buf *bp;
|
||||
struct dinode *dip;
|
||||
|
||||
for(inum = 1; inum < sb.ninodes; inum++){
|
||||
bp = bread(dev, IBLOCK(inum, sb));
|
||||
dip = (struct dinode*)bp->data + inum%IPB;
|
||||
if(dip->type == 0){ // a free inode
|
||||
memset(dip, 0, sizeof(*dip));
|
||||
dip->type = type;
|
||||
log_write(bp); // mark it allocated on the disk
|
||||
brelse(bp);
|
||||
return iget(dev, inum);
|
||||
}
|
||||
brelse(bp);
|
||||
}
|
||||
panic("ialloc: no inodes");
|
||||
}
|
||||
|
||||
// Copy a modified in-memory inode to disk.
|
||||
// Must be called after every change to an ip->xxx field
|
||||
// that lives on disk, since i-node cache is write-through.
|
||||
// Caller must hold ip->lock.
|
||||
void
|
||||
iupdate(struct inode *ip)
|
||||
{
|
||||
struct buf *bp;
|
||||
struct dinode *dip;
|
||||
|
||||
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
|
||||
dip = (struct dinode*)bp->data + ip->inum%IPB;
|
||||
dip->type = ip->type;
|
||||
dip->major = ip->major;
|
||||
dip->minor = ip->minor;
|
||||
dip->nlink = ip->nlink;
|
||||
dip->size = ip->size;
|
||||
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
// Find the inode with number inum on device dev
|
||||
// and return the in-memory copy. Does not lock
|
||||
// the inode and does not read it from disk.
|
||||
static struct inode*
|
||||
iget(uint dev, uint inum)
|
||||
{
|
||||
struct inode *ip, *empty;
|
||||
|
||||
acquire(&icache.lock);
|
||||
|
||||
// Is the inode already cached?
|
||||
empty = 0;
|
||||
for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){
|
||||
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
|
||||
ip->ref++;
|
||||
release(&icache.lock);
|
||||
return ip;
|
||||
}
|
||||
if(empty == 0 && ip->ref == 0) // Remember empty slot.
|
||||
empty = ip;
|
||||
}
|
||||
|
||||
// Recycle an inode cache entry.
|
||||
if(empty == 0)
|
||||
panic("iget: no inodes");
|
||||
|
||||
ip = empty;
|
||||
ip->dev = dev;
|
||||
ip->inum = inum;
|
||||
ip->ref = 1;
|
||||
ip->valid = 0;
|
||||
release(&icache.lock);
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
// Increment reference count for ip.
|
||||
// Returns ip to enable ip = idup(ip1) idiom.
|
||||
struct inode*
|
||||
idup(struct inode *ip)
|
||||
{
|
||||
acquire(&icache.lock);
|
||||
ip->ref++;
|
||||
release(&icache.lock);
|
||||
return ip;
|
||||
}
|
||||
|
||||
// Lock the given inode.
|
||||
// Reads the inode from disk if necessary.
|
||||
void
|
||||
ilock(struct inode *ip)
|
||||
{
|
||||
struct buf *bp;
|
||||
struct dinode *dip;
|
||||
|
||||
if(ip == 0 || ip->ref < 1)
|
||||
panic("ilock");
|
||||
|
||||
acquiresleep(&ip->lock);
|
||||
|
||||
if(ip->valid == 0){
|
||||
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
|
||||
dip = (struct dinode*)bp->data + ip->inum%IPB;
|
||||
ip->type = dip->type;
|
||||
ip->major = dip->major;
|
||||
ip->minor = dip->minor;
|
||||
ip->nlink = dip->nlink;
|
||||
ip->size = dip->size;
|
||||
memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
|
||||
brelse(bp);
|
||||
ip->valid = 1;
|
||||
if(ip->type == 0)
|
||||
panic("ilock: no type");
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the given inode.
|
||||
void
|
||||
iunlock(struct inode *ip)
|
||||
{
|
||||
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
|
||||
panic("iunlock");
|
||||
|
||||
releasesleep(&ip->lock);
|
||||
}
|
||||
|
||||
// Drop a reference to an in-memory inode.
|
||||
// If that was the last reference, the inode cache entry can
|
||||
// be recycled.
|
||||
// If that was the last reference and the inode has no links
|
||||
// to it, free the inode (and its content) on disk.
|
||||
// All calls to iput() must be inside a transaction in
|
||||
// case it has to free the inode.
|
||||
void
|
||||
iput(struct inode *ip)
|
||||
{
|
||||
acquiresleep(&ip->lock);
|
||||
if(ip->valid && ip->nlink == 0){
|
||||
acquire(&icache.lock);
|
||||
int r = ip->ref;
|
||||
release(&icache.lock);
|
||||
if(r == 1){
|
||||
// inode has no links and no other references: truncate and free.
|
||||
itrunc(ip);
|
||||
ip->type = 0;
|
||||
iupdate(ip);
|
||||
ip->valid = 0;
|
||||
}
|
||||
}
|
||||
releasesleep(&ip->lock);
|
||||
|
||||
acquire(&icache.lock);
|
||||
ip->ref--;
|
||||
release(&icache.lock);
|
||||
}
|
||||
|
||||
// Common idiom: unlock, then put.
|
||||
void
|
||||
iunlockput(struct inode *ip)
|
||||
{
|
||||
iunlock(ip);
|
||||
iput(ip);
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Inode content
|
||||
//
|
||||
// The content (data) associated with each inode is stored
|
||||
// in blocks on the disk. The first NDIRECT block numbers
|
||||
// are listed in ip->addrs[]. The next NINDIRECT blocks are
|
||||
// listed in block ip->addrs[NDIRECT].
|
||||
|
||||
// Return the disk block address of the nth block in inode ip.
|
||||
// If there is no such block, bmap allocates one.
|
||||
static uint
|
||||
bmap(struct inode *ip, uint bn)
|
||||
{
|
||||
uint addr, *a;
|
||||
struct buf *bp;
|
||||
|
||||
if(bn < NDIRECT){
|
||||
if((addr = ip->addrs[bn]) == 0)
|
||||
ip->addrs[bn] = addr = balloc(ip->dev);
|
||||
return addr;
|
||||
}
|
||||
bn -= NDIRECT;
|
||||
|
||||
if(bn < NINDIRECT){
|
||||
// Load indirect block, allocating if necessary.
|
||||
if((addr = ip->addrs[NDIRECT]) == 0)
|
||||
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
|
||||
bp = bread(ip->dev, addr);
|
||||
a = (uint*)bp->data;
|
||||
if((addr = a[bn]) == 0){
|
||||
a[bn] = addr = balloc(ip->dev);
|
||||
log_write(bp);
|
||||
}
|
||||
brelse(bp);
|
||||
return addr;
|
||||
}
|
||||
|
||||
panic("bmap: out of range");
|
||||
}
|
||||
|
||||
// Truncate inode (discard contents).
|
||||
// Only called when the inode has no links
|
||||
// to it (no directory entries referring to it)
|
||||
// and has no in-memory reference to it (is
|
||||
// not an open file or current directory).
|
||||
static void
|
||||
itrunc(struct inode *ip)
|
||||
{
|
||||
int i, j;
|
||||
struct buf *bp;
|
||||
uint *a;
|
||||
|
||||
for(i = 0; i < NDIRECT; i++){
|
||||
if(ip->addrs[i]){
|
||||
bfree(ip->dev, ip->addrs[i]);
|
||||
ip->addrs[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(ip->addrs[NDIRECT]){
|
||||
bp = bread(ip->dev, ip->addrs[NDIRECT]);
|
||||
a = (uint*)bp->data;
|
||||
for(j = 0; j < NINDIRECT; j++){
|
||||
if(a[j])
|
||||
bfree(ip->dev, a[j]);
|
||||
}
|
||||
brelse(bp);
|
||||
bfree(ip->dev, ip->addrs[NDIRECT]);
|
||||
ip->addrs[NDIRECT] = 0;
|
||||
}
|
||||
|
||||
ip->size = 0;
|
||||
iupdate(ip);
|
||||
}
|
||||
|
||||
// Copy stat information from inode.
|
||||
// Caller must hold ip->lock.
|
||||
void
|
||||
stati(struct inode *ip, struct stat *st)
|
||||
{
|
||||
st->dev = ip->dev;
|
||||
st->ino = ip->inum;
|
||||
st->type = ip->type;
|
||||
st->nlink = ip->nlink;
|
||||
st->size = ip->size;
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Read data from inode.
|
||||
// Caller must hold ip->lock.
|
||||
int
|
||||
readi(struct inode *ip, char *dst, uint off, uint n)
|
||||
{
|
||||
uint tot, m;
|
||||
struct buf *bp;
|
||||
|
||||
if(ip->type == T_DEV){
|
||||
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read)
|
||||
return -1;
|
||||
return devsw[ip->major].read(ip, dst, n);
|
||||
}
|
||||
|
||||
if(off > ip->size || off + n < off)
|
||||
return -1;
|
||||
if(off + n > ip->size)
|
||||
n = ip->size - off;
|
||||
|
||||
for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
|
||||
bp = bread(ip->dev, bmap(ip, off/BSIZE));
|
||||
m = min(n - tot, BSIZE - off%BSIZE);
|
||||
memmove(dst, bp->data + off%BSIZE, m);
|
||||
brelse(bp);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// PAGEBREAK!
|
||||
// Write data to inode.
|
||||
// Caller must hold ip->lock.
|
||||
int
|
||||
writei(struct inode *ip, char *src, uint off, uint n)
|
||||
{
|
||||
uint tot, m;
|
||||
struct buf *bp;
|
||||
|
||||
if(ip->type == T_DEV){
|
||||
if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write)
|
||||
return -1;
|
||||
return devsw[ip->major].write(ip, src, n);
|
||||
}
|
||||
|
||||
if(off > ip->size || off + n < off)
|
||||
return -1;
|
||||
if(off + n > MAXFILE*BSIZE)
|
||||
return -1;
|
||||
|
||||
for(tot=0; tot<n; tot+=m, off+=m, src+=m){
|
||||
bp = bread(ip->dev, bmap(ip, off/BSIZE));
|
||||
m = min(n - tot, BSIZE - off%BSIZE);
|
||||
memmove(bp->data + off%BSIZE, src, m);
|
||||
log_write(bp);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
if(n > 0 && off > ip->size){
|
||||
ip->size = off;
|
||||
iupdate(ip);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Directories
|
||||
|
||||
int
|
||||
namecmp(const char *s, const char *t)
|
||||
{
|
||||
return strncmp(s, t, DIRSIZ);
|
||||
}
|
||||
|
||||
// Look for a directory entry in a directory.
|
||||
// If found, set *poff to byte offset of entry.
|
||||
struct inode*
|
||||
dirlookup(struct inode *dp, char *name, uint *poff)
|
||||
{
|
||||
uint off, inum;
|
||||
struct dirent de;
|
||||
|
||||
if(dp->type != T_DIR)
|
||||
panic("dirlookup not DIR");
|
||||
|
||||
for(off = 0; off < dp->size; off += sizeof(de)){
|
||||
if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||
panic("dirlookup read");
|
||||
if(de.inum == 0)
|
||||
continue;
|
||||
if(namecmp(name, de.name) == 0){
|
||||
// entry matches path element
|
||||
if(poff)
|
||||
*poff = off;
|
||||
inum = de.inum;
|
||||
return iget(dp->dev, inum);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a new directory entry (name, inum) into the directory dp.
|
||||
int
|
||||
dirlink(struct inode *dp, char *name, uint inum)
|
||||
{
|
||||
int off;
|
||||
struct dirent de;
|
||||
struct inode *ip;
|
||||
|
||||
// Check that name is not present.
|
||||
if((ip = dirlookup(dp, name, 0)) != 0){
|
||||
iput(ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Look for an empty dirent.
|
||||
for(off = 0; off < dp->size; off += sizeof(de)){
|
||||
if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||
panic("dirlink read");
|
||||
if(de.inum == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(de.name, name, DIRSIZ);
|
||||
de.inum = inum;
|
||||
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||
panic("dirlink");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Paths
|
||||
|
||||
// Copy the next path element from path into name.
|
||||
// Return a pointer to the element following the copied one.
|
||||
// The returned path has no leading slashes,
|
||||
// so the caller can check *path=='\0' to see if the name is the last one.
|
||||
// If no name to remove, return 0.
|
||||
//
|
||||
// Examples:
|
||||
// skipelem("a/bb/c", name) = "bb/c", setting name = "a"
|
||||
// skipelem("///a//bb", name) = "bb", setting name = "a"
|
||||
// skipelem("a", name) = "", setting name = "a"
|
||||
// skipelem("", name) = skipelem("////", name) = 0
|
||||
//
|
||||
static char*
|
||||
skipelem(char *path, char *name)
|
||||
{
|
||||
char *s;
|
||||
int len;
|
||||
|
||||
while(*path == '/')
|
||||
path++;
|
||||
if(*path == 0)
|
||||
return 0;
|
||||
s = path;
|
||||
while(*path != '/' && *path != 0)
|
||||
path++;
|
||||
len = path - s;
|
||||
if(len >= DIRSIZ)
|
||||
memmove(name, s, DIRSIZ);
|
||||
else {
|
||||
memmove(name, s, len);
|
||||
name[len] = 0;
|
||||
}
|
||||
while(*path == '/')
|
||||
path++;
|
||||
return path;
|
||||
}
|
||||
|
||||
// Look up and return the inode for a path name.
|
||||
// If parent != 0, return the inode for the parent and copy the final
|
||||
// path element into name, which must have room for DIRSIZ bytes.
|
||||
// Must be called inside a transaction since it calls iput().
|
||||
static struct inode*
|
||||
namex(char *path, int nameiparent, char *name)
|
||||
{
|
||||
struct inode *ip, *next;
|
||||
|
||||
if(*path == '/')
|
||||
ip = iget(ROOTDEV, ROOTINO);
|
||||
else
|
||||
ip = idup(myproc()->cwd);
|
||||
|
||||
while((path = skipelem(path, name)) != 0){
|
||||
ilock(ip);
|
||||
if(ip->type != T_DIR){
|
||||
iunlockput(ip);
|
||||
return 0;
|
||||
}
|
||||
if(nameiparent && *path == '\0'){
|
||||
// Stop one level early.
|
||||
iunlock(ip);
|
||||
return ip;
|
||||
}
|
||||
if((next = dirlookup(ip, name, 0)) == 0){
|
||||
iunlockput(ip);
|
||||
return 0;
|
||||
}
|
||||
iunlockput(ip);
|
||||
ip = next;
|
||||
}
|
||||
if(nameiparent){
|
||||
iput(ip);
|
||||
return 0;
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
struct inode*
|
||||
namei(char *path)
|
||||
{
|
||||
char name[DIRSIZ];
|
||||
return namex(path, 0, name);
|
||||
}
|
||||
|
||||
struct inode*
|
||||
nameiparent(char *path, char *name)
|
||||
{
|
||||
return namex(path, 1, name);
|
||||
}
|
||||
57
xv6-public/fs.h
Normal file
57
xv6-public/fs.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// On-disk file system format.
|
||||
// Both the kernel and user programs use this header file.
|
||||
|
||||
|
||||
#define ROOTINO 1 // root i-number
|
||||
#define BSIZE 512 // block size
|
||||
|
||||
// Disk layout:
|
||||
// [ boot block | super block | log | inode blocks |
|
||||
// free bit map | data blocks]
|
||||
//
|
||||
// mkfs computes the super block and builds an initial file system. The
|
||||
// super block describes the disk layout:
|
||||
struct superblock {
|
||||
uint size; // Size of file system image (blocks)
|
||||
uint nblocks; // Number of data blocks
|
||||
uint ninodes; // Number of inodes.
|
||||
uint nlog; // Number of log blocks
|
||||
uint logstart; // Block number of first log block
|
||||
uint inodestart; // Block number of first inode block
|
||||
uint bmapstart; // Block number of first free map block
|
||||
};
|
||||
|
||||
#define NDIRECT 12
|
||||
#define NINDIRECT (BSIZE / sizeof(uint))
|
||||
#define MAXFILE (NDIRECT + NINDIRECT)
|
||||
|
||||
// On-disk inode structure
|
||||
struct dinode {
|
||||
short type; // File type
|
||||
short major; // Major device number (T_DEV only)
|
||||
short minor; // Minor device number (T_DEV only)
|
||||
short nlink; // Number of links to inode in file system
|
||||
uint size; // Size of file (bytes)
|
||||
uint addrs[NDIRECT+1]; // Data block addresses
|
||||
};
|
||||
|
||||
// Inodes per block.
|
||||
#define IPB (BSIZE / sizeof(struct dinode))
|
||||
|
||||
// Block containing inode i
|
||||
#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart)
|
||||
|
||||
// Bitmap bits per block
|
||||
#define BPB (BSIZE*8)
|
||||
|
||||
// Block of free map containing bit for block b
|
||||
#define BBLOCK(b, sb) (b/BPB + sb.bmapstart)
|
||||
|
||||
// Directory is a file containing a sequence of dirent structures.
|
||||
#define DIRSIZ 14
|
||||
|
||||
struct dirent {
|
||||
ushort inum;
|
||||
char name[DIRSIZ];
|
||||
};
|
||||
|
||||
291
xv6-public/gdbutil
Normal file
291
xv6-public/gdbutil
Normal file
@@ -0,0 +1,291 @@
|
||||
# -*- gdb-script -*-
|
||||
|
||||
# Utility functions to pretty-print x86 segment/interrupt descriptors.
|
||||
# To load this file, run "source gdbutil" in gdb.
|
||||
# printdesc and printdescs are the main entry points.
|
||||
|
||||
# IA32 2007, Volume 3A, Table 3-2
|
||||
set $STS_T16A = 0x1
|
||||
set $STS_LDT = 0x2
|
||||
set $STS_T16B = 0x3
|
||||
set $STS_CG16 = 0x4
|
||||
set $STS_TG = 0x5
|
||||
set $STS_IG16 = 0x6
|
||||
set $STS_TG16 = 0x7
|
||||
set $STS_T32A = 0x9
|
||||
set $STS_T32B = 0xB
|
||||
set $STS_CG32 = 0xC
|
||||
set $STS_IG32 = 0xE
|
||||
set $STS_TG32 = 0xF
|
||||
|
||||
define outputsts
|
||||
while 1
|
||||
if $arg0 == $STS_T16A
|
||||
echo STS_T16A
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_LDT
|
||||
echo STS_LDT\
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_T16B
|
||||
echo STS_T16B
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_CG16
|
||||
echo STS_CG16
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_TG
|
||||
echo STS_TG\ \
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_IG16
|
||||
echo STS_IG16
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_TG16
|
||||
echo STS_TG16
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_T32A
|
||||
echo STS_T32A
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_T32B
|
||||
echo STS_T32B
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_CG32
|
||||
echo STS_CG32
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_IG32
|
||||
echo STS_IG32
|
||||
loop_break
|
||||
end
|
||||
if $arg0 == $STS_TG32
|
||||
echo STS_TG32
|
||||
loop_break
|
||||
end
|
||||
echo Reserved
|
||||
loop_break
|
||||
end
|
||||
end
|
||||
|
||||
# IA32 2007, Volume 3A, Table 3-1
|
||||
set $STA_X = 0x8
|
||||
set $STA_E = 0x4
|
||||
set $STA_C = 0x4
|
||||
set $STA_W = 0x2
|
||||
set $STA_R = 0x2
|
||||
set $STA_A = 0x1
|
||||
|
||||
define outputsta
|
||||
if $arg0 & $STA_X
|
||||
# Code segment
|
||||
echo code
|
||||
if $arg0 & $STA_C
|
||||
echo |STA_C
|
||||
end
|
||||
if $arg0 & $STA_R
|
||||
echo |STA_R
|
||||
end
|
||||
else
|
||||
# Data segment
|
||||
echo data
|
||||
if $arg0 & $STA_E
|
||||
echo |STA_E
|
||||
end
|
||||
if $arg0 & $STA_W
|
||||
echo |STA_W
|
||||
end
|
||||
end
|
||||
if $arg0 & $STA_A
|
||||
echo |STA_A
|
||||
else
|
||||
printf " "
|
||||
end
|
||||
end
|
||||
|
||||
# xv6-specific
|
||||
set $SEG_KCODE = 1
|
||||
set $SEG_KDATA = 2
|
||||
set $SEG_KCPU = 3
|
||||
set $SEG_UCODE = 4
|
||||
set $SEG_UDATA = 5
|
||||
set $SEG_TSS = 6
|
||||
|
||||
define outputcs
|
||||
if ($arg0 & 4) == 0
|
||||
if $arg0 >> 3 == $SEG_KCODE
|
||||
printf "SEG_KCODE<<3"
|
||||
end
|
||||
if $arg0 >> 3 == $SEG_KDATA
|
||||
printf "SEG_KDATA<<3"
|
||||
end
|
||||
if $arg0 >> 3 == $SEG_KCPU
|
||||
printf "SEG_KCPU<<3"
|
||||
end
|
||||
if $arg0 >> 3 == $SEG_UCODE
|
||||
printf "SEG_UCODE<<3"
|
||||
end
|
||||
if $arg0 >> 3 == $SEG_UDATA
|
||||
printf "SEG_UDATA<<3"
|
||||
end
|
||||
if $arg0 >> 3 == $SEG_TSS
|
||||
printf "SEG_TSS<<3"
|
||||
end
|
||||
if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6)
|
||||
printf "GDT[%d]", $arg0 >> 3
|
||||
end
|
||||
else
|
||||
printf "LDT[%d]", $arg0 >> 3
|
||||
end
|
||||
if ($arg0 & 3) > 0
|
||||
printf "|"
|
||||
outputdpl ($arg0&3)
|
||||
end
|
||||
end
|
||||
|
||||
define outputdpl
|
||||
if $arg0 == 0
|
||||
printf "DPL_KERN"
|
||||
else
|
||||
if $arg0 == 3
|
||||
printf "DPL_USER"
|
||||
else
|
||||
printf "DPL%d", $arg0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
define printdesc
|
||||
if $argc != 1
|
||||
echo Usage: printdesc expr
|
||||
else
|
||||
_printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1]
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
|
||||
document printdesc
|
||||
Print an x86 segment or gate descriptor.
|
||||
printdesc EXPR
|
||||
EXPR must evaluate to a descriptor value. It can be of any C type.
|
||||
end
|
||||
|
||||
define _printdesc
|
||||
_printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15)
|
||||
end
|
||||
|
||||
define _printdesc1
|
||||
# 2:P 3:DPL 4:S 5:Type
|
||||
if $arg2 == 0
|
||||
printf "P = 0 (Not present)"
|
||||
else
|
||||
printf "type = "
|
||||
if $arg4 == 0
|
||||
# System segment
|
||||
outputsts $arg5
|
||||
printf " (0x%x) ", $arg5
|
||||
_printsysdesc $arg0 $arg1 $arg5
|
||||
else
|
||||
# Code/data segment
|
||||
outputsta $arg5
|
||||
printf " "
|
||||
_printsegdesc $arg0 $arg1
|
||||
end
|
||||
|
||||
printf " DPL = "
|
||||
outputdpl $arg3
|
||||
printf " (%d)", $arg3
|
||||
end
|
||||
end
|
||||
|
||||
define _printsysdesc
|
||||
# 2:Type
|
||||
# GDB's || is buggy
|
||||
if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16)
|
||||
# Gate descriptor
|
||||
_printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16)
|
||||
else
|
||||
# System segment descriptor
|
||||
_printsegdesc $arg0 $arg1
|
||||
end
|
||||
end
|
||||
|
||||
define _printgate
|
||||
# IA32 2007, Voume 3A, Figure 5-2
|
||||
# 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16
|
||||
printf "CS = "
|
||||
outputcs $arg1
|
||||
printf " (%d)", $arg1
|
||||
|
||||
if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16)
|
||||
printf " Offset = "
|
||||
output/a $arg3 << 16 | $arg2
|
||||
end
|
||||
end
|
||||
|
||||
define _printsegdesc
|
||||
# IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1
|
||||
_printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1)
|
||||
if ($arg1>>12&1) == 1
|
||||
printf " AVL = %d", $arg1>>20&1
|
||||
if ($arg1>>11&1) == 0
|
||||
# Data segment
|
||||
if ($arg1>>22&1) == 0
|
||||
printf " B = small (0) "
|
||||
else
|
||||
printf " B = big (1) "
|
||||
end
|
||||
else
|
||||
# Code segment
|
||||
printf " D = "
|
||||
if ($arg1>>22&1) == 0
|
||||
printf "16-bit (0)"
|
||||
else
|
||||
printf "32-bit (1)"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
define _printsegdesc1
|
||||
# 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G
|
||||
printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24)
|
||||
printf " limit = 0x"
|
||||
if $arg5 == 0
|
||||
printf "%08x", $arg3 | ($arg4<<16)
|
||||
else
|
||||
printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF
|
||||
end
|
||||
end
|
||||
|
||||
define printdescs
|
||||
if $argc < 1 || $argc > 2
|
||||
echo Usage: printdescs expr [count]
|
||||
else
|
||||
if $argc == 1
|
||||
_printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0]))
|
||||
else
|
||||
_printdescs ($arg0) ($arg1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
document printdescs
|
||||
Print an array of x86 segment or gate descriptors.
|
||||
printdescs EXPR [COUNT]
|
||||
EXPR must evaluate to an array of descriptors.
|
||||
end
|
||||
|
||||
define _printdescs
|
||||
set $i = 0
|
||||
while $i < $arg1
|
||||
printf "[%d] ", $i
|
||||
printdesc $arg0[$i]
|
||||
set $i = $i + 1
|
||||
end
|
||||
end
|
||||
107
xv6-public/grep.c
Normal file
107
xv6-public/grep.c
Normal file
@@ -0,0 +1,107 @@
|
||||
// Simple grep. Only supports ^ . * $ operators.
|
||||
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
char buf[1024];
|
||||
int match(char*, char*);
|
||||
|
||||
void
|
||||
grep(char *pattern, int fd)
|
||||
{
|
||||
int n, m;
|
||||
char *p, *q;
|
||||
|
||||
m = 0;
|
||||
while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){
|
||||
m += n;
|
||||
buf[m] = '\0';
|
||||
p = buf;
|
||||
while((q = strchr(p, '\n')) != 0){
|
||||
*q = 0;
|
||||
if(match(pattern, p)){
|
||||
*q = '\n';
|
||||
write(1, p, q+1 - p);
|
||||
}
|
||||
p = q+1;
|
||||
}
|
||||
if(p == buf)
|
||||
m = 0;
|
||||
if(m > 0){
|
||||
m -= p - buf;
|
||||
memmove(buf, p, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, i;
|
||||
char *pattern;
|
||||
|
||||
if(argc <= 1){
|
||||
printf(2, "usage: grep pattern [file ...]\n");
|
||||
exit();
|
||||
}
|
||||
pattern = argv[1];
|
||||
|
||||
if(argc <= 2){
|
||||
grep(pattern, 0);
|
||||
exit();
|
||||
}
|
||||
|
||||
for(i = 2; i < argc; i++){
|
||||
if((fd = open(argv[i], 0)) < 0){
|
||||
printf(1, "grep: cannot open %s\n", argv[i]);
|
||||
exit();
|
||||
}
|
||||
grep(pattern, fd);
|
||||
close(fd);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
// Regexp matcher from Kernighan & Pike,
|
||||
// The Practice of Programming, Chapter 9.
|
||||
|
||||
int matchhere(char*, char*);
|
||||
int matchstar(int, char*, char*);
|
||||
|
||||
int
|
||||
match(char *re, char *text)
|
||||
{
|
||||
if(re[0] == '^')
|
||||
return matchhere(re+1, text);
|
||||
do{ // must look at empty string
|
||||
if(matchhere(re, text))
|
||||
return 1;
|
||||
}while(*text++ != '\0');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// matchhere: search for re at beginning of text
|
||||
int matchhere(char *re, char *text)
|
||||
{
|
||||
if(re[0] == '\0')
|
||||
return 1;
|
||||
if(re[1] == '*')
|
||||
return matchstar(re[0], re+2, text);
|
||||
if(re[0] == '$' && re[1] == '\0')
|
||||
return *text == '\0';
|
||||
if(*text!='\0' && (re[0]=='.' || re[0]==*text))
|
||||
return matchhere(re+1, text+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// matchstar: search for c*re at beginning of text
|
||||
int matchstar(int c, char *re, char *text)
|
||||
{
|
||||
do{ // a * matches zero or more instances
|
||||
if(matchhere(re, text))
|
||||
return 1;
|
||||
}while(*text!='\0' && (*text++==c || c=='.'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
169
xv6-public/ide.c
Normal file
169
xv6-public/ide.c
Normal file
@@ -0,0 +1,169 @@
|
||||
// Simple PIO-based (non-DMA) IDE driver code.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
#include "traps.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "buf.h"
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define IDE_BSY 0x80
|
||||
#define IDE_DRDY 0x40
|
||||
#define IDE_DF 0x20
|
||||
#define IDE_ERR 0x01
|
||||
|
||||
#define IDE_CMD_READ 0x20
|
||||
#define IDE_CMD_WRITE 0x30
|
||||
#define IDE_CMD_RDMUL 0xc4
|
||||
#define IDE_CMD_WRMUL 0xc5
|
||||
|
||||
// idequeue points to the buf now being read/written to the disk.
|
||||
// idequeue->qnext points to the next buf to be processed.
|
||||
// You must hold idelock while manipulating queue.
|
||||
|
||||
static struct spinlock idelock;
|
||||
static struct buf *idequeue;
|
||||
|
||||
static int havedisk1;
|
||||
static void idestart(struct buf*);
|
||||
|
||||
// Wait for IDE disk to become ready.
|
||||
static int
|
||||
idewait(int checkerr)
|
||||
{
|
||||
int r;
|
||||
|
||||
while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
|
||||
;
|
||||
if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ideinit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
initlock(&idelock, "ide");
|
||||
ioapicenable(IRQ_IDE, ncpu - 1);
|
||||
idewait(0);
|
||||
|
||||
// Check if disk 1 is present
|
||||
outb(0x1f6, 0xe0 | (1<<4));
|
||||
for(i=0; i<1000; i++){
|
||||
if(inb(0x1f7) != 0){
|
||||
havedisk1 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch back to disk 0.
|
||||
outb(0x1f6, 0xe0 | (0<<4));
|
||||
}
|
||||
|
||||
// Start the request for b. Caller must hold idelock.
|
||||
static void
|
||||
idestart(struct buf *b)
|
||||
{
|
||||
if(b == 0)
|
||||
panic("idestart");
|
||||
if(b->blockno >= FSSIZE)
|
||||
panic("incorrect blockno");
|
||||
int sector_per_block = BSIZE/SECTOR_SIZE;
|
||||
int sector = b->blockno * sector_per_block;
|
||||
int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL;
|
||||
int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL;
|
||||
|
||||
if (sector_per_block > 7) panic("idestart");
|
||||
|
||||
idewait(0);
|
||||
outb(0x3f6, 0); // generate interrupt
|
||||
outb(0x1f2, sector_per_block); // number of sectors
|
||||
outb(0x1f3, sector & 0xff);
|
||||
outb(0x1f4, (sector >> 8) & 0xff);
|
||||
outb(0x1f5, (sector >> 16) & 0xff);
|
||||
outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((sector>>24)&0x0f));
|
||||
if(b->flags & B_DIRTY){
|
||||
outb(0x1f7, write_cmd);
|
||||
outsl(0x1f0, b->data, BSIZE/4);
|
||||
} else {
|
||||
outb(0x1f7, read_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt handler.
|
||||
void
|
||||
ideintr(void)
|
||||
{
|
||||
struct buf *b;
|
||||
|
||||
// First queued buffer is the active request.
|
||||
acquire(&idelock);
|
||||
|
||||
if((b = idequeue) == 0){
|
||||
release(&idelock);
|
||||
return;
|
||||
}
|
||||
idequeue = b->qnext;
|
||||
|
||||
// Read data if needed.
|
||||
if(!(b->flags & B_DIRTY) && idewait(1) >= 0)
|
||||
insl(0x1f0, b->data, BSIZE/4);
|
||||
|
||||
// Wake process waiting for this buf.
|
||||
b->flags |= B_VALID;
|
||||
b->flags &= ~B_DIRTY;
|
||||
wakeup(b);
|
||||
|
||||
// Start disk on next buf in queue.
|
||||
if(idequeue != 0)
|
||||
idestart(idequeue);
|
||||
|
||||
release(&idelock);
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// 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)
|
||||
{
|
||||
struct buf **pp;
|
||||
|
||||
if(!holdingsleep(&b->lock))
|
||||
panic("iderw: buf not locked");
|
||||
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
|
||||
panic("iderw: nothing to do");
|
||||
if(b->dev != 0 && !havedisk1)
|
||||
panic("iderw: ide disk 1 not present");
|
||||
|
||||
|
||||
acquire(&idelock); //DOC:acquire-lock
|
||||
// sti();
|
||||
// Append b to idequeue.
|
||||
b->qnext = 0;
|
||||
for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue
|
||||
;
|
||||
*pp = b;
|
||||
|
||||
// Start disk if necessary.
|
||||
if(idequeue == b)
|
||||
idestart(b);
|
||||
|
||||
// Wait for request to finish.
|
||||
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
|
||||
sleep(b, &idelock);
|
||||
}
|
||||
|
||||
// cli();
|
||||
release(&idelock);
|
||||
}
|
||||
37
xv6-public/init.c
Normal file
37
xv6-public/init.c
Normal file
@@ -0,0 +1,37 @@
|
||||
// init: The initial user-level program
|
||||
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
#include "fcntl.h"
|
||||
|
||||
char *argv[] = { "sh", 0 };
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int pid, wpid;
|
||||
|
||||
if(open("console", O_RDWR) < 0){
|
||||
mknod("console", 1, 1);
|
||||
open("console", O_RDWR);
|
||||
}
|
||||
dup(0); // stdout
|
||||
dup(0); // stderr
|
||||
|
||||
for(;;){
|
||||
printf(1, "init: starting sh\n");
|
||||
pid = fork();
|
||||
if(pid < 0){
|
||||
printf(1, "init: fork failed\n");
|
||||
exit();
|
||||
}
|
||||
if(pid == 0){
|
||||
exec("sh", argv);
|
||||
printf(1, "init: exec sh failed\n");
|
||||
exit();
|
||||
}
|
||||
while((wpid=wait()) >= 0 && wpid != pid)
|
||||
printf(1, "zombie!\n");
|
||||
}
|
||||
}
|
||||
32
xv6-public/initcode.S
Normal file
32
xv6-public/initcode.S
Normal file
@@ -0,0 +1,32 @@
|
||||
# Initial process execs /init.
|
||||
# This code runs in user space.
|
||||
|
||||
#include "syscall.h"
|
||||
#include "traps.h"
|
||||
|
||||
|
||||
# exec(init, argv)
|
||||
.globl start
|
||||
start:
|
||||
pushl $argv
|
||||
pushl $init
|
||||
pushl $0 // where caller pc would be
|
||||
movl $SYS_exec, %eax
|
||||
int $T_SYSCALL
|
||||
|
||||
# for(;;) exit();
|
||||
exit:
|
||||
movl $SYS_exit, %eax
|
||||
int $T_SYSCALL
|
||||
jmp exit
|
||||
|
||||
# char init[] = "/init\0";
|
||||
init:
|
||||
.string "/init\0"
|
||||
|
||||
# char *argv[] = { init, 0 };
|
||||
.p2align 2
|
||||
argv:
|
||||
.long init
|
||||
.long 0
|
||||
|
||||
75
xv6-public/ioapic.c
Normal file
75
xv6-public/ioapic.c
Normal file
@@ -0,0 +1,75 @@
|
||||
// The I/O APIC manages hardware interrupts for an SMP system.
|
||||
// http://www.intel.com/design/chipsets/datashts/29056601.pdf
|
||||
// See also picirq.c.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "traps.h"
|
||||
|
||||
#define IOAPIC 0xFEC00000 // Default physical address of IO APIC
|
||||
|
||||
#define REG_ID 0x00 // Register index: ID
|
||||
#define REG_VER 0x01 // Register index: version
|
||||
#define REG_TABLE 0x10 // Redirection table base
|
||||
|
||||
// The redirection table starts at REG_TABLE and uses
|
||||
// two registers to configure each interrupt.
|
||||
// The first (low) register in a pair contains configuration bits.
|
||||
// The second (high) register contains a bitmask telling which
|
||||
// CPUs can serve that interrupt.
|
||||
#define INT_DISABLED 0x00010000 // Interrupt disabled
|
||||
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
|
||||
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
|
||||
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
|
||||
|
||||
volatile struct ioapic *ioapic;
|
||||
|
||||
// IO APIC MMIO structure: write reg, then read or write data.
|
||||
struct ioapic {
|
||||
uint reg;
|
||||
uint pad[3];
|
||||
uint data;
|
||||
};
|
||||
|
||||
static uint
|
||||
ioapicread(int reg)
|
||||
{
|
||||
ioapic->reg = reg;
|
||||
return ioapic->data;
|
||||
}
|
||||
|
||||
static void
|
||||
ioapicwrite(int reg, uint data)
|
||||
{
|
||||
ioapic->reg = reg;
|
||||
ioapic->data = data;
|
||||
}
|
||||
|
||||
void
|
||||
ioapicinit(void)
|
||||
{
|
||||
int i, id, maxintr;
|
||||
|
||||
ioapic = (volatile struct ioapic*)IOAPIC;
|
||||
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
|
||||
id = ioapicread(REG_ID) >> 24;
|
||||
if(id != ioapicid)
|
||||
cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
|
||||
|
||||
// Mark all interrupts edge-triggered, active high, disabled,
|
||||
// and not routed to any CPUs.
|
||||
for(i = 0; i <= maxintr; i++){
|
||||
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
|
||||
ioapicwrite(REG_TABLE+2*i+1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ioapicenable(int irq, int cpunum)
|
||||
{
|
||||
// Mark interrupt edge-triggered, active high,
|
||||
// enabled, and routed to the given cpunum,
|
||||
// which happens to be that cpu's APIC ID.
|
||||
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
|
||||
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
|
||||
}
|
||||
96
xv6-public/kalloc.c
Normal file
96
xv6-public/kalloc.c
Normal file
@@ -0,0 +1,96 @@
|
||||
// Physical memory allocator, intended to allocate
|
||||
// memory for user processes, kernel stacks, page table pages,
|
||||
// and pipe buffers. Allocates 4096-byte pages.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
void freerange(void *vstart, void *vend);
|
||||
extern char end[]; // first address after kernel loaded from ELF file
|
||||
// defined by the kernel linker script in kernel.ld
|
||||
|
||||
struct run {
|
||||
struct run *next;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
int use_lock;
|
||||
struct run *freelist;
|
||||
} kmem;
|
||||
|
||||
// Initialization happens in two phases.
|
||||
// 1. main() calls kinit1() while still using entrypgdir to place just
|
||||
// the pages mapped by entrypgdir on free list.
|
||||
// 2. main() calls kinit2() with the rest of the physical pages
|
||||
// after installing a full page table that maps them on all cores.
|
||||
void
|
||||
kinit1(void *vstart, void *vend)
|
||||
{
|
||||
initlock(&kmem.lock, "kmem");
|
||||
kmem.use_lock = 0;
|
||||
freerange(vstart, vend);
|
||||
}
|
||||
|
||||
void
|
||||
kinit2(void *vstart, void *vend)
|
||||
{
|
||||
freerange(vstart, vend);
|
||||
kmem.use_lock = 1;
|
||||
}
|
||||
|
||||
void
|
||||
freerange(void *vstart, void *vend)
|
||||
{
|
||||
char *p;
|
||||
p = (char*)PGROUNDUP((uint)vstart);
|
||||
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
|
||||
kfree(p);
|
||||
}
|
||||
//PAGEBREAK: 21
|
||||
// Free the page of physical memory pointed at by v,
|
||||
// which normally should have been returned by a
|
||||
// call to kalloc(). (The exception is when
|
||||
// initializing the allocator; see kinit above.)
|
||||
void
|
||||
kfree(char *v)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
|
||||
panic("kfree");
|
||||
|
||||
// Fill with junk to catch dangling refs.
|
||||
memset(v, 1, PGSIZE);
|
||||
|
||||
if(kmem.use_lock)
|
||||
acquire(&kmem.lock);
|
||||
r = (struct run*)v;
|
||||
r->next = kmem.freelist;
|
||||
kmem.freelist = r;
|
||||
if(kmem.use_lock)
|
||||
release(&kmem.lock);
|
||||
}
|
||||
|
||||
// Allocate one 4096-byte page of physical memory.
|
||||
// Returns a pointer that the kernel can use.
|
||||
// Returns 0 if the memory cannot be allocated.
|
||||
char*
|
||||
kalloc(void)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
if(kmem.use_lock)
|
||||
acquire(&kmem.lock);
|
||||
r = kmem.freelist;
|
||||
if(r)
|
||||
kmem.freelist = r->next;
|
||||
if(kmem.use_lock)
|
||||
release(&kmem.lock);
|
||||
return (char*)r;
|
||||
}
|
||||
|
||||
50
xv6-public/kbd.c
Normal file
50
xv6-public/kbd.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "types.h"
|
||||
#include "x86.h"
|
||||
#include "defs.h"
|
||||
#include "kbd.h"
|
||||
|
||||
int
|
||||
kbdgetc(void)
|
||||
{
|
||||
static uint shift;
|
||||
static uchar *charcode[4] = {
|
||||
normalmap, shiftmap, ctlmap, ctlmap
|
||||
};
|
||||
uint st, data, c;
|
||||
|
||||
st = inb(KBSTATP);
|
||||
if((st & KBS_DIB) == 0)
|
||||
return -1;
|
||||
data = inb(KBDATAP);
|
||||
|
||||
if(data == 0xE0){
|
||||
shift |= E0ESC;
|
||||
return 0;
|
||||
} else if(data & 0x80){
|
||||
// Key released
|
||||
data = (shift & E0ESC ? data : data & 0x7F);
|
||||
shift &= ~(shiftcode[data] | E0ESC);
|
||||
return 0;
|
||||
} else if(shift & E0ESC){
|
||||
// Last character was an E0 escape; or with 0x80
|
||||
data |= 0x80;
|
||||
shift &= ~E0ESC;
|
||||
}
|
||||
|
||||
shift |= shiftcode[data];
|
||||
shift ^= togglecode[data];
|
||||
c = charcode[shift & (CTL | SHIFT)][data];
|
||||
if(shift & CAPSLOCK){
|
||||
if('a' <= c && c <= 'z')
|
||||
c += 'A' - 'a';
|
||||
else if('A' <= c && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
kbdintr(void)
|
||||
{
|
||||
consoleintr(kbdgetc);
|
||||
}
|
||||
112
xv6-public/kbd.h
Normal file
112
xv6-public/kbd.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// PC keyboard interface constants
|
||||
|
||||
#define KBSTATP 0x64 // kbd controller status port(I)
|
||||
#define KBS_DIB 0x01 // kbd data in buffer
|
||||
#define KBDATAP 0x60 // kbd data port(I)
|
||||
|
||||
#define NO 0
|
||||
|
||||
#define SHIFT (1<<0)
|
||||
#define CTL (1<<1)
|
||||
#define ALT (1<<2)
|
||||
|
||||
#define CAPSLOCK (1<<3)
|
||||
#define NUMLOCK (1<<4)
|
||||
#define SCROLLLOCK (1<<5)
|
||||
|
||||
#define E0ESC (1<<6)
|
||||
|
||||
// Special keycodes
|
||||
#define KEY_HOME 0xE0
|
||||
#define KEY_END 0xE1
|
||||
#define KEY_UP 0xE2
|
||||
#define KEY_DN 0xE3
|
||||
#define KEY_LF 0xE4
|
||||
#define KEY_RT 0xE5
|
||||
#define KEY_PGUP 0xE6
|
||||
#define KEY_PGDN 0xE7
|
||||
#define KEY_INS 0xE8
|
||||
#define KEY_DEL 0xE9
|
||||
|
||||
// C('A') == Control-A
|
||||
#define C(x) (x - '@')
|
||||
|
||||
static uchar shiftcode[256] =
|
||||
{
|
||||
[0x1D] CTL,
|
||||
[0x2A] SHIFT,
|
||||
[0x36] SHIFT,
|
||||
[0x38] ALT,
|
||||
[0x9D] CTL,
|
||||
[0xB8] ALT
|
||||
};
|
||||
|
||||
static uchar togglecode[256] =
|
||||
{
|
||||
[0x3A] CAPSLOCK,
|
||||
[0x45] NUMLOCK,
|
||||
[0x46] SCROLLLOCK
|
||||
};
|
||||
|
||||
static uchar normalmap[256] =
|
||||
{
|
||||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
||||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
||||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
||||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0x9C] '\n', // KP_Enter
|
||||
[0xB5] '/', // KP_Div
|
||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uchar shiftmap[256] =
|
||||
{
|
||||
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
|
||||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
||||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
||||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
||||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0x9C] '\n', // KP_Enter
|
||||
[0xB5] '/', // KP_Div
|
||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uchar ctlmap[256] =
|
||||
{
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
|
||||
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
|
||||
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
|
||||
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
|
||||
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
|
||||
[0x9C] '\r', // KP_Enter
|
||||
[0xB5] C('/'), // KP_Div
|
||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
68
xv6-public/kernel.ld
Normal file
68
xv6-public/kernel.ld
Normal file
@@ -0,0 +1,68 @@
|
||||
/* Simple linker script for the JOS kernel.
|
||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
|
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Link the kernel at this address: "." means the current address */
|
||||
/* Must be equal to KERNLINK */
|
||||
. = 0x80100000;
|
||||
|
||||
.text : AT(0x100000) {
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/* Include debugging information in kernel memory */
|
||||
.stab : {
|
||||
PROVIDE(__STAB_BEGIN__ = .);
|
||||
*(.stab);
|
||||
PROVIDE(__STAB_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
.stabstr : {
|
||||
PROVIDE(__STABSTR_BEGIN__ = .);
|
||||
*(.stabstr);
|
||||
PROVIDE(__STABSTR_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
/* Adjust the address for the data segment to the next page */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/* 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. */
|
||||
PROVIDE(data = .);
|
||||
|
||||
/* The data segment */
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
PROVIDE(edata = .);
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack)
|
||||
}
|
||||
}
|
||||
17
xv6-public/kill.c
Normal file
17
xv6-public/kill.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc < 2){
|
||||
printf(2, "usage: kill pid...\n");
|
||||
exit();
|
||||
}
|
||||
for(i=1; i<argc; i++)
|
||||
kill(atoi(argv[i]));
|
||||
exit();
|
||||
}
|
||||
229
xv6-public/lapic.c
Normal file
229
xv6-public/lapic.c
Normal file
@@ -0,0 +1,229 @@
|
||||
// The local APIC manages internal (non-I/O) interrupts.
|
||||
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
|
||||
|
||||
#include "param.h"
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "date.h"
|
||||
#include "memlayout.h"
|
||||
#include "traps.h"
|
||||
#include "mmu.h"
|
||||
#include "x86.h"
|
||||
|
||||
// Local APIC registers, divided by 4 for use as uint[] indices.
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
volatile uint *lapic; // Initialized in mp.c
|
||||
|
||||
//PAGEBREAK!
|
||||
static void
|
||||
lapicw(int index, int value)
|
||||
{
|
||||
lapic[index] = value;
|
||||
lapic[ID]; // wait for write to finish, by reading
|
||||
}
|
||||
|
||||
void
|
||||
lapicinit(void)
|
||||
{
|
||||
if(!lapic)
|
||||
return;
|
||||
|
||||
// Enable local APIC; set spurious interrupt vector.
|
||||
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
|
||||
|
||||
// The timer repeatedly counts down at bus frequency
|
||||
// from lapic[TICR] and then issues an interrupt.
|
||||
// If xv6 cared more about precise timekeeping,
|
||||
// TICR would be calibrated using an external time source.
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
|
||||
// Disable logical interrupt lines.
|
||||
lapicw(LINT0, MASKED);
|
||||
lapicw(LINT1, MASKED);
|
||||
|
||||
// Disable performance counter overflow interrupts
|
||||
// on machines that provide that interrupt entry.
|
||||
if(((lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
|
||||
// Map error interrupt to IRQ_ERROR.
|
||||
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
|
||||
|
||||
// Clear error status register (requires back-to-back writes).
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
|
||||
// Ack any outstanding interrupts.
|
||||
lapicw(EOI, 0);
|
||||
|
||||
// Send an Init Level De-Assert to synchronise arbitration ID's.
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
|
||||
// Enable interrupts on the APIC (but not on the processor).
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
int
|
||||
lapicid(void)
|
||||
{
|
||||
if (!lapic)
|
||||
return 0;
|
||||
return lapic[ID] >> 24;
|
||||
}
|
||||
|
||||
// Acknowledge interrupt.
|
||||
void
|
||||
lapiceoi(void)
|
||||
{
|
||||
if(lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
// Spin for a given number of microseconds.
|
||||
// On real hardware would want to tune this dynamically.
|
||||
void
|
||||
microdelay(int us)
|
||||
{
|
||||
}
|
||||
|
||||
#define CMOS_PORT 0x70
|
||||
#define CMOS_RETURN 0x71
|
||||
|
||||
// Start additional processor running entry code at addr.
|
||||
// See Appendix B of MultiProcessor Specification.
|
||||
void
|
||||
lapicstartap(uchar apicid, uint addr)
|
||||
{
|
||||
int i;
|
||||
ushort *wrv;
|
||||
|
||||
// "The BSP must initialize CMOS shutdown code to 0AH
|
||||
// and the warm reset vector (DWORD based at 40:67) to point at
|
||||
// the AP startup code prior to the [universal startup algorithm]."
|
||||
outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code
|
||||
outb(CMOS_PORT+1, 0x0A);
|
||||
wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector
|
||||
wrv[0] = 0;
|
||||
wrv[1] = addr >> 4;
|
||||
|
||||
// "Universal startup algorithm."
|
||||
// Send INIT (level-triggered) interrupt to reset other CPU.
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
microdelay(200);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
microdelay(100); // should be 10ms, but too slow in Bochs!
|
||||
|
||||
// Send startup IPI (twice!) to enter code.
|
||||
// Regular hardware is supposed to only accept a STARTUP
|
||||
// when it is in the halted state due to an INIT. So the second
|
||||
// should be ignored, but it is part of the official Intel algorithm.
|
||||
// Bochs complains about the second one. Too bad for Bochs.
|
||||
for(i = 0; i < 2; i++){
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, STARTUP | (addr>>12));
|
||||
microdelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
#define CMOS_STATA 0x0a
|
||||
#define CMOS_STATB 0x0b
|
||||
#define CMOS_UIP (1 << 7) // RTC update in progress
|
||||
|
||||
#define SECS 0x00
|
||||
#define MINS 0x02
|
||||
#define HOURS 0x04
|
||||
#define DAY 0x07
|
||||
#define MONTH 0x08
|
||||
#define YEAR 0x09
|
||||
|
||||
static uint
|
||||
cmos_read(uint reg)
|
||||
{
|
||||
outb(CMOS_PORT, reg);
|
||||
microdelay(200);
|
||||
|
||||
return inb(CMOS_RETURN);
|
||||
}
|
||||
|
||||
static void
|
||||
fill_rtcdate(struct rtcdate *r)
|
||||
{
|
||||
r->second = cmos_read(SECS);
|
||||
r->minute = cmos_read(MINS);
|
||||
r->hour = cmos_read(HOURS);
|
||||
r->day = cmos_read(DAY);
|
||||
r->month = cmos_read(MONTH);
|
||||
r->year = cmos_read(YEAR);
|
||||
}
|
||||
|
||||
// qemu seems to use 24-hour GWT and the values are BCD encoded
|
||||
void
|
||||
cmostime(struct rtcdate *r)
|
||||
{
|
||||
struct rtcdate t1, t2;
|
||||
int sb, bcd;
|
||||
|
||||
sb = cmos_read(CMOS_STATB);
|
||||
|
||||
bcd = (sb & (1 << 2)) == 0;
|
||||
|
||||
// make sure CMOS doesn't modify time while we read it
|
||||
for(;;) {
|
||||
fill_rtcdate(&t1);
|
||||
if(cmos_read(CMOS_STATA) & CMOS_UIP)
|
||||
continue;
|
||||
fill_rtcdate(&t2);
|
||||
if(memcmp(&t1, &t2, sizeof(t1)) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// convert
|
||||
if(bcd) {
|
||||
#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
|
||||
CONV(second);
|
||||
CONV(minute);
|
||||
CONV(hour );
|
||||
CONV(day );
|
||||
CONV(month );
|
||||
CONV(year );
|
||||
#undef CONV
|
||||
}
|
||||
|
||||
*r = t1;
|
||||
r->year += 2000;
|
||||
}
|
||||
15
xv6-public/ln.c
Normal file
15
xv6-public/ln.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 3){
|
||||
printf(2, "Usage: ln old new\n");
|
||||
exit();
|
||||
}
|
||||
if(link(argv[1], argv[2]) < 0)
|
||||
printf(2, "link %s %s: failed\n", argv[1], argv[2]);
|
||||
exit();
|
||||
}
|
||||
234
xv6-public/log.c
Normal file
234
xv6-public/log.c
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "buf.h"
|
||||
|
||||
// Simple logging that allows concurrent FS system calls.
|
||||
//
|
||||
// A log transaction contains the updates of multiple FS system
|
||||
// calls. The logging system only commits when there are
|
||||
// no FS system calls active. Thus there is never
|
||||
// any reasoning required about whether a commit might
|
||||
// write an uncommitted system call's updates to disk.
|
||||
//
|
||||
// A system call should call begin_op()/end_op() to mark
|
||||
// its start and end. Usually begin_op() just increments
|
||||
// the count of in-progress FS system calls and returns.
|
||||
// But if it thinks the log is close to running out, it
|
||||
// sleeps until the last outstanding end_op() commits.
|
||||
//
|
||||
// The log is a physical re-do log containing disk blocks.
|
||||
// The on-disk log format:
|
||||
// header block, containing block #s for block A, B, C, ...
|
||||
// block A
|
||||
// block B
|
||||
// block C
|
||||
// ...
|
||||
// Log appends are synchronous.
|
||||
|
||||
// Contents of the header block, used for both the on-disk header block
|
||||
// and to keep track in memory of logged block# before commit.
|
||||
struct logheader {
|
||||
int n;
|
||||
int block[LOGSIZE];
|
||||
};
|
||||
|
||||
struct log {
|
||||
struct spinlock lock;
|
||||
int start;
|
||||
int size;
|
||||
int outstanding; // how many FS sys calls are executing.
|
||||
int committing; // in commit(), please wait.
|
||||
int dev;
|
||||
struct logheader lh;
|
||||
};
|
||||
struct log log;
|
||||
|
||||
static void recover_from_log(void);
|
||||
static void commit();
|
||||
|
||||
void
|
||||
initlog(int dev)
|
||||
{
|
||||
if (sizeof(struct logheader) >= BSIZE)
|
||||
panic("initlog: too big logheader");
|
||||
|
||||
struct superblock sb;
|
||||
initlock(&log.lock, "log");
|
||||
readsb(dev, &sb);
|
||||
log.start = sb.logstart;
|
||||
log.size = sb.nlog;
|
||||
log.dev = dev;
|
||||
recover_from_log();
|
||||
}
|
||||
|
||||
// Copy committed blocks from log to their home location
|
||||
static void
|
||||
install_trans(void)
|
||||
{
|
||||
int tail;
|
||||
|
||||
for (tail = 0; tail < log.lh.n; tail++) {
|
||||
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
|
||||
struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
|
||||
memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
|
||||
bwrite(dbuf); // write dst to disk
|
||||
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.n = lh->n;
|
||||
for (i = 0; i < log.lh.n; i++) {
|
||||
log.lh.block[i] = lh->block[i];
|
||||
}
|
||||
brelse(buf);
|
||||
}
|
||||
|
||||
// Write in-memory log header to disk.
|
||||
// This is the true point at which the
|
||||
// current transaction commits.
|
||||
static void
|
||||
write_head(void)
|
||||
{
|
||||
struct buf *buf = bread(log.dev, log.start);
|
||||
struct logheader *hb = (struct logheader *) (buf->data);
|
||||
int i;
|
||||
hb->n = log.lh.n;
|
||||
for (i = 0; i < log.lh.n; i++) {
|
||||
hb->block[i] = log.lh.block[i];
|
||||
}
|
||||
bwrite(buf);
|
||||
brelse(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
recover_from_log(void)
|
||||
{
|
||||
read_head();
|
||||
install_trans(); // if committed, copy from log to disk
|
||||
log.lh.n = 0;
|
||||
write_head(); // clear the log
|
||||
}
|
||||
|
||||
// called at the start of each FS system call.
|
||||
void
|
||||
begin_op(void)
|
||||
{
|
||||
acquire(&log.lock);
|
||||
while(1){
|
||||
if(log.committing){
|
||||
sleep(&log, &log.lock);
|
||||
} else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
|
||||
// this op might exhaust log space; wait for commit.
|
||||
sleep(&log, &log.lock);
|
||||
} else {
|
||||
log.outstanding += 1;
|
||||
release(&log.lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called at the end of each FS system call.
|
||||
// commits if this was the last outstanding operation.
|
||||
void
|
||||
end_op(void)
|
||||
{
|
||||
int do_commit = 0;
|
||||
|
||||
acquire(&log.lock);
|
||||
log.outstanding -= 1;
|
||||
if(log.committing)
|
||||
panic("log.committing");
|
||||
if(log.outstanding == 0){
|
||||
do_commit = 1;
|
||||
log.committing = 1;
|
||||
} else {
|
||||
// begin_op() may be waiting for log space,
|
||||
// and decrementing log.outstanding has decreased
|
||||
// the amount of reserved space.
|
||||
wakeup(&log);
|
||||
}
|
||||
release(&log.lock);
|
||||
|
||||
if(do_commit){
|
||||
// call commit w/o holding locks, since not allowed
|
||||
// to sleep with locks.
|
||||
commit();
|
||||
acquire(&log.lock);
|
||||
log.committing = 0;
|
||||
wakeup(&log);
|
||||
release(&log.lock);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy modified blocks from cache to log.
|
||||
static void
|
||||
write_log(void)
|
||||
{
|
||||
int tail;
|
||||
|
||||
for (tail = 0; tail < log.lh.n; tail++) {
|
||||
struct buf *to = bread(log.dev, log.start+tail+1); // log block
|
||||
struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
|
||||
memmove(to->data, from->data, BSIZE);
|
||||
bwrite(to); // write the log
|
||||
brelse(from);
|
||||
brelse(to);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
commit()
|
||||
{
|
||||
if (log.lh.n > 0) {
|
||||
write_log(); // Write modified blocks from cache to log
|
||||
write_head(); // Write header to disk -- the real commit
|
||||
install_trans(); // Now install writes to home locations
|
||||
log.lh.n = 0;
|
||||
write_head(); // Erase the transaction from the log
|
||||
}
|
||||
}
|
||||
|
||||
// Caller has modified b->data and is done with the buffer.
|
||||
// Record the block number and pin in the cache with B_DIRTY.
|
||||
// commit()/write_log() will do the disk write.
|
||||
//
|
||||
// log_write() replaces bwrite(); a typical use is:
|
||||
// bp = bread(...)
|
||||
// modify bp->data[]
|
||||
// log_write(bp)
|
||||
// brelse(bp)
|
||||
void
|
||||
log_write(struct buf *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
|
||||
panic("too big a transaction");
|
||||
if (log.outstanding < 1)
|
||||
panic("log_write outside of trans");
|
||||
|
||||
acquire(&log.lock);
|
||||
for (i = 0; i < log.lh.n; i++) {
|
||||
if (log.lh.block[i] == b->blockno) // log absorbtion
|
||||
break;
|
||||
}
|
||||
log.lh.block[i] = b->blockno;
|
||||
if (i == log.lh.n)
|
||||
log.lh.n++;
|
||||
b->flags |= B_DIRTY; // prevent eviction
|
||||
release(&log.lock);
|
||||
}
|
||||
|
||||
85
xv6-public/ls.c
Normal file
85
xv6-public/ls.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
#include "fs.h"
|
||||
|
||||
char*
|
||||
fmtname(char *path)
|
||||
{
|
||||
static char buf[DIRSIZ+1];
|
||||
char *p;
|
||||
|
||||
// Find first character after last slash.
|
||||
for(p=path+strlen(path); p >= path && *p != '/'; p--)
|
||||
;
|
||||
p++;
|
||||
|
||||
// Return blank-padded name.
|
||||
if(strlen(p) >= DIRSIZ)
|
||||
return p;
|
||||
memmove(buf, p, strlen(p));
|
||||
memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
ls(char *path)
|
||||
{
|
||||
char buf[512], *p;
|
||||
int fd;
|
||||
struct dirent de;
|
||||
struct stat st;
|
||||
|
||||
if((fd = open(path, 0)) < 0){
|
||||
printf(2, "ls: cannot open %s\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if(fstat(fd, &st) < 0){
|
||||
printf(2, "ls: cannot stat %s\n", path);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(st.type){
|
||||
case T_FILE:
|
||||
printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size);
|
||||
break;
|
||||
|
||||
case T_DIR:
|
||||
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
|
||||
printf(1, "ls: path too long\n");
|
||||
break;
|
||||
}
|
||||
strcpy(buf, path);
|
||||
p = buf+strlen(buf);
|
||||
*p++ = '/';
|
||||
while(read(fd, &de, sizeof(de)) == sizeof(de)){
|
||||
if(de.inum == 0)
|
||||
continue;
|
||||
memmove(p, de.name, DIRSIZ);
|
||||
p[DIRSIZ] = 0;
|
||||
if(stat(buf, &st) < 0){
|
||||
printf(1, "ls: cannot stat %s\n", buf);
|
||||
continue;
|
||||
}
|
||||
printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc < 2){
|
||||
ls(".");
|
||||
exit();
|
||||
}
|
||||
for(i=1; i<argc; i++)
|
||||
ls(argv[i]);
|
||||
exit();
|
||||
}
|
||||
116
xv6-public/main.c
Normal file
116
xv6-public/main.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
|
||||
static void startothers(void);
|
||||
static void mpmain(void) __attribute__((noreturn));
|
||||
extern pde_t *kpgdir;
|
||||
extern char end[]; // first address after kernel loaded from ELF file
|
||||
|
||||
// Bootstrap processor starts running C code here.
|
||||
// Allocate a real stack and switch to it, first
|
||||
// doing some setup required for memory allocator to work.
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
kinit1(end, P2V(4*1024*1024)); // phys page allocator
|
||||
kvmalloc(); // kernel page table
|
||||
mpinit(); // detect other processors
|
||||
lapicinit(); // interrupt controller
|
||||
seginit(); // segment descriptors
|
||||
picinit(); // disable pic
|
||||
ioapicinit(); // another interrupt controller
|
||||
consoleinit(); // console hardware
|
||||
uartinit(); // serial port
|
||||
pinit(); // process table
|
||||
tvinit(); // trap vectors
|
||||
binit(); // buffer cache
|
||||
fileinit(); // file table
|
||||
ideinit(); // disk
|
||||
startothers(); // start other processors
|
||||
kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
|
||||
userinit(); // first user process
|
||||
mpmain(); // finish this processor's setup
|
||||
}
|
||||
|
||||
// Other CPUs jump here from entryother.S.
|
||||
static void
|
||||
mpenter(void)
|
||||
{
|
||||
switchkvm();
|
||||
seginit();
|
||||
lapicinit();
|
||||
mpmain();
|
||||
}
|
||||
|
||||
// Common CPU setup code.
|
||||
static void
|
||||
mpmain(void)
|
||||
{
|
||||
cprintf("cpu%d: starting %d\n", cpuid(), cpuid());
|
||||
idtinit(); // load idt register
|
||||
xchg(&(mycpu()->started), 1); // tell startothers() we're up
|
||||
scheduler(); // start running processes
|
||||
}
|
||||
|
||||
pde_t entrypgdir[]; // For entry.S
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
startothers(void)
|
||||
{
|
||||
extern uchar _binary_entryother_start[], _binary_entryother_size[];
|
||||
uchar *code;
|
||||
struct cpu *c;
|
||||
char *stack;
|
||||
|
||||
// Write entry code to unused memory at 0x7000.
|
||||
// The linker has placed the image of entryother.S in
|
||||
// _binary_entryother_start.
|
||||
code = P2V(0x7000);
|
||||
memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);
|
||||
|
||||
for(c = cpus; c < cpus+ncpu; c++){
|
||||
if(c == mycpu()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell entryother.S what stack to use, where to enter, and what
|
||||
// pgdir to use. We cannot use kpgdir yet, because the AP processor
|
||||
// is running in low memory, so we use entrypgdir for the APs too.
|
||||
stack = kalloc();
|
||||
*(void**)(code-4) = stack + KSTACKSIZE;
|
||||
*(void(**)(void))(code-8) = mpenter;
|
||||
*(int**)(code-12) = (void *) V2P(entrypgdir);
|
||||
|
||||
lapicstartap(c->apicid, V2P(code));
|
||||
|
||||
// wait for cpu to finish mpmain()
|
||||
while(c->started == 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// The boot page table used in entry.S and entryother.S.
|
||||
// Page directories (and page tables) must start on page boundaries,
|
||||
// hence the __aligned__ attribute.
|
||||
// PTE_PS in a page directory entry enables 4Mbyte pages.
|
||||
|
||||
__attribute__((__aligned__(PGSIZE)))
|
||||
pde_t entrypgdir[NPDENTRIES] = {
|
||||
// Map VA's [0, 4MB) to PA's [0, 4MB)
|
||||
[0] = (0) | PTE_P | PTE_W | PTE_PS,
|
||||
// Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
|
||||
[KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
|
||||
};
|
||||
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
|
||||
60
xv6-public/memide.c
Normal file
60
xv6-public/memide.c
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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 "sleeplock.h"
|
||||
#include "fs.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/BSIZE;
|
||||
}
|
||||
|
||||
// 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(!holdingsleep(&b->lock))
|
||||
panic("iderw: buf not locked");
|
||||
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->blockno >= disksize)
|
||||
panic("iderw: block out of range");
|
||||
|
||||
p = memdisk + b->blockno*BSIZE;
|
||||
|
||||
if(b->flags & B_DIRTY){
|
||||
b->flags &= ~B_DIRTY;
|
||||
memmove(p, b->data, BSIZE);
|
||||
} else
|
||||
memmove(b->data, p, BSIZE);
|
||||
b->flags |= B_VALID;
|
||||
}
|
||||
15
xv6-public/memlayout.h
Normal file
15
xv6-public/memlayout.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Memory layout
|
||||
|
||||
#define EXTMEM 0x100000 // Start of extended memory
|
||||
#define PHYSTOP 0xE000000 // Top physical memory
|
||||
#define DEVSPACE 0xFE000000 // Other devices are at high addresses
|
||||
|
||||
// Key addresses for address space layout (see kmap in vm.c for layout)
|
||||
#define KERNBASE 0x80000000 // First kernel virtual address
|
||||
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
|
||||
|
||||
#define V2P(a) (((uint) (a)) - KERNBASE)
|
||||
#define P2V(a) ((void *)(((char *) (a)) + KERNBASE))
|
||||
|
||||
#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
|
||||
#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts
|
||||
23
xv6-public/mkdir.c
Normal file
23
xv6-public/mkdir.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc < 2){
|
||||
printf(2, "Usage: mkdir files...\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
for(i = 1; i < argc; i++){
|
||||
if(mkdir(argv[i]) < 0){
|
||||
printf(2, "mkdir: %s failed to create\n", argv[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
297
xv6-public/mkfs.c
Normal file
297
xv6-public/mkfs.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define stat xv6_stat // avoid clash with host struct stat
|
||||
#include "types.h"
|
||||
#include "fs.h"
|
||||
#include "stat.h"
|
||||
#include "param.h"
|
||||
|
||||
#ifndef static_assert
|
||||
#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0)
|
||||
#endif
|
||||
|
||||
#define NINODES 200
|
||||
|
||||
// Disk layout:
|
||||
// [ boot block | sb block | log | inode blocks | free bit map | data blocks ]
|
||||
|
||||
int nbitmap = FSSIZE/(BSIZE*8) + 1;
|
||||
int ninodeblocks = NINODES / IPB + 1;
|
||||
int nlog = LOGSIZE;
|
||||
int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
|
||||
int nblocks; // Number of data blocks
|
||||
|
||||
int fsfd;
|
||||
struct superblock sb;
|
||||
char zeroes[BSIZE];
|
||||
uint freeinode = 1;
|
||||
uint freeblock;
|
||||
|
||||
|
||||
void balloc(int);
|
||||
void wsect(uint, void*);
|
||||
void winode(uint, struct dinode*);
|
||||
void rinode(uint inum, struct dinode *ip);
|
||||
void rsect(uint sec, void *buf);
|
||||
uint ialloc(ushort type);
|
||||
void iappend(uint inum, void *p, int n);
|
||||
|
||||
// convert to intel byte order
|
||||
ushort
|
||||
xshort(ushort x)
|
||||
{
|
||||
ushort y;
|
||||
uchar *a = (uchar*)&y;
|
||||
a[0] = x;
|
||||
a[1] = x >> 8;
|
||||
return y;
|
||||
}
|
||||
|
||||
uint
|
||||
xint(uint x)
|
||||
{
|
||||
uint y;
|
||||
uchar *a = (uchar*)&y;
|
||||
a[0] = x;
|
||||
a[1] = x >> 8;
|
||||
a[2] = x >> 16;
|
||||
a[3] = x >> 24;
|
||||
return y;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i, cc, fd;
|
||||
uint rootino, inum, off;
|
||||
struct dirent de;
|
||||
char buf[BSIZE];
|
||||
struct dinode din;
|
||||
|
||||
|
||||
static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");
|
||||
|
||||
if(argc < 2){
|
||||
fprintf(stderr, "Usage: mkfs fs.img files...\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert((BSIZE % sizeof(struct dinode)) == 0);
|
||||
assert((BSIZE % sizeof(struct dirent)) == 0);
|
||||
|
||||
fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
|
||||
if(fsfd < 0){
|
||||
perror(argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 1 fs block = 1 disk sector
|
||||
nmeta = 2 + nlog + ninodeblocks + nbitmap;
|
||||
nblocks = FSSIZE - nmeta;
|
||||
|
||||
sb.size = xint(FSSIZE);
|
||||
sb.nblocks = xint(nblocks);
|
||||
sb.ninodes = xint(NINODES);
|
||||
sb.nlog = xint(nlog);
|
||||
sb.logstart = xint(2);
|
||||
sb.inodestart = xint(2+nlog);
|
||||
sb.bmapstart = xint(2+nlog+ninodeblocks);
|
||||
|
||||
printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n",
|
||||
nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE);
|
||||
|
||||
freeblock = nmeta; // the first free block that we can allocate
|
||||
|
||||
for(i = 0; i < FSSIZE; i++)
|
||||
wsect(i, zeroes);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memmove(buf, &sb, sizeof(sb));
|
||||
wsect(1, buf);
|
||||
|
||||
rootino = ialloc(T_DIR);
|
||||
assert(rootino == ROOTINO);
|
||||
|
||||
bzero(&de, sizeof(de));
|
||||
de.inum = xshort(rootino);
|
||||
strcpy(de.name, ".");
|
||||
iappend(rootino, &de, sizeof(de));
|
||||
|
||||
bzero(&de, sizeof(de));
|
||||
de.inum = xshort(rootino);
|
||||
strcpy(de.name, "..");
|
||||
iappend(rootino, &de, sizeof(de));
|
||||
|
||||
for(i = 2; i < argc; i++){
|
||||
assert(index(argv[i], '/') == 0);
|
||||
|
||||
if((fd = open(argv[i], 0)) < 0){
|
||||
perror(argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Skip leading _ in name when writing to file system.
|
||||
// The binaries are named _rm, _cat, etc. to keep the
|
||||
// build operating system from trying to execute them
|
||||
// in place of system binaries like rm and cat.
|
||||
if(argv[i][0] == '_')
|
||||
++argv[i];
|
||||
|
||||
inum = ialloc(T_FILE);
|
||||
|
||||
bzero(&de, sizeof(de));
|
||||
de.inum = xshort(inum);
|
||||
strncpy(de.name, argv[i], DIRSIZ);
|
||||
iappend(rootino, &de, sizeof(de));
|
||||
|
||||
while((cc = read(fd, buf, sizeof(buf))) > 0)
|
||||
iappend(inum, buf, cc);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// fix size of root inode dir
|
||||
rinode(rootino, &din);
|
||||
off = xint(din.size);
|
||||
off = ((off/BSIZE) + 1) * BSIZE;
|
||||
din.size = xint(off);
|
||||
winode(rootino, &din);
|
||||
|
||||
balloc(freeblock);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
wsect(uint sec, void *buf)
|
||||
{
|
||||
if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){
|
||||
perror("lseek");
|
||||
exit(1);
|
||||
}
|
||||
if(write(fsfd, buf, BSIZE) != BSIZE){
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
winode(uint inum, struct dinode *ip)
|
||||
{
|
||||
char buf[BSIZE];
|
||||
uint bn;
|
||||
struct dinode *dip;
|
||||
|
||||
bn = IBLOCK(inum, sb);
|
||||
rsect(bn, buf);
|
||||
dip = ((struct dinode*)buf) + (inum % IPB);
|
||||
*dip = *ip;
|
||||
wsect(bn, buf);
|
||||
}
|
||||
|
||||
void
|
||||
rinode(uint inum, struct dinode *ip)
|
||||
{
|
||||
char buf[BSIZE];
|
||||
uint bn;
|
||||
struct dinode *dip;
|
||||
|
||||
bn = IBLOCK(inum, sb);
|
||||
rsect(bn, buf);
|
||||
dip = ((struct dinode*)buf) + (inum % IPB);
|
||||
*ip = *dip;
|
||||
}
|
||||
|
||||
void
|
||||
rsect(uint sec, void *buf)
|
||||
{
|
||||
if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){
|
||||
perror("lseek");
|
||||
exit(1);
|
||||
}
|
||||
if(read(fsfd, buf, BSIZE) != BSIZE){
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint
|
||||
ialloc(ushort type)
|
||||
{
|
||||
uint inum = freeinode++;
|
||||
struct dinode din;
|
||||
|
||||
bzero(&din, sizeof(din));
|
||||
din.type = xshort(type);
|
||||
din.nlink = xshort(1);
|
||||
din.size = xint(0);
|
||||
winode(inum, &din);
|
||||
return inum;
|
||||
}
|
||||
|
||||
void
|
||||
balloc(int used)
|
||||
{
|
||||
uchar buf[BSIZE];
|
||||
int i;
|
||||
|
||||
printf("balloc: first %d blocks have been allocated\n", used);
|
||||
assert(used < BSIZE*8);
|
||||
bzero(buf, BSIZE);
|
||||
for(i = 0; i < used; i++){
|
||||
buf[i/8] = buf[i/8] | (0x1 << (i%8));
|
||||
}
|
||||
printf("balloc: write bitmap block at sector %d\n", sb.bmapstart);
|
||||
wsect(sb.bmapstart, buf);
|
||||
}
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
void
|
||||
iappend(uint inum, void *xp, int n)
|
||||
{
|
||||
char *p = (char*)xp;
|
||||
uint fbn, off, n1;
|
||||
struct dinode din;
|
||||
char buf[BSIZE];
|
||||
uint indirect[NINDIRECT];
|
||||
uint x;
|
||||
|
||||
rinode(inum, &din);
|
||||
off = xint(din.size);
|
||||
// printf("append inum %d at off %d sz %d\n", inum, off, n);
|
||||
while(n > 0){
|
||||
fbn = off / BSIZE;
|
||||
assert(fbn < MAXFILE);
|
||||
if(fbn < NDIRECT){
|
||||
if(xint(din.addrs[fbn]) == 0){
|
||||
din.addrs[fbn] = xint(freeblock++);
|
||||
}
|
||||
x = xint(din.addrs[fbn]);
|
||||
} else {
|
||||
if(xint(din.addrs[NDIRECT]) == 0){
|
||||
din.addrs[NDIRECT] = xint(freeblock++);
|
||||
}
|
||||
rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
|
||||
if(indirect[fbn - NDIRECT] == 0){
|
||||
indirect[fbn - NDIRECT] = xint(freeblock++);
|
||||
wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
|
||||
}
|
||||
x = xint(indirect[fbn-NDIRECT]);
|
||||
}
|
||||
n1 = min(n, (fbn + 1) * BSIZE - off);
|
||||
rsect(x, buf);
|
||||
bcopy(p, buf + off - (fbn * BSIZE), n1);
|
||||
wsect(x, buf);
|
||||
n -= n1;
|
||||
off += n1;
|
||||
p += n1;
|
||||
}
|
||||
din.size = xint(off);
|
||||
winode(inum, &din);
|
||||
}
|
||||
181
xv6-public/mmu.h
Normal file
181
xv6-public/mmu.h
Normal file
@@ -0,0 +1,181 @@
|
||||
// This file contains definitions for the
|
||||
// x86 memory management unit (MMU).
|
||||
|
||||
// Eflags register
|
||||
#define FL_IF 0x00000200 // Interrupt Enable
|
||||
|
||||
// Control Register flags
|
||||
#define CR0_PE 0x00000001 // Protection Enable
|
||||
#define CR0_WP 0x00010000 // Write Protect
|
||||
#define CR0_PG 0x80000000 // Paging
|
||||
|
||||
#define CR4_PSE 0x00000010 // Page size extension
|
||||
|
||||
// various segment selectors.
|
||||
#define SEG_KCODE 1 // kernel code
|
||||
#define SEG_KDATA 2 // kernel data+stack
|
||||
#define SEG_UCODE 3 // user code
|
||||
#define SEG_UDATA 4 // user data+stack
|
||||
#define SEG_TSS 5 // this process's task state
|
||||
|
||||
// cpu->gdt[NSEGS] holds the above segments.
|
||||
#define NSEGS 6
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
// Segment Descriptor
|
||||
struct segdesc {
|
||||
uint lim_15_0 : 16; // Low bits of segment limit
|
||||
uint base_15_0 : 16; // Low bits of segment base address
|
||||
uint base_23_16 : 8; // Middle bits of segment base address
|
||||
uint type : 4; // Segment type (see STS_ constants)
|
||||
uint s : 1; // 0 = system, 1 = application
|
||||
uint dpl : 2; // Descriptor Privilege Level
|
||||
uint p : 1; // Present
|
||||
uint lim_19_16 : 4; // High bits of segment limit
|
||||
uint avl : 1; // Unused (available for software use)
|
||||
uint rsv1 : 1; // Reserved
|
||||
uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||
uint g : 1; // Granularity: limit scaled by 4K when set
|
||||
uint base_31_24 : 8; // High bits of segment base address
|
||||
};
|
||||
|
||||
// Normal segment
|
||||
#define SEG(type, base, lim, dpl) (struct segdesc) \
|
||||
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
|
||||
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
|
||||
#define SEG16(type, base, lim, dpl) (struct segdesc) \
|
||||
{ (lim) & 0xffff, (uint)(base) & 0xffff, \
|
||||
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
|
||||
#endif
|
||||
|
||||
#define DPL_USER 0x3 // User DPL
|
||||
|
||||
// Application segment type bits
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
|
||||
// System segment type bits
|
||||
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
// A virtual address 'la' has a three-part structure as follows:
|
||||
//
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
// | Page Directory | Page Table | Offset within Page |
|
||||
// | Index | Index | |
|
||||
// +----------------+----------------+---------------------+
|
||||
// \--- PDX(va) --/ \--- PTX(va) --/
|
||||
|
||||
// page directory index
|
||||
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)
|
||||
|
||||
// page table index
|
||||
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)
|
||||
|
||||
// construct virtual address from indexes and offset
|
||||
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
|
||||
// Page directory and page table constants.
|
||||
#define NPDENTRIES 1024 // # directory entries per page directory
|
||||
#define NPTENTRIES 1024 // # PTEs per page table
|
||||
#define PGSIZE 4096 // bytes mapped by a page
|
||||
|
||||
#define PTXSHIFT 12 // offset of PTX in a linear address
|
||||
#define PDXSHIFT 22 // offset of PDX in a linear address
|
||||
|
||||
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
|
||||
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
|
||||
|
||||
// Page table/directory entry flags.
|
||||
#define PTE_P 0x001 // Present
|
||||
#define PTE_W 0x002 // Writeable
|
||||
#define PTE_U 0x004 // User
|
||||
#define PTE_PS 0x080 // Page Size
|
||||
|
||||
// Address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
|
||||
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
typedef uint pte_t;
|
||||
|
||||
// Task state segment format
|
||||
struct taskstate {
|
||||
uint link; // Old ts selector
|
||||
uint esp0; // Stack pointers and segment selectors
|
||||
ushort ss0; // after an increase in privilege level
|
||||
ushort padding1;
|
||||
uint *esp1;
|
||||
ushort ss1;
|
||||
ushort padding2;
|
||||
uint *esp2;
|
||||
ushort ss2;
|
||||
ushort padding3;
|
||||
void *cr3; // Page directory base
|
||||
uint *eip; // Saved state from last task switch
|
||||
uint eflags;
|
||||
uint eax; // More saved state (registers)
|
||||
uint ecx;
|
||||
uint edx;
|
||||
uint ebx;
|
||||
uint *esp;
|
||||
uint *ebp;
|
||||
uint esi;
|
||||
uint edi;
|
||||
ushort es; // Even more saved state (segment selectors)
|
||||
ushort padding4;
|
||||
ushort cs;
|
||||
ushort padding5;
|
||||
ushort ss;
|
||||
ushort padding6;
|
||||
ushort ds;
|
||||
ushort padding7;
|
||||
ushort fs;
|
||||
ushort padding8;
|
||||
ushort gs;
|
||||
ushort padding9;
|
||||
ushort ldt;
|
||||
ushort padding10;
|
||||
ushort t; // Trap on task switch
|
||||
ushort iomb; // I/O map base address
|
||||
};
|
||||
|
||||
// Gate descriptors for interrupts and traps
|
||||
struct gatedesc {
|
||||
uint off_15_0 : 16; // low 16 bits of offset in segment
|
||||
uint cs : 16; // code segment selector
|
||||
uint args : 5; // # args, 0 for interrupt/trap gates
|
||||
uint rsv1 : 3; // reserved(should be zero I guess)
|
||||
uint type : 4; // type(STS_{IG32,TG32})
|
||||
uint s : 1; // must be 0 (system)
|
||||
uint dpl : 2; // descriptor(meaning new) privilege level
|
||||
uint p : 1; // Present
|
||||
uint off_31_16 : 16; // high bits of offset in segment
|
||||
};
|
||||
|
||||
// Set up a normal interrupt/trap gate descriptor.
|
||||
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
|
||||
// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
|
||||
// - sel: Code segment selector for interrupt/trap handler
|
||||
// - off: Offset in code segment for interrupt/trap handler
|
||||
// - dpl: Descriptor Privilege Level -
|
||||
// the privilege level required for software to invoke
|
||||
// this interrupt/trap gate explicitly using an int instruction.
|
||||
#define SETGATE(gate, istrap, sel, off, d) \
|
||||
{ \
|
||||
(gate).off_15_0 = (uint)(off) & 0xffff; \
|
||||
(gate).cs = (sel); \
|
||||
(gate).args = 0; \
|
||||
(gate).rsv1 = 0; \
|
||||
(gate).type = (istrap) ? STS_TG32 : STS_IG32; \
|
||||
(gate).s = 0; \
|
||||
(gate).dpl = (d); \
|
||||
(gate).p = 1; \
|
||||
(gate).off_31_16 = (uint)(off) >> 16; \
|
||||
}
|
||||
|
||||
#endif
|
||||
139
xv6-public/mp.c
Normal file
139
xv6-public/mp.c
Normal file
@@ -0,0 +1,139 @@
|
||||
// Multiprocessor support
|
||||
// Search memory for MP description structures.
|
||||
// http://developer.intel.com/design/pentium/datashts/24201606.pdf
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mp.h"
|
||||
#include "x86.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
|
||||
struct cpu cpus[NCPU];
|
||||
int ncpu;
|
||||
uchar ioapicid;
|
||||
|
||||
static uchar
|
||||
sum(uchar *addr, int len)
|
||||
{
|
||||
int i, sum;
|
||||
|
||||
sum = 0;
|
||||
for(i=0; i<len; i++)
|
||||
sum += addr[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Look for an MP structure in the len bytes at addr.
|
||||
static struct mp*
|
||||
mpsearch1(uint a, int len)
|
||||
{
|
||||
uchar *e, *p, *addr;
|
||||
|
||||
addr = P2V(a);
|
||||
e = addr+len;
|
||||
for(p = addr; p < e; p += sizeof(struct mp))
|
||||
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
|
||||
return (struct mp*)p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search for the MP Floating Pointer Structure, which according to the
|
||||
// spec is in one of the following three locations:
|
||||
// 1) in the first KB of the EBDA;
|
||||
// 2) in the last KB of system base memory;
|
||||
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
|
||||
static struct mp*
|
||||
mpsearch(void)
|
||||
{
|
||||
uchar *bda;
|
||||
uint p;
|
||||
struct mp *mp;
|
||||
|
||||
bda = (uchar *) P2V(0x400);
|
||||
if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
|
||||
if((mp = mpsearch1(p, 1024)))
|
||||
return mp;
|
||||
} else {
|
||||
p = ((bda[0x14]<<8)|bda[0x13])*1024;
|
||||
if((mp = mpsearch1(p-1024, 1024)))
|
||||
return mp;
|
||||
}
|
||||
return mpsearch1(0xF0000, 0x10000);
|
||||
}
|
||||
|
||||
// Search for an MP configuration table. For now,
|
||||
// don't accept the default configurations (physaddr == 0).
|
||||
// Check for correct signature, calculate the checksum and,
|
||||
// if correct, check the version.
|
||||
// To do: check extended table checksum.
|
||||
static struct mpconf*
|
||||
mpconfig(struct mp **pmp)
|
||||
{
|
||||
struct mpconf *conf;
|
||||
struct mp *mp;
|
||||
|
||||
if((mp = mpsearch()) == 0 || mp->physaddr == 0)
|
||||
return 0;
|
||||
conf = (struct mpconf*) P2V((uint) mp->physaddr);
|
||||
if(memcmp(conf, "PCMP", 4) != 0)
|
||||
return 0;
|
||||
if(conf->version != 1 && conf->version != 4)
|
||||
return 0;
|
||||
if(sum((uchar*)conf, conf->length) != 0)
|
||||
return 0;
|
||||
*pmp = mp;
|
||||
return conf;
|
||||
}
|
||||
|
||||
void
|
||||
mpinit(void)
|
||||
{
|
||||
uchar *p, *e;
|
||||
int ismp;
|
||||
struct mp *mp;
|
||||
struct mpconf *conf;
|
||||
struct mpproc *proc;
|
||||
struct mpioapic *ioapic;
|
||||
|
||||
if((conf = mpconfig(&mp)) == 0)
|
||||
panic("Expect to run on an SMP");
|
||||
ismp = 1;
|
||||
lapic = (uint*)conf->lapicaddr;
|
||||
for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
|
||||
switch(*p){
|
||||
case MPPROC:
|
||||
proc = (struct mpproc*)p;
|
||||
if(ncpu < NCPU) {
|
||||
cpus[ncpu].apicid = proc->apicid; // apicid may differ from ncpu
|
||||
ncpu++;
|
||||
}
|
||||
p += sizeof(struct mpproc);
|
||||
continue;
|
||||
case MPIOAPIC:
|
||||
ioapic = (struct mpioapic*)p;
|
||||
ioapicid = ioapic->apicno;
|
||||
p += sizeof(struct mpioapic);
|
||||
continue;
|
||||
case MPBUS:
|
||||
case MPIOINTR:
|
||||
case MPLINTR:
|
||||
p += 8;
|
||||
continue;
|
||||
default:
|
||||
ismp = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!ismp)
|
||||
panic("Didn't find a suitable machine");
|
||||
|
||||
if(mp->imcrp){
|
||||
// Bochs doesn't support IMCR, so this doesn't run on Bochs.
|
||||
// But it would on real hardware.
|
||||
outb(0x22, 0x70); // Select IMCR
|
||||
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
|
||||
}
|
||||
}
|
||||
56
xv6-public/mp.h
Normal file
56
xv6-public/mp.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// See MultiProcessor Specification Version 1.[14]
|
||||
|
||||
struct mp { // floating pointer
|
||||
uchar signature[4]; // "_MP_"
|
||||
void *physaddr; // phys addr of MP config table
|
||||
uchar length; // 1
|
||||
uchar specrev; // [14]
|
||||
uchar checksum; // all bytes must add up to 0
|
||||
uchar type; // MP system config type
|
||||
uchar imcrp;
|
||||
uchar reserved[3];
|
||||
};
|
||||
|
||||
struct mpconf { // configuration table header
|
||||
uchar signature[4]; // "PCMP"
|
||||
ushort length; // total table length
|
||||
uchar version; // [14]
|
||||
uchar checksum; // all bytes must add up to 0
|
||||
uchar product[20]; // product id
|
||||
uint *oemtable; // OEM table pointer
|
||||
ushort oemlength; // OEM table length
|
||||
ushort entry; // entry count
|
||||
uint *lapicaddr; // address of local APIC
|
||||
ushort xlength; // extended table length
|
||||
uchar xchecksum; // extended table checksum
|
||||
uchar reserved;
|
||||
};
|
||||
|
||||
struct mpproc { // processor table entry
|
||||
uchar type; // entry type (0)
|
||||
uchar apicid; // local APIC id
|
||||
uchar version; // local APIC verison
|
||||
uchar flags; // CPU flags
|
||||
#define MPBOOT 0x02 // This proc is the bootstrap processor.
|
||||
uchar signature[4]; // CPU signature
|
||||
uint feature; // feature flags from CPUID instruction
|
||||
uchar reserved[8];
|
||||
};
|
||||
|
||||
struct mpioapic { // I/O APIC table entry
|
||||
uchar type; // entry type (2)
|
||||
uchar apicno; // I/O APIC id
|
||||
uchar version; // I/O APIC version
|
||||
uchar flags; // I/O APIC flags
|
||||
uint *addr; // I/O APIC address
|
||||
};
|
||||
|
||||
// Table entry types
|
||||
#define MPPROC 0x00 // One per processor
|
||||
#define MPBUS 0x01 // One per bus
|
||||
#define MPIOAPIC 0x02 // One per I/O APIC
|
||||
#define MPIOINTR 0x03 // One per bus interrupt source
|
||||
#define MPLINTR 0x04 // One per system interrupt source
|
||||
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
14
xv6-public/param.h
Normal file
14
xv6-public/param.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#define NPROC 64 // maximum number of processes
|
||||
#define KSTACKSIZE 4096 // size of per-process kernel stack
|
||||
#define NCPU 8 // maximum number of CPUs
|
||||
#define NOFILE 16 // open files per process
|
||||
#define NFILE 100 // open files per system
|
||||
#define NINODE 50 // maximum number of active i-nodes
|
||||
#define NDEV 10 // maximum major device number
|
||||
#define ROOTDEV 1 // device number of file system root disk
|
||||
#define MAXARG 32 // max exec arguments
|
||||
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
|
||||
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
|
||||
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache
|
||||
#define FSSIZE 1000 // size of file system in blocks
|
||||
|
||||
19
xv6-public/picirq.c
Normal file
19
xv6-public/picirq.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "types.h"
|
||||
#include "x86.h"
|
||||
#include "traps.h"
|
||||
|
||||
// I/O Addresses of the two programmable interrupt controllers
|
||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
||||
|
||||
// Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware.
|
||||
void
|
||||
picinit(void)
|
||||
{
|
||||
// mask all interrupts
|
||||
outb(IO_PIC1+1, 0xFF);
|
||||
outb(IO_PIC2+1, 0xFF);
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Blank page.
|
||||
121
xv6-public/pipe.c
Normal file
121
xv6-public/pipe.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "fs.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "file.h"
|
||||
|
||||
#define PIPESIZE 512
|
||||
|
||||
struct pipe {
|
||||
struct spinlock lock;
|
||||
char data[PIPESIZE];
|
||||
uint nread; // number of bytes read
|
||||
uint nwrite; // number of bytes written
|
||||
int readopen; // read fd is still open
|
||||
int writeopen; // write fd is still open
|
||||
};
|
||||
|
||||
int
|
||||
pipealloc(struct file **f0, struct file **f1)
|
||||
{
|
||||
struct pipe *p;
|
||||
|
||||
p = 0;
|
||||
*f0 = *f1 = 0;
|
||||
if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
|
||||
goto bad;
|
||||
if((p = (struct pipe*)kalloc()) == 0)
|
||||
goto bad;
|
||||
p->readopen = 1;
|
||||
p->writeopen = 1;
|
||||
p->nwrite = 0;
|
||||
p->nread = 0;
|
||||
initlock(&p->lock, "pipe");
|
||||
(*f0)->type = FD_PIPE;
|
||||
(*f0)->readable = 1;
|
||||
(*f0)->writable = 0;
|
||||
(*f0)->pipe = p;
|
||||
(*f1)->type = FD_PIPE;
|
||||
(*f1)->readable = 0;
|
||||
(*f1)->writable = 1;
|
||||
(*f1)->pipe = p;
|
||||
return 0;
|
||||
|
||||
//PAGEBREAK: 20
|
||||
bad:
|
||||
if(p)
|
||||
kfree((char*)p);
|
||||
if(*f0)
|
||||
fileclose(*f0);
|
||||
if(*f1)
|
||||
fileclose(*f1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
pipeclose(struct pipe *p, int writable)
|
||||
{
|
||||
acquire(&p->lock);
|
||||
if(writable){
|
||||
p->writeopen = 0;
|
||||
wakeup(&p->nread);
|
||||
} else {
|
||||
p->readopen = 0;
|
||||
wakeup(&p->nwrite);
|
||||
}
|
||||
if(p->readopen == 0 && p->writeopen == 0){
|
||||
release(&p->lock);
|
||||
kfree((char*)p);
|
||||
} else
|
||||
release(&p->lock);
|
||||
}
|
||||
|
||||
//PAGEBREAK: 40
|
||||
int
|
||||
pipewrite(struct pipe *p, char *addr, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
acquire(&p->lock);
|
||||
for(i = 0; i < n; i++){
|
||||
while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full
|
||||
if(p->readopen == 0 || myproc()->killed){
|
||||
release(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
wakeup(&p->nread);
|
||||
sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep
|
||||
}
|
||||
p->data[p->nwrite++ % PIPESIZE] = addr[i];
|
||||
}
|
||||
wakeup(&p->nread); //DOC: pipewrite-wakeup1
|
||||
release(&p->lock);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
piperead(struct pipe *p, char *addr, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
acquire(&p->lock);
|
||||
while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty
|
||||
if(myproc()->killed){
|
||||
release(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
sleep(&p->nread, &p->lock); //DOC: piperead-sleep
|
||||
}
|
||||
for(i = 0; i < n; i++){ //DOC: piperead-copy
|
||||
if(p->nread == p->nwrite)
|
||||
break;
|
||||
addr[i] = p->data[p->nread++ % PIPESIZE];
|
||||
}
|
||||
wakeup(&p->nwrite); //DOC: piperead-wakeup
|
||||
release(&p->lock);
|
||||
return i;
|
||||
}
|
||||
36
xv6-public/pr.pl
Normal file
36
xv6-public/pr.pl
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use POSIX qw(strftime);
|
||||
|
||||
if($ARGV[0] eq "-h"){
|
||||
shift @ARGV;
|
||||
$h = $ARGV[0];
|
||||
shift @ARGV;
|
||||
}else{
|
||||
$h = $ARGV[0];
|
||||
}
|
||||
|
||||
$page = 0;
|
||||
$now = strftime "%b %e %H:%M %Y", localtime;
|
||||
|
||||
@lines = <>;
|
||||
for($i=0; $i<@lines; $i+=50){
|
||||
print "\n\n";
|
||||
++$page;
|
||||
print "$now $h Page $page\n";
|
||||
print "\n\n";
|
||||
for($j=$i; $j<@lines && $j<$i +50; $j++){
|
||||
$lines[$j] =~ s!//DOC.*!!;
|
||||
print $lines[$j];
|
||||
}
|
||||
for(; $j<$i+50; $j++){
|
||||
print "\n";
|
||||
}
|
||||
$sheet = "";
|
||||
if($lines[$i] =~ /^([0-9][0-9])[0-9][0-9] /){
|
||||
$sheet = "Sheet $1";
|
||||
}
|
||||
print "\n\n";
|
||||
print "$sheet\n";
|
||||
print "\n\n";
|
||||
}
|
||||
85
xv6-public/printf.c
Normal file
85
xv6-public/printf.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
static void
|
||||
putc(int fd, char c)
|
||||
{
|
||||
write(fd, &c, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
printint(int fd, int xx, int base, int sgn)
|
||||
{
|
||||
static char digits[] = "0123456789ABCDEF";
|
||||
char buf[16];
|
||||
int i, neg;
|
||||
uint x;
|
||||
|
||||
neg = 0;
|
||||
if(sgn && xx < 0){
|
||||
neg = 1;
|
||||
x = -xx;
|
||||
} else {
|
||||
x = xx;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do{
|
||||
buf[i++] = digits[x % base];
|
||||
}while((x /= base) != 0);
|
||||
if(neg)
|
||||
buf[i++] = '-';
|
||||
|
||||
while(--i >= 0)
|
||||
putc(fd, buf[i]);
|
||||
}
|
||||
|
||||
// Print to the given fd. Only understands %d, %x, %p, %s.
|
||||
void
|
||||
printf(int fd, const char *fmt, ...)
|
||||
{
|
||||
char *s;
|
||||
int c, i, state;
|
||||
uint *ap;
|
||||
|
||||
state = 0;
|
||||
ap = (uint*)(void*)&fmt + 1;
|
||||
for(i = 0; fmt[i]; i++){
|
||||
c = fmt[i] & 0xff;
|
||||
if(state == 0){
|
||||
if(c == '%'){
|
||||
state = '%';
|
||||
} else {
|
||||
putc(fd, c);
|
||||
}
|
||||
} else if(state == '%'){
|
||||
if(c == 'd'){
|
||||
printint(fd, *ap, 10, 1);
|
||||
ap++;
|
||||
} else if(c == 'x' || c == 'p'){
|
||||
printint(fd, *ap, 16, 0);
|
||||
ap++;
|
||||
} else if(c == 's'){
|
||||
s = (char*)*ap;
|
||||
ap++;
|
||||
if(s == 0)
|
||||
s = "(null)";
|
||||
while(*s != 0){
|
||||
putc(fd, *s);
|
||||
s++;
|
||||
}
|
||||
} else if(c == 'c'){
|
||||
putc(fd, *ap);
|
||||
ap++;
|
||||
} else if(c == '%'){
|
||||
putc(fd, c);
|
||||
} else {
|
||||
// Unknown % sequence. Print it to draw attention.
|
||||
putc(fd, '%');
|
||||
putc(fd, c);
|
||||
}
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
xv6-public/printpcs
Normal file
14
xv6-public/printpcs
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Decode the symbols from a panic EIP list
|
||||
|
||||
# Find a working addr2line
|
||||
for p in i386-jos-elf-addr2line addr2line; do
|
||||
if which $p 2>&1 >/dev/null && \
|
||||
$p -h 2>&1 | grep -q '\belf32-i386\b'; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Enable as much pretty-printing as this addr2line can do
|
||||
$p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@"
|
||||
534
xv6-public/proc.c
Normal file
534
xv6-public/proc.c
Normal file
@@ -0,0 +1,534 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "x86.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct proc proc[NPROC];
|
||||
} ptable;
|
||||
|
||||
static struct proc *initproc;
|
||||
|
||||
int nextpid = 1;
|
||||
extern void forkret(void);
|
||||
extern void trapret(void);
|
||||
|
||||
static void wakeup1(void *chan);
|
||||
|
||||
void
|
||||
pinit(void)
|
||||
{
|
||||
initlock(&ptable.lock, "ptable");
|
||||
}
|
||||
|
||||
// Must be called with interrupts disabled
|
||||
int
|
||||
cpuid() {
|
||||
return mycpu()-cpus;
|
||||
}
|
||||
|
||||
// Must be called with interrupts disabled to avoid the caller being
|
||||
// rescheduled between reading lapicid and running through the loop.
|
||||
struct cpu*
|
||||
mycpu(void)
|
||||
{
|
||||
int apicid, i;
|
||||
|
||||
if(readeflags()&FL_IF)
|
||||
panic("mycpu called with interrupts enabled\n");
|
||||
|
||||
apicid = lapicid();
|
||||
// APIC IDs are not guaranteed to be contiguous. Maybe we should have
|
||||
// a reverse map, or reserve a register to store &cpus[i].
|
||||
for (i = 0; i < ncpu; ++i) {
|
||||
if (cpus[i].apicid == apicid)
|
||||
return &cpus[i];
|
||||
}
|
||||
panic("unknown apicid\n");
|
||||
}
|
||||
|
||||
// Disable interrupts so that we are not rescheduled
|
||||
// while reading proc from the cpu structure
|
||||
struct proc*
|
||||
myproc(void) {
|
||||
struct cpu *c;
|
||||
struct proc *p;
|
||||
pushcli();
|
||||
c = mycpu();
|
||||
p = c->proc;
|
||||
popcli();
|
||||
return p;
|
||||
}
|
||||
|
||||
//PAGEBREAK: 32
|
||||
// Look in the process table for an UNUSED proc.
|
||||
// If found, change state to EMBRYO and initialize
|
||||
// state required to run in the kernel.
|
||||
// Otherwise return 0.
|
||||
static struct proc*
|
||||
allocproc(void)
|
||||
{
|
||||
struct proc *p;
|
||||
char *sp;
|
||||
|
||||
acquire(&ptable.lock);
|
||||
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)
|
||||
if(p->state == UNUSED)
|
||||
goto found;
|
||||
|
||||
release(&ptable.lock);
|
||||
return 0;
|
||||
|
||||
found:
|
||||
p->state = EMBRYO;
|
||||
p->pid = nextpid++;
|
||||
p->ticks = 0;
|
||||
release(&ptable.lock);
|
||||
|
||||
// Allocate kernel stack.
|
||||
if((p->kstack = kalloc()) == 0){
|
||||
p->state = UNUSED;
|
||||
return 0;
|
||||
}
|
||||
sp = p->kstack + KSTACKSIZE;
|
||||
|
||||
// Leave room for trap frame.
|
||||
sp -= sizeof *p->tf;
|
||||
p->tf = (struct trapframe*)sp;
|
||||
|
||||
// Set up new context to start executing at forkret,
|
||||
// which returns to trapret.
|
||||
sp -= 4;
|
||||
*(uint*)sp = (uint)trapret;
|
||||
|
||||
sp -= sizeof *p->context;
|
||||
p->context = (struct context*)sp;
|
||||
memset(p->context, 0, sizeof *p->context);
|
||||
p->context->eip = (uint)forkret;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
//PAGEBREAK: 32
|
||||
// Set up first user process.
|
||||
void
|
||||
userinit(void)
|
||||
{
|
||||
struct proc *p;
|
||||
extern char _binary_initcode_start[], _binary_initcode_size[];
|
||||
|
||||
p = allocproc();
|
||||
|
||||
initproc = p;
|
||||
if((p->pgdir = setupkvm()) == 0)
|
||||
panic("userinit: out of memory?");
|
||||
inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);
|
||||
p->sz = PGSIZE;
|
||||
memset(p->tf, 0, sizeof(*p->tf));
|
||||
p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
|
||||
p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
|
||||
p->tf->es = p->tf->ds;
|
||||
p->tf->ss = p->tf->ds;
|
||||
p->tf->eflags = FL_IF;
|
||||
p->tf->esp = PGSIZE;
|
||||
p->tf->eip = 0; // beginning of initcode.S
|
||||
|
||||
safestrcpy(p->name, "initcode", sizeof(p->name));
|
||||
p->cwd = namei("/");
|
||||
|
||||
// this assignment to p->state lets other cores
|
||||
// run this process. the acquire forces the above
|
||||
// writes to be visible, and the lock is also needed
|
||||
// because the assignment might not be atomic.
|
||||
acquire(&ptable.lock);
|
||||
|
||||
p->state = RUNNABLE;
|
||||
|
||||
release(&ptable.lock);
|
||||
}
|
||||
|
||||
// Grow current process's memory by n bytes.
|
||||
// Return 0 on success, -1 on failure.
|
||||
int
|
||||
growproc(int n)
|
||||
{
|
||||
uint sz;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
sz = curproc->sz;
|
||||
if(n > 0){
|
||||
if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0)
|
||||
return -1;
|
||||
} else if(n < 0){
|
||||
if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0)
|
||||
return -1;
|
||||
}
|
||||
curproc->sz = sz;
|
||||
switchuvm(curproc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a new process copying p as the parent.
|
||||
// Sets up stack to return as if from system call.
|
||||
// Caller must set state of returned proc to RUNNABLE.
|
||||
int
|
||||
fork(void)
|
||||
{
|
||||
int i, pid;
|
||||
struct proc *np;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
// Allocate process.
|
||||
if((np = allocproc()) == 0){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy process state from proc.
|
||||
if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){
|
||||
kfree(np->kstack);
|
||||
np->kstack = 0;
|
||||
np->state = UNUSED;
|
||||
return -1;
|
||||
}
|
||||
np->sz = curproc->sz;
|
||||
np->parent = curproc;
|
||||
*np->tf = *curproc->tf;
|
||||
|
||||
// Clear %eax so that fork returns 0 in the child.
|
||||
np->tf->eax = 0;
|
||||
|
||||
for(i = 0; i < NOFILE; i++)
|
||||
if(curproc->ofile[i])
|
||||
np->ofile[i] = filedup(curproc->ofile[i]);
|
||||
np->cwd = idup(curproc->cwd);
|
||||
|
||||
safestrcpy(np->name, curproc->name, sizeof(curproc->name));
|
||||
|
||||
pid = np->pid;
|
||||
|
||||
acquire(&ptable.lock);
|
||||
|
||||
np->state = RUNNABLE;
|
||||
|
||||
release(&ptable.lock);
|
||||
|
||||
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 *curproc = myproc();
|
||||
struct proc *p;
|
||||
int fd;
|
||||
|
||||
if(curproc == initproc)
|
||||
panic("init exiting");
|
||||
|
||||
// Close all open files.
|
||||
for(fd = 0; fd < NOFILE; fd++){
|
||||
if(curproc->ofile[fd]){
|
||||
fileclose(curproc->ofile[fd]);
|
||||
curproc->ofile[fd] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
begin_op();
|
||||
iput(curproc->cwd);
|
||||
end_op();
|
||||
curproc->cwd = 0;
|
||||
|
||||
acquire(&ptable.lock);
|
||||
|
||||
// Parent might be sleeping in wait().
|
||||
wakeup1(curproc->parent);
|
||||
|
||||
// Pass abandoned children to init.
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
|
||||
if(p->parent == curproc){
|
||||
p->parent = initproc;
|
||||
if(p->state == ZOMBIE)
|
||||
wakeup1(initproc);
|
||||
}
|
||||
}
|
||||
|
||||
// Jump into the scheduler, never to return.
|
||||
curproc->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;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
acquire(&ptable.lock);
|
||||
for(;;){
|
||||
// Scan through table looking for exited children.
|
||||
havekids = 0;
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
|
||||
if(p->parent != curproc)
|
||||
continue;
|
||||
havekids = 1;
|
||||
if(p->state == ZOMBIE){
|
||||
// Found one.
|
||||
pid = p->pid;
|
||||
kfree(p->kstack);
|
||||
p->kstack = 0;
|
||||
freevm(p->pgdir);
|
||||
p->pid = 0;
|
||||
p->parent = 0;
|
||||
p->name[0] = 0;
|
||||
p->killed = 0;
|
||||
p->state = UNUSED;
|
||||
release(&ptable.lock);
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
// No point waiting if we don't have any children.
|
||||
if(!havekids || curproc->killed){
|
||||
release(&ptable.lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Wait for children to exit. (See wakeup1 call in proc_exit.)
|
||||
sleep(curproc, &ptable.lock); //DOC: wait-sleep
|
||||
}
|
||||
}
|
||||
|
||||
//PAGEBREAK: 42
|
||||
// Per-CPU process scheduler.
|
||||
// Each CPU calls scheduler() after setting itself up.
|
||||
// Scheduler never returns. It loops, doing:
|
||||
// - choose a process to run
|
||||
// - swtch to start running that process
|
||||
// - eventually that process transfers control
|
||||
// via swtch back to the scheduler.
|
||||
void
|
||||
scheduler(void)
|
||||
{
|
||||
struct proc *p;
|
||||
struct cpu *c = mycpu();
|
||||
c->proc = 0;
|
||||
|
||||
for(;;){
|
||||
// Enable interrupts on this processor.
|
||||
sti();
|
||||
|
||||
// Loop over process table looking for process to run.
|
||||
acquire(&ptable.lock);
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
|
||||
if(p->state != RUNNABLE)
|
||||
continue;
|
||||
|
||||
// Switch to chosen process. It is the process's job
|
||||
// to release ptable.lock and then reacquire it
|
||||
// before jumping back to us.
|
||||
c->proc = p;
|
||||
switchuvm(p);
|
||||
p->state = RUNNING;
|
||||
|
||||
swtch(&(c->scheduler), p->context);
|
||||
switchkvm();
|
||||
|
||||
// Process is done running for now.
|
||||
// It should have changed its p->state before coming back.
|
||||
c->proc = 0;
|
||||
}
|
||||
release(&ptable.lock);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Enter scheduler. Must hold only ptable.lock
|
||||
// and have changed proc->state. Saves and restores
|
||||
// intena because intena is a property of this
|
||||
// kernel thread, not this CPU. It should
|
||||
// be proc->intena and proc->ncli, but that would
|
||||
// break in the few places where a lock is held but
|
||||
// there's no process.
|
||||
void
|
||||
sched(void)
|
||||
{
|
||||
int intena;
|
||||
struct proc *p = myproc();
|
||||
|
||||
if(!holding(&ptable.lock))
|
||||
panic("sched ptable.lock");
|
||||
if(mycpu()->ncli != 1)
|
||||
panic("sched locks");
|
||||
if(p->state == RUNNING)
|
||||
panic("sched running");
|
||||
if(readeflags()&FL_IF)
|
||||
panic("sched interruptible");
|
||||
intena = mycpu()->intena;
|
||||
swtch(&p->context, mycpu()->scheduler);
|
||||
mycpu()->intena = intena;
|
||||
}
|
||||
|
||||
// Give up the CPU for one scheduling round.
|
||||
void
|
||||
yield(void)
|
||||
{
|
||||
acquire(&ptable.lock); //DOC: yieldlock
|
||||
myproc()->state = RUNNABLE;
|
||||
sched();
|
||||
release(&ptable.lock);
|
||||
}
|
||||
|
||||
// A fork child's very first scheduling by scheduler()
|
||||
// will swtch here. "Return" to user space.
|
||||
void
|
||||
forkret(void)
|
||||
{
|
||||
static int first = 1;
|
||||
// Still holding ptable.lock from scheduler.
|
||||
release(&ptable.lock);
|
||||
|
||||
if (first) {
|
||||
// Some initialization functions must be run in the context
|
||||
// of a regular process (e.g., they call sleep), and thus cannot
|
||||
// be run from main().
|
||||
first = 0;
|
||||
iinit(ROOTDEV);
|
||||
initlog(ROOTDEV);
|
||||
}
|
||||
|
||||
// Return to "caller", actually trapret (see allocproc).
|
||||
}
|
||||
|
||||
// Atomically release lock and sleep on chan.
|
||||
// Reacquires lock when awakened.
|
||||
void
|
||||
sleep(void *chan, struct spinlock *lk)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
|
||||
if(p == 0)
|
||||
panic("sleep");
|
||||
|
||||
if(lk == 0)
|
||||
panic("sleep without lk");
|
||||
|
||||
// Must acquire ptable.lock in order to
|
||||
// change p->state and then call sched.
|
||||
// Once we hold ptable.lock, we can be
|
||||
// guaranteed that we won't miss any wakeup
|
||||
// (wakeup runs with ptable.lock locked),
|
||||
// so it's okay to release lk.
|
||||
if(lk != &ptable.lock){ //DOC: sleeplock0
|
||||
acquire(&ptable.lock); //DOC: sleeplock1
|
||||
release(lk);
|
||||
}
|
||||
// Go to sleep.
|
||||
p->chan = chan;
|
||||
p->state = SLEEPING;
|
||||
|
||||
sched();
|
||||
|
||||
// Tidy up.
|
||||
p->chan = 0;
|
||||
|
||||
// Reacquire original lock.
|
||||
if(lk != &ptable.lock){ //DOC: sleeplock2
|
||||
release(&ptable.lock);
|
||||
acquire(lk);
|
||||
}
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Wake up all processes sleeping on chan.
|
||||
// The ptable lock must be held.
|
||||
static void
|
||||
wakeup1(void *chan)
|
||||
{
|
||||
struct proc *p;
|
||||
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)
|
||||
if(p->state == SLEEPING && p->chan == chan)
|
||||
p->state = RUNNABLE;
|
||||
}
|
||||
|
||||
// Wake up all processes sleeping on chan.
|
||||
void
|
||||
wakeup(void *chan)
|
||||
{
|
||||
acquire(&ptable.lock);
|
||||
wakeup1(chan);
|
||||
release(&ptable.lock);
|
||||
}
|
||||
|
||||
// Kill the process with the given pid.
|
||||
// Process won't exit until it returns
|
||||
// to user space (see trap in trap.c).
|
||||
int
|
||||
kill(int pid)
|
||||
{
|
||||
struct proc *p;
|
||||
|
||||
acquire(&ptable.lock);
|
||||
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
|
||||
if(p->pid == pid){
|
||||
p->killed = 1;
|
||||
// Wake process from sleep if necessary.
|
||||
if(p->state == SLEEPING)
|
||||
p->state = RUNNABLE;
|
||||
release(&ptable.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
release(&ptable.lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//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");
|
||||
}
|
||||
}
|
||||
61
xv6-public/proc.h
Normal file
61
xv6-public/proc.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Per-CPU state
|
||||
struct cpu {
|
||||
uchar apicid; // Local APIC ID
|
||||
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 started; // Has the CPU started?
|
||||
int ncli; // Depth of pushcli nesting.
|
||||
int intena; // Were interrupts enabled before pushcli?
|
||||
struct proc *proc; // The process running on this cpu or null
|
||||
};
|
||||
|
||||
extern struct cpu cpus[NCPU];
|
||||
extern int ncpu;
|
||||
|
||||
//PAGEBREAK: 17
|
||||
// Saved registers for kernel context switches.
|
||||
// Don't need to save all the segment registers (%cs, etc),
|
||||
// because they are constant across kernel contexts.
|
||||
// Don't need to save %eax, %ecx, %edx, because the
|
||||
// x86 convention is that the caller has saved them.
|
||||
// Contexts are stored at the bottom of the stack they
|
||||
// describe; the stack pointer is the address of the context.
|
||||
// The layout of the context matches the layout of the stack in swtch.S
|
||||
// at the "Switch stacks" comment. Switch doesn't save eip explicitly,
|
||||
// but it is on the stack and allocproc() manipulates it.
|
||||
struct context {
|
||||
uint edi;
|
||||
uint esi;
|
||||
uint ebx;
|
||||
uint ebp;
|
||||
uint eip;
|
||||
};
|
||||
|
||||
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
|
||||
|
||||
// Per-process state
|
||||
struct proc {
|
||||
uint sz; // Size of process memory (bytes)
|
||||
pde_t* pgdir; // Page table
|
||||
char *kstack; // Bottom of kernel stack for this process
|
||||
enum procstate state; // Process state
|
||||
int pid; // Process ID
|
||||
struct proc *parent; // Parent process
|
||||
struct trapframe *tf; // Trap frame for current syscall
|
||||
struct context *context; // swtch() here to run process
|
||||
void *chan; // If non-zero, sleeping on chan
|
||||
int killed; // If non-zero, have been killed
|
||||
struct file *ofile[NOFILE]; // Open files
|
||||
struct inode *cwd; // Current directory
|
||||
char name[16]; // Process name (debugging)
|
||||
int alarminterval;
|
||||
void (*alarmhandler)(); // pointer of handler
|
||||
int ticks;
|
||||
};
|
||||
|
||||
// Process memory is laid out contiguously, low addresses first:
|
||||
// text
|
||||
// original data and bss
|
||||
// fixed-size stack
|
||||
// expandable heap
|
||||
23
xv6-public/rm.c
Normal file
23
xv6-public/rm.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc < 2){
|
||||
printf(2, "Usage: rm files...\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
for(i = 1; i < argc; i++){
|
||||
if(unlink(argv[i]) < 0){
|
||||
printf(2, "rm: %s failed to delete\n", argv[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
246
xv6-public/runoff
Normal file
246
xv6-public/runoff
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo This script takes a minute to run. Be patient. 1>&2
|
||||
|
||||
LC_CTYPE=C export LC_CTYPE
|
||||
|
||||
# pad stdin to multiple of 120 lines
|
||||
pad()
|
||||
{
|
||||
awk '{print} END{for(; NR%120!=0; NR++) print ""}'
|
||||
}
|
||||
|
||||
# create formatted (numbered) files
|
||||
mkdir -p fmt
|
||||
rm -f fmt/*
|
||||
cp README fmt
|
||||
echo > fmt/blank
|
||||
files=`grep -v '^#' runoff.list | awk '{print $1}'`
|
||||
n=99
|
||||
for i in $files
|
||||
do
|
||||
./runoff1 -n $n $i >fmt/$i
|
||||
nn=`tail -1 fmt/$i | sed 's/ .*//; s/^0*//'`
|
||||
if [ "x$nn" != x ]; then
|
||||
n=$nn
|
||||
fi
|
||||
done
|
||||
|
||||
# create table of contents
|
||||
cat toc.hdr >fmt/toc
|
||||
pr -e8 -t runoff.list | awk '
|
||||
/^[a-z0-9]/ {
|
||||
s=$0
|
||||
f="fmt/"$1
|
||||
getline<f
|
||||
close(f)
|
||||
n=$1
|
||||
printf("%02d %s\n", n/100, s);
|
||||
printf("TOC: %04d %s\n", n, s) >"fmt/tocdata"
|
||||
next
|
||||
}
|
||||
{
|
||||
print
|
||||
}' | pr -3 -t >>fmt/toc
|
||||
cat toc.ftr >>fmt/toc
|
||||
|
||||
# check for bad alignments
|
||||
perl -e '
|
||||
$leftwarn = 0;
|
||||
while(<>){
|
||||
chomp;
|
||||
s!#.*!!;
|
||||
s!\s+! !g;
|
||||
s! +$!!;
|
||||
next if /^$/;
|
||||
|
||||
if(/TOC: (\d+) (.*)/){
|
||||
$toc{$2} = $1;
|
||||
next;
|
||||
}
|
||||
|
||||
if(/sheet1: (left|right)$/){
|
||||
print STDERR "assuming that sheet 1 is a $1 page. double-check!\n";
|
||||
$left = $1 eq "left" ? "13579" : "02468";
|
||||
$right = $1 eq "left" ? "02468" : "13579";
|
||||
next;
|
||||
}
|
||||
|
||||
if(/even: (.*)/){
|
||||
$file = $1;
|
||||
if(!defined($toc{$file})){
|
||||
print STDERR "Have no toc for $file\n";
|
||||
next;
|
||||
}
|
||||
if($toc{$file} =~ /^\d\d[^0]/){
|
||||
print STDERR "$file does not start on a fresh page.\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
if(/odd: (.*)/){
|
||||
$file = $1;
|
||||
if(!defined($toc{$file})){
|
||||
print STDERR "Have no toc for $file\n";
|
||||
next;
|
||||
}
|
||||
if($toc{$file} !~ /^\d\d5/){
|
||||
print STDERR "$file does not start on a second half page.\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
if(/(left|right): (.*)/){
|
||||
$what = $1;
|
||||
$file = $2;
|
||||
if(!defined($toc{$file})){
|
||||
print STDERR "Have no toc for $file\n";
|
||||
next;
|
||||
}
|
||||
if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){
|
||||
print STDERR "$file does not start on a left page [$toc{$file}]\n";
|
||||
}
|
||||
# why does this not work if I inline $x in the if?
|
||||
$x = ($toc{$file} =~ /^\d[$right][05]/);
|
||||
if($what eq "right" && !$x){
|
||||
print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
print STDERR "Unknown spec: $_\n";
|
||||
}
|
||||
' fmt/tocdata runoff.spec
|
||||
|
||||
# make definition list
|
||||
cd fmt
|
||||
perl -e '
|
||||
while(<>) {
|
||||
chomp;
|
||||
|
||||
s!//.*!!;
|
||||
s!/\*([^*]|[*][^/])*\*/!!g;
|
||||
s!\s! !g;
|
||||
s! +$!!;
|
||||
|
||||
# look for declarations like char* x;
|
||||
if (/^[0-9]+ typedef .* u(int|short|long|char);/) {
|
||||
next;
|
||||
}
|
||||
if (/^[0-9]+ extern/) {
|
||||
next;
|
||||
}
|
||||
if (/^[0-9]+ struct [a-zA-Z0-9_]+;/) {
|
||||
next;
|
||||
}
|
||||
if (/^([0-9]+) #define +([A-za-z0-9_]+) +?\(.*/) {
|
||||
print "$1 $2\n"
|
||||
}
|
||||
elsif (/^([0-9]+) #define +([A-Za-z0-9_]+) +([^ ]+)/) {
|
||||
print "$1 $2 $3\n";
|
||||
}
|
||||
elsif (/^([0-9]+) #define +([A-Za-z0-9_]+)/) {
|
||||
print "$1 $2\n";
|
||||
}
|
||||
|
||||
if(/^^([0-9]+) \.globl ([a-zA-Z0-9_]+)/){
|
||||
$isglobl{$2} = 1;
|
||||
}
|
||||
if(/^^([0-9]+) ([a-zA-Z0-9_]+):$/ && $isglobl{$2}){
|
||||
print "$1 $2\n";
|
||||
}
|
||||
|
||||
if (/\(/) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (/^([0-9]+) (((static|struct|extern|union|enum) +)*([A-Za-z0-9_]+))( .*)? +([A-Za-z_][A-Za-z0-9_]*)(,|;|=| =)/) {
|
||||
print "$1 $7\n";
|
||||
}
|
||||
|
||||
elsif(/^([0-9]+) (enum|struct|union) +([A-Za-z0-9_]+) +{/){
|
||||
print "$1 $3\n";
|
||||
}
|
||||
# TODO: enum members
|
||||
}
|
||||
' $files >defs
|
||||
|
||||
(for i in $files
|
||||
do
|
||||
case "$i" in
|
||||
*.S)
|
||||
cat $i | sed 's;#.*;;; s;//.*;;;'
|
||||
;;
|
||||
*)
|
||||
cat $i | sed 's;//.*;;; s;"([^"\\]|\\.)*";;;'
|
||||
esac
|
||||
done
|
||||
) >alltext
|
||||
|
||||
perl -n -e 'print if s/^([0-9]+ [a-zA-Z0-9_]+)\(.*$/\1/;' alltext |
|
||||
egrep -v ' (STUB|usage|main|if|for)$' >>defs
|
||||
#perl -n -e 'print if s/^([0-9]+) STUB\(([a-zA-Z0-9_]+)\)$/\1 \2/;' alltext \
|
||||
# >>defs
|
||||
(
|
||||
>s.defs
|
||||
|
||||
# make reference list
|
||||
for i in `awk '{print $2}' defs | sort -f | uniq`
|
||||
do
|
||||
defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'`
|
||||
echo $i $defs >>s.defs
|
||||
uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'`
|
||||
if [ "x$defs" != "x$uses" ]; then
|
||||
echo $i $defs
|
||||
echo $uses |fmt -29 | sed 's/^/ /'
|
||||
# else
|
||||
# echo $i defined but not used >&2
|
||||
fi
|
||||
done
|
||||
) >refs
|
||||
|
||||
# build defs list
|
||||
awk '
|
||||
{
|
||||
printf("%04d %s\n", $2, $1);
|
||||
for(i=3; i<=NF; i++)
|
||||
printf("%04d \" \n", $i);
|
||||
}
|
||||
' s.defs > t.defs
|
||||
|
||||
# format the whole thing
|
||||
(
|
||||
../pr.pl README
|
||||
../pr.pl -h "table of contents" toc
|
||||
# pr -t -2 t.defs | ../pr.pl -h "definitions" | pad
|
||||
pr -t -l50 -2 refs | ../pr.pl -h "cross-references" | pad
|
||||
# pr.pl -h "definitions" -2 t.defs | pad
|
||||
# pr.pl -h "cross-references" -2 refs | pad
|
||||
../pr.pl blank # make sheet 1 start on left page
|
||||
../pr.pl blank
|
||||
for i in $files
|
||||
do
|
||||
../pr.pl -h "xv6/$i" $i
|
||||
done
|
||||
) | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >all.ps
|
||||
grep Pages: all.ps
|
||||
|
||||
# if we have the nice font, use it
|
||||
nicefont=LucidaSans-Typewriter83
|
||||
if [ ! -f ../$nicefont ]
|
||||
then
|
||||
if git cat-file blob font:$nicefont > ../$nicefont~; then
|
||||
mv ../$nicefont~ ../$nicefont
|
||||
fi
|
||||
fi
|
||||
if [ -f ../$nicefont ]
|
||||
then
|
||||
echo nicefont
|
||||
(sed 1q all.ps; cat ../$nicefont; sed "1d; s/Courier/$nicefont/" all.ps) >allf.ps
|
||||
else
|
||||
echo ugly font!
|
||||
cp all.ps allf.ps
|
||||
fi
|
||||
ps2pdf allf.ps ../xv6.pdf
|
||||
# cd ..
|
||||
# pdftops xv6.pdf xv6.ps
|
||||
80
xv6-public/runoff.list
Normal file
80
xv6-public/runoff.list
Normal file
@@ -0,0 +1,80 @@
|
||||
# basic headers
|
||||
types.h
|
||||
param.h
|
||||
memlayout.h
|
||||
defs.h
|
||||
x86.h
|
||||
asm.h
|
||||
mmu.h
|
||||
elf.h
|
||||
date.h
|
||||
|
||||
# entering xv6
|
||||
entry.S
|
||||
entryother.S
|
||||
main.c
|
||||
|
||||
# locks
|
||||
spinlock.h
|
||||
spinlock.c
|
||||
|
||||
# processes
|
||||
vm.c
|
||||
proc.h
|
||||
proc.c
|
||||
swtch.S
|
||||
kalloc.c
|
||||
|
||||
# system calls
|
||||
traps.h
|
||||
vectors.pl
|
||||
trapasm.S
|
||||
trap.c
|
||||
syscall.h
|
||||
syscall.c
|
||||
sysproc.c
|
||||
|
||||
# file system
|
||||
buf.h
|
||||
sleeplock.h
|
||||
fcntl.h
|
||||
stat.h
|
||||
fs.h
|
||||
file.h
|
||||
ide.c
|
||||
bio.c
|
||||
sleeplock.c
|
||||
log.c
|
||||
fs.c
|
||||
file.c
|
||||
sysfile.c
|
||||
exec.c
|
||||
|
||||
# pipes
|
||||
pipe.c
|
||||
|
||||
# string operations
|
||||
string.c
|
||||
|
||||
# low-level hardware
|
||||
mp.h
|
||||
mp.c
|
||||
lapic.c
|
||||
ioapic.c
|
||||
kbd.h
|
||||
kbd.c
|
||||
console.c
|
||||
uart.c
|
||||
|
||||
# user-level
|
||||
initcode.S
|
||||
usys.S
|
||||
init.c
|
||||
sh.c
|
||||
|
||||
# bootloader
|
||||
bootasm.S
|
||||
bootmain.c
|
||||
|
||||
# link
|
||||
kernel.ld
|
||||
102
xv6-public/runoff.spec
Normal file
102
xv6-public/runoff.spec
Normal file
@@ -0,0 +1,102 @@
|
||||
# Is sheet 01 (after the TOC) a left sheet or a right sheet?
|
||||
sheet1: left
|
||||
|
||||
# "left" and "right" specify which page of a two-page spread a file
|
||||
# must start on. "left" means that a file must start on the first of
|
||||
# the two pages. "right" means it must start on the second of the two
|
||||
# pages. The file may start in either column.
|
||||
#
|
||||
# "even" and "odd" specify which column a file must start on. "even"
|
||||
# means it must start in the left of the two columns (00). "odd" means it
|
||||
# must start in the right of the two columns (50).
|
||||
#
|
||||
# You'd think these would be the other way around.
|
||||
|
||||
# types.h either
|
||||
# param.h either
|
||||
# defs.h either
|
||||
# x86.h either
|
||||
# asm.h either
|
||||
# mmu.h either
|
||||
# elf.h either
|
||||
# mp.h either
|
||||
|
||||
even: entry.S # mild preference
|
||||
even: entryother.S # mild preference
|
||||
even: main.c
|
||||
# mp.c don't care at all
|
||||
# even: initcode.S
|
||||
# odd: init.c
|
||||
|
||||
left: spinlock.h
|
||||
even: spinlock.h
|
||||
|
||||
# This gets struct proc and allocproc on the same spread
|
||||
left: proc.h
|
||||
even: proc.h
|
||||
|
||||
# goal is to have two action-packed 2-page spreads,
|
||||
# one with
|
||||
# userinit growproc fork exit wait
|
||||
# and another with
|
||||
# scheduler sched yield forkret sleep wakeup1 wakeup
|
||||
right: proc.c # VERY important
|
||||
even: proc.c # VERY important
|
||||
|
||||
# A few more action packed spreads
|
||||
# page table creation and process loading
|
||||
# walkpgdir mappages setupkvm switch[ku]vm inituvm (loaduvm)
|
||||
# process memory management
|
||||
# allocuvm deallocuvm freevm
|
||||
left: vm.c
|
||||
|
||||
even: kalloc.c # mild preference
|
||||
|
||||
# syscall.h either
|
||||
# trapasm.S either
|
||||
# traps.h either
|
||||
# even: trap.c
|
||||
# vectors.pl either
|
||||
# syscall.c either
|
||||
# sysproc.c either
|
||||
|
||||
# buf.h either
|
||||
# dev.h either
|
||||
# fcntl.h either
|
||||
# stat.h either
|
||||
# file.h either
|
||||
# fs.h either
|
||||
# fsvar.h either
|
||||
# left: ide.c # mild preference
|
||||
even: ide.c
|
||||
# odd: bio.c
|
||||
|
||||
# log.c fits nicely in a spread
|
||||
even: log.c
|
||||
left: log.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
|
||||
# fileinit filealloc filedup fileclose filestat fileread filewrite
|
||||
# starting on 2nd column of a right page is not terrible either
|
||||
odd: fs.c # VERY important
|
||||
left: fs.c # mild preference
|
||||
# file.c either
|
||||
# exec.c either
|
||||
# sysfile.c either
|
||||
|
||||
# Mild preference, but makes spreads of mp.c, lapic.c, and ioapic.c+picirq.c
|
||||
even: mp.c
|
||||
left: mp.c
|
||||
|
||||
# even: pipe.c # mild preference
|
||||
# string.c either
|
||||
# left: kbd.h # mild preference
|
||||
even: kbd.h
|
||||
even: console.c
|
||||
odd: sh.c
|
||||
|
||||
even: bootasm.S # mild preference
|
||||
even: bootmain.c # mild preference
|
||||
108
xv6-public/runoff1
Normal file
108
xv6-public/runoff1
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
$n = 0;
|
||||
$v = 0;
|
||||
if($ARGV[0] eq "-v") {
|
||||
$v = 1;
|
||||
shift @ARGV;
|
||||
}
|
||||
if($ARGV[0] eq "-n") {
|
||||
$n = $ARGV[1];
|
||||
shift @ARGV;
|
||||
shift @ARGV;
|
||||
}
|
||||
$n = int(($n+49)/50)*50 - 1;
|
||||
|
||||
$file = $ARGV[0];
|
||||
@lines = <>;
|
||||
$linenum = 0;
|
||||
foreach (@lines) {
|
||||
$linenum++;
|
||||
chomp;
|
||||
s/\s+$//;
|
||||
if(length() >= 75){
|
||||
print STDERR "$file:$linenum: line too long\n";
|
||||
}
|
||||
}
|
||||
@outlines = ();
|
||||
$nextout = 0;
|
||||
|
||||
for($i=0; $i<@lines; ){
|
||||
# Skip leading blank lines.
|
||||
$i++ while $i<@lines && $lines[$i] =~ /^$/;
|
||||
last if $i>=@lines;
|
||||
|
||||
# If the rest of the file fits, use the whole thing.
|
||||
if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
|
||||
$breakbefore = @lines;
|
||||
}else{
|
||||
# Find a good next page break;
|
||||
# Hope for end of function.
|
||||
# but settle for a blank line (but not first blank line
|
||||
# in function, which comes after variable declarations).
|
||||
$breakbefore = $i;
|
||||
$lastblank = $i;
|
||||
$sawbrace = 0;
|
||||
$breaksize = 15; # 15 lines to get to function
|
||||
for($j=$i; $j<$i+50 && $j < @lines; $j++){
|
||||
if($lines[$j] =~ /PAGEBREAK!/){
|
||||
$lines[$j] = "";
|
||||
$breakbefore = $j;
|
||||
$breaksize = 100;
|
||||
last;
|
||||
}
|
||||
if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){
|
||||
$breaksize = $1;
|
||||
$breakbefore = $j;
|
||||
$lines[$j] = "";
|
||||
}
|
||||
if($lines[$j] =~ /^};?$/){
|
||||
$breakbefore = $j+1;
|
||||
$breaksize = 15;
|
||||
}
|
||||
if($lines[$j] =~ /^{$/){
|
||||
$sawbrace = 1;
|
||||
}
|
||||
if($lines[$j] =~ /^$/){
|
||||
if($sawbrace){
|
||||
$sawbrace = 0;
|
||||
}else{
|
||||
$lastblank = $j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($j<@lines && $lines[$j] =~ /^$/){
|
||||
$lastblank = $j;
|
||||
}
|
||||
|
||||
# If we are not putting enough on a page, try a blank line.
|
||||
if($breakbefore - $i < 50 - $breaksize && $lastblank > $breakbefore && $lastblank >= $i+50 - 5){
|
||||
if($v){
|
||||
print STDERR "breakbefore $breakbefore i $i breaksize $breaksize\n";
|
||||
}
|
||||
$breakbefore = $lastblank;
|
||||
$breaksize = 5; # only 5 lines to get to blank line
|
||||
}
|
||||
|
||||
# If we are not putting enough on a page, force a full page.
|
||||
if($breakbefore - $i < 50 - $breaksize && $breakbefore != @lines){
|
||||
$breakbefore = $i + 50;
|
||||
$breakbefore = @lines if @lines < $breakbefore;
|
||||
}
|
||||
|
||||
if($breakbefore < $i+2){
|
||||
$breakbefore = $i+2;
|
||||
}
|
||||
}
|
||||
|
||||
# Emit the page.
|
||||
$i50 = $i + 50;
|
||||
for(; $i<$breakbefore; $i++){
|
||||
printf "%04d %s\n", ++$n, $lines[$i];
|
||||
}
|
||||
|
||||
# Finish page
|
||||
for($j=$i; $j<$i50; $j++){
|
||||
printf "%04d \n", ++$n;
|
||||
}
|
||||
}
|
||||
493
xv6-public/sh.c
Normal file
493
xv6-public/sh.c
Normal file
@@ -0,0 +1,493 @@
|
||||
// Shell.
|
||||
|
||||
#include "types.h"
|
||||
#include "user.h"
|
||||
#include "fcntl.h"
|
||||
|
||||
// Parsed command representation
|
||||
#define EXEC 1
|
||||
#define REDIR 2
|
||||
#define PIPE 3
|
||||
#define LIST 4
|
||||
#define BACK 5
|
||||
|
||||
#define MAXARGS 10
|
||||
|
||||
struct cmd {
|
||||
int type;
|
||||
};
|
||||
|
||||
struct execcmd {
|
||||
int type;
|
||||
char *argv[MAXARGS];
|
||||
char *eargv[MAXARGS];
|
||||
};
|
||||
|
||||
struct redircmd {
|
||||
int type;
|
||||
struct cmd *cmd;
|
||||
char *file;
|
||||
char *efile;
|
||||
int mode;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct pipecmd {
|
||||
int type;
|
||||
struct cmd *left;
|
||||
struct cmd *right;
|
||||
};
|
||||
|
||||
struct listcmd {
|
||||
int type;
|
||||
struct cmd *left;
|
||||
struct cmd *right;
|
||||
};
|
||||
|
||||
struct backcmd {
|
||||
int type;
|
||||
struct cmd *cmd;
|
||||
};
|
||||
|
||||
int fork1(void); // Fork but panics on failure.
|
||||
void panic(char*);
|
||||
struct cmd *parsecmd(char*);
|
||||
|
||||
// Execute cmd. Never returns.
|
||||
void
|
||||
runcmd(struct cmd *cmd)
|
||||
{
|
||||
int p[2];
|
||||
struct backcmd *bcmd;
|
||||
struct execcmd *ecmd;
|
||||
struct listcmd *lcmd;
|
||||
struct pipecmd *pcmd;
|
||||
struct redircmd *rcmd;
|
||||
|
||||
if(cmd == 0)
|
||||
exit();
|
||||
|
||||
switch(cmd->type){
|
||||
default:
|
||||
panic("runcmd");
|
||||
|
||||
case EXEC:
|
||||
ecmd = (struct execcmd*)cmd;
|
||||
if(ecmd->argv[0] == 0)
|
||||
exit();
|
||||
exec(ecmd->argv[0], ecmd->argv);
|
||||
printf(2, "exec %s failed\n", ecmd->argv[0]);
|
||||
break;
|
||||
|
||||
case REDIR:
|
||||
rcmd = (struct redircmd*)cmd;
|
||||
close(rcmd->fd);
|
||||
if(open(rcmd->file, rcmd->mode) < 0){
|
||||
printf(2, "open %s failed\n", rcmd->file);
|
||||
exit();
|
||||
}
|
||||
runcmd(rcmd->cmd);
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
lcmd = (struct listcmd*)cmd;
|
||||
if(fork1() == 0)
|
||||
runcmd(lcmd->left);
|
||||
wait();
|
||||
runcmd(lcmd->right);
|
||||
break;
|
||||
|
||||
case PIPE:
|
||||
pcmd = (struct pipecmd*)cmd;
|
||||
if(pipe(p) < 0)
|
||||
panic("pipe");
|
||||
if(fork1() == 0){
|
||||
close(1);
|
||||
dup(p[1]);
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
runcmd(pcmd->left);
|
||||
}
|
||||
if(fork1() == 0){
|
||||
close(0);
|
||||
dup(p[0]);
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
runcmd(pcmd->right);
|
||||
}
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
wait();
|
||||
wait();
|
||||
break;
|
||||
|
||||
case BACK:
|
||||
bcmd = (struct backcmd*)cmd;
|
||||
if(fork1() == 0)
|
||||
runcmd(bcmd->cmd);
|
||||
break;
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
int
|
||||
getcmd(char *buf, int nbuf)
|
||||
{
|
||||
printf(2, "$ ");
|
||||
memset(buf, 0, nbuf);
|
||||
gets(buf, nbuf);
|
||||
if(buf[0] == 0) // EOF
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
static char buf[100];
|
||||
int fd;
|
||||
|
||||
// Ensure that three file descriptors are open.
|
||||
while((fd = open("console", O_RDWR)) >= 0){
|
||||
if(fd >= 3){
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read and run input commands.
|
||||
while(getcmd(buf, sizeof(buf)) >= 0){
|
||||
if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
|
||||
// Chdir must be called by the parent, not the child.
|
||||
buf[strlen(buf)-1] = 0; // chop \n
|
||||
if(chdir(buf+3) < 0)
|
||||
printf(2, "cannot cd %s\n", buf+3);
|
||||
continue;
|
||||
}
|
||||
if(fork1() == 0)
|
||||
runcmd(parsecmd(buf));
|
||||
wait();
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
panic(char *s)
|
||||
{
|
||||
printf(2, "%s\n", s);
|
||||
exit();
|
||||
}
|
||||
|
||||
int
|
||||
fork1(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
if(pid == -1)
|
||||
panic("fork");
|
||||
return pid;
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
// Constructors
|
||||
|
||||
struct cmd*
|
||||
execcmd(void)
|
||||
{
|
||||
struct execcmd *cmd;
|
||||
|
||||
cmd = malloc(sizeof(*cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->type = EXEC;
|
||||
return (struct cmd*)cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
|
||||
{
|
||||
struct redircmd *cmd;
|
||||
|
||||
cmd = malloc(sizeof(*cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->type = REDIR;
|
||||
cmd->cmd = subcmd;
|
||||
cmd->file = file;
|
||||
cmd->efile = efile;
|
||||
cmd->mode = mode;
|
||||
cmd->fd = fd;
|
||||
return (struct cmd*)cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
pipecmd(struct cmd *left, struct cmd *right)
|
||||
{
|
||||
struct pipecmd *cmd;
|
||||
|
||||
cmd = malloc(sizeof(*cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->type = PIPE;
|
||||
cmd->left = left;
|
||||
cmd->right = right;
|
||||
return (struct cmd*)cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
listcmd(struct cmd *left, struct cmd *right)
|
||||
{
|
||||
struct listcmd *cmd;
|
||||
|
||||
cmd = malloc(sizeof(*cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->type = LIST;
|
||||
cmd->left = left;
|
||||
cmd->right = right;
|
||||
return (struct cmd*)cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
backcmd(struct cmd *subcmd)
|
||||
{
|
||||
struct backcmd *cmd;
|
||||
|
||||
cmd = malloc(sizeof(*cmd));
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->type = BACK;
|
||||
cmd->cmd = subcmd;
|
||||
return (struct cmd*)cmd;
|
||||
}
|
||||
//PAGEBREAK!
|
||||
// Parsing
|
||||
|
||||
char whitespace[] = " \t\r\n\v";
|
||||
char symbols[] = "<|>&;()";
|
||||
|
||||
int
|
||||
gettoken(char **ps, char *es, char **q, char **eq)
|
||||
{
|
||||
char *s;
|
||||
int ret;
|
||||
|
||||
s = *ps;
|
||||
while(s < es && strchr(whitespace, *s))
|
||||
s++;
|
||||
if(q)
|
||||
*q = s;
|
||||
ret = *s;
|
||||
switch(*s){
|
||||
case 0:
|
||||
break;
|
||||
case '|':
|
||||
case '(':
|
||||
case ')':
|
||||
case ';':
|
||||
case '&':
|
||||
case '<':
|
||||
s++;
|
||||
break;
|
||||
case '>':
|
||||
s++;
|
||||
if(*s == '>'){
|
||||
ret = '+';
|
||||
s++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = 'a';
|
||||
while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
if(eq)
|
||||
*eq = s;
|
||||
|
||||
while(s < es && strchr(whitespace, *s))
|
||||
s++;
|
||||
*ps = s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
peek(char **ps, char *es, char *toks)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = *ps;
|
||||
while(s < es && strchr(whitespace, *s))
|
||||
s++;
|
||||
*ps = s;
|
||||
return *s && strchr(toks, *s);
|
||||
}
|
||||
|
||||
struct cmd *parseline(char**, char*);
|
||||
struct cmd *parsepipe(char**, char*);
|
||||
struct cmd *parseexec(char**, char*);
|
||||
struct cmd *nulterminate(struct cmd*);
|
||||
|
||||
struct cmd*
|
||||
parsecmd(char *s)
|
||||
{
|
||||
char *es;
|
||||
struct cmd *cmd;
|
||||
|
||||
es = s + strlen(s);
|
||||
cmd = parseline(&s, es);
|
||||
peek(&s, es, "");
|
||||
if(s != es){
|
||||
printf(2, "leftovers: %s\n", s);
|
||||
panic("syntax");
|
||||
}
|
||||
nulterminate(cmd);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
parseline(char **ps, char *es)
|
||||
{
|
||||
struct cmd *cmd;
|
||||
|
||||
cmd = parsepipe(ps, es);
|
||||
while(peek(ps, es, "&")){
|
||||
gettoken(ps, es, 0, 0);
|
||||
cmd = backcmd(cmd);
|
||||
}
|
||||
if(peek(ps, es, ";")){
|
||||
gettoken(ps, es, 0, 0);
|
||||
cmd = listcmd(cmd, parseline(ps, es));
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
parsepipe(char **ps, char *es)
|
||||
{
|
||||
struct cmd *cmd;
|
||||
|
||||
cmd = parseexec(ps, es);
|
||||
if(peek(ps, es, "|")){
|
||||
gettoken(ps, es, 0, 0);
|
||||
cmd = pipecmd(cmd, parsepipe(ps, es));
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
parseredirs(struct cmd *cmd, char **ps, char *es)
|
||||
{
|
||||
int tok;
|
||||
char *q, *eq;
|
||||
|
||||
while(peek(ps, es, "<>")){
|
||||
tok = gettoken(ps, es, 0, 0);
|
||||
if(gettoken(ps, es, &q, &eq) != 'a')
|
||||
panic("missing file for redirection");
|
||||
switch(tok){
|
||||
case '<':
|
||||
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
|
||||
break;
|
||||
case '>':
|
||||
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
||||
break;
|
||||
case '+': // >>
|
||||
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
parseblock(char **ps, char *es)
|
||||
{
|
||||
struct cmd *cmd;
|
||||
|
||||
if(!peek(ps, es, "("))
|
||||
panic("parseblock");
|
||||
gettoken(ps, es, 0, 0);
|
||||
cmd = parseline(ps, es);
|
||||
if(!peek(ps, es, ")"))
|
||||
panic("syntax - missing )");
|
||||
gettoken(ps, es, 0, 0);
|
||||
cmd = parseredirs(cmd, ps, es);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
struct cmd*
|
||||
parseexec(char **ps, char *es)
|
||||
{
|
||||
char *q, *eq;
|
||||
int tok, argc;
|
||||
struct execcmd *cmd;
|
||||
struct cmd *ret;
|
||||
|
||||
if(peek(ps, es, "("))
|
||||
return parseblock(ps, es);
|
||||
|
||||
ret = execcmd();
|
||||
cmd = (struct execcmd*)ret;
|
||||
|
||||
argc = 0;
|
||||
ret = parseredirs(ret, ps, es);
|
||||
while(!peek(ps, es, "|)&;")){
|
||||
if((tok=gettoken(ps, es, &q, &eq)) == 0)
|
||||
break;
|
||||
if(tok != 'a')
|
||||
panic("syntax");
|
||||
cmd->argv[argc] = q;
|
||||
cmd->eargv[argc] = eq;
|
||||
argc++;
|
||||
if(argc >= MAXARGS)
|
||||
panic("too many args");
|
||||
ret = parseredirs(ret, ps, es);
|
||||
}
|
||||
cmd->argv[argc] = 0;
|
||||
cmd->eargv[argc] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// NUL-terminate all the counted strings.
|
||||
struct cmd*
|
||||
nulterminate(struct cmd *cmd)
|
||||
{
|
||||
int i;
|
||||
struct backcmd *bcmd;
|
||||
struct execcmd *ecmd;
|
||||
struct listcmd *lcmd;
|
||||
struct pipecmd *pcmd;
|
||||
struct redircmd *rcmd;
|
||||
|
||||
if(cmd == 0)
|
||||
return 0;
|
||||
|
||||
switch(cmd->type){
|
||||
case EXEC:
|
||||
ecmd = (struct execcmd*)cmd;
|
||||
for(i=0; ecmd->argv[i]; i++)
|
||||
*ecmd->eargv[i] = 0;
|
||||
break;
|
||||
|
||||
case REDIR:
|
||||
rcmd = (struct redircmd*)cmd;
|
||||
nulterminate(rcmd->cmd);
|
||||
*rcmd->efile = 0;
|
||||
break;
|
||||
|
||||
case PIPE:
|
||||
pcmd = (struct pipecmd*)cmd;
|
||||
nulterminate(pcmd->left);
|
||||
nulterminate(pcmd->right);
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
lcmd = (struct listcmd*)cmd;
|
||||
nulterminate(lcmd->left);
|
||||
nulterminate(lcmd->right);
|
||||
break;
|
||||
|
||||
case BACK:
|
||||
bcmd = (struct backcmd*)cmd;
|
||||
nulterminate(bcmd->cmd);
|
||||
break;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
3
xv6-public/show1
Normal file
3
xv6-public/show1
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps
|
||||
19
xv6-public/sign.pl
Normal file
19
xv6-public/sign.pl
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
|
||||
|
||||
$n = sysread(SIG, $buf, 1000);
|
||||
|
||||
if($n > 510){
|
||||
print STDERR "boot block too large: $n bytes (max 510)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print STDERR "boot block is $n bytes (max 510)\n";
|
||||
|
||||
$buf .= "\0" x (510-$n);
|
||||
$buf .= "\x55\xAA";
|
||||
|
||||
open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
|
||||
print SIG $buf;
|
||||
close SIG;
|
||||
134
xv6-public/sleep1.p
Normal file
134
xv6-public/sleep1.p
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
This file defines a Promela model for xv6's
|
||||
acquire, release, sleep, and wakeup, along with
|
||||
a model of a simple producer/consumer queue.
|
||||
|
||||
To run:
|
||||
spinp sleep1.p
|
||||
|
||||
(You may need to install Spin, available at http://spinroot.com/.)
|
||||
|
||||
After a successful run spin prints something like:
|
||||
|
||||
unreached in proctype consumer
|
||||
(0 of 37 states)
|
||||
unreached in proctype producer
|
||||
(0 of 23 states)
|
||||
|
||||
After an unsuccessful run, the spinp script prints
|
||||
an execution trace that causes a deadlock.
|
||||
|
||||
The safe body of producer reads:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
If this is changed to:
|
||||
|
||||
x = value; value = x + 1; x = 0;
|
||||
acquire(lk);
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
then a deadlock can happen, because the non-atomic
|
||||
increment of value conflicts with the non-atomic
|
||||
decrement in consumer, causing value to have a bad value.
|
||||
Try this.
|
||||
|
||||
If it is changed to:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup(0);
|
||||
i = i + 1;
|
||||
|
||||
then nothing bad happens: it is okay to wakeup after release
|
||||
instead of before, although it seems morally wrong.
|
||||
*/
|
||||
|
||||
#define ITER 4
|
||||
#define N 2
|
||||
|
||||
bit lk;
|
||||
byte value;
|
||||
bit sleeping[N];
|
||||
|
||||
inline acquire(x)
|
||||
{
|
||||
atomic { x == 0; x = 1 }
|
||||
}
|
||||
|
||||
inline release(x)
|
||||
{
|
||||
assert x==1;
|
||||
x = 0
|
||||
}
|
||||
|
||||
inline sleep(cond, lk)
|
||||
{
|
||||
assert !sleeping[_pid];
|
||||
if
|
||||
:: cond ->
|
||||
skip
|
||||
:: else ->
|
||||
atomic { release(lk); sleeping[_pid] = 1 };
|
||||
sleeping[_pid] == 0;
|
||||
acquire(lk)
|
||||
fi
|
||||
}
|
||||
|
||||
inline wakeup()
|
||||
{
|
||||
w = 0;
|
||||
do
|
||||
:: w < N ->
|
||||
sleeping[w] = 0;
|
||||
w = w + 1
|
||||
:: else ->
|
||||
break
|
||||
od
|
||||
}
|
||||
|
||||
active[N] proctype consumer()
|
||||
{
|
||||
byte i, x;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
sleep(value > 0, lk);
|
||||
x = value; value = x - 1; x = 0;
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
||||
active[N] proctype producer()
|
||||
{
|
||||
byte i, x, w;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup();
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
||||
56
xv6-public/sleeplock.c
Normal file
56
xv6-public/sleeplock.c
Normal file
@@ -0,0 +1,56 @@
|
||||
// Sleeping locks
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "x86.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
|
||||
void
|
||||
initsleeplock(struct sleeplock *lk, char *name)
|
||||
{
|
||||
initlock(&lk->lk, "sleep lock");
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
acquiresleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
while (lk->locked) {
|
||||
sleep(lk, &lk->lk);
|
||||
}
|
||||
lk->locked = 1;
|
||||
lk->pid = myproc()->pid;
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
void
|
||||
releasesleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
wakeup(lk);
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
int
|
||||
holdingsleep(struct sleeplock *lk)
|
||||
{
|
||||
int r;
|
||||
|
||||
acquire(&lk->lk);
|
||||
r = lk->locked && (lk->pid == myproc()->pid);
|
||||
release(&lk->lk);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
10
xv6-public/sleeplock.h
Normal file
10
xv6-public/sleeplock.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Long-term locks for processes
|
||||
struct sleeplock {
|
||||
uint locked; // Is the lock held?
|
||||
struct spinlock lk; // spinlock protecting this sleep lock
|
||||
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
int pid; // Process holding lock
|
||||
};
|
||||
|
||||
126
xv6-public/spinlock.c
Normal file
126
xv6-public/spinlock.c
Normal file
@@ -0,0 +1,126 @@
|
||||
// Mutual exclusion spin locks.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "x86.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
void
|
||||
initlock(struct spinlock *lk, char *name)
|
||||
{
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->cpu = 0;
|
||||
}
|
||||
|
||||
// Acquire the lock.
|
||||
// Loops (spins) until the lock is acquired.
|
||||
// Holding a lock for a long time may cause
|
||||
// other CPUs to waste time spinning to acquire it.
|
||||
void
|
||||
acquire(struct spinlock *lk)
|
||||
{
|
||||
pushcli(); // disable interrupts to avoid deadlock.
|
||||
if(holding(lk))
|
||||
panic("acquire");
|
||||
|
||||
// The xchg is atomic.
|
||||
while(xchg(&lk->locked, 1) != 0)
|
||||
;
|
||||
|
||||
// Tell the C compiler and the processor to not move loads or stores
|
||||
// past this point, to ensure that the critical section's memory
|
||||
// references happen after the lock is acquired.
|
||||
__sync_synchronize();
|
||||
|
||||
// Record info about lock acquisition for debugging.
|
||||
lk->cpu = mycpu();
|
||||
getcallerpcs(&lk, lk->pcs);
|
||||
}
|
||||
|
||||
// Release the lock.
|
||||
void
|
||||
release(struct spinlock *lk)
|
||||
{
|
||||
if(!holding(lk))
|
||||
panic("release");
|
||||
|
||||
lk->pcs[0] = 0;
|
||||
lk->cpu = 0;
|
||||
|
||||
// Tell the C compiler and the processor to not move loads or stores
|
||||
// past this point, to ensure that all the stores in the critical
|
||||
// section are visible to other cores before the lock is released.
|
||||
// Both the C compiler and the hardware may re-order loads and
|
||||
// stores; __sync_synchronize() tells them both not to.
|
||||
__sync_synchronize();
|
||||
|
||||
// Release the lock, equivalent to lk->locked = 0.
|
||||
// This code can't use a C assignment, since it might
|
||||
// not be atomic. A real OS would use C atomics here.
|
||||
asm volatile("movl $0, %0" : "+m" (lk->locked) : );
|
||||
|
||||
popcli();
|
||||
}
|
||||
|
||||
// Record the current call stack in pcs[] by following the %ebp chain.
|
||||
void
|
||||
getcallerpcs(void *v, uint pcs[])
|
||||
{
|
||||
uint *ebp;
|
||||
int i;
|
||||
|
||||
ebp = (uint*)v - 2;
|
||||
for(i = 0; i < 10; i++){
|
||||
if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
|
||||
break;
|
||||
pcs[i] = ebp[1]; // saved %eip
|
||||
ebp = (uint*)ebp[0]; // saved %ebp
|
||||
}
|
||||
for(; i < 10; i++)
|
||||
pcs[i] = 0;
|
||||
}
|
||||
|
||||
// Check whether this cpu is holding the lock.
|
||||
int
|
||||
holding(struct spinlock *lock)
|
||||
{
|
||||
int r;
|
||||
pushcli();
|
||||
r = lock->locked && lock->cpu == mycpu();
|
||||
popcli();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// Pushcli/popcli are like cli/sti except that they are matched:
|
||||
// it takes two popcli to undo two pushcli. Also, if interrupts
|
||||
// are off, then pushcli, popcli leaves them off.
|
||||
|
||||
void
|
||||
pushcli(void)
|
||||
{
|
||||
int eflags;
|
||||
|
||||
eflags = readeflags();
|
||||
cli();
|
||||
if(mycpu()->ncli == 0)
|
||||
mycpu()->intena = eflags & FL_IF;
|
||||
mycpu()->ncli += 1;
|
||||
}
|
||||
|
||||
void
|
||||
popcli(void)
|
||||
{
|
||||
if(readeflags()&FL_IF)
|
||||
panic("popcli - interruptible");
|
||||
if(--mycpu()->ncli < 0)
|
||||
panic("popcli");
|
||||
if(mycpu()->ncli == 0 && mycpu()->intena)
|
||||
sti();
|
||||
}
|
||||
|
||||
11
xv6-public/spinlock.h
Normal file
11
xv6-public/spinlock.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Mutual exclusion lock.
|
||||
struct spinlock {
|
||||
uint locked; // Is the lock held?
|
||||
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
struct cpu *cpu; // The cpu holding the lock.
|
||||
uint pcs[10]; // The call stack (an array of program counters)
|
||||
// that locked the lock.
|
||||
};
|
||||
|
||||
16
xv6-public/spinp
Normal file
16
xv6-public/spinp
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# != 1 ] || [ ! -f "$1" ]; then
|
||||
echo 'usage: spinp file.p' 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f $1.trail
|
||||
spin -a $1 || exit 1
|
||||
cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c
|
||||
pan -i
|
||||
rm pan.* pan
|
||||
if [ -f $1.trail ]; then
|
||||
spin -t -p $1
|
||||
fi
|
||||
|
||||
11
xv6-public/stat.h
Normal file
11
xv6-public/stat.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#define T_DIR 1 // Directory
|
||||
#define T_FILE 2 // File
|
||||
#define T_DEV 3 // Device
|
||||
|
||||
struct stat {
|
||||
short type; // Type of file
|
||||
int dev; // File system's disk device
|
||||
uint ino; // Inode number
|
||||
short nlink; // Number of links to file
|
||||
uint size; // Size of file in bytes
|
||||
};
|
||||
49
xv6-public/stressfs.c
Normal file
49
xv6-public/stressfs.c
Normal file
@@ -0,0 +1,49 @@
|
||||
// Demonstrate that moving the "acquire" in iderw after the loop that
|
||||
// appends to the idequeue results in a race.
|
||||
|
||||
// For this to work, you should also add a spin within iderw's
|
||||
// idequeue traversal loop. Adding the following demonstrated a panic
|
||||
// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU:
|
||||
// for (i = 0; i < 40000; i++)
|
||||
// asm volatile("");
|
||||
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
#include "fs.h"
|
||||
#include "fcntl.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, i;
|
||||
char path[] = "stressfs0";
|
||||
char data[512];
|
||||
|
||||
printf(1, "stressfs starting\n");
|
||||
memset(data, 'a', sizeof(data));
|
||||
|
||||
for(i = 0; i < 4; i++)
|
||||
if(fork() > 0)
|
||||
break;
|
||||
|
||||
printf(1, "write %d\n", i);
|
||||
|
||||
path[8] += i;
|
||||
fd = open(path, O_CREATE | O_RDWR);
|
||||
for(i = 0; i < 20; i++)
|
||||
// printf(fd, "%d\n", i);
|
||||
write(fd, data, sizeof(data));
|
||||
close(fd);
|
||||
|
||||
printf(1, "read\n");
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
for (i = 0; i < 20; i++)
|
||||
read(fd, data, sizeof(data));
|
||||
close(fd);
|
||||
|
||||
wait();
|
||||
|
||||
exit();
|
||||
}
|
||||
105
xv6-public/string.c
Normal file
105
xv6-public/string.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "types.h"
|
||||
#include "x86.h"
|
||||
|
||||
void*
|
||||
memset(void *dst, int c, uint n)
|
||||
{
|
||||
if ((int)dst%4 == 0 && n%4 == 0){
|
||||
c &= 0xFF;
|
||||
stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4);
|
||||
} else
|
||||
stosb(dst, c, n);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, uint n)
|
||||
{
|
||||
const uchar *s1, *s2;
|
||||
|
||||
s1 = v1;
|
||||
s2 = v2;
|
||||
while(n-- > 0){
|
||||
if(*s1 != *s2)
|
||||
return *s1 - *s2;
|
||||
s1++, s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void*
|
||||
memmove(void *dst, const void *src, uint n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if(s < d && s + n > d){
|
||||
s += n;
|
||||
d += n;
|
||||
while(n-- > 0)
|
||||
*--d = *--s;
|
||||
} else
|
||||
while(n-- > 0)
|
||||
*d++ = *s++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// memcpy exists to placate GCC. Use memmove.
|
||||
void*
|
||||
memcpy(void *dst, const void *src, uint n)
|
||||
{
|
||||
return memmove(dst, src, n);
|
||||
}
|
||||
|
||||
int
|
||||
strncmp(const char *p, const char *q, uint n)
|
||||
{
|
||||
while(n > 0 && *p && *p == *q)
|
||||
n--, p++, q++;
|
||||
if(n == 0)
|
||||
return 0;
|
||||
return (uchar)*p - (uchar)*q;
|
||||
}
|
||||
|
||||
char*
|
||||
strncpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os;
|
||||
|
||||
os = s;
|
||||
while(n-- > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
while(n-- > 0)
|
||||
*s++ = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
// Like strncpy but guaranteed to NUL-terminate.
|
||||
char*
|
||||
safestrcpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os;
|
||||
|
||||
os = s;
|
||||
if(n <= 0)
|
||||
return os;
|
||||
while(--n > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
*s = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
int
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for(n = 0; s[n]; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
||||
29
xv6-public/swtch.S
Normal file
29
xv6-public/swtch.S
Normal file
@@ -0,0 +1,29 @@
|
||||
# Context switch
|
||||
#
|
||||
# void swtch(struct context **old, struct context *new);
|
||||
#
|
||||
# Save the current registers on the stack, creating
|
||||
# a struct context, and save its address in *old.
|
||||
# Switch stacks to new and pop previously-saved registers.
|
||||
|
||||
.globl swtch
|
||||
swtch:
|
||||
movl 4(%esp), %eax
|
||||
movl 8(%esp), %edx
|
||||
|
||||
# Save old callee-saved registers
|
||||
pushl %ebp
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
|
||||
# Switch stacks
|
||||
movl %esp, (%eax)
|
||||
movl %edx, %esp
|
||||
|
||||
# Load new callee-saved registers
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
ret
|
||||
186
xv6-public/syscall.c
Normal file
186
xv6-public/syscall.c
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
#include "syscall.h"
|
||||
|
||||
// User code makes a system call with INT T_SYSCALL.
|
||||
// System call number in %eax.
|
||||
// Arguments on the stack, from the user call to the C
|
||||
// library system call function. The saved user %esp points
|
||||
// to a saved program counter, and then the first argument.
|
||||
|
||||
// Fetch the int at addr from the current process.
|
||||
int
|
||||
fetchint(uint addr, int *ip)
|
||||
{
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
if(addr >= curproc->sz || addr+4 > curproc->sz)
|
||||
return -1;
|
||||
*ip = *(int*)(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fetch the nul-terminated string at addr from the current process.
|
||||
// Doesn't actually copy the string - just sets *pp to point at it.
|
||||
// Returns length of string, not including nul.
|
||||
int
|
||||
fetchstr(uint addr, char **pp)
|
||||
{
|
||||
char *s, *ep;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
if(addr >= curproc->sz)
|
||||
return -1;
|
||||
*pp = (char*)addr;
|
||||
ep = (char*)curproc->sz;
|
||||
for(s = *pp; s < ep; s++){
|
||||
if(*s == 0)
|
||||
return s - *pp;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fetch the nth 32-bit system call argument.
|
||||
int
|
||||
argint(int n, int *ip)
|
||||
{
|
||||
return fetchint((myproc()->tf->esp) + 4 + 4*n, ip);
|
||||
}
|
||||
|
||||
// Fetch the nth word-sized system call argument as a pointer
|
||||
// to a block of memory of size bytes. Check that the pointer
|
||||
// lies within the process address space.
|
||||
int
|
||||
argptr(int n, char **pp, int size)
|
||||
{
|
||||
int i;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
if(argint(n, &i) < 0)
|
||||
return -1;
|
||||
if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz)
|
||||
return -1;
|
||||
*pp = (char*)i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fetch the nth word-sized system call argument as a string pointer.
|
||||
// Check that the pointer is valid and the string is nul-terminated.
|
||||
// (There is no shared writable memory, so the string can't change
|
||||
// between this check and being used by the kernel.)
|
||||
int
|
||||
argstr(int n, char **pp)
|
||||
{
|
||||
int addr;
|
||||
if(argint(n, &addr) < 0)
|
||||
return -1;
|
||||
return fetchstr(addr, pp);
|
||||
}
|
||||
|
||||
extern int sys_chdir(void);
|
||||
extern int sys_close(void);
|
||||
extern int sys_dup(void);
|
||||
extern int sys_dup2(void);
|
||||
|
||||
extern int sys_exec(void);
|
||||
extern int sys_exit(void);
|
||||
extern int sys_fork(void);
|
||||
extern int sys_fstat(void);
|
||||
extern int sys_getpid(void);
|
||||
extern int sys_kill(void);
|
||||
extern int sys_link(void);
|
||||
extern int sys_mkdir(void);
|
||||
extern int sys_mknod(void);
|
||||
extern int sys_open(void);
|
||||
extern int sys_pipe(void);
|
||||
extern int sys_read(void);
|
||||
extern int sys_sbrk(void);
|
||||
extern int sys_sleep(void);
|
||||
extern int sys_unlink(void);
|
||||
extern int sys_wait(void);
|
||||
extern int sys_write(void);
|
||||
extern int sys_uptime(void);
|
||||
extern int sys_date(void);
|
||||
extern int sys_alarm(void);
|
||||
|
||||
// 函数指针数组
|
||||
// 初始化中写 [] 是指syscalls[SYS_fork=1] = sys_fork
|
||||
static int (*syscalls[])(void) = {
|
||||
[SYS_fork] sys_fork,
|
||||
[SYS_exit] sys_exit,
|
||||
[SYS_wait] sys_wait,
|
||||
[SYS_pipe] sys_pipe,
|
||||
[SYS_read] sys_read,
|
||||
[SYS_kill] sys_kill,
|
||||
[SYS_exec] sys_exec,
|
||||
[SYS_fstat] sys_fstat,
|
||||
[SYS_chdir] sys_chdir,
|
||||
[SYS_dup] sys_dup,
|
||||
[SYS_dup2] sys_dup2,
|
||||
[SYS_getpid] sys_getpid,
|
||||
[SYS_sbrk] sys_sbrk,
|
||||
[SYS_sleep] sys_sleep,
|
||||
[SYS_uptime] sys_uptime,
|
||||
[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,
|
||||
[SYS_date] sys_date,
|
||||
[SYS_alarm] sys_alarm,
|
||||
|
||||
};
|
||||
/*
|
||||
static char *syscall_name[] = {
|
||||
[SYS_fork] "fork",
|
||||
[SYS_exit] "exit",
|
||||
[SYS_wait] "wait",
|
||||
[SYS_pipe] "pipe",
|
||||
[SYS_read] "read",
|
||||
[SYS_kill] "kill",
|
||||
[SYS_exec] "exec",
|
||||
[SYS_fstat] "fstat",
|
||||
[SYS_chdir] "chdir",
|
||||
[SYS_dup] "dup",
|
||||
[SYS_getpid] "getpid",
|
||||
[SYS_sbrk] "sbrk",
|
||||
[SYS_sleep] "sleep",
|
||||
[SYS_uptime] "uptime",
|
||||
[SYS_open] "open",
|
||||
[SYS_write] "write",
|
||||
[SYS_mknod] "mknod",
|
||||
[SYS_unlink] "unlink",
|
||||
[SYS_link] "link",
|
||||
[SYS_mkdir] "mkdir",
|
||||
[SYS_close] "close",
|
||||
[SYS_date] "date",
|
||||
[SYS_alarm] "alarm",
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
void
|
||||
syscall(void)
|
||||
{
|
||||
int num;
|
||||
// 关中断读取进程信息
|
||||
struct proc *curproc = myproc();
|
||||
//num 取值应该[1-最大的系统调用序号]
|
||||
num = curproc->tf->eax;
|
||||
// 检查系统调用是否存在
|
||||
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
|
||||
curproc->tf->eax = syscalls[num](); //存储系统调用返回值
|
||||
// cprintf("%s: -> %d\n",syscall_name[num], curproc->tf->eax);
|
||||
} else {
|
||||
cprintf("%d %s: unknown sys call %d\n",
|
||||
curproc->pid, curproc->name, num);
|
||||
curproc->tf->eax = -1;
|
||||
}
|
||||
}
|
||||
25
xv6-public/syscall.h
Normal file
25
xv6-public/syscall.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// System call numbers
|
||||
#define SYS_fork 1
|
||||
#define SYS_exit 2
|
||||
#define SYS_wait 3
|
||||
#define SYS_pipe 4
|
||||
#define SYS_read 5
|
||||
#define SYS_kill 6
|
||||
#define SYS_exec 7
|
||||
#define SYS_fstat 8
|
||||
#define SYS_chdir 9
|
||||
#define SYS_dup 10
|
||||
#define SYS_getpid 11
|
||||
#define SYS_sbrk 12
|
||||
#define SYS_sleep 13
|
||||
#define SYS_uptime 14
|
||||
#define SYS_open 15
|
||||
#define SYS_write 16
|
||||
#define SYS_mknod 17
|
||||
#define SYS_unlink 18
|
||||
#define SYS_link 19
|
||||
#define SYS_mkdir 20
|
||||
#define SYS_close 21
|
||||
#define SYS_date 22
|
||||
#define SYS_dup2 23
|
||||
#define SYS_alarm 24
|
||||
469
xv6-public/sysfile.c
Normal file
469
xv6-public/sysfile.c
Normal file
@@ -0,0 +1,469 @@
|
||||
//
|
||||
// File-system system calls.
|
||||
// Mostly argument checking, since we don't trust
|
||||
// user code, and calls into file.c and fs.c.
|
||||
//
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "stat.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "fs.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "file.h"
|
||||
#include "fcntl.h"
|
||||
#include "date.h"
|
||||
|
||||
// Fetch the nth word-sized system call argument as a file descriptor
|
||||
// and return both the descriptor and the corresponding struct file.
|
||||
static int
|
||||
argfd(int n, int *pfd, struct file **pf)
|
||||
{
|
||||
int fd;
|
||||
struct file *f;
|
||||
|
||||
if(argint(n, &fd) < 0)
|
||||
return -1;
|
||||
if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0)
|
||||
return -1;
|
||||
if(pfd)
|
||||
*pfd = fd;
|
||||
if(pf)
|
||||
*pf = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate a file descriptor for the given file.
|
||||
// Takes over file reference from caller on success.
|
||||
static int
|
||||
fdalloc(struct file *f)
|
||||
{
|
||||
int fd;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
for(fd = 0; fd < NOFILE; fd++){
|
||||
if(curproc->ofile[fd] == 0){
|
||||
curproc->ofile[fd] = f;
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
sys_dup(void)
|
||||
{
|
||||
struct file *f;
|
||||
int fd;
|
||||
|
||||
if(argfd(0, 0, &f) < 0)
|
||||
return -1;
|
||||
if((fd=fdalloc(f)) < 0)
|
||||
return -1;
|
||||
filedup(f);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
sys_dup2(void)
|
||||
{
|
||||
|
||||
int old_fd,new_fd;
|
||||
struct file *f1, *f2;
|
||||
if(argfd(0, &old_fd, &f1) < 0 || argint(1, &new_fd) < 0)
|
||||
return -1;
|
||||
if(new_fd < 0 || new_fd >= NOFILE)
|
||||
return -1;
|
||||
if(new_fd != old_fd) {
|
||||
f2 = myproc()->ofile[new_fd];
|
||||
if(f2) {
|
||||
// 检查该文件描述符是否在使用中
|
||||
fileclose(f2);
|
||||
}
|
||||
myproc()->ofile[new_fd] = f1;
|
||||
// 增加文件f1的引用计数
|
||||
filedup(f1);
|
||||
}
|
||||
return new_fd;
|
||||
}
|
||||
int
|
||||
sys_read(void)
|
||||
{
|
||||
struct file *f;
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
|
||||
return -1;
|
||||
return fileread(f, p, n);
|
||||
}
|
||||
|
||||
int
|
||||
sys_write(void)
|
||||
{
|
||||
struct file *f;
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
|
||||
return -1;
|
||||
return filewrite(f, p, n);
|
||||
}
|
||||
|
||||
int
|
||||
sys_close(void)
|
||||
{
|
||||
int fd;
|
||||
struct file *f;
|
||||
|
||||
if(argfd(0, &fd, &f) < 0)
|
||||
return -1;
|
||||
myproc()->ofile[fd] = 0;
|
||||
fileclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sys_fstat(void)
|
||||
{
|
||||
struct file *f;
|
||||
struct stat *st;
|
||||
|
||||
if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
|
||||
return -1;
|
||||
return filestat(f, st);
|
||||
}
|
||||
|
||||
// Create the path new as a link to the same inode as old.
|
||||
int
|
||||
sys_link(void)
|
||||
{
|
||||
char name[DIRSIZ], *new, *old;
|
||||
struct inode *dp, *ip;
|
||||
|
||||
if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
|
||||
return -1;
|
||||
|
||||
begin_op();
|
||||
if((ip = namei(old)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ilock(ip);
|
||||
if(ip->type == T_DIR){
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ip->nlink++;
|
||||
iupdate(ip);
|
||||
iunlock(ip);
|
||||
|
||||
if((dp = nameiparent(new, name)) == 0)
|
||||
goto bad;
|
||||
ilock(dp);
|
||||
if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
|
||||
iunlockput(dp);
|
||||
goto bad;
|
||||
}
|
||||
iunlockput(dp);
|
||||
iput(ip);
|
||||
|
||||
end_op();
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
ilock(ip);
|
||||
ip->nlink--;
|
||||
iupdate(ip);
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Is the directory dp empty except for "." and ".." ?
|
||||
static int
|
||||
isdirempty(struct inode *dp)
|
||||
{
|
||||
int off;
|
||||
struct dirent de;
|
||||
|
||||
for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
|
||||
if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||
panic("isdirempty: readi");
|
||||
if(de.inum != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//PAGEBREAK!
|
||||
int
|
||||
sys_unlink(void)
|
||||
{
|
||||
struct inode *ip, *dp;
|
||||
struct dirent de;
|
||||
char name[DIRSIZ], *path;
|
||||
uint off;
|
||||
|
||||
if(argstr(0, &path) < 0)
|
||||
return -1;
|
||||
|
||||
begin_op();
|
||||
if((dp = nameiparent(path, name)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ilock(dp);
|
||||
|
||||
// Cannot unlink "." or "..".
|
||||
if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
|
||||
goto bad;
|
||||
|
||||
if((ip = dirlookup(dp, name, &off)) == 0)
|
||||
goto bad;
|
||||
ilock(ip);
|
||||
|
||||
if(ip->nlink < 1)
|
||||
panic("unlink: nlink < 1");
|
||||
if(ip->type == T_DIR && !isdirempty(ip)){
|
||||
iunlockput(ip);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
memset(&de, 0, sizeof(de));
|
||||
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||
panic("unlink: writei");
|
||||
if(ip->type == T_DIR){
|
||||
dp->nlink--;
|
||||
iupdate(dp);
|
||||
}
|
||||
iunlockput(dp);
|
||||
|
||||
ip->nlink--;
|
||||
iupdate(ip);
|
||||
iunlockput(ip);
|
||||
|
||||
end_op();
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
iunlockput(dp);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct inode*
|
||||
create(char *path, short type, short major, short minor)
|
||||
{
|
||||
uint off;
|
||||
struct inode *ip, *dp;
|
||||
char name[DIRSIZ];
|
||||
|
||||
if((dp = nameiparent(path, name)) == 0)
|
||||
return 0;
|
||||
ilock(dp);
|
||||
|
||||
if((ip = dirlookup(dp, name, &off)) != 0){
|
||||
iunlockput(dp);
|
||||
ilock(ip);
|
||||
if(type == T_FILE && ip->type == T_FILE)
|
||||
return ip;
|
||||
iunlockput(ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((ip = ialloc(dp->dev, type)) == 0)
|
||||
panic("create: ialloc");
|
||||
|
||||
ilock(ip);
|
||||
ip->major = major;
|
||||
ip->minor = minor;
|
||||
ip->nlink = 1;
|
||||
iupdate(ip);
|
||||
|
||||
if(type == T_DIR){ // Create . and .. entries.
|
||||
dp->nlink++; // for ".."
|
||||
iupdate(dp);
|
||||
// No ip->nlink++ for ".": avoid cyclic ref count.
|
||||
if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
|
||||
panic("create dots");
|
||||
}
|
||||
|
||||
if(dirlink(dp, name, ip->inum) < 0)
|
||||
panic("create: dirlink");
|
||||
|
||||
iunlockput(dp);
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
int
|
||||
sys_open(void)
|
||||
{
|
||||
char *path;
|
||||
int fd, omode;
|
||||
struct file *f;
|
||||
struct inode *ip;
|
||||
|
||||
if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
|
||||
return -1;
|
||||
|
||||
begin_op();
|
||||
|
||||
if(omode & O_CREATE){
|
||||
ip = create(path, T_FILE, 0, 0);
|
||||
if(ip == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if((ip = namei(path)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
ilock(ip);
|
||||
if(ip->type == T_DIR && omode != O_RDONLY){
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
|
||||
if(f)
|
||||
fileclose(f);
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
iunlock(ip);
|
||||
end_op();
|
||||
|
||||
f->type = FD_INODE;
|
||||
f->ip = ip;
|
||||
f->off = 0;
|
||||
f->readable = !(omode & O_WRONLY);
|
||||
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
sys_mkdir(void)
|
||||
{
|
||||
char *path;
|
||||
struct inode *ip;
|
||||
|
||||
begin_op();
|
||||
if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sys_mknod(void)
|
||||
{
|
||||
struct inode *ip;
|
||||
char *path;
|
||||
int major, minor;
|
||||
|
||||
begin_op();
|
||||
if((argstr(0, &path)) < 0 ||
|
||||
argint(1, &major) < 0 ||
|
||||
argint(2, &minor) < 0 ||
|
||||
(ip = create(path, T_DEV, major, minor)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sys_chdir(void)
|
||||
{
|
||||
char *path;
|
||||
struct inode *ip;
|
||||
struct proc *curproc = myproc();
|
||||
|
||||
begin_op();
|
||||
if(argstr(0, &path) < 0 || (ip = namei(path)) == 0){
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
ilock(ip);
|
||||
if(ip->type != T_DIR){
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return -1;
|
||||
}
|
||||
iunlock(ip);
|
||||
iput(curproc->cwd);
|
||||
end_op();
|
||||
curproc->cwd = ip;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sys_exec(void)
|
||||
{
|
||||
char *path, *argv[MAXARG];
|
||||
int i;
|
||||
uint uargv, uarg;
|
||||
|
||||
if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
|
||||
return -1;
|
||||
}
|
||||
memset(argv, 0, sizeof(argv));
|
||||
for(i=0;; i++){
|
||||
if(i >= NELEM(argv))
|
||||
return -1;
|
||||
if(fetchint(uargv+4*i, (int*)&uarg) < 0)
|
||||
return -1;
|
||||
if(uarg == 0){
|
||||
argv[i] = 0;
|
||||
break;
|
||||
}
|
||||
if(fetchstr(uarg, &argv[i]) < 0)
|
||||
return -1;
|
||||
}
|
||||
return exec(path, argv);
|
||||
}
|
||||
|
||||
int
|
||||
sys_pipe(void)
|
||||
{
|
||||
int *fd;
|
||||
struct file *rf, *wf;
|
||||
int fd0, fd1;
|
||||
|
||||
if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
|
||||
return -1;
|
||||
if(pipealloc(&rf, &wf) < 0)
|
||||
return -1;
|
||||
fd0 = -1;
|
||||
if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
|
||||
if(fd0 >= 0)
|
||||
myproc()->ofile[fd0] = 0;
|
||||
fileclose(rf);
|
||||
fileclose(wf);
|
||||
return -1;
|
||||
}
|
||||
fd[0] = fd0;
|
||||
fd[1] = fd1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
121
xv6-public/sysproc.c
Normal file
121
xv6-public/sysproc.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "types.h"
|
||||
#include "x86.h"
|
||||
#include "defs.h"
|
||||
#include "date.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
|
||||
int
|
||||
sys_fork(void)
|
||||
{
|
||||
return fork();
|
||||
}
|
||||
|
||||
int
|
||||
sys_exit(void)
|
||||
{
|
||||
exit();
|
||||
return 0; // not reached
|
||||
}
|
||||
|
||||
int
|
||||
sys_wait(void)
|
||||
{
|
||||
return wait();
|
||||
}
|
||||
|
||||
int
|
||||
sys_kill(void)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if(argint(0, &pid) < 0)
|
||||
return -1;
|
||||
return kill(pid);
|
||||
}
|
||||
|
||||
int
|
||||
sys_getpid(void)
|
||||
{
|
||||
return myproc()->pid;
|
||||
}
|
||||
|
||||
int
|
||||
sys_sbrk(void)
|
||||
{
|
||||
int addr;
|
||||
int n;
|
||||
|
||||
if(argint(0, &n) < 0)
|
||||
return -1;
|
||||
addr = myproc()->sz;
|
||||
myproc()->sz += n;
|
||||
return addr;
|
||||
}
|
||||
|
||||
int
|
||||
sys_sleep(void)
|
||||
{
|
||||
int n;
|
||||
uint ticks0;
|
||||
|
||||
if(argint(0, &n) < 0)
|
||||
return -1;
|
||||
acquire(&tickslock);
|
||||
ticks0 = ticks;
|
||||
while(ticks - ticks0 < n){
|
||||
if(myproc()->killed){
|
||||
release(&tickslock);
|
||||
return -1;
|
||||
}
|
||||
sleep(&ticks, &tickslock);
|
||||
}
|
||||
release(&tickslock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return how many clock tick interrupts have occurred
|
||||
// since start.
|
||||
int
|
||||
sys_uptime(void)
|
||||
{
|
||||
uint xticks;
|
||||
|
||||
acquire(&tickslock);
|
||||
xticks = ticks;
|
||||
release(&tickslock);
|
||||
return xticks;
|
||||
}
|
||||
|
||||
int
|
||||
sys_date(void)
|
||||
{
|
||||
struct rtcdate *r;
|
||||
if(argptr(0, (void*)&r, sizeof(struct rtcdate)) < 0)
|
||||
return -1;
|
||||
|
||||
cmostime(r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sys_alarm(void)
|
||||
{
|
||||
// 间隔
|
||||
int interval;
|
||||
// 函数指针
|
||||
void (*handler)(void);
|
||||
|
||||
if(argint(0, &interval) < 0)
|
||||
return -1;
|
||||
if(argptr(1, (char **)&handler, 1) < 0)
|
||||
return -1;
|
||||
|
||||
myproc()->alarminterval = interval;
|
||||
myproc()->alarmhandler = handler;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
13
xv6-public/toc.ftr
Normal file
13
xv6-public/toc.ftr
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
The source listing is preceded by a cross-reference that lists every defined
|
||||
constant, struct, global variable, and function in xv6. Each entry gives,
|
||||
on the same line as the name, the line number (or, in a few cases, numbers)
|
||||
where the name is defined. Successive lines in an entry list the line
|
||||
numbers where the name is used. For example, this entry:
|
||||
|
||||
swtch 2658
|
||||
0374 2428 2466 2657 2658
|
||||
|
||||
indicates that swtch is defined on line 2658 and is mentioned on five lines
|
||||
on sheets 03, 24, and 26.
|
||||
6
xv6-public/toc.hdr
Normal file
6
xv6-public/toc.hdr
Normal file
@@ -0,0 +1,6 @@
|
||||
The numbers to the left of the file names in the table are sheet numbers.
|
||||
The source code has been printed in a double column format with fifty
|
||||
lines per column, giving one hundred lines per sheet (or page).
|
||||
Thus there is a convenient relationship between line numbers and sheet numbers.
|
||||
|
||||
|
||||
148
xv6-public/trap.c
Normal file
148
xv6-public/trap.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
#include "traps.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
// Interrupt descriptor table (shared by all CPUs).
|
||||
struct gatedesc idt[256];
|
||||
extern uint vectors[]; // in vectors.S: array of 256 entry pointers
|
||||
struct spinlock tickslock;
|
||||
uint ticks;
|
||||
|
||||
void
|
||||
tvinit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 256; i++)
|
||||
SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
|
||||
SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);
|
||||
|
||||
initlock(&tickslock, "time");
|
||||
}
|
||||
|
||||
void
|
||||
idtinit(void)
|
||||
{
|
||||
lidt(idt, sizeof(idt));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//PAGEBREAK: 41
|
||||
void
|
||||
trap(struct trapframe *tf)
|
||||
{
|
||||
if(tf->trapno == T_SYSCALL){
|
||||
if(myproc()->killed)
|
||||
exit();
|
||||
myproc()->tf = tf;
|
||||
syscall();
|
||||
if(myproc()->killed)
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
char *mem;
|
||||
switch(tf->trapno){
|
||||
case T_IRQ0 + IRQ_TIMER:
|
||||
if(cpuid() == 0){
|
||||
acquire(&tickslock);
|
||||
ticks++;
|
||||
|
||||
wakeup(&ticks);
|
||||
release(&tickslock);
|
||||
}
|
||||
if(myproc() != 0 && (tf->cs & 3) == 3){
|
||||
myproc()->ticks++;
|
||||
// 没有alarm任务的proc, 不会进入以下if,因为alarminterval为0
|
||||
if(myproc()->ticks == myproc()->alarminterval) {
|
||||
myproc()->ticks = 0;
|
||||
// 为什么不能像下面这样调用函数
|
||||
//myproc()->alarmhandler();
|
||||
tf->esp -= 4;
|
||||
// eip压栈
|
||||
*(uint *)(tf->esp) = tf->eip;
|
||||
tf->eip = (uint)myproc()->alarmhandler;
|
||||
}
|
||||
}
|
||||
|
||||
lapiceoi();
|
||||
break;
|
||||
case T_IRQ0 + IRQ_IDE:
|
||||
ideintr();
|
||||
lapiceoi();
|
||||
break;
|
||||
case T_IRQ0 + IRQ_IDE+1:
|
||||
// Bochs generates spurious IDE1 interrupts.
|
||||
break;
|
||||
case T_IRQ0 + IRQ_KBD:
|
||||
kbdintr();
|
||||
lapiceoi();
|
||||
break;
|
||||
case T_IRQ0 + IRQ_COM1:
|
||||
uartintr();
|
||||
lapiceoi();
|
||||
break;
|
||||
case T_IRQ0 + 7:
|
||||
case T_IRQ0 + IRQ_SPURIOUS:
|
||||
cprintf("cpu%d: spurious interrupt at %x:%x\n",
|
||||
cpuid(), tf->cs, tf->eip);
|
||||
lapiceoi();
|
||||
break;
|
||||
|
||||
// T_PGFLT = 14
|
||||
case T_PGFLT:
|
||||
// kallo或mappages执行失败,则继续执行下面的default.
|
||||
// In user space, assume process misbehaved.
|
||||
// cr2包含发生页面错误时的线性地址.
|
||||
mem = kalloc();
|
||||
if(mem != 0){
|
||||
uint va = PGROUNDDOWN(rcr2());
|
||||
memset(mem, 0, PGSIZE);
|
||||
extern int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);
|
||||
if(mappages(myproc()->pgdir,(void *)va, PGSIZE, V2P(mem), PTE_W|PTE_U) >= 0) {
|
||||
// 全部执行成功才break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//PAGEBREAK: 13
|
||||
default:
|
||||
// tf->cs&3 不明白
|
||||
if(myproc() == 0 || (tf->cs&3) == 0){
|
||||
// In kernel, it must be our mistake.
|
||||
cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
|
||||
tf->trapno, cpuid(), tf->eip, rcr2());
|
||||
panic("trap");
|
||||
}
|
||||
// In user space, assume process misbehaved.
|
||||
|
||||
cprintf("pid %d %s: trap %d err %d on cpu %d "
|
||||
"eip 0x%x addr 0x%x--kill proc\n",
|
||||
myproc()->pid, myproc()->name, tf->trapno,
|
||||
tf->err, cpuid(), tf->eip, rcr2());
|
||||
myproc()->killed = 1;
|
||||
}
|
||||
|
||||
// Force process exit if it has been killed and is in user space.
|
||||
// (If it is still executing in the kernel, let it keep running
|
||||
// until it gets to the regular system call return.)
|
||||
if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
|
||||
exit();
|
||||
|
||||
// Force process to give up CPU on clock tick.
|
||||
// If interrupts were on while locks held, would need to check nlock.
|
||||
if(myproc() && myproc()->state == RUNNING &&
|
||||
tf->trapno == T_IRQ0+IRQ_TIMER)
|
||||
yield();
|
||||
|
||||
// Check if the process has been killed since we yielded
|
||||
if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
|
||||
exit();
|
||||
}
|
||||
32
xv6-public/trapasm.S
Normal file
32
xv6-public/trapasm.S
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "mmu.h"
|
||||
|
||||
# vectors.S sends all traps here.
|
||||
.globl alltraps
|
||||
alltraps:
|
||||
# Build trap frame.
|
||||
pushl %ds
|
||||
pushl %es
|
||||
pushl %fs
|
||||
pushl %gs
|
||||
pushal
|
||||
|
||||
# Set up data segments.
|
||||
movw $(SEG_KDATA<<3), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
# Call trap(tf), where tf=%esp
|
||||
pushl %esp
|
||||
call trap
|
||||
addl $4, %esp
|
||||
|
||||
# Return falls through to trapret...
|
||||
.globl trapret
|
||||
trapret:
|
||||
popal
|
||||
popl %gs
|
||||
popl %fs
|
||||
popl %es
|
||||
popl %ds
|
||||
addl $0x8, %esp # trapno and errcode
|
||||
iret
|
||||
38
xv6-public/traps.h
Normal file
38
xv6-public/traps.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// x86 trap and interrupt constants.
|
||||
|
||||
// Processor-defined:
|
||||
#define T_DIVIDE 0 // divide error
|
||||
#define T_DEBUG 1 // debug exception
|
||||
#define T_NMI 2 // non-maskable interrupt
|
||||
#define T_BRKPT 3 // breakpoint
|
||||
#define T_OFLOW 4 // overflow
|
||||
#define T_BOUND 5 // bounds check
|
||||
#define T_ILLOP 6 // illegal opcode
|
||||
#define T_DEVICE 7 // device not available
|
||||
#define T_DBLFLT 8 // double fault
|
||||
// #define T_COPROC 9 // reserved (not used since 486)
|
||||
#define T_TSS 10 // invalid task switch segment
|
||||
#define T_SEGNP 11 // segment not present
|
||||
#define T_STACK 12 // stack exception
|
||||
#define T_GPFLT 13 // general protection fault
|
||||
#define T_PGFLT 14 // page fault
|
||||
// #define T_RES 15 // reserved
|
||||
#define T_FPERR 16 // floating point error
|
||||
#define T_ALIGN 17 // aligment check
|
||||
#define T_MCHK 18 // machine check
|
||||
#define T_SIMDERR 19 // SIMD floating point error
|
||||
|
||||
// These are arbitrarily chosen, but with care not to overlap
|
||||
// processor defined exceptions or interrupt vectors.
|
||||
#define T_SYSCALL 64 // system call
|
||||
#define T_DEFAULT 500 // catchall
|
||||
|
||||
#define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ
|
||||
|
||||
#define IRQ_TIMER 0
|
||||
#define IRQ_KBD 1
|
||||
#define IRQ_COM1 4
|
||||
#define IRQ_IDE 14
|
||||
#define IRQ_ERROR 19
|
||||
#define IRQ_SPURIOUS 31
|
||||
|
||||
4
xv6-public/types.h
Normal file
4
xv6-public/types.h
Normal file
@@ -0,0 +1,4 @@
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned char uchar;
|
||||
typedef uint pde_t;
|
||||
77
xv6-public/uart.c
Normal file
77
xv6-public/uart.c
Normal file
@@ -0,0 +1,77 @@
|
||||
// Intel 8250 serial port (UART).
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "traps.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
|
||||
#define COM1 0x3f8
|
||||
|
||||
static int uart; // is there a uart?
|
||||
|
||||
void
|
||||
uartinit(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
// Turn off the FIFO
|
||||
outb(COM1+2, 0);
|
||||
|
||||
// 9600 baud, 8 data bits, 1 stop bit, parity off.
|
||||
outb(COM1+3, 0x80); // Unlock divisor
|
||||
outb(COM1+0, 115200/9600);
|
||||
outb(COM1+1, 0);
|
||||
outb(COM1+3, 0x03); // Lock divisor, 8 data bits.
|
||||
outb(COM1+4, 0);
|
||||
outb(COM1+1, 0x01); // Enable receive interrupts.
|
||||
|
||||
// If status is 0xFF, no serial port.
|
||||
if(inb(COM1+5) == 0xFF)
|
||||
return;
|
||||
uart = 1;
|
||||
|
||||
// Acknowledge pre-existing interrupt conditions;
|
||||
// enable interrupts.
|
||||
inb(COM1+2);
|
||||
inb(COM1+0);
|
||||
ioapicenable(IRQ_COM1, 0);
|
||||
|
||||
// Announce that we're here.
|
||||
for(p="xv6...\n"; *p; p++)
|
||||
uartputc(*p);
|
||||
}
|
||||
|
||||
void
|
||||
uartputc(int c)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(!uart)
|
||||
return;
|
||||
for(i = 0; i < 128 && !(inb(COM1+5) & 0x20); i++)
|
||||
microdelay(10);
|
||||
outb(COM1+0, c);
|
||||
}
|
||||
|
||||
static int
|
||||
uartgetc(void)
|
||||
{
|
||||
if(!uart)
|
||||
return -1;
|
||||
if(!(inb(COM1+5) & 0x01))
|
||||
return -1;
|
||||
return inb(COM1+0);
|
||||
}
|
||||
|
||||
void
|
||||
uartintr(void)
|
||||
{
|
||||
consoleintr(uartgetc);
|
||||
}
|
||||
106
xv6-public/ulib.c
Normal file
106
xv6-public/ulib.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "fcntl.h"
|
||||
#include "user.h"
|
||||
#include "x86.h"
|
||||
|
||||
char*
|
||||
strcpy(char *s, const char *t)
|
||||
{
|
||||
char *os;
|
||||
|
||||
os = s;
|
||||
while((*s++ = *t++) != 0)
|
||||
;
|
||||
return os;
|
||||
}
|
||||
|
||||
int
|
||||
strcmp(const char *p, const char *q)
|
||||
{
|
||||
while(*p && *p == *q)
|
||||
p++, q++;
|
||||
return (uchar)*p - (uchar)*q;
|
||||
}
|
||||
|
||||
uint
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for(n = 0; s[n]; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
||||
void*
|
||||
memset(void *dst, int c, uint n)
|
||||
{
|
||||
stosb(dst, c, n);
|
||||
return dst;
|
||||
}
|
||||
|
||||
char*
|
||||
strchr(const char *s, char c)
|
||||
{
|
||||
for(; *s; s++)
|
||||
if(*s == c)
|
||||
return (char*)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char*
|
||||
gets(char *buf, int max)
|
||||
{
|
||||
int i, cc;
|
||||
char c;
|
||||
|
||||
for(i=0; i+1 < max; ){
|
||||
cc = read(0, &c, 1);
|
||||
if(cc < 1)
|
||||
break;
|
||||
buf[i++] = c;
|
||||
if(c == '\n' || c == '\r')
|
||||
break;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
int
|
||||
stat(const char *n, struct stat *st)
|
||||
{
|
||||
int fd;
|
||||
int r;
|
||||
|
||||
fd = open(n, O_RDONLY);
|
||||
if(fd < 0)
|
||||
return -1;
|
||||
r = fstat(fd, st);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
atoi(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
while('0' <= *s && *s <= '9')
|
||||
n = n*10 + *s++ - '0';
|
||||
return n;
|
||||
}
|
||||
|
||||
void*
|
||||
memmove(void *vdst, const void *vsrc, int n)
|
||||
{
|
||||
char *dst;
|
||||
const char *src;
|
||||
|
||||
dst = vdst;
|
||||
src = vsrc;
|
||||
while(n-- > 0)
|
||||
*dst++ = *src++;
|
||||
return vdst;
|
||||
}
|
||||
90
xv6-public/umalloc.c
Normal file
90
xv6-public/umalloc.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
#include "param.h"
|
||||
|
||||
// Memory allocator by Kernighan and Ritchie,
|
||||
// The C programming Language, 2nd ed. Section 8.7.
|
||||
|
||||
typedef long Align;
|
||||
|
||||
union header {
|
||||
struct {
|
||||
union header *ptr;
|
||||
uint size;
|
||||
} s;
|
||||
Align x;
|
||||
};
|
||||
|
||||
typedef union header Header;
|
||||
|
||||
static Header base;
|
||||
static Header *freep;
|
||||
|
||||
void
|
||||
free(void *ap)
|
||||
{
|
||||
Header *bp, *p;
|
||||
|
||||
bp = (Header*)ap - 1;
|
||||
for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
|
||||
if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
|
||||
break;
|
||||
if(bp + bp->s.size == p->s.ptr){
|
||||
bp->s.size += p->s.ptr->s.size;
|
||||
bp->s.ptr = p->s.ptr->s.ptr;
|
||||
} else
|
||||
bp->s.ptr = p->s.ptr;
|
||||
if(p + p->s.size == bp){
|
||||
p->s.size += bp->s.size;
|
||||
p->s.ptr = bp->s.ptr;
|
||||
} else
|
||||
p->s.ptr = bp;
|
||||
freep = p;
|
||||
}
|
||||
|
||||
static Header*
|
||||
morecore(uint nu)
|
||||
{
|
||||
char *p;
|
||||
Header *hp;
|
||||
|
||||
if(nu < 4096)
|
||||
nu = 4096;
|
||||
p = sbrk(nu * sizeof(Header));
|
||||
if(p == (char*)-1)
|
||||
return 0;
|
||||
hp = (Header*)p;
|
||||
hp->s.size = nu;
|
||||
free((void*)(hp + 1));
|
||||
return freep;
|
||||
}
|
||||
|
||||
void*
|
||||
malloc(uint nbytes)
|
||||
{
|
||||
Header *p, *prevp;
|
||||
uint nunits;
|
||||
|
||||
nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1;
|
||||
if((prevp = freep) == 0){
|
||||
base.s.ptr = freep = prevp = &base;
|
||||
base.s.size = 0;
|
||||
}
|
||||
for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){
|
||||
if(p->s.size >= nunits){
|
||||
if(p->s.size == nunits)
|
||||
prevp->s.ptr = p->s.ptr;
|
||||
else {
|
||||
p->s.size -= nunits;
|
||||
p += p->s.size;
|
||||
p->s.size = nunits;
|
||||
}
|
||||
freep = prevp;
|
||||
return (void*)(p + 1);
|
||||
}
|
||||
if(p == freep)
|
||||
if((p = morecore(nunits)) == 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
44
xv6-public/user.h
Normal file
44
xv6-public/user.h
Normal file
@@ -0,0 +1,44 @@
|
||||
struct stat;
|
||||
struct rtcdate;
|
||||
|
||||
// system calls
|
||||
int fork(void);
|
||||
int exit(void) __attribute__((noreturn));
|
||||
int wait(void);
|
||||
int pipe(int*);
|
||||
int write(int, const void*, int);
|
||||
int read(int, void*, int);
|
||||
int close(int);
|
||||
int kill(int);
|
||||
int exec(char*, char**);
|
||||
int open(const char*, int);
|
||||
int mknod(const char*, short, short);
|
||||
int unlink(const char*);
|
||||
int fstat(int fd, struct stat*);
|
||||
int link(const char*, const char*);
|
||||
int mkdir(const char*);
|
||||
int chdir(const char*);
|
||||
int dup(int);
|
||||
int getpid(void);
|
||||
char* sbrk(int);
|
||||
int sleep(int);
|
||||
int uptime(void);
|
||||
int date(struct rtcdate* );
|
||||
int dup2(int , int);
|
||||
int alarm(int, void (*)());
|
||||
|
||||
|
||||
|
||||
// ulib.c
|
||||
int stat(const char*, struct stat*);
|
||||
char* strcpy(char*, const char*);
|
||||
void *memmove(void*, const void*, int);
|
||||
char* strchr(const char*, char c);
|
||||
int strcmp(const char*, const char*);
|
||||
void printf(int, const char*, ...);
|
||||
char* gets(char*, int max);
|
||||
uint strlen(const char*);
|
||||
void* memset(void*, int, uint);
|
||||
void* malloc(uint);
|
||||
void free(void*);
|
||||
int atoi(const char*);
|
||||
1803
xv6-public/usertests.c
Normal file
1803
xv6-public/usertests.c
Normal file
File diff suppressed because it is too large
Load Diff
41
xv6-public/usys.S
Normal file
41
xv6-public/usys.S
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "syscall.h"
|
||||
#include "traps.h"
|
||||
|
||||
#define SYSCALL(name) \
|
||||
.globl name; \
|
||||
name: \
|
||||
movl $SYS_ ## name, %eax; \
|
||||
int $T_SYSCALL; \
|
||||
ret
|
||||
|
||||
/*
|
||||
* 什么时候执行这段?
|
||||
* 应该系统启动过程就会执行,注册一个函数!
|
||||
* 当调用name()时,自动进入这个函数,再转向sys_name()函数
|
||||
*/
|
||||
|
||||
SYSCALL(fork)
|
||||
SYSCALL(exit)
|
||||
SYSCALL(wait)
|
||||
SYSCALL(pipe)
|
||||
SYSCALL(read)
|
||||
SYSCALL(write)
|
||||
SYSCALL(close)
|
||||
SYSCALL(kill)
|
||||
SYSCALL(exec)
|
||||
SYSCALL(open)
|
||||
SYSCALL(mknod)
|
||||
SYSCALL(unlink)
|
||||
SYSCALL(fstat)
|
||||
SYSCALL(link)
|
||||
SYSCALL(mkdir)
|
||||
SYSCALL(chdir)
|
||||
SYSCALL(dup)
|
||||
SYSCALL(dup2)
|
||||
SYSCALL(getpid)
|
||||
SYSCALL(sbrk)
|
||||
SYSCALL(sleep)
|
||||
SYSCALL(uptime)
|
||||
SYSCALL(date)
|
||||
SYSCALL(alarm)
|
||||
|
||||
114
xv6-public/uthread.c
Normal file
114
xv6-public/uthread.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "types.h"
|
||||
#include "stat.h"
|
||||
#include "user.h"
|
||||
|
||||
/* Possible states of a thread; */
|
||||
#define FREE 0x0
|
||||
#define RUNNING 0x1
|
||||
#define RUNNABLE 0x2
|
||||
|
||||
#define STACK_SIZE 8192
|
||||
#define MAX_THREAD 4
|
||||
|
||||
typedef struct thread thread_t, *thread_p;
|
||||
typedef struct mutex mutex_t, *mutex_p;
|
||||
|
||||
struct thread {
|
||||
int sp; /* saved stack pointer */
|
||||
char stack[STACK_SIZE]; /* the thread's stack */
|
||||
int state; /* FREE, RUNNING, RUNNABLE */
|
||||
};
|
||||
static thread_t all_thread[MAX_THREAD];
|
||||
thread_p current_thread;
|
||||
thread_p next_thread;
|
||||
extern void thread_switch(void);
|
||||
|
||||
void
|
||||
thread_init(void)
|
||||
{
|
||||
// main() is thread 0, which will make the first invocation to
|
||||
// thread_schedule(). it needs a stack so that the first thread_switch() can
|
||||
// save thread 0's state. thread_schedule() won't run the main thread ever
|
||||
// again, because its state is set to RUNNING, and thread_schedule() selects
|
||||
// a RUNNABLE thread.
|
||||
current_thread = &all_thread[0];
|
||||
current_thread->state = RUNNING;
|
||||
}
|
||||
|
||||
static void
|
||||
thread_schedule(void)
|
||||
{
|
||||
thread_p t;
|
||||
|
||||
/* Find another runnable thread. */
|
||||
next_thread = 0;
|
||||
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
|
||||
if (t->state == RUNNABLE && t != current_thread) {
|
||||
next_thread = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (t >= all_thread + MAX_THREAD && current_thread->state == RUNNABLE) {
|
||||
/* The current thread is the only runnable thread; run it. */
|
||||
next_thread = current_thread;
|
||||
}
|
||||
|
||||
if (next_thread == 0) {
|
||||
printf(2, "thread_schedule: no runnable threads\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (current_thread != next_thread) { /* switch threads? */
|
||||
next_thread->state = RUNNING;
|
||||
thread_switch();
|
||||
} else
|
||||
next_thread = 0;
|
||||
}
|
||||
|
||||
void
|
||||
thread_create(void (*func)())
|
||||
{
|
||||
thread_p t;
|
||||
|
||||
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
|
||||
if (t->state == FREE) break;
|
||||
}
|
||||
t->sp = (int) (t->stack + STACK_SIZE); // set sp to the top of the stack
|
||||
t->sp -= 4; // space for return address
|
||||
* (int *) (t->sp) = (int)func; // push return address on stack
|
||||
t->sp -= 32; // space for registers that thread_switch expects
|
||||
t->state = RUNNABLE;
|
||||
}
|
||||
|
||||
void
|
||||
thread_yield(void)
|
||||
{
|
||||
current_thread->state = RUNNABLE;
|
||||
thread_schedule();
|
||||
}
|
||||
|
||||
static void
|
||||
mythread(void)
|
||||
{
|
||||
int i;
|
||||
printf(1, "my thread running\n");
|
||||
for (i = 0; i < 100; i++) {
|
||||
printf(1, "my thread 0x%x\n", (int) current_thread);
|
||||
thread_yield();
|
||||
}
|
||||
printf(1, "my thread: exit\n");
|
||||
current_thread->state = FREE;
|
||||
thread_schedule();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
thread_init();
|
||||
thread_create(mythread);
|
||||
thread_create(mythread);
|
||||
thread_schedule();
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user