first commit
This commit is contained in:
4
.dir-locals.el
Normal file
4
.dir-locals.el
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
((c-mode
|
||||||
|
(indent-tabs-mode . nil)
|
||||||
|
(c-file-style . "bsd")
|
||||||
|
(c-basic-offset . 2)))
|
||||||
22
.editorconfig
Normal file
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
; https://editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{c,h}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.S]
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
[*.ld]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
6
.gdbinit.tmpl-riscv
Normal file
6
.gdbinit.tmpl-riscv
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
set confirm off
|
||||||
|
set architecture riscv:rv64
|
||||||
|
target remote 127.0.0.1:1234
|
||||||
|
symbol-file kernel/kernel
|
||||||
|
set disassemble-next-line auto
|
||||||
|
set riscv use-compressed-breakpoints yes
|
||||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
*~
|
||||||
|
_*
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.asm
|
||||||
|
*.sym
|
||||||
|
*.img
|
||||||
|
vectors.S
|
||||||
|
bootblock
|
||||||
|
entryother
|
||||||
|
initcode
|
||||||
|
initcode.out
|
||||||
|
kernelmemfs
|
||||||
|
mkfs
|
||||||
|
kernel/kernel
|
||||||
|
user/usys.S
|
||||||
|
.gdbinit
|
||||||
|
*.zip
|
||||||
|
xv6.out*
|
||||||
|
.vagrant/
|
||||||
|
submissions/
|
||||||
|
ph
|
||||||
|
barrier
|
||||||
|
/lab-*.json
|
||||||
|
.DS_Store
|
||||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
The xv6 software is:
|
||||||
|
|
||||||
|
Copyright (c) 2006-2019 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.
|
||||||
|
|
||||||
369
Makefile
Normal file
369
Makefile
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
|
||||||
|
# To compile and run with a lab solution, set the lab name in conf/lab.mk
|
||||||
|
# (e.g., LAB=util). Run make grade to test solution with the lab's
|
||||||
|
# grade script (e.g., grade-lab-util).
|
||||||
|
|
||||||
|
-include conf/lab.mk
|
||||||
|
|
||||||
|
K=kernel
|
||||||
|
U=user
|
||||||
|
|
||||||
|
OBJS = \
|
||||||
|
$K/entry.o \
|
||||||
|
$K/kalloc.o \
|
||||||
|
$K/string.o \
|
||||||
|
$K/main.o \
|
||||||
|
$K/vm.o \
|
||||||
|
$K/proc.o \
|
||||||
|
$K/swtch.o \
|
||||||
|
$K/trampoline.o \
|
||||||
|
$K/trap.o \
|
||||||
|
$K/syscall.o \
|
||||||
|
$K/sysproc.o \
|
||||||
|
$K/bio.o \
|
||||||
|
$K/fs.o \
|
||||||
|
$K/log.o \
|
||||||
|
$K/sleeplock.o \
|
||||||
|
$K/file.o \
|
||||||
|
$K/pipe.o \
|
||||||
|
$K/exec.o \
|
||||||
|
$K/sysfile.o \
|
||||||
|
$K/kernelvec.o \
|
||||||
|
$K/plic.o \
|
||||||
|
$K/virtio_disk.o
|
||||||
|
|
||||||
|
OBJS_KCSAN = \
|
||||||
|
$K/start.o \
|
||||||
|
$K/console.o \
|
||||||
|
$K/printf.o \
|
||||||
|
$K/uart.o \
|
||||||
|
$K/spinlock.o
|
||||||
|
|
||||||
|
ifdef KCSAN
|
||||||
|
OBJS_KCSAN += \
|
||||||
|
$K/kcsan.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),$(filter $(LAB), lock))
|
||||||
|
OBJS += \
|
||||||
|
$K/stats.o\
|
||||||
|
$K/sprintf.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(LAB),net)
|
||||||
|
OBJS += \
|
||||||
|
$K/e1000.o \
|
||||||
|
$K/net.o \
|
||||||
|
$K/sysnet.o \
|
||||||
|
$K/pci.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# riscv64-unknown-elf- or riscv64-linux-gnu-
|
||||||
|
# perhaps in /opt/riscv/bin
|
||||||
|
#TOOLPREFIX =
|
||||||
|
|
||||||
|
# Try to infer the correct TOOLPREFIX if not set
|
||||||
|
ifndef TOOLPREFIX
|
||||||
|
TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
|
||||||
|
then echo 'riscv64-unknown-elf-'; \
|
||||||
|
elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
|
||||||
|
then echo 'riscv64-linux-gnu-'; \
|
||||||
|
elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
|
||||||
|
then echo 'riscv64-unknown-linux-gnu-'; \
|
||||||
|
else echo "***" 1>&2; \
|
||||||
|
echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \
|
||||||
|
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
|
||||||
|
echo "***" 1>&2; exit 1; fi)
|
||||||
|
endif
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv64
|
||||||
|
|
||||||
|
CC = $(TOOLPREFIX)gcc
|
||||||
|
AS = $(TOOLPREFIX)gas
|
||||||
|
LD = $(TOOLPREFIX)ld
|
||||||
|
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||||
|
OBJDUMP = $(TOOLPREFIX)objdump
|
||||||
|
|
||||||
|
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2
|
||||||
|
|
||||||
|
ifdef LAB
|
||||||
|
LABUPPER = $(shell echo $(LAB) | tr a-z A-Z)
|
||||||
|
XCFLAGS += -DSOL_$(LABUPPER) -DLAB_$(LABUPPER)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS += $(XCFLAGS)
|
||||||
|
CFLAGS += -MD
|
||||||
|
CFLAGS += -mcmodel=medany
|
||||||
|
CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax
|
||||||
|
CFLAGS += -I.
|
||||||
|
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||||
|
|
||||||
|
ifeq ($(LAB),net)
|
||||||
|
CFLAGS += -DNET_TESTS_PORT=$(SERVERPORT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef KCSAN
|
||||||
|
CFLAGS += -DKCSAN
|
||||||
|
KCSANFLAG = -fsanitize=thread -fno-inline
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
LDFLAGS = -z max-page-size=4096
|
||||||
|
|
||||||
|
$K/kernel: $(OBJS) $(OBJS_KCSAN) $K/kernel.ld $U/initcode
|
||||||
|
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJS_KCSAN)
|
||||||
|
$(OBJDUMP) -S $K/kernel > $K/kernel.asm
|
||||||
|
$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
|
||||||
|
|
||||||
|
$(OBJS): EXTRAFLAG := $(KCSANFLAG)
|
||||||
|
|
||||||
|
$K/%.o: $K/%.c
|
||||||
|
$(CC) $(CFLAGS) $(EXTRAFLAG) -c -o $@ $<
|
||||||
|
|
||||||
|
|
||||||
|
$U/initcode: $U/initcode.S
|
||||||
|
$(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o
|
||||||
|
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o
|
||||||
|
$(OBJCOPY) -S -O binary $U/initcode.out $U/initcode
|
||||||
|
$(OBJDUMP) -S $U/initcode.o > $U/initcode.asm
|
||||||
|
|
||||||
|
tags: $(OBJS) _init
|
||||||
|
etags *.S *.c
|
||||||
|
|
||||||
|
ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o
|
||||||
|
|
||||||
|
ifeq ($(LAB),$(filter $(LAB), lock))
|
||||||
|
ULIB += $U/statistics.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
_%: %.o $(ULIB)
|
||||||
|
$(LD) $(LDFLAGS) -T $U/user.ld -o $@ $^
|
||||||
|
$(OBJDUMP) -S $@ > $*.asm
|
||||||
|
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
|
||||||
|
|
||||||
|
$U/usys.S : $U/usys.pl
|
||||||
|
perl $U/usys.pl > $U/usys.S
|
||||||
|
|
||||||
|
$U/usys.o : $U/usys.S
|
||||||
|
$(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S
|
||||||
|
|
||||||
|
$U/_forktest: $U/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 $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o
|
||||||
|
$(OBJDUMP) -S $U/_forktest > $U/forktest.asm
|
||||||
|
|
||||||
|
mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h
|
||||||
|
gcc $(XCFLAGS) -Werror -Wall -I. -o mkfs/mkfs 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=\
|
||||||
|
$U/_cat\
|
||||||
|
$U/_echo\
|
||||||
|
$U/_forktest\
|
||||||
|
$U/_grep\
|
||||||
|
$U/_init\
|
||||||
|
$U/_kill\
|
||||||
|
$U/_ln\
|
||||||
|
$U/_ls\
|
||||||
|
$U/_mkdir\
|
||||||
|
$U/_rm\
|
||||||
|
$U/_sh\
|
||||||
|
$U/_stressfs\
|
||||||
|
$U/_usertests\
|
||||||
|
$U/_grind\
|
||||||
|
$U/_wc\
|
||||||
|
$U/_zombie\
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(LAB),$(filter $(LAB), lock))
|
||||||
|
UPROGS += \
|
||||||
|
$U/_stats
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),traps)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_call\
|
||||||
|
$U/_bttest
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),lazy)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_lazytests
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),cow)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_cowtest
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),thread)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_uthread
|
||||||
|
|
||||||
|
$U/uthread_switch.o : $U/uthread_switch.S
|
||||||
|
$(CC) $(CFLAGS) -c -o $U/uthread_switch.o $U/uthread_switch.S
|
||||||
|
|
||||||
|
$U/_uthread: $U/uthread.o $U/uthread_switch.o $(ULIB)
|
||||||
|
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_uthread $U/uthread.o $U/uthread_switch.o $(ULIB)
|
||||||
|
$(OBJDUMP) -S $U/_uthread > $U/uthread.asm
|
||||||
|
|
||||||
|
ph: notxv6/ph.c
|
||||||
|
gcc -o ph -g -O2 $(XCFLAGS) notxv6/ph.c -pthread
|
||||||
|
|
||||||
|
barrier: notxv6/barrier.c
|
||||||
|
gcc -o barrier -g -O2 $(XCFLAGS) notxv6/barrier.c -pthread
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),pgtbl)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_pgtbltest
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),lock)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_kalloctest\
|
||||||
|
$U/_bcachetest
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LAB),fs)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_bigfile
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(LAB),net)
|
||||||
|
UPROGS += \
|
||||||
|
$U/_nettests
|
||||||
|
endif
|
||||||
|
|
||||||
|
UEXTRA=
|
||||||
|
ifeq ($(LAB),util)
|
||||||
|
UEXTRA += user/xargstest.sh
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS)
|
||||||
|
mkfs/mkfs fs.img README $(UEXTRA) $(UPROGS)
|
||||||
|
|
||||||
|
-include kernel/*.d user/*.d
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
|
||||||
|
*/*.o */*.d */*.asm */*.sym \
|
||||||
|
$U/initcode $U/initcode.out $K/kernel fs.img \
|
||||||
|
mkfs/mkfs .gdbinit \
|
||||||
|
$U/usys.S \
|
||||||
|
$(UPROGS) \
|
||||||
|
*.zip \
|
||||||
|
ph barrier
|
||||||
|
|
||||||
|
# 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 := 3
|
||||||
|
endif
|
||||||
|
ifeq ($(LAB),fs)
|
||||||
|
CPUS := 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
FWDPORT = $(shell expr `id -u` % 5000 + 25999)
|
||||||
|
|
||||||
|
QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic
|
||||||
|
QEMUOPTS += -global virtio-mmio.force-legacy=false
|
||||||
|
QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
|
||||||
|
QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
|
||||||
|
|
||||||
|
ifeq ($(LAB),net)
|
||||||
|
QEMUOPTS += -netdev user,id=net0,hostfwd=udp::$(FWDPORT)-:2000 -object filter-dump,id=net0,netdev=net0,file=packets.pcap
|
||||||
|
QEMUOPTS += -device e1000,netdev=net0,bus=pcie.0
|
||||||
|
endif
|
||||||
|
|
||||||
|
qemu: $K/kernel fs.img
|
||||||
|
$(QEMU) $(QEMUOPTS)
|
||||||
|
|
||||||
|
.gdbinit: .gdbinit.tmpl-riscv
|
||||||
|
sed "s/:1234/:$(GDBPORT)/" < $^ > $@
|
||||||
|
|
||||||
|
qemu-gdb: $K/kernel .gdbinit fs.img
|
||||||
|
@echo "*** Now run 'gdb' in another window." 1>&2
|
||||||
|
$(QEMU) $(QEMUOPTS) -S $(QEMUGDB)
|
||||||
|
|
||||||
|
ifeq ($(LAB),net)
|
||||||
|
# try to generate a unique port for the echo server
|
||||||
|
SERVERPORT = $(shell expr `id -u` % 5000 + 25099)
|
||||||
|
|
||||||
|
server:
|
||||||
|
python3 server.py $(SERVERPORT)
|
||||||
|
|
||||||
|
ping:
|
||||||
|
python3 ping.py $(FWDPORT)
|
||||||
|
endif
|
||||||
|
|
||||||
|
##
|
||||||
|
## FOR testing lab grading script
|
||||||
|
##
|
||||||
|
|
||||||
|
ifneq ($(V),@)
|
||||||
|
GRADEFLAGS += -v
|
||||||
|
endif
|
||||||
|
|
||||||
|
print-gdbport:
|
||||||
|
@echo $(GDBPORT)
|
||||||
|
|
||||||
|
grade:
|
||||||
|
@echo $(MAKE) clean
|
||||||
|
@$(MAKE) clean || \
|
||||||
|
(echo "'make clean' failed. HINT: Do you have another running instance of xv6?" && exit 1)
|
||||||
|
./grade-lab-$(LAB) $(GRADEFLAGS)
|
||||||
|
|
||||||
|
##
|
||||||
|
## FOR submissions
|
||||||
|
##
|
||||||
|
|
||||||
|
submit-check:
|
||||||
|
@if ! test -d .git; then \
|
||||||
|
echo No .git directory, is this a git repository?; \
|
||||||
|
false; \
|
||||||
|
fi
|
||||||
|
@if test "$$(git symbolic-ref HEAD)" != refs/heads/$(LAB); then \
|
||||||
|
git branch; \
|
||||||
|
read -p "You are not on the $(LAB) branch. Hand-in the current branch? [y/N] " r; \
|
||||||
|
test "$$r" = y; \
|
||||||
|
fi
|
||||||
|
@if ! git diff-files --quiet || ! git diff-index --quiet --cached HEAD; then \
|
||||||
|
git status -s; \
|
||||||
|
echo; \
|
||||||
|
echo "You have uncomitted changes. Please commit or stash them."; \
|
||||||
|
false; \
|
||||||
|
fi
|
||||||
|
@if test -n "`git status -s`"; then \
|
||||||
|
git status -s; \
|
||||||
|
read -p "Untracked files will not be handed in. Continue? [y/N] " r; \
|
||||||
|
test "$$r" = y; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
zipball: clean submit-check
|
||||||
|
git archive --verbose --format zip --output lab.zip HEAD
|
||||||
|
|
||||||
|
.PHONY: zipball clean grade submit-check
|
||||||
49
README
Normal file
49
README
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
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 RISC-V 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.1810/, which provides
|
||||||
|
pointers to on-line resources for v6.
|
||||||
|
|
||||||
|
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
|
||||||
|
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian
|
||||||
|
Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,
|
||||||
|
eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel
|
||||||
|
Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt
|
||||||
|
Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John
|
||||||
|
Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller,
|
||||||
|
Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin
|
||||||
|
Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan
|
||||||
|
Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel
|
||||||
|
Nider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude
|
||||||
|
Rich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya
|
||||||
|
Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal,
|
||||||
|
Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe,
|
||||||
|
Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng,
|
||||||
|
ZhUyU1997, and Zou Chang Wei.
|
||||||
|
|
||||||
|
|
||||||
|
The code in the files that constitute xv6 is
|
||||||
|
Copyright 2006-2022 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.1810, so we are more interested in
|
||||||
|
simplifications and clarifications than new features.
|
||||||
|
|
||||||
|
BUILDING AND RUNNING XV6
|
||||||
|
|
||||||
|
You will need a RISC-V "newlib" tool chain from
|
||||||
|
https://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for
|
||||||
|
riscv64-softmmu. Once they are installed, and in your shell
|
||||||
|
search path, you can run "make qemu".
|
||||||
1
conf/lab.mk
Normal file
1
conf/lab.mk
Normal file
@ -0,0 +1 @@
|
|||||||
|
LAB=util
|
||||||
86
grade-lab-util
Executable file
86
grade-lab-util
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import re
|
||||||
|
from gradelib import *
|
||||||
|
|
||||||
|
r = Runner(save("xv6.out"))
|
||||||
|
|
||||||
|
@test(5, "sleep, no arguments")
|
||||||
|
def test_sleep_no_args():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'sleep'
|
||||||
|
]))
|
||||||
|
r.match(no=["exec .* failed", "$ sleep\n$"])
|
||||||
|
|
||||||
|
@test(5, "sleep, returns")
|
||||||
|
def test_sleep_no_args():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'sleep',
|
||||||
|
'echo OK'
|
||||||
|
]))
|
||||||
|
r.match('^OK$', no=["exec .* failed", "$ sleep\n$"])
|
||||||
|
|
||||||
|
@test(10, "sleep, makes syscall")
|
||||||
|
def test_sleep():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'sleep 10',
|
||||||
|
'echo FAIL'
|
||||||
|
]), stop_breakpoint('sys_sleep'))
|
||||||
|
r.match('\\$ sleep 10', no=['FAIL'])
|
||||||
|
|
||||||
|
@test(20, "pingpong")
|
||||||
|
def test_pingpong():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'pingpong', 'echo OK'
|
||||||
|
]))
|
||||||
|
r.match('^\\d+: received ping$', '^\\d+: received pong$', '^OK$')
|
||||||
|
|
||||||
|
@test(20, "primes")
|
||||||
|
def test_primes():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'primes', 'echo OK'
|
||||||
|
]))
|
||||||
|
args = ['prime %d' % i for i in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]]
|
||||||
|
args.append('^OK$')
|
||||||
|
r.match(*args)
|
||||||
|
|
||||||
|
@test(10, "find, in current directory")
|
||||||
|
def test_find_curdir():
|
||||||
|
fn = random_str()
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'echo > %s' % fn,
|
||||||
|
'find . %s' % fn
|
||||||
|
]))
|
||||||
|
r.match('./%s' % fn)
|
||||||
|
|
||||||
|
@test(10, "find, recursive")
|
||||||
|
def test_find_recursive():
|
||||||
|
needle = random_str()
|
||||||
|
dirs = [random_str() for _ in range(3)]
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'mkdir %s' % dirs[0],
|
||||||
|
'echo > %s/%s' % (dirs[0], needle),
|
||||||
|
'mkdir %s/%s' % (dirs[0], dirs[1]),
|
||||||
|
'echo > %s/%s/%s' % (dirs[0], dirs[1], needle),
|
||||||
|
'mkdir %s' % dirs[2],
|
||||||
|
'echo > %s/%s' % (dirs[2], needle),
|
||||||
|
'find . %s' % needle
|
||||||
|
]))
|
||||||
|
r.match('./%s/%s' % (dirs[0], needle),
|
||||||
|
'./%s/%s/%s' % (dirs[0], dirs[1], needle),
|
||||||
|
'./%s/%s' % (dirs[2], needle))
|
||||||
|
|
||||||
|
@test(19, "xargs")
|
||||||
|
def test_xargs():
|
||||||
|
r.run_qemu(shell_script([
|
||||||
|
'sh < xargstest.sh',
|
||||||
|
'echo DONE',
|
||||||
|
], 'DONE'))
|
||||||
|
matches = re.findall("hello", r.qemu.output)
|
||||||
|
assert_equal(len(matches), 3, "Number of appearances of 'hello'")
|
||||||
|
|
||||||
|
@test(1, "time")
|
||||||
|
def test_time():
|
||||||
|
check_time()
|
||||||
|
|
||||||
|
run_tests()
|
||||||
628
gradelib.py
Normal file
628
gradelib.py
Normal file
@ -0,0 +1,628 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys, os, re, time, socket, select, subprocess, errno, shutil, random, string, json
|
||||||
|
from subprocess import check_call, Popen
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Test structure
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["test", "end_part", "run_tests", "get_current_test"]
|
||||||
|
|
||||||
|
TESTS = []
|
||||||
|
TOTAL = POSSIBLE = 0
|
||||||
|
PART_TOTAL = PART_POSSIBLE = 0
|
||||||
|
CURRENT_TEST = None
|
||||||
|
GRADES = {}
|
||||||
|
|
||||||
|
def test(points, title=None, parent=None):
|
||||||
|
"""Decorator for declaring test functions. If title is None, the
|
||||||
|
title of the test will be derived from the function name by
|
||||||
|
stripping the leading "test_" and replacing underscores with
|
||||||
|
spaces."""
|
||||||
|
|
||||||
|
def register_test(fn, title=title):
|
||||||
|
if not title:
|
||||||
|
assert fn.__name__.startswith("test_")
|
||||||
|
title = fn.__name__[5:].replace("_", " ")
|
||||||
|
if parent:
|
||||||
|
title = " " + title
|
||||||
|
|
||||||
|
def run_test():
|
||||||
|
global TOTAL, POSSIBLE, CURRENT_TEST, GRADES
|
||||||
|
|
||||||
|
# Handle test dependencies
|
||||||
|
if run_test.complete:
|
||||||
|
return run_test.ok
|
||||||
|
run_test.complete = True
|
||||||
|
parent_failed = False
|
||||||
|
if parent:
|
||||||
|
parent_failed = not parent()
|
||||||
|
|
||||||
|
# Run the test
|
||||||
|
fail = None
|
||||||
|
start = time.time()
|
||||||
|
CURRENT_TEST = run_test
|
||||||
|
sys.stdout.write("== Test %s == " % title)
|
||||||
|
if parent:
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
try:
|
||||||
|
if parent_failed:
|
||||||
|
raise AssertionError('Parent failed: %s' % parent.__name__)
|
||||||
|
fn()
|
||||||
|
except AssertionError as e:
|
||||||
|
fail = str(e)
|
||||||
|
|
||||||
|
# Display and handle test result
|
||||||
|
POSSIBLE += points
|
||||||
|
if points:
|
||||||
|
print("%s: %s" % (title, \
|
||||||
|
(color("red", "FAIL") if fail else color("green", "OK"))), end=' ')
|
||||||
|
if time.time() - start > 0.1:
|
||||||
|
print("(%.1fs)" % (time.time() - start), end=' ')
|
||||||
|
print()
|
||||||
|
if fail:
|
||||||
|
print(" %s" % fail.replace("\n", "\n "))
|
||||||
|
else:
|
||||||
|
TOTAL += points
|
||||||
|
if points:
|
||||||
|
GRADES[title] = 0 if fail else points
|
||||||
|
|
||||||
|
for callback in run_test.on_finish:
|
||||||
|
callback(fail)
|
||||||
|
CURRENT_TEST = None
|
||||||
|
|
||||||
|
run_test.ok = not fail
|
||||||
|
return run_test.ok
|
||||||
|
|
||||||
|
# Record test metadata on the test wrapper function
|
||||||
|
run_test.__name__ = fn.__name__
|
||||||
|
run_test.title = title
|
||||||
|
run_test.complete = False
|
||||||
|
run_test.ok = False
|
||||||
|
run_test.on_finish = []
|
||||||
|
TESTS.append(run_test)
|
||||||
|
return run_test
|
||||||
|
return register_test
|
||||||
|
|
||||||
|
def end_part(name):
|
||||||
|
def show_part():
|
||||||
|
global PART_TOTAL, PART_POSSIBLE
|
||||||
|
print("Part %s score: %d/%d" % \
|
||||||
|
(name, TOTAL - PART_TOTAL, POSSIBLE - PART_POSSIBLE))
|
||||||
|
print()
|
||||||
|
PART_TOTAL, PART_POSSIBLE = TOTAL, POSSIBLE
|
||||||
|
show_part.title = ""
|
||||||
|
TESTS.append(show_part)
|
||||||
|
|
||||||
|
def write_results():
|
||||||
|
global options
|
||||||
|
if not options.results:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(options.results, "w") as f:
|
||||||
|
f.write(json.dumps(GRADES))
|
||||||
|
except OSError as e:
|
||||||
|
print("Provided a bad results path. Error:", e)
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
"""Set up for testing and run the registered test functions."""
|
||||||
|
|
||||||
|
# Handle command line
|
||||||
|
global options
|
||||||
|
parser = OptionParser(usage="usage: %prog [-v] [filters...]")
|
||||||
|
parser.add_option("-v", "--verbose", action="store_true",
|
||||||
|
help="print commands")
|
||||||
|
parser.add_option("--color", choices=["never", "always", "auto"],
|
||||||
|
default="auto", help="never, always, or auto")
|
||||||
|
parser.add_option("--results", help="results file path")
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
# Start with a full build to catch build errors
|
||||||
|
make()
|
||||||
|
|
||||||
|
# Clean the file system if there is one
|
||||||
|
reset_fs()
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
limit = list(map(str.lower, args))
|
||||||
|
try:
|
||||||
|
for test in TESTS:
|
||||||
|
if not limit or any(l in test.title.lower() for l in limit):
|
||||||
|
test()
|
||||||
|
if not limit:
|
||||||
|
write_results()
|
||||||
|
print("Score: %d/%d" % (TOTAL, POSSIBLE))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
if TOTAL < POSSIBLE:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def get_current_test():
|
||||||
|
if not CURRENT_TEST:
|
||||||
|
raise RuntimeError("No test is running")
|
||||||
|
return CURRENT_TEST
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Assertions
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["assert_equal", "assert_lines_match"]
|
||||||
|
|
||||||
|
def assert_equal(got, expect, msg=""):
|
||||||
|
if got == expect:
|
||||||
|
return
|
||||||
|
if msg:
|
||||||
|
msg += "\n"
|
||||||
|
raise AssertionError("%sgot:\n %s\nexpected:\n %s" %
|
||||||
|
(msg, str(got).replace("\n", "\n "),
|
||||||
|
str(expect).replace("\n", "\n ")))
|
||||||
|
|
||||||
|
def assert_lines_match(text, *regexps, **kw):
|
||||||
|
"""Assert that all of regexps match some line in text. If a 'no'
|
||||||
|
keyword argument is given, it must be a list of regexps that must
|
||||||
|
*not* match any line in text."""
|
||||||
|
|
||||||
|
def assert_lines_match_kw(no=[]):
|
||||||
|
return no
|
||||||
|
no = assert_lines_match_kw(**kw)
|
||||||
|
|
||||||
|
# Check text against regexps
|
||||||
|
lines = text.splitlines()
|
||||||
|
good = set()
|
||||||
|
bad = set()
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if any(re.match(r, line) for r in regexps):
|
||||||
|
good.add(i)
|
||||||
|
regexps = [r for r in regexps if not re.match(r, line)]
|
||||||
|
if any(re.match(r, line) for r in no):
|
||||||
|
bad.add(i)
|
||||||
|
|
||||||
|
if not regexps and not bad:
|
||||||
|
return
|
||||||
|
|
||||||
|
# We failed; construct an informative failure message
|
||||||
|
show = set()
|
||||||
|
for lineno in good.union(bad):
|
||||||
|
for offset in range(-2, 3):
|
||||||
|
show.add(lineno + offset)
|
||||||
|
if regexps:
|
||||||
|
show.update(n for n in range(len(lines) - 5, len(lines)))
|
||||||
|
|
||||||
|
msg = []
|
||||||
|
last = -1
|
||||||
|
for lineno in sorted(show):
|
||||||
|
if 0 <= lineno < len(lines):
|
||||||
|
if lineno != last + 1:
|
||||||
|
msg.append("...")
|
||||||
|
last = lineno
|
||||||
|
msg.append("%s %s" % (color("red", "BAD ") if lineno in bad else
|
||||||
|
color("green", "GOOD") if lineno in good
|
||||||
|
else " ",
|
||||||
|
lines[lineno]))
|
||||||
|
if last != len(lines) - 1:
|
||||||
|
msg.append("...")
|
||||||
|
if bad:
|
||||||
|
msg.append("unexpected lines in output")
|
||||||
|
for r in regexps:
|
||||||
|
msg.append(color("red", "MISSING") + " '%s'" % r)
|
||||||
|
raise AssertionError("\n".join(msg))
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Utilities
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["make", "maybe_unlink", "reset_fs", "color", "random_str", "check_time", "check_answers"]
|
||||||
|
|
||||||
|
MAKE_TIMESTAMP = 0
|
||||||
|
|
||||||
|
def pre_make():
|
||||||
|
"""Delay prior to running make to ensure file mtimes change."""
|
||||||
|
while int(time.time()) == MAKE_TIMESTAMP:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def post_make():
|
||||||
|
"""Record the time after make completes so that the next run of
|
||||||
|
make can be delayed if needed."""
|
||||||
|
global MAKE_TIMESTAMP
|
||||||
|
MAKE_TIMESTAMP = int(time.time())
|
||||||
|
|
||||||
|
def make(*target):
|
||||||
|
pre_make()
|
||||||
|
if Popen(("make",) + target).wait():
|
||||||
|
sys.exit(1)
|
||||||
|
post_make()
|
||||||
|
|
||||||
|
def show_command(cmd):
|
||||||
|
from pipes import quote
|
||||||
|
print("\n$", " ".join(map(quote, cmd)))
|
||||||
|
|
||||||
|
def maybe_unlink(*paths):
|
||||||
|
for path in paths:
|
||||||
|
try:
|
||||||
|
os.unlink(path)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
COLORS = {"default": "\033[0m", "red": "\033[31m", "green": "\033[32m"}
|
||||||
|
|
||||||
|
def color(name, text):
|
||||||
|
if options.color == "always" or (options.color == "auto" and os.isatty(1)):
|
||||||
|
return COLORS[name] + text + COLORS["default"]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def reset_fs():
|
||||||
|
if os.path.exists("obj/fs/clean-fs.img"):
|
||||||
|
shutil.copyfile("obj/fs/clean-fs.img", "obj/fs/fs.img")
|
||||||
|
|
||||||
|
def random_str(n=8):
|
||||||
|
letters = string.ascii_letters + string.digits
|
||||||
|
return ''.join(random.choice(letters) for _ in range(n))
|
||||||
|
|
||||||
|
def check_time():
|
||||||
|
try:
|
||||||
|
print("")
|
||||||
|
with open('time.txt') as f:
|
||||||
|
d = f.read().strip()
|
||||||
|
if not re.match(r'^\d+$', d):
|
||||||
|
raise AssertionError('time.txt does not contain a single integer (number of hours spent on the lab)')
|
||||||
|
except IOError:
|
||||||
|
raise AssertionError('Cannot read time.txt')
|
||||||
|
|
||||||
|
def check_answers(file, n=10):
|
||||||
|
try:
|
||||||
|
print("")
|
||||||
|
with open(file) as f:
|
||||||
|
d = f.read().strip()
|
||||||
|
if len(d) < n:
|
||||||
|
raise AssertionError('%s does not seem to contain enough text' % file)
|
||||||
|
except IOError:
|
||||||
|
raise AssertionError('Cannot read %s' % file)
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Controllers
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["QEMU", "GDBClient"]
|
||||||
|
|
||||||
|
class QEMU(object):
|
||||||
|
_GDBPORT = None
|
||||||
|
|
||||||
|
def __init__(self, *make_args):
|
||||||
|
# Check that QEMU is not currently running
|
||||||
|
try:
|
||||||
|
GDBClient(self.get_gdb_port(), timeout=0).close()
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print("""\
|
||||||
|
GDB stub found on port %d.
|
||||||
|
QEMU appears to already be running. Please exit it if possible or use
|
||||||
|
'killall qemu' or 'killall qemu.real'.""" % self.get_gdb_port(), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if options.verbose:
|
||||||
|
show_command(("make",) + make_args)
|
||||||
|
cmd = ("make", "-s", "--no-print-directory") + make_args
|
||||||
|
self.proc = Popen(cmd, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
stdin=subprocess.PIPE)
|
||||||
|
# Accumulated output as a string
|
||||||
|
self.output = ""
|
||||||
|
# Accumulated output as a bytearray
|
||||||
|
self.outbytes = bytearray()
|
||||||
|
self.on_output = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_gdb_port():
|
||||||
|
if QEMU._GDBPORT is None:
|
||||||
|
p = Popen(["make", "-s", "--no-print-directory", "print-gdbport"],
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
(out, _) = p.communicate()
|
||||||
|
if p.returncode:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Failed to get gdbport: make exited with %d" %
|
||||||
|
p.returncode)
|
||||||
|
QEMU._GDBPORT = int(out)
|
||||||
|
return QEMU._GDBPORT
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
if self.proc:
|
||||||
|
return self.proc.stdout.fileno()
|
||||||
|
|
||||||
|
def handle_read(self):
|
||||||
|
buf = os.read(self.proc.stdout.fileno(), 4096)
|
||||||
|
self.outbytes.extend(buf)
|
||||||
|
self.output = self.outbytes.decode("utf-8", "replace")
|
||||||
|
for callback in self.on_output:
|
||||||
|
callback(buf)
|
||||||
|
if buf == b"":
|
||||||
|
self.wait()
|
||||||
|
return
|
||||||
|
|
||||||
|
def write(self, buf):
|
||||||
|
if isinstance(buf, str):
|
||||||
|
buf = buf.encode('utf-8')
|
||||||
|
self.proc.stdin.write(buf)
|
||||||
|
self.proc.stdin.flush()
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
if self.proc:
|
||||||
|
self.proc.wait()
|
||||||
|
self.proc = None
|
||||||
|
|
||||||
|
def kill(self):
|
||||||
|
if self.proc:
|
||||||
|
self.proc.terminate()
|
||||||
|
|
||||||
|
class GDBClient(object):
|
||||||
|
def __init__(self, port, timeout=15):
|
||||||
|
start = time.time()
|
||||||
|
while True:
|
||||||
|
self.sock = socket.socket()
|
||||||
|
try:
|
||||||
|
self.sock.settimeout(1)
|
||||||
|
self.sock.connect(("localhost", port))
|
||||||
|
break
|
||||||
|
except socket.error:
|
||||||
|
if time.time() >= start + timeout:
|
||||||
|
raise
|
||||||
|
self.__buf = ""
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
if self.sock:
|
||||||
|
return self.sock.fileno()
|
||||||
|
|
||||||
|
def handle_read(self):
|
||||||
|
try:
|
||||||
|
data = self.sock.recv(4096).decode("ascii", "replace")
|
||||||
|
except socket.error:
|
||||||
|
data = ""
|
||||||
|
if data == "":
|
||||||
|
self.sock.close()
|
||||||
|
self.sock = None
|
||||||
|
return
|
||||||
|
self.__buf += data
|
||||||
|
|
||||||
|
while True:
|
||||||
|
m = re.search(r"\$([^#]*)#[0-9a-zA-Z]{2}", self.__buf)
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
pkt = m.group(1)
|
||||||
|
self.__buf = self.__buf[m.end():]
|
||||||
|
|
||||||
|
if pkt.startswith("T05"):
|
||||||
|
# Breakpoint
|
||||||
|
raise TerminateTest
|
||||||
|
|
||||||
|
def __send(self, cmd):
|
||||||
|
packet = "$%s#%02x" % (cmd, sum(map(ord, cmd)) % 256)
|
||||||
|
self.sock.sendall(packet.encode("ascii"))
|
||||||
|
|
||||||
|
def __send_break(self):
|
||||||
|
self.sock.sendall(b"\x03")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.sock:
|
||||||
|
self.sock.close()
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
def cont(self):
|
||||||
|
self.__send("c")
|
||||||
|
|
||||||
|
def breakpoint(self, addr):
|
||||||
|
self.__send("Z1,%x,1" % addr)
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# QEMU test runner
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["TerminateTest", "Runner"]
|
||||||
|
|
||||||
|
class TerminateTest(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Runner():
|
||||||
|
def __init__(self, *default_monitors):
|
||||||
|
self.__default_monitors = default_monitors
|
||||||
|
|
||||||
|
def run_qemu(self, *monitors, **kw):
|
||||||
|
"""Run a QEMU-based test. monitors should functions that will
|
||||||
|
be called with this Runner instance once QEMU and GDB are
|
||||||
|
started. Typically, they should register callbacks that throw
|
||||||
|
TerminateTest when stop events occur. The target_base
|
||||||
|
argument gives the make target to run. The make_args argument
|
||||||
|
should be a list of additional arguments to pass to make. The
|
||||||
|
timeout argument bounds how long to run before returning."""
|
||||||
|
|
||||||
|
def run_qemu_kw(target_base="qemu", make_args=[], timeout=30):
|
||||||
|
return target_base, make_args, timeout
|
||||||
|
target_base, make_args, timeout = run_qemu_kw(**kw)
|
||||||
|
|
||||||
|
# Start QEMU
|
||||||
|
pre_make()
|
||||||
|
self.qemu = QEMU(target_base + "-gdb", *make_args)
|
||||||
|
self.gdb = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Wait for QEMU to start or make to fail. This will set
|
||||||
|
# self.gdb if QEMU starts.
|
||||||
|
self.qemu.on_output = [self.__monitor_start]
|
||||||
|
self.__react([self.qemu], timeout=90)
|
||||||
|
self.qemu.on_output = []
|
||||||
|
if self.gdb is None:
|
||||||
|
print("Failed to connect to QEMU; output:")
|
||||||
|
print(self.qemu.output)
|
||||||
|
sys.exit(1)
|
||||||
|
post_make()
|
||||||
|
|
||||||
|
# QEMU and GDB are up
|
||||||
|
self.reactors = [self.qemu, self.gdb]
|
||||||
|
|
||||||
|
# Start monitoring
|
||||||
|
for m in self.__default_monitors + monitors:
|
||||||
|
m(self)
|
||||||
|
|
||||||
|
# Run and react
|
||||||
|
self.gdb.cont()
|
||||||
|
self.__react(self.reactors, timeout)
|
||||||
|
finally:
|
||||||
|
# Shutdown QEMU
|
||||||
|
try:
|
||||||
|
if self.gdb is None:
|
||||||
|
sys.exit(1)
|
||||||
|
self.qemu.kill()
|
||||||
|
self.__react(self.reactors, 5)
|
||||||
|
self.gdb.close()
|
||||||
|
self.qemu.wait()
|
||||||
|
except:
|
||||||
|
print("""\
|
||||||
|
Failed to shutdown QEMU. You might need to 'killall qemu' or
|
||||||
|
'killall qemu.real'.
|
||||||
|
""")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def __monitor_start(self, output):
|
||||||
|
if b"\n" in output:
|
||||||
|
try:
|
||||||
|
self.gdb = GDBClient(self.qemu.get_gdb_port(), timeout=2)
|
||||||
|
raise TerminateTest
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
if not len(output):
|
||||||
|
raise TerminateTest
|
||||||
|
|
||||||
|
def __react(self, reactors, timeout):
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
timeleft = deadline - time.time()
|
||||||
|
if timeleft < 0:
|
||||||
|
sys.stdout.write("Timeout! ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
return
|
||||||
|
|
||||||
|
rset = [r for r in reactors if r.fileno() is not None]
|
||||||
|
if not rset:
|
||||||
|
return
|
||||||
|
|
||||||
|
rset, _, _ = select.select(rset, [], [], timeleft)
|
||||||
|
for reactor in rset:
|
||||||
|
reactor.handle_read()
|
||||||
|
except TerminateTest:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def user_test(self, binary, *monitors, **kw):
|
||||||
|
"""Run a user test using the specified binary. Monitors and
|
||||||
|
keyword arguments are as for run_qemu. This runs on a disk
|
||||||
|
snapshot unless the keyword argument 'snapshot' is False."""
|
||||||
|
|
||||||
|
maybe_unlink("obj/kern/init.o", "obj/kern/kernel")
|
||||||
|
if kw.pop("snapshot", True):
|
||||||
|
kw.setdefault("make_args", []).append("QEMUEXTRA+=-snapshot")
|
||||||
|
self.run_qemu(target_base="run-%s" % binary, *monitors, **kw)
|
||||||
|
|
||||||
|
def match(self, *args, **kwargs):
|
||||||
|
"""Shortcut to call assert_lines_match on the most recent QEMU
|
||||||
|
output."""
|
||||||
|
|
||||||
|
assert_lines_match(self.qemu.output, *args, **kwargs)
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Monitors
|
||||||
|
#
|
||||||
|
|
||||||
|
__all__ += ["save", "stop_breakpoint", "call_on_line", "stop_on_line", "shell_script"]
|
||||||
|
|
||||||
|
def save(path):
|
||||||
|
"""Return a monitor that writes QEMU's output to path. If the
|
||||||
|
test fails, copy the output to path.test-name."""
|
||||||
|
|
||||||
|
def setup_save(runner):
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
runner.qemu.on_output.append(f.write)
|
||||||
|
get_current_test().on_finish.append(save_on_finish)
|
||||||
|
|
||||||
|
def save_on_finish(fail):
|
||||||
|
f.flush()
|
||||||
|
save_path = path + "." + get_current_test().__name__[5:]
|
||||||
|
if fail:
|
||||||
|
shutil.copyfile(path, save_path)
|
||||||
|
print(" QEMU output saved to %s" % save_path)
|
||||||
|
elif os.path.exists(save_path):
|
||||||
|
os.unlink(save_path)
|
||||||
|
print(" (Old %s failure log removed)" % save_path)
|
||||||
|
|
||||||
|
f = open(path, "wb")
|
||||||
|
return setup_save
|
||||||
|
|
||||||
|
def stop_breakpoint(addr):
|
||||||
|
"""Returns a monitor that stops when addr is reached. addr may be
|
||||||
|
a number or the name of a symbol."""
|
||||||
|
|
||||||
|
def setup_breakpoint(runner):
|
||||||
|
if isinstance(addr, str):
|
||||||
|
addrs = [int(sym[:16], 16) for sym in open("kernel/kernel.sym")
|
||||||
|
if sym[17:].strip() == addr]
|
||||||
|
assert len(addrs), "Symbol %s not found" % addr
|
||||||
|
runner.gdb.breakpoint(addrs[0])
|
||||||
|
else:
|
||||||
|
runner.gdb.breakpoint(addr)
|
||||||
|
return setup_breakpoint
|
||||||
|
|
||||||
|
def call_on_line(regexp, callback):
|
||||||
|
"""Returns a monitor that calls 'callback' when QEMU prints a line
|
||||||
|
matching 'regexp'."""
|
||||||
|
|
||||||
|
def setup_call_on_line(runner):
|
||||||
|
buf = bytearray()
|
||||||
|
def handle_output(output):
|
||||||
|
buf.extend(output)
|
||||||
|
while b"\n" in buf:
|
||||||
|
line, buf[:] = buf.split(b"\n", 1)
|
||||||
|
line = line.decode("utf-8", "replace")
|
||||||
|
if re.match(regexp, line):
|
||||||
|
callback(line)
|
||||||
|
runner.qemu.on_output.append(handle_output)
|
||||||
|
return setup_call_on_line
|
||||||
|
|
||||||
|
def stop_on_line(regexp):
|
||||||
|
"""Returns a monitor that stops when QEMU prints a line matching
|
||||||
|
'regexp'."""
|
||||||
|
|
||||||
|
def stop(line):
|
||||||
|
raise TerminateTest
|
||||||
|
return call_on_line(regexp, stop)
|
||||||
|
|
||||||
|
def shell_script(script, terminate_match=None):
|
||||||
|
"""Returns a monitor that plays the script, and stops when the script is
|
||||||
|
done executing."""
|
||||||
|
|
||||||
|
def setup_call_on_line(runner):
|
||||||
|
class context:
|
||||||
|
n = 0
|
||||||
|
buf = bytearray()
|
||||||
|
def handle_output(output):
|
||||||
|
context.buf.extend(output)
|
||||||
|
if terminate_match is not None:
|
||||||
|
if re.match(terminate_match, context.buf.decode('utf-8', 'replace')):
|
||||||
|
raise TerminateTest
|
||||||
|
if b'$ ' in context.buf:
|
||||||
|
context.buf = bytearray()
|
||||||
|
if context.n < len(script):
|
||||||
|
runner.qemu.write(script[context.n])
|
||||||
|
runner.qemu.write('\n')
|
||||||
|
context.n += 1
|
||||||
|
else:
|
||||||
|
if terminate_match is None:
|
||||||
|
raise TerminateTest
|
||||||
|
runner.qemu.on_output.append(handle_output)
|
||||||
|
return setup_call_on_line
|
||||||
153
kernel/bio.c
Normal file
153
kernel/bio.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
struct buf buf[NBUF];
|
||||||
|
|
||||||
|
// Linked list of all buffers, through prev/next.
|
||||||
|
// Sorted by how recently the buffer was used.
|
||||||
|
// head.next is most recent, head.prev is least.
|
||||||
|
struct buf head;
|
||||||
|
} bcache;
|
||||||
|
|
||||||
|
void
|
||||||
|
binit(void)
|
||||||
|
{
|
||||||
|
struct buf *b;
|
||||||
|
|
||||||
|
initlock(&bcache.lock, "bcache");
|
||||||
|
|
||||||
|
// 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 the least recently used (LRU) unused buffer.
|
||||||
|
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
|
||||||
|
if(b->refcnt == 0) {
|
||||||
|
b->dev = dev;
|
||||||
|
b->blockno = blockno;
|
||||||
|
b->valid = 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->valid) {
|
||||||
|
virtio_disk_rw(b, 0);
|
||||||
|
b->valid = 1;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write b's contents to disk. Must be locked.
|
||||||
|
void
|
||||||
|
bwrite(struct buf *b)
|
||||||
|
{
|
||||||
|
if(!holdingsleep(&b->lock))
|
||||||
|
panic("bwrite");
|
||||||
|
virtio_disk_rw(b, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release a locked buffer.
|
||||||
|
// Move to the head of the most-recently-used 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bpin(struct buf *b) {
|
||||||
|
acquire(&bcache.lock);
|
||||||
|
b->refcnt++;
|
||||||
|
release(&bcache.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bunpin(struct buf *b) {
|
||||||
|
acquire(&bcache.lock);
|
||||||
|
b->refcnt--;
|
||||||
|
release(&bcache.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
12
kernel/buf.h
Normal file
12
kernel/buf.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
struct buf {
|
||||||
|
int valid; // has data been read from disk?
|
||||||
|
int disk; // does disk "own" buf?
|
||||||
|
uint dev;
|
||||||
|
uint blockno;
|
||||||
|
struct sleeplock lock;
|
||||||
|
uint refcnt;
|
||||||
|
struct buf *prev; // LRU cache list
|
||||||
|
struct buf *next;
|
||||||
|
uchar data[BSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
192
kernel/console.c
Normal file
192
kernel/console.c
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
//
|
||||||
|
// Console input and output, to the uart.
|
||||||
|
// Reads are line at a time.
|
||||||
|
// Implements special input characters:
|
||||||
|
// newline -- end of line
|
||||||
|
// control-h -- backspace
|
||||||
|
// control-u -- kill line
|
||||||
|
// control-d -- end of file
|
||||||
|
// control-p -- print process list
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "proc.h"
|
||||||
|
|
||||||
|
#define BACKSPACE 0x100
|
||||||
|
#define C(x) ((x)-'@') // Control-x
|
||||||
|
|
||||||
|
//
|
||||||
|
// send one character to the uart.
|
||||||
|
// called by printf(), and to echo input characters,
|
||||||
|
// but not from write().
|
||||||
|
//
|
||||||
|
void
|
||||||
|
consputc(int c)
|
||||||
|
{
|
||||||
|
if(c == BACKSPACE){
|
||||||
|
// if the user typed backspace, overwrite with a space.
|
||||||
|
uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
|
||||||
|
} else {
|
||||||
|
uartputc_sync(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
|
||||||
|
// input
|
||||||
|
#define INPUT_BUF_SIZE 128
|
||||||
|
char buf[INPUT_BUF_SIZE];
|
||||||
|
uint r; // Read index
|
||||||
|
uint w; // Write index
|
||||||
|
uint e; // Edit index
|
||||||
|
} cons;
|
||||||
|
|
||||||
|
//
|
||||||
|
// user write()s to the console go here.
|
||||||
|
//
|
||||||
|
int
|
||||||
|
consolewrite(int user_src, uint64 src, int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < n; i++){
|
||||||
|
char c;
|
||||||
|
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
||||||
|
break;
|
||||||
|
uartputc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// user read()s from the console go here.
|
||||||
|
// copy (up to) a whole input line to dst.
|
||||||
|
// user_dist indicates whether dst is a user
|
||||||
|
// or kernel address.
|
||||||
|
//
|
||||||
|
int
|
||||||
|
consoleread(int user_dst, uint64 dst, int n)
|
||||||
|
{
|
||||||
|
uint target;
|
||||||
|
int c;
|
||||||
|
char cbuf;
|
||||||
|
|
||||||
|
target = n;
|
||||||
|
acquire(&cons.lock);
|
||||||
|
while(n > 0){
|
||||||
|
// wait until interrupt handler has put some
|
||||||
|
// input into cons.buffer.
|
||||||
|
while(cons.r == cons.w){
|
||||||
|
if(killed(myproc())){
|
||||||
|
release(&cons.lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sleep(&cons.r, &cons.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
c = cons.buf[cons.r++ % INPUT_BUF_SIZE];
|
||||||
|
|
||||||
|
if(c == C('D')){ // end-of-file
|
||||||
|
if(n < target){
|
||||||
|
// Save ^D for next time, to make sure
|
||||||
|
// caller gets a 0-byte result.
|
||||||
|
cons.r--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the input byte to the user-space buffer.
|
||||||
|
cbuf = c;
|
||||||
|
if(either_copyout(user_dst, dst, &cbuf, 1) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
dst++;
|
||||||
|
--n;
|
||||||
|
|
||||||
|
if(c == '\n'){
|
||||||
|
// a whole line has arrived, return to
|
||||||
|
// the user-level read().
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&cons.lock);
|
||||||
|
|
||||||
|
return target - n;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// the console input interrupt handler.
|
||||||
|
// uartintr() calls this for input character.
|
||||||
|
// do erase/kill processing, append to cons.buf,
|
||||||
|
// wake up consoleread() if a whole line has arrived.
|
||||||
|
//
|
||||||
|
void
|
||||||
|
consoleintr(int c)
|
||||||
|
{
|
||||||
|
acquire(&cons.lock);
|
||||||
|
|
||||||
|
switch(c){
|
||||||
|
case C('P'): // Print process list.
|
||||||
|
procdump();
|
||||||
|
break;
|
||||||
|
case C('U'): // Kill line.
|
||||||
|
while(cons.e != cons.w &&
|
||||||
|
cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){
|
||||||
|
cons.e--;
|
||||||
|
consputc(BACKSPACE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C('H'): // Backspace
|
||||||
|
case '\x7f': // Delete key
|
||||||
|
if(cons.e != cons.w){
|
||||||
|
cons.e--;
|
||||||
|
consputc(BACKSPACE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){
|
||||||
|
c = (c == '\r') ? '\n' : c;
|
||||||
|
|
||||||
|
// echo back to the user.
|
||||||
|
consputc(c);
|
||||||
|
|
||||||
|
// store for consumption by consoleread().
|
||||||
|
cons.buf[cons.e++ % INPUT_BUF_SIZE] = c;
|
||||||
|
|
||||||
|
if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){
|
||||||
|
// wake up consoleread() if a whole line (or end-of-file)
|
||||||
|
// has arrived.
|
||||||
|
cons.w = cons.e;
|
||||||
|
wakeup(&cons.r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
release(&cons.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
consoleinit(void)
|
||||||
|
{
|
||||||
|
initlock(&cons.lock, "cons");
|
||||||
|
|
||||||
|
uartinit();
|
||||||
|
|
||||||
|
// connect read and write system calls
|
||||||
|
// to consoleread and consolewrite.
|
||||||
|
devsw[CONSOLE].read = consoleread;
|
||||||
|
devsw[CONSOLE].write = consolewrite;
|
||||||
|
}
|
||||||
189
kernel/defs.h
Normal file
189
kernel/defs.h
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
struct buf;
|
||||||
|
struct context;
|
||||||
|
struct file;
|
||||||
|
struct inode;
|
||||||
|
struct pipe;
|
||||||
|
struct proc;
|
||||||
|
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*);
|
||||||
|
void bpin(struct buf*);
|
||||||
|
void bunpin(struct buf*);
|
||||||
|
|
||||||
|
// console.c
|
||||||
|
void consoleinit(void);
|
||||||
|
void consoleintr(int);
|
||||||
|
void consputc(int);
|
||||||
|
|
||||||
|
// 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*, uint64, int n);
|
||||||
|
int filestat(struct file*, uint64 addr);
|
||||||
|
int filewrite(struct file*, uint64, int n);
|
||||||
|
|
||||||
|
// fs.c
|
||||||
|
void fsinit(int);
|
||||||
|
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();
|
||||||
|
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*, int, uint64, uint, uint);
|
||||||
|
void stati(struct inode*, struct stat*);
|
||||||
|
int writei(struct inode*, int, uint64, uint, uint);
|
||||||
|
void itrunc(struct inode*);
|
||||||
|
|
||||||
|
// ramdisk.c
|
||||||
|
void ramdiskinit(void);
|
||||||
|
void ramdiskintr(void);
|
||||||
|
void ramdiskrw(struct buf*);
|
||||||
|
|
||||||
|
// kalloc.c
|
||||||
|
void* kalloc(void);
|
||||||
|
void kfree(void *);
|
||||||
|
void kinit(void);
|
||||||
|
|
||||||
|
// log.c
|
||||||
|
void initlog(int, struct superblock*);
|
||||||
|
void log_write(struct buf*);
|
||||||
|
void begin_op(void);
|
||||||
|
void end_op(void);
|
||||||
|
|
||||||
|
// pipe.c
|
||||||
|
int pipealloc(struct file**, struct file**);
|
||||||
|
void pipeclose(struct pipe*, int);
|
||||||
|
int piperead(struct pipe*, uint64, int);
|
||||||
|
int pipewrite(struct pipe*, uint64, int);
|
||||||
|
|
||||||
|
// printf.c
|
||||||
|
void printf(char*, ...);
|
||||||
|
void panic(char*) __attribute__((noreturn));
|
||||||
|
void printfinit(void);
|
||||||
|
|
||||||
|
// proc.c
|
||||||
|
int cpuid(void);
|
||||||
|
void exit(int);
|
||||||
|
int fork(void);
|
||||||
|
int growproc(int);
|
||||||
|
void proc_mapstacks(pagetable_t);
|
||||||
|
pagetable_t proc_pagetable(struct proc *);
|
||||||
|
void proc_freepagetable(pagetable_t, uint64);
|
||||||
|
int kill(int);
|
||||||
|
int killed(struct proc*);
|
||||||
|
void setkilled(struct proc*);
|
||||||
|
struct cpu* mycpu(void);
|
||||||
|
struct cpu* getmycpu(void);
|
||||||
|
struct proc* myproc();
|
||||||
|
void procinit(void);
|
||||||
|
void scheduler(void) __attribute__((noreturn));
|
||||||
|
void sched(void);
|
||||||
|
void sleep(void*, struct spinlock*);
|
||||||
|
void userinit(void);
|
||||||
|
int wait(uint64);
|
||||||
|
void wakeup(void*);
|
||||||
|
void yield(void);
|
||||||
|
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
|
||||||
|
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
|
||||||
|
void procdump(void);
|
||||||
|
|
||||||
|
// swtch.S
|
||||||
|
void swtch(struct context*, struct context*);
|
||||||
|
|
||||||
|
// spinlock.c
|
||||||
|
void acquire(struct spinlock*);
|
||||||
|
int holding(struct spinlock*);
|
||||||
|
void initlock(struct spinlock*, char*);
|
||||||
|
void release(struct spinlock*);
|
||||||
|
void push_off(void);
|
||||||
|
void pop_off(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
|
||||||
|
void argint(int, int*);
|
||||||
|
int argstr(int, char*, int);
|
||||||
|
void argaddr(int, uint64 *);
|
||||||
|
int fetchstr(uint64, char*, int);
|
||||||
|
int fetchaddr(uint64, uint64*);
|
||||||
|
void syscall();
|
||||||
|
|
||||||
|
// trap.c
|
||||||
|
extern uint ticks;
|
||||||
|
void trapinit(void);
|
||||||
|
void trapinithart(void);
|
||||||
|
extern struct spinlock tickslock;
|
||||||
|
void usertrapret(void);
|
||||||
|
|
||||||
|
// uart.c
|
||||||
|
void uartinit(void);
|
||||||
|
void uartintr(void);
|
||||||
|
void uartputc(int);
|
||||||
|
void uartputc_sync(int);
|
||||||
|
int uartgetc(void);
|
||||||
|
|
||||||
|
// vm.c
|
||||||
|
void kvminit(void);
|
||||||
|
void kvminithart(void);
|
||||||
|
void kvmmap(pagetable_t, uint64, uint64, uint64, int);
|
||||||
|
int mappages(pagetable_t, uint64, uint64, uint64, int);
|
||||||
|
pagetable_t uvmcreate(void);
|
||||||
|
void uvmfirst(pagetable_t, uchar *, uint);
|
||||||
|
uint64 uvmalloc(pagetable_t, uint64, uint64, int);
|
||||||
|
uint64 uvmdealloc(pagetable_t, uint64, uint64);
|
||||||
|
int uvmcopy(pagetable_t, pagetable_t, uint64);
|
||||||
|
void uvmfree(pagetable_t, uint64);
|
||||||
|
void uvmunmap(pagetable_t, uint64, uint64, int);
|
||||||
|
void uvmclear(pagetable_t, uint64);
|
||||||
|
pte_t * walk(pagetable_t, uint64, int);
|
||||||
|
uint64 walkaddr(pagetable_t, uint64);
|
||||||
|
int copyout(pagetable_t, uint64, char *, uint64);
|
||||||
|
int copyin(pagetable_t, char *, uint64, uint64);
|
||||||
|
int copyinstr(pagetable_t, char *, uint64, uint64);
|
||||||
|
|
||||||
|
// plic.c
|
||||||
|
void plicinit(void);
|
||||||
|
void plicinithart(void);
|
||||||
|
int plic_claim(void);
|
||||||
|
void plic_complete(int);
|
||||||
|
|
||||||
|
// virtio_disk.c
|
||||||
|
void virtio_disk_init(void);
|
||||||
|
void virtio_disk_rw(struct buf *, int);
|
||||||
|
void virtio_disk_intr(void);
|
||||||
|
|
||||||
|
// number of elements in fixed-size array
|
||||||
|
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
|
||||||
42
kernel/elf.h
Normal file
42
kernel/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;
|
||||||
|
uint64 entry;
|
||||||
|
uint64 phoff;
|
||||||
|
uint64 shoff;
|
||||||
|
uint flags;
|
||||||
|
ushort ehsize;
|
||||||
|
ushort phentsize;
|
||||||
|
ushort phnum;
|
||||||
|
ushort shentsize;
|
||||||
|
ushort shnum;
|
||||||
|
ushort shstrndx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Program section header
|
||||||
|
struct proghdr {
|
||||||
|
uint32 type;
|
||||||
|
uint32 flags;
|
||||||
|
uint64 off;
|
||||||
|
uint64 vaddr;
|
||||||
|
uint64 paddr;
|
||||||
|
uint64 filesz;
|
||||||
|
uint64 memsz;
|
||||||
|
uint64 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
|
||||||
21
kernel/entry.S
Normal file
21
kernel/entry.S
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# qemu -kernel loads the kernel at 0x80000000
|
||||||
|
# and causes each hart (i.e. CPU) to jump there.
|
||||||
|
# kernel.ld causes the following code to
|
||||||
|
# be placed at 0x80000000.
|
||||||
|
.section .text
|
||||||
|
.global _entry
|
||||||
|
_entry:
|
||||||
|
# set up a stack for C.
|
||||||
|
# stack0 is declared in start.c,
|
||||||
|
# with a 4096-byte stack per CPU.
|
||||||
|
# sp = stack0 + (hartid * 4096)
|
||||||
|
la sp, stack0
|
||||||
|
li a0, 1024*4
|
||||||
|
csrr a1, mhartid
|
||||||
|
addi a1, a1, 1
|
||||||
|
mul a0, a0, a1
|
||||||
|
add sp, sp, a0
|
||||||
|
# jump to start() in start.c
|
||||||
|
call start
|
||||||
|
spin:
|
||||||
|
j spin
|
||||||
166
kernel/exec.c
Normal file
166
kernel/exec.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
static int loadseg(pde_t *, uint64, struct inode *, uint, uint);
|
||||||
|
|
||||||
|
int flags2perm(int flags)
|
||||||
|
{
|
||||||
|
int perm = 0;
|
||||||
|
if(flags & 0x1)
|
||||||
|
perm = PTE_X;
|
||||||
|
if(flags & 0x2)
|
||||||
|
perm |= PTE_W;
|
||||||
|
return perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
exec(char *path, char **argv)
|
||||||
|
{
|
||||||
|
char *s, *last;
|
||||||
|
int i, off;
|
||||||
|
uint64 argc, sz = 0, sp, ustack[MAXARG], stackbase;
|
||||||
|
struct elfhdr elf;
|
||||||
|
struct inode *ip;
|
||||||
|
struct proghdr ph;
|
||||||
|
pagetable_t pagetable = 0, oldpagetable;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
|
||||||
|
if((ip = namei(path)) == 0){
|
||||||
|
end_op();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ilock(ip);
|
||||||
|
|
||||||
|
// Check ELF header
|
||||||
|
if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
if(elf.magic != ELF_MAGIC)
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
if((pagetable = proc_pagetable(p)) == 0)
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
// Load program into memory.
|
||||||
|
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
|
||||||
|
if(readi(ip, 0, (uint64)&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(ph.vaddr % PGSIZE != 0)
|
||||||
|
goto bad;
|
||||||
|
uint64 sz1;
|
||||||
|
if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, flags2perm(ph.flags))) == 0)
|
||||||
|
goto bad;
|
||||||
|
sz = sz1;
|
||||||
|
if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
ip = 0;
|
||||||
|
|
||||||
|
p = myproc();
|
||||||
|
uint64 oldsz = p->sz;
|
||||||
|
|
||||||
|
// Allocate two pages at the next page boundary.
|
||||||
|
// Make the first inaccessible as a stack guard.
|
||||||
|
// Use the second as the user stack.
|
||||||
|
sz = PGROUNDUP(sz);
|
||||||
|
uint64 sz1;
|
||||||
|
if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE, PTE_W)) == 0)
|
||||||
|
goto bad;
|
||||||
|
sz = sz1;
|
||||||
|
uvmclear(pagetable, sz-2*PGSIZE);
|
||||||
|
sp = sz;
|
||||||
|
stackbase = sp - PGSIZE;
|
||||||
|
|
||||||
|
// Push argument strings, prepare rest of stack in ustack.
|
||||||
|
for(argc = 0; argv[argc]; argc++) {
|
||||||
|
if(argc >= MAXARG)
|
||||||
|
goto bad;
|
||||||
|
sp -= strlen(argv[argc]) + 1;
|
||||||
|
sp -= sp % 16; // riscv sp must be 16-byte aligned
|
||||||
|
if(sp < stackbase)
|
||||||
|
goto bad;
|
||||||
|
if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
|
||||||
|
goto bad;
|
||||||
|
ustack[argc] = sp;
|
||||||
|
}
|
||||||
|
ustack[argc] = 0;
|
||||||
|
|
||||||
|
// push the array of argv[] pointers.
|
||||||
|
sp -= (argc+1) * sizeof(uint64);
|
||||||
|
sp -= sp % 16;
|
||||||
|
if(sp < stackbase)
|
||||||
|
goto bad;
|
||||||
|
if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
// arguments to user main(argc, argv)
|
||||||
|
// argc is returned via the system call return
|
||||||
|
// value, which goes in a0.
|
||||||
|
p->trapframe->a1 = sp;
|
||||||
|
|
||||||
|
// Save program name for debugging.
|
||||||
|
for(last=s=path; *s; s++)
|
||||||
|
if(*s == '/')
|
||||||
|
last = s+1;
|
||||||
|
safestrcpy(p->name, last, sizeof(p->name));
|
||||||
|
|
||||||
|
// Commit to the user image.
|
||||||
|
oldpagetable = p->pagetable;
|
||||||
|
p->pagetable = pagetable;
|
||||||
|
p->sz = sz;
|
||||||
|
p->trapframe->epc = elf.entry; // initial program counter = main
|
||||||
|
p->trapframe->sp = sp; // initial stack pointer
|
||||||
|
proc_freepagetable(oldpagetable, oldsz);
|
||||||
|
|
||||||
|
return argc; // this ends up in a0, the first argument to main(argc, argv)
|
||||||
|
|
||||||
|
bad:
|
||||||
|
if(pagetable)
|
||||||
|
proc_freepagetable(pagetable, sz);
|
||||||
|
if(ip){
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a program segment into pagetable at virtual address va.
|
||||||
|
// va must be page-aligned
|
||||||
|
// and the pages from va to va+sz must already be mapped.
|
||||||
|
// Returns 0 on success, -1 on failure.
|
||||||
|
static int
|
||||||
|
loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz)
|
||||||
|
{
|
||||||
|
uint i, n;
|
||||||
|
uint64 pa;
|
||||||
|
|
||||||
|
for(i = 0; i < sz; i += PGSIZE){
|
||||||
|
pa = walkaddr(pagetable, va + i);
|
||||||
|
if(pa == 0)
|
||||||
|
panic("loadseg: address should exist");
|
||||||
|
if(sz - i < PGSIZE)
|
||||||
|
n = sz - i;
|
||||||
|
else
|
||||||
|
n = PGSIZE;
|
||||||
|
if(readi(ip, 0, (uint64)pa, offset+i, n) != n)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
kernel/fcntl.h
Normal file
5
kernel/fcntl.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#define O_RDONLY 0x000
|
||||||
|
#define O_WRONLY 0x001
|
||||||
|
#define O_RDWR 0x002
|
||||||
|
#define O_CREATE 0x200
|
||||||
|
#define O_TRUNC 0x400
|
||||||
182
kernel/file.c
Normal file
182
kernel/file.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
//
|
||||||
|
// Support functions for system calls that involve file descriptors.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "proc.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 || ff.type == FD_DEVICE){
|
||||||
|
begin_op();
|
||||||
|
iput(ff.ip);
|
||||||
|
end_op();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get metadata about file f.
|
||||||
|
// addr is a user virtual address, pointing to a struct stat.
|
||||||
|
int
|
||||||
|
filestat(struct file *f, uint64 addr)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if(f->type == FD_INODE || f->type == FD_DEVICE){
|
||||||
|
ilock(f->ip);
|
||||||
|
stati(f->ip, &st);
|
||||||
|
iunlock(f->ip);
|
||||||
|
if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from file f.
|
||||||
|
// addr is a user virtual address.
|
||||||
|
int
|
||||||
|
fileread(struct file *f, uint64 addr, int n)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
if(f->readable == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(f->type == FD_PIPE){
|
||||||
|
r = piperead(f->pipe, addr, n);
|
||||||
|
} else if(f->type == FD_DEVICE){
|
||||||
|
if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read)
|
||||||
|
return -1;
|
||||||
|
r = devsw[f->major].read(1, addr, n);
|
||||||
|
} else if(f->type == FD_INODE){
|
||||||
|
ilock(f->ip);
|
||||||
|
if((r = readi(f->ip, 1, addr, f->off, n)) > 0)
|
||||||
|
f->off += r;
|
||||||
|
iunlock(f->ip);
|
||||||
|
} else {
|
||||||
|
panic("fileread");
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to file f.
|
||||||
|
// addr is a user virtual address.
|
||||||
|
int
|
||||||
|
filewrite(struct file *f, uint64 addr, int n)
|
||||||
|
{
|
||||||
|
int r, ret = 0;
|
||||||
|
|
||||||
|
if(f->writable == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(f->type == FD_PIPE){
|
||||||
|
ret = pipewrite(f->pipe, addr, n);
|
||||||
|
} else if(f->type == FD_DEVICE){
|
||||||
|
if(f->major < 0 || f->major >= NDEV || !devsw[f->major].write)
|
||||||
|
return -1;
|
||||||
|
ret = devsw[f->major].write(1, addr, n);
|
||||||
|
} else 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) * BSIZE;
|
||||||
|
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, 1, addr + i, f->off, n1)) > 0)
|
||||||
|
f->off += r;
|
||||||
|
iunlock(f->ip);
|
||||||
|
end_op();
|
||||||
|
|
||||||
|
if(r != n1){
|
||||||
|
// error from writei
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += r;
|
||||||
|
}
|
||||||
|
ret = (i == n ? n : -1);
|
||||||
|
} else {
|
||||||
|
panic("filewrite");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
40
kernel/file.h
Normal file
40
kernel/file.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
struct file {
|
||||||
|
enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type;
|
||||||
|
int ref; // reference count
|
||||||
|
char readable;
|
||||||
|
char writable;
|
||||||
|
struct pipe *pipe; // FD_PIPE
|
||||||
|
struct inode *ip; // FD_INODE and FD_DEVICE
|
||||||
|
uint off; // FD_INODE
|
||||||
|
short major; // FD_DEVICE
|
||||||
|
};
|
||||||
|
|
||||||
|
#define major(dev) ((dev) >> 16 & 0xFFFF)
|
||||||
|
#define minor(dev) ((dev) & 0xFFFF)
|
||||||
|
#define mkdev(m,n) ((uint)((m)<<16| (n)))
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
};
|
||||||
|
|
||||||
|
// map major device number to device functions.
|
||||||
|
struct devsw {
|
||||||
|
int (*read)(int, uint64, int);
|
||||||
|
int (*write)(int, uint64, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct devsw devsw[];
|
||||||
|
|
||||||
|
#define CONSOLE 1
|
||||||
697
kernel/fs.c
Normal file
697
kernel/fs.c
Normal file
@ -0,0 +1,697 @@
|
|||||||
|
// 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 "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
// there should be one superblock per disk device, but we run with
|
||||||
|
// only one device
|
||||||
|
struct superblock sb;
|
||||||
|
|
||||||
|
// Read the super block.
|
||||||
|
static void
|
||||||
|
readsb(int dev, struct superblock *sb)
|
||||||
|
{
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
bp = bread(dev, 1);
|
||||||
|
memmove(sb, bp->data, sizeof(*sb));
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init fs
|
||||||
|
void
|
||||||
|
fsinit(int dev) {
|
||||||
|
readsb(dev, &sb);
|
||||||
|
if(sb.magic != FSMAGIC)
|
||||||
|
panic("invalid file system");
|
||||||
|
initlog(dev, &sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// returns 0 if out of disk space.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
printf("balloc: out of blocks\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a disk block.
|
||||||
|
static void
|
||||||
|
bfree(int dev, uint b)
|
||||||
|
{
|
||||||
|
struct buf *bp;
|
||||||
|
int bi, m;
|
||||||
|
|
||||||
|
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 block
|
||||||
|
// sb.inodestart. Each inode has a number, indicating its
|
||||||
|
// position on the disk.
|
||||||
|
//
|
||||||
|
// The kernel keeps a table of in-use inodes in memory
|
||||||
|
// to provide a place for synchronizing access
|
||||||
|
// to inodes used by multiple processes. The in-memory
|
||||||
|
// 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 table: an entry in the inode table
|
||||||
|
// 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 table entry and increments its ref; iput()
|
||||||
|
// decrements ref.
|
||||||
|
//
|
||||||
|
// * Valid: the information (type, size, &c) in an inode
|
||||||
|
// table 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 in the table 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 itable.lock spin-lock protects the allocation of itable
|
||||||
|
// 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 itable.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];
|
||||||
|
} itable;
|
||||||
|
|
||||||
|
void
|
||||||
|
iinit()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
initlock(&itable.lock, "itable");
|
||||||
|
for(i = 0; i < NINODE; i++) {
|
||||||
|
initsleeplock(&itable.inode[i].lock, "inode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inode* iget(uint dev, uint inum);
|
||||||
|
|
||||||
|
// Allocate an inode on device dev.
|
||||||
|
// Mark it as allocated by giving it type type.
|
||||||
|
// Returns an unlocked but allocated and referenced inode,
|
||||||
|
// or NULL if there is no free 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);
|
||||||
|
}
|
||||||
|
printf("ialloc: no inodes\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a modified in-memory inode to disk.
|
||||||
|
// Must be called after every change to an ip->xxx field
|
||||||
|
// that lives on disk.
|
||||||
|
// 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(&itable.lock);
|
||||||
|
|
||||||
|
// Is the inode already in the table?
|
||||||
|
empty = 0;
|
||||||
|
for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){
|
||||||
|
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
|
||||||
|
ip->ref++;
|
||||||
|
release(&itable.lock);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
if(empty == 0 && ip->ref == 0) // Remember empty slot.
|
||||||
|
empty = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recycle an inode entry.
|
||||||
|
if(empty == 0)
|
||||||
|
panic("iget: no inodes");
|
||||||
|
|
||||||
|
ip = empty;
|
||||||
|
ip->dev = dev;
|
||||||
|
ip->inum = inum;
|
||||||
|
ip->ref = 1;
|
||||||
|
ip->valid = 0;
|
||||||
|
release(&itable.lock);
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment reference count for ip.
|
||||||
|
// Returns ip to enable ip = idup(ip1) idiom.
|
||||||
|
struct inode*
|
||||||
|
idup(struct inode *ip)
|
||||||
|
{
|
||||||
|
acquire(&itable.lock);
|
||||||
|
ip->ref++;
|
||||||
|
release(&itable.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 table 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)
|
||||||
|
{
|
||||||
|
acquire(&itable.lock);
|
||||||
|
|
||||||
|
if(ip->ref == 1 && ip->valid && ip->nlink == 0){
|
||||||
|
// inode has no links and no other references: truncate and free.
|
||||||
|
|
||||||
|
// ip->ref == 1 means no other process can have ip locked,
|
||||||
|
// so this acquiresleep() won't block (or deadlock).
|
||||||
|
acquiresleep(&ip->lock);
|
||||||
|
|
||||||
|
release(&itable.lock);
|
||||||
|
|
||||||
|
itrunc(ip);
|
||||||
|
ip->type = 0;
|
||||||
|
iupdate(ip);
|
||||||
|
ip->valid = 0;
|
||||||
|
|
||||||
|
releasesleep(&ip->lock);
|
||||||
|
|
||||||
|
acquire(&itable.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
ip->ref--;
|
||||||
|
release(&itable.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common idiom: unlock, then put.
|
||||||
|
void
|
||||||
|
iunlockput(struct inode *ip)
|
||||||
|
{
|
||||||
|
iunlock(ip);
|
||||||
|
iput(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// returns 0 if out of disk space.
|
||||||
|
static uint
|
||||||
|
bmap(struct inode *ip, uint bn)
|
||||||
|
{
|
||||||
|
uint addr, *a;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
if(bn < NDIRECT){
|
||||||
|
if((addr = ip->addrs[bn]) == 0){
|
||||||
|
addr = balloc(ip->dev);
|
||||||
|
if(addr == 0)
|
||||||
|
return 0;
|
||||||
|
ip->addrs[bn] = addr;
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
bn -= NDIRECT;
|
||||||
|
|
||||||
|
if(bn < NINDIRECT){
|
||||||
|
// Load indirect block, allocating if necessary.
|
||||||
|
if((addr = ip->addrs[NDIRECT]) == 0){
|
||||||
|
addr = balloc(ip->dev);
|
||||||
|
if(addr == 0)
|
||||||
|
return 0;
|
||||||
|
ip->addrs[NDIRECT] = addr;
|
||||||
|
}
|
||||||
|
bp = bread(ip->dev, addr);
|
||||||
|
a = (uint*)bp->data;
|
||||||
|
if((addr = a[bn]) == 0){
|
||||||
|
addr = balloc(ip->dev);
|
||||||
|
if(addr){
|
||||||
|
a[bn] = addr;
|
||||||
|
log_write(bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("bmap: out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate inode (discard contents).
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read data from inode.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
// If user_dst==1, then dst is a user virtual address;
|
||||||
|
// otherwise, dst is a kernel address.
|
||||||
|
int
|
||||||
|
readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
|
||||||
|
{
|
||||||
|
uint tot, m;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
if(off > ip->size || off + n < off)
|
||||||
|
return 0;
|
||||||
|
if(off + n > ip->size)
|
||||||
|
n = ip->size - off;
|
||||||
|
|
||||||
|
for(tot=0; tot<n; tot+=m, off+=m, dst+=m){
|
||||||
|
uint addr = bmap(ip, off/BSIZE);
|
||||||
|
if(addr == 0)
|
||||||
|
break;
|
||||||
|
bp = bread(ip->dev, addr);
|
||||||
|
m = min(n - tot, BSIZE - off%BSIZE);
|
||||||
|
if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) {
|
||||||
|
brelse(bp);
|
||||||
|
tot = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
return tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to inode.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
// If user_src==1, then src is a user virtual address;
|
||||||
|
// otherwise, src is a kernel address.
|
||||||
|
// Returns the number of bytes successfully written.
|
||||||
|
// If the return value is less than the requested n,
|
||||||
|
// there was an error of some kind.
|
||||||
|
int
|
||||||
|
writei(struct inode *ip, int user_src, uint64 src, uint off, uint n)
|
||||||
|
{
|
||||||
|
uint tot, m;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
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){
|
||||||
|
uint addr = bmap(ip, off/BSIZE);
|
||||||
|
if(addr == 0)
|
||||||
|
break;
|
||||||
|
bp = bread(ip->dev, addr);
|
||||||
|
m = min(n - tot, BSIZE - off%BSIZE);
|
||||||
|
if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) {
|
||||||
|
brelse(bp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(off > ip->size)
|
||||||
|
ip->size = off;
|
||||||
|
|
||||||
|
// write the i-node back to disk even if the size didn't change
|
||||||
|
// because the loop above might have called bmap() and added a new
|
||||||
|
// block to ip->addrs[].
|
||||||
|
iupdate(ip);
|
||||||
|
|
||||||
|
return tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, 0, (uint64)&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.
|
||||||
|
// Returns 0 on success, -1 on failure (e.g. out of disk blocks).
|
||||||
|
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, 0, (uint64)&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, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
60
kernel/fs.h
Normal file
60
kernel/fs.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// On-disk file system format.
|
||||||
|
// Both the kernel and user programs use this header file.
|
||||||
|
|
||||||
|
|
||||||
|
#define ROOTINO 1 // root i-number
|
||||||
|
#define BSIZE 1024 // 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 magic; // Must be FSMAGIC
|
||||||
|
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 FSMAGIC 0x10203040
|
||||||
|
|
||||||
|
#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_DEVICE only)
|
||||||
|
short minor; // Minor device number (T_DEVICE 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];
|
||||||
|
};
|
||||||
|
|
||||||
82
kernel/kalloc.c
Normal file
82
kernel/kalloc.c
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Physical memory allocator, for user processes,
|
||||||
|
// kernel stacks, page-table pages,
|
||||||
|
// and pipe buffers. Allocates whole 4096-byte pages.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
void freerange(void *pa_start, void *pa_end);
|
||||||
|
|
||||||
|
extern char end[]; // first address after kernel.
|
||||||
|
// defined by kernel.ld.
|
||||||
|
|
||||||
|
struct run {
|
||||||
|
struct run *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
struct run *freelist;
|
||||||
|
} kmem;
|
||||||
|
|
||||||
|
void
|
||||||
|
kinit()
|
||||||
|
{
|
||||||
|
initlock(&kmem.lock, "kmem");
|
||||||
|
freerange(end, (void*)PHYSTOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freerange(void *pa_start, void *pa_end)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
p = (char*)PGROUNDUP((uint64)pa_start);
|
||||||
|
for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
|
||||||
|
kfree(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the page of physical memory pointed at by pa,
|
||||||
|
// which normally should have been returned by a
|
||||||
|
// call to kalloc(). (The exception is when
|
||||||
|
// initializing the allocator; see kinit above.)
|
||||||
|
void
|
||||||
|
kfree(void *pa)
|
||||||
|
{
|
||||||
|
struct run *r;
|
||||||
|
|
||||||
|
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
|
||||||
|
panic("kfree");
|
||||||
|
|
||||||
|
// Fill with junk to catch dangling refs.
|
||||||
|
memset(pa, 1, PGSIZE);
|
||||||
|
|
||||||
|
r = (struct run*)pa;
|
||||||
|
|
||||||
|
acquire(&kmem.lock);
|
||||||
|
r->next = kmem.freelist;
|
||||||
|
kmem.freelist = r;
|
||||||
|
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.
|
||||||
|
void *
|
||||||
|
kalloc(void)
|
||||||
|
{
|
||||||
|
struct run *r;
|
||||||
|
|
||||||
|
acquire(&kmem.lock);
|
||||||
|
r = kmem.freelist;
|
||||||
|
if(r)
|
||||||
|
kmem.freelist = r->next;
|
||||||
|
release(&kmem.lock);
|
||||||
|
|
||||||
|
if(r)
|
||||||
|
memset((char*)r, 5, PGSIZE); // fill with junk
|
||||||
|
return (void*)r;
|
||||||
|
}
|
||||||
44
kernel/kernel.ld
Normal file
44
kernel/kernel.ld
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
ENTRY( _entry )
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* ensure that entry.S / _entry is at 0x80000000,
|
||||||
|
* where qemu's -kernel jumps.
|
||||||
|
*/
|
||||||
|
. = 0x80000000;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.text .text.*)
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
_trampoline = .;
|
||||||
|
*(trampsec)
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
|
||||||
|
PROVIDE(etext = .);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.sdata .sdata.*) /* do not need to distinguish this from .data */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.data .data.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.sbss .sbss.*) /* do not need to distinguish this from .bss */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.bss .bss.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
PROVIDE(end = .);
|
||||||
|
}
|
||||||
124
kernel/kernelvec.S
Normal file
124
kernel/kernelvec.S
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#
|
||||||
|
# interrupts and exceptions while in supervisor
|
||||||
|
# mode come here.
|
||||||
|
#
|
||||||
|
# the current stack is a kernel stack.
|
||||||
|
# push all registers, call kerneltrap().
|
||||||
|
# when kerneltrap() returns, restore registers, return.
|
||||||
|
#
|
||||||
|
.globl kerneltrap
|
||||||
|
.globl kernelvec
|
||||||
|
.align 4
|
||||||
|
kernelvec:
|
||||||
|
# make room to save registers.
|
||||||
|
addi sp, sp, -256
|
||||||
|
|
||||||
|
# save the registers.
|
||||||
|
sd ra, 0(sp)
|
||||||
|
sd sp, 8(sp)
|
||||||
|
sd gp, 16(sp)
|
||||||
|
sd tp, 24(sp)
|
||||||
|
sd t0, 32(sp)
|
||||||
|
sd t1, 40(sp)
|
||||||
|
sd t2, 48(sp)
|
||||||
|
sd s0, 56(sp)
|
||||||
|
sd s1, 64(sp)
|
||||||
|
sd a0, 72(sp)
|
||||||
|
sd a1, 80(sp)
|
||||||
|
sd a2, 88(sp)
|
||||||
|
sd a3, 96(sp)
|
||||||
|
sd a4, 104(sp)
|
||||||
|
sd a5, 112(sp)
|
||||||
|
sd a6, 120(sp)
|
||||||
|
sd a7, 128(sp)
|
||||||
|
sd s2, 136(sp)
|
||||||
|
sd s3, 144(sp)
|
||||||
|
sd s4, 152(sp)
|
||||||
|
sd s5, 160(sp)
|
||||||
|
sd s6, 168(sp)
|
||||||
|
sd s7, 176(sp)
|
||||||
|
sd s8, 184(sp)
|
||||||
|
sd s9, 192(sp)
|
||||||
|
sd s10, 200(sp)
|
||||||
|
sd s11, 208(sp)
|
||||||
|
sd t3, 216(sp)
|
||||||
|
sd t4, 224(sp)
|
||||||
|
sd t5, 232(sp)
|
||||||
|
sd t6, 240(sp)
|
||||||
|
|
||||||
|
# call the C trap handler in trap.c
|
||||||
|
call kerneltrap
|
||||||
|
|
||||||
|
# restore registers.
|
||||||
|
ld ra, 0(sp)
|
||||||
|
ld sp, 8(sp)
|
||||||
|
ld gp, 16(sp)
|
||||||
|
# not tp (contains hartid), in case we moved CPUs
|
||||||
|
ld t0, 32(sp)
|
||||||
|
ld t1, 40(sp)
|
||||||
|
ld t2, 48(sp)
|
||||||
|
ld s0, 56(sp)
|
||||||
|
ld s1, 64(sp)
|
||||||
|
ld a0, 72(sp)
|
||||||
|
ld a1, 80(sp)
|
||||||
|
ld a2, 88(sp)
|
||||||
|
ld a3, 96(sp)
|
||||||
|
ld a4, 104(sp)
|
||||||
|
ld a5, 112(sp)
|
||||||
|
ld a6, 120(sp)
|
||||||
|
ld a7, 128(sp)
|
||||||
|
ld s2, 136(sp)
|
||||||
|
ld s3, 144(sp)
|
||||||
|
ld s4, 152(sp)
|
||||||
|
ld s5, 160(sp)
|
||||||
|
ld s6, 168(sp)
|
||||||
|
ld s7, 176(sp)
|
||||||
|
ld s8, 184(sp)
|
||||||
|
ld s9, 192(sp)
|
||||||
|
ld s10, 200(sp)
|
||||||
|
ld s11, 208(sp)
|
||||||
|
ld t3, 216(sp)
|
||||||
|
ld t4, 224(sp)
|
||||||
|
ld t5, 232(sp)
|
||||||
|
ld t6, 240(sp)
|
||||||
|
|
||||||
|
addi sp, sp, 256
|
||||||
|
|
||||||
|
# return to whatever we were doing in the kernel.
|
||||||
|
sret
|
||||||
|
|
||||||
|
#
|
||||||
|
# machine-mode timer interrupt.
|
||||||
|
#
|
||||||
|
.globl timervec
|
||||||
|
.align 4
|
||||||
|
timervec:
|
||||||
|
# start.c has set up the memory that mscratch points to:
|
||||||
|
# scratch[0,8,16] : register save area.
|
||||||
|
# scratch[24] : address of CLINT's MTIMECMP register.
|
||||||
|
# scratch[32] : desired interval between interrupts.
|
||||||
|
|
||||||
|
csrrw a0, mscratch, a0
|
||||||
|
sd a1, 0(a0)
|
||||||
|
sd a2, 8(a0)
|
||||||
|
sd a3, 16(a0)
|
||||||
|
|
||||||
|
# schedule the next timer interrupt
|
||||||
|
# by adding interval to mtimecmp.
|
||||||
|
ld a1, 24(a0) # CLINT_MTIMECMP(hart)
|
||||||
|
ld a2, 32(a0) # interval
|
||||||
|
ld a3, 0(a1)
|
||||||
|
add a3, a3, a2
|
||||||
|
sd a3, 0(a1)
|
||||||
|
|
||||||
|
# arrange for a supervisor software interrupt
|
||||||
|
# after this handler returns.
|
||||||
|
li a1, 2
|
||||||
|
csrw sip, a1
|
||||||
|
|
||||||
|
ld a3, 16(a0)
|
||||||
|
ld a2, 8(a0)
|
||||||
|
ld a1, 0(a0)
|
||||||
|
csrrw a0, mscratch, a0
|
||||||
|
|
||||||
|
mret
|
||||||
236
kernel/log.c
Normal file
236
kernel/log.c
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "riscv.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, struct superblock *sb)
|
||||||
|
{
|
||||||
|
if (sizeof(struct logheader) >= BSIZE)
|
||||||
|
panic("initlog: too big logheader");
|
||||||
|
|
||||||
|
initlock(&log.lock, "log");
|
||||||
|
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(int recovering)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
if(recovering == 0)
|
||||||
|
bunpin(dbuf);
|
||||||
|
brelse(lbuf);
|
||||||
|
brelse(dbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the log header from disk into the in-memory log header
|
||||||
|
static void
|
||||||
|
read_head(void)
|
||||||
|
{
|
||||||
|
struct buf *buf = bread(log.dev, log.start);
|
||||||
|
struct logheader *lh = (struct logheader *) (buf->data);
|
||||||
|
int i;
|
||||||
|
log.lh.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(1); // 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(0); // 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 by increasing refcnt.
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
acquire(&log.lock);
|
||||||
|
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");
|
||||||
|
|
||||||
|
for (i = 0; i < log.lh.n; i++) {
|
||||||
|
if (log.lh.block[i] == b->blockno) // log absorption
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log.lh.block[i] = b->blockno;
|
||||||
|
if (i == log.lh.n) { // Add new block to log?
|
||||||
|
bpin(b);
|
||||||
|
log.lh.n++;
|
||||||
|
}
|
||||||
|
release(&log.lock);
|
||||||
|
}
|
||||||
|
|
||||||
45
kernel/main.c
Normal file
45
kernel/main.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
volatile static int started = 0;
|
||||||
|
|
||||||
|
// start() jumps here in supervisor mode on all CPUs.
|
||||||
|
void
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
if(cpuid() == 0){
|
||||||
|
consoleinit();
|
||||||
|
printfinit();
|
||||||
|
printf("\n");
|
||||||
|
printf("xv6 kernel is booting\n");
|
||||||
|
printf("\n");
|
||||||
|
kinit(); // physical page allocator
|
||||||
|
kvminit(); // create kernel page table
|
||||||
|
kvminithart(); // turn on paging
|
||||||
|
procinit(); // process table
|
||||||
|
trapinit(); // trap vectors
|
||||||
|
trapinithart(); // install kernel trap vector
|
||||||
|
plicinit(); // set up interrupt controller
|
||||||
|
plicinithart(); // ask PLIC for device interrupts
|
||||||
|
binit(); // buffer cache
|
||||||
|
iinit(); // inode table
|
||||||
|
fileinit(); // file table
|
||||||
|
virtio_disk_init(); // emulated hard disk
|
||||||
|
userinit(); // first user process
|
||||||
|
__sync_synchronize();
|
||||||
|
started = 1;
|
||||||
|
} else {
|
||||||
|
while(started == 0)
|
||||||
|
;
|
||||||
|
__sync_synchronize();
|
||||||
|
printf("hart %d starting\n", cpuid());
|
||||||
|
kvminithart(); // turn on paging
|
||||||
|
trapinithart(); // install kernel trap vector
|
||||||
|
plicinithart(); // ask PLIC for device interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler();
|
||||||
|
}
|
||||||
64
kernel/memlayout.h
Normal file
64
kernel/memlayout.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Physical memory layout
|
||||||
|
|
||||||
|
// qemu -machine virt is set up like this,
|
||||||
|
// based on qemu's hw/riscv/virt.c:
|
||||||
|
//
|
||||||
|
// 00001000 -- boot ROM, provided by qemu
|
||||||
|
// 02000000 -- CLINT
|
||||||
|
// 0C000000 -- PLIC
|
||||||
|
// 10000000 -- uart0
|
||||||
|
// 10001000 -- virtio disk
|
||||||
|
// 80000000 -- boot ROM jumps here in machine mode
|
||||||
|
// -kernel loads the kernel here
|
||||||
|
// unused RAM after 80000000.
|
||||||
|
|
||||||
|
// the kernel uses physical memory thus:
|
||||||
|
// 80000000 -- entry.S, then kernel text and data
|
||||||
|
// end -- start of kernel page allocation area
|
||||||
|
// PHYSTOP -- end RAM used by the kernel
|
||||||
|
|
||||||
|
// qemu puts UART registers here in physical memory.
|
||||||
|
#define UART0 0x10000000L
|
||||||
|
#define UART0_IRQ 10
|
||||||
|
|
||||||
|
// virtio mmio interface
|
||||||
|
#define VIRTIO0 0x10001000
|
||||||
|
#define VIRTIO0_IRQ 1
|
||||||
|
|
||||||
|
// core local interruptor (CLINT), which contains the timer.
|
||||||
|
#define CLINT 0x2000000L
|
||||||
|
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
|
||||||
|
#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot.
|
||||||
|
|
||||||
|
// qemu puts platform-level interrupt controller (PLIC) here.
|
||||||
|
#define PLIC 0x0c000000L
|
||||||
|
#define PLIC_PRIORITY (PLIC + 0x0)
|
||||||
|
#define PLIC_PENDING (PLIC + 0x1000)
|
||||||
|
#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)
|
||||||
|
#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)
|
||||||
|
#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000)
|
||||||
|
|
||||||
|
// the kernel expects there to be RAM
|
||||||
|
// for use by the kernel and user pages
|
||||||
|
// from physical address 0x80000000 to PHYSTOP.
|
||||||
|
#define KERNBASE 0x80000000L
|
||||||
|
#define PHYSTOP (KERNBASE + 128*1024*1024)
|
||||||
|
|
||||||
|
// map the trampoline page to the highest address,
|
||||||
|
// in both user and kernel space.
|
||||||
|
#define TRAMPOLINE (MAXVA - PGSIZE)
|
||||||
|
|
||||||
|
// map kernel stacks beneath the trampoline,
|
||||||
|
// each surrounded by invalid guard pages.
|
||||||
|
#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)
|
||||||
|
|
||||||
|
// User memory layout.
|
||||||
|
// Address zero first:
|
||||||
|
// text
|
||||||
|
// original data and bss
|
||||||
|
// fixed-size stack
|
||||||
|
// expandable heap
|
||||||
|
// ...
|
||||||
|
// TRAPFRAME (p->trapframe, used by the trampoline)
|
||||||
|
// TRAMPOLINE (the same page as in the kernel)
|
||||||
|
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
||||||
13
kernel/param.h
Normal file
13
kernel/param.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#define NPROC 64 // maximum number of processes
|
||||||
|
#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 2000 // size of file system in blocks
|
||||||
|
#define MAXPATH 128 // maximum file path name
|
||||||
130
kernel/pipe.c
Normal file
130
kernel/pipe.c
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "fs.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 *pi;
|
||||||
|
|
||||||
|
pi = 0;
|
||||||
|
*f0 = *f1 = 0;
|
||||||
|
if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
|
||||||
|
goto bad;
|
||||||
|
if((pi = (struct pipe*)kalloc()) == 0)
|
||||||
|
goto bad;
|
||||||
|
pi->readopen = 1;
|
||||||
|
pi->writeopen = 1;
|
||||||
|
pi->nwrite = 0;
|
||||||
|
pi->nread = 0;
|
||||||
|
initlock(&pi->lock, "pipe");
|
||||||
|
(*f0)->type = FD_PIPE;
|
||||||
|
(*f0)->readable = 1;
|
||||||
|
(*f0)->writable = 0;
|
||||||
|
(*f0)->pipe = pi;
|
||||||
|
(*f1)->type = FD_PIPE;
|
||||||
|
(*f1)->readable = 0;
|
||||||
|
(*f1)->writable = 1;
|
||||||
|
(*f1)->pipe = pi;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bad:
|
||||||
|
if(pi)
|
||||||
|
kfree((char*)pi);
|
||||||
|
if(*f0)
|
||||||
|
fileclose(*f0);
|
||||||
|
if(*f1)
|
||||||
|
fileclose(*f1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pipeclose(struct pipe *pi, int writable)
|
||||||
|
{
|
||||||
|
acquire(&pi->lock);
|
||||||
|
if(writable){
|
||||||
|
pi->writeopen = 0;
|
||||||
|
wakeup(&pi->nread);
|
||||||
|
} else {
|
||||||
|
pi->readopen = 0;
|
||||||
|
wakeup(&pi->nwrite);
|
||||||
|
}
|
||||||
|
if(pi->readopen == 0 && pi->writeopen == 0){
|
||||||
|
release(&pi->lock);
|
||||||
|
kfree((char*)pi);
|
||||||
|
} else
|
||||||
|
release(&pi->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pipewrite(struct pipe *pi, uint64 addr, int n)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
struct proc *pr = myproc();
|
||||||
|
|
||||||
|
acquire(&pi->lock);
|
||||||
|
while(i < n){
|
||||||
|
if(pi->readopen == 0 || killed(pr)){
|
||||||
|
release(&pi->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full
|
||||||
|
wakeup(&pi->nread);
|
||||||
|
sleep(&pi->nwrite, &pi->lock);
|
||||||
|
} else {
|
||||||
|
char ch;
|
||||||
|
if(copyin(pr->pagetable, &ch, addr + i, 1) == -1)
|
||||||
|
break;
|
||||||
|
pi->data[pi->nwrite++ % PIPESIZE] = ch;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wakeup(&pi->nread);
|
||||||
|
release(&pi->lock);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
piperead(struct pipe *pi, uint64 addr, int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct proc *pr = myproc();
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
acquire(&pi->lock);
|
||||||
|
while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty
|
||||||
|
if(killed(pr)){
|
||||||
|
release(&pi->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sleep(&pi->nread, &pi->lock); //DOC: piperead-sleep
|
||||||
|
}
|
||||||
|
for(i = 0; i < n; i++){ //DOC: piperead-copy
|
||||||
|
if(pi->nread == pi->nwrite)
|
||||||
|
break;
|
||||||
|
ch = pi->data[pi->nread++ % PIPESIZE];
|
||||||
|
if(copyout(pr->pagetable, addr + i, &ch, 1) == -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wakeup(&pi->nwrite); //DOC: piperead-wakeup
|
||||||
|
release(&pi->lock);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
47
kernel/plic.c
Normal file
47
kernel/plic.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// the riscv Platform Level Interrupt Controller (PLIC).
|
||||||
|
//
|
||||||
|
|
||||||
|
void
|
||||||
|
plicinit(void)
|
||||||
|
{
|
||||||
|
// set desired IRQ priorities non-zero (otherwise disabled).
|
||||||
|
*(uint32*)(PLIC + UART0_IRQ*4) = 1;
|
||||||
|
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
plicinithart(void)
|
||||||
|
{
|
||||||
|
int hart = cpuid();
|
||||||
|
|
||||||
|
// set enable bits for this hart's S-mode
|
||||||
|
// for the uart and virtio disk.
|
||||||
|
*(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
|
||||||
|
|
||||||
|
// set this hart's S-mode priority threshold to 0.
|
||||||
|
*(uint32*)PLIC_SPRIORITY(hart) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask the PLIC what interrupt we should serve.
|
||||||
|
int
|
||||||
|
plic_claim(void)
|
||||||
|
{
|
||||||
|
int hart = cpuid();
|
||||||
|
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell the PLIC we've served this IRQ.
|
||||||
|
void
|
||||||
|
plic_complete(int irq)
|
||||||
|
{
|
||||||
|
int hart = cpuid();
|
||||||
|
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
||||||
|
}
|
||||||
135
kernel/printf.c
Normal file
135
kernel/printf.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// formatted console output -- printf, panic.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "proc.h"
|
||||||
|
|
||||||
|
volatile int panicked = 0;
|
||||||
|
|
||||||
|
// lock to avoid interleaving concurrent printf's.
|
||||||
|
static struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
int locking;
|
||||||
|
} pr;
|
||||||
|
|
||||||
|
static char digits[] = "0123456789abcdef";
|
||||||
|
|
||||||
|
static void
|
||||||
|
printint(int xx, int base, int sign)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printptr(uint64 x)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
consputc('0');
|
||||||
|
consputc('x');
|
||||||
|
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
|
||||||
|
consputc(digits[x >> (sizeof(uint64) * 8 - 4)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print to the console. only understands %d, %x, %p, %s.
|
||||||
|
void
|
||||||
|
printf(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int i, c, locking;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
locking = pr.locking;
|
||||||
|
if(locking)
|
||||||
|
acquire(&pr.lock);
|
||||||
|
|
||||||
|
if (fmt == 0)
|
||||||
|
panic("null fmt");
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
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(va_arg(ap, int), 10, 1);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
printint(va_arg(ap, int), 16, 1);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
printptr(va_arg(ap, uint64));
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if((s = va_arg(ap, char*)) == 0)
|
||||||
|
s = "(null)";
|
||||||
|
for(; *s; s++)
|
||||||
|
consputc(*s);
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
consputc('%');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Print unknown % sequence to draw attention.
|
||||||
|
consputc('%');
|
||||||
|
consputc(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if(locking)
|
||||||
|
release(&pr.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
panic(char *s)
|
||||||
|
{
|
||||||
|
pr.locking = 0;
|
||||||
|
printf("panic: ");
|
||||||
|
printf(s);
|
||||||
|
printf("\n");
|
||||||
|
panicked = 1; // freeze uart output from other CPUs
|
||||||
|
for(;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
printfinit(void)
|
||||||
|
{
|
||||||
|
initlock(&pr.lock, "pr");
|
||||||
|
pr.locking = 1;
|
||||||
|
}
|
||||||
688
kernel/proc.c
Normal file
688
kernel/proc.c
Normal file
@ -0,0 +1,688 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
struct cpu cpus[NCPU];
|
||||||
|
|
||||||
|
struct proc proc[NPROC];
|
||||||
|
|
||||||
|
struct proc *initproc;
|
||||||
|
|
||||||
|
int nextpid = 1;
|
||||||
|
struct spinlock pid_lock;
|
||||||
|
|
||||||
|
extern void forkret(void);
|
||||||
|
static void freeproc(struct proc *p);
|
||||||
|
|
||||||
|
extern char trampoline[]; // trampoline.S
|
||||||
|
|
||||||
|
// helps ensure that wakeups of wait()ing
|
||||||
|
// parents are not lost. helps obey the
|
||||||
|
// memory model when using p->parent.
|
||||||
|
// must be acquired before any p->lock.
|
||||||
|
struct spinlock wait_lock;
|
||||||
|
|
||||||
|
// Allocate a page for each process's kernel stack.
|
||||||
|
// Map it high in memory, followed by an invalid
|
||||||
|
// guard page.
|
||||||
|
void
|
||||||
|
proc_mapstacks(pagetable_t kpgtbl)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++) {
|
||||||
|
char *pa = kalloc();
|
||||||
|
if(pa == 0)
|
||||||
|
panic("kalloc");
|
||||||
|
uint64 va = KSTACK((int) (p - proc));
|
||||||
|
kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the proc table.
|
||||||
|
void
|
||||||
|
procinit(void)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
initlock(&pid_lock, "nextpid");
|
||||||
|
initlock(&wait_lock, "wait_lock");
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++) {
|
||||||
|
initlock(&p->lock, "proc");
|
||||||
|
p->state = UNUSED;
|
||||||
|
p->kstack = KSTACK((int) (p - proc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be called with interrupts disabled,
|
||||||
|
// to prevent race with process being moved
|
||||||
|
// to a different CPU.
|
||||||
|
int
|
||||||
|
cpuid()
|
||||||
|
{
|
||||||
|
int id = r_tp();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return this CPU's cpu struct.
|
||||||
|
// Interrupts must be disabled.
|
||||||
|
struct cpu*
|
||||||
|
mycpu(void)
|
||||||
|
{
|
||||||
|
int id = cpuid();
|
||||||
|
struct cpu *c = &cpus[id];
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the current struct proc *, or zero if none.
|
||||||
|
struct proc*
|
||||||
|
myproc(void)
|
||||||
|
{
|
||||||
|
push_off();
|
||||||
|
struct cpu *c = mycpu();
|
||||||
|
struct proc *p = c->proc;
|
||||||
|
pop_off();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
allocpid()
|
||||||
|
{
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
acquire(&pid_lock);
|
||||||
|
pid = nextpid;
|
||||||
|
nextpid = nextpid + 1;
|
||||||
|
release(&pid_lock);
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look in the process table for an UNUSED proc.
|
||||||
|
// If found, initialize state required to run in the kernel,
|
||||||
|
// and return with p->lock held.
|
||||||
|
// If there are no free procs, or a memory allocation fails, return 0.
|
||||||
|
static struct proc*
|
||||||
|
allocproc(void)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++) {
|
||||||
|
acquire(&p->lock);
|
||||||
|
if(p->state == UNUSED) {
|
||||||
|
goto found;
|
||||||
|
} else {
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
found:
|
||||||
|
p->pid = allocpid();
|
||||||
|
p->state = USED;
|
||||||
|
|
||||||
|
// Allocate a trapframe page.
|
||||||
|
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
|
||||||
|
freeproc(p);
|
||||||
|
release(&p->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An empty user page table.
|
||||||
|
p->pagetable = proc_pagetable(p);
|
||||||
|
if(p->pagetable == 0){
|
||||||
|
freeproc(p);
|
||||||
|
release(&p->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up new context to start executing at forkret,
|
||||||
|
// which returns to user space.
|
||||||
|
memset(&p->context, 0, sizeof(p->context));
|
||||||
|
p->context.ra = (uint64)forkret;
|
||||||
|
p->context.sp = p->kstack + PGSIZE;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free a proc structure and the data hanging from it,
|
||||||
|
// including user pages.
|
||||||
|
// p->lock must be held.
|
||||||
|
static void
|
||||||
|
freeproc(struct proc *p)
|
||||||
|
{
|
||||||
|
if(p->trapframe)
|
||||||
|
kfree((void*)p->trapframe);
|
||||||
|
p->trapframe = 0;
|
||||||
|
if(p->pagetable)
|
||||||
|
proc_freepagetable(p->pagetable, p->sz);
|
||||||
|
p->pagetable = 0;
|
||||||
|
p->sz = 0;
|
||||||
|
p->pid = 0;
|
||||||
|
p->parent = 0;
|
||||||
|
p->name[0] = 0;
|
||||||
|
p->chan = 0;
|
||||||
|
p->killed = 0;
|
||||||
|
p->xstate = 0;
|
||||||
|
p->state = UNUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a user page table for a given process, with no user memory,
|
||||||
|
// but with trampoline and trapframe pages.
|
||||||
|
pagetable_t
|
||||||
|
proc_pagetable(struct proc *p)
|
||||||
|
{
|
||||||
|
pagetable_t pagetable;
|
||||||
|
|
||||||
|
// An empty page table.
|
||||||
|
pagetable = uvmcreate();
|
||||||
|
if(pagetable == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// map the trampoline code (for system call return)
|
||||||
|
// at the highest user virtual address.
|
||||||
|
// only the supervisor uses it, on the way
|
||||||
|
// to/from user space, so not PTE_U.
|
||||||
|
if(mappages(pagetable, TRAMPOLINE, PGSIZE,
|
||||||
|
(uint64)trampoline, PTE_R | PTE_X) < 0){
|
||||||
|
uvmfree(pagetable, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// map the trapframe page just below the trampoline page, for
|
||||||
|
// trampoline.S.
|
||||||
|
if(mappages(pagetable, TRAPFRAME, PGSIZE,
|
||||||
|
(uint64)(p->trapframe), PTE_R | PTE_W) < 0){
|
||||||
|
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
|
||||||
|
uvmfree(pagetable, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagetable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a process's page table, and free the
|
||||||
|
// physical memory it refers to.
|
||||||
|
void
|
||||||
|
proc_freepagetable(pagetable_t pagetable, uint64 sz)
|
||||||
|
{
|
||||||
|
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
|
||||||
|
uvmunmap(pagetable, TRAPFRAME, 1, 0);
|
||||||
|
uvmfree(pagetable, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a user program that calls exec("/init")
|
||||||
|
// assembled from ../user/initcode.S
|
||||||
|
// od -t xC ../user/initcode
|
||||||
|
uchar initcode[] = {
|
||||||
|
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02,
|
||||||
|
0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02,
|
||||||
|
0x93, 0x08, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00,
|
||||||
|
0x93, 0x08, 0x20, 0x00, 0x73, 0x00, 0x00, 0x00,
|
||||||
|
0xef, 0xf0, 0x9f, 0xff, 0x2f, 0x69, 0x6e, 0x69,
|
||||||
|
0x74, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up first user process.
|
||||||
|
void
|
||||||
|
userinit(void)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
p = allocproc();
|
||||||
|
initproc = p;
|
||||||
|
|
||||||
|
// allocate one user page and copy initcode's instructions
|
||||||
|
// and data into it.
|
||||||
|
uvmfirst(p->pagetable, initcode, sizeof(initcode));
|
||||||
|
p->sz = PGSIZE;
|
||||||
|
|
||||||
|
// prepare for the very first "return" from kernel to user.
|
||||||
|
p->trapframe->epc = 0; // user program counter
|
||||||
|
p->trapframe->sp = PGSIZE; // user stack pointer
|
||||||
|
|
||||||
|
safestrcpy(p->name, "initcode", sizeof(p->name));
|
||||||
|
p->cwd = namei("/");
|
||||||
|
|
||||||
|
p->state = RUNNABLE;
|
||||||
|
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow or shrink user memory by n bytes.
|
||||||
|
// Return 0 on success, -1 on failure.
|
||||||
|
int
|
||||||
|
growproc(int n)
|
||||||
|
{
|
||||||
|
uint64 sz;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
sz = p->sz;
|
||||||
|
if(n > 0){
|
||||||
|
if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if(n < 0){
|
||||||
|
sz = uvmdealloc(p->pagetable, sz, sz + n);
|
||||||
|
}
|
||||||
|
p->sz = sz;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new process, copying the parent.
|
||||||
|
// Sets up child kernel stack to return as if from fork() system call.
|
||||||
|
int
|
||||||
|
fork(void)
|
||||||
|
{
|
||||||
|
int i, pid;
|
||||||
|
struct proc *np;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
// Allocate process.
|
||||||
|
if((np = allocproc()) == 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy user memory from parent to child.
|
||||||
|
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
|
||||||
|
freeproc(np);
|
||||||
|
release(&np->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
np->sz = p->sz;
|
||||||
|
|
||||||
|
// copy saved user registers.
|
||||||
|
*(np->trapframe) = *(p->trapframe);
|
||||||
|
|
||||||
|
// Cause fork to return 0 in the child.
|
||||||
|
np->trapframe->a0 = 0;
|
||||||
|
|
||||||
|
// increment reference counts on open file descriptors.
|
||||||
|
for(i = 0; i < NOFILE; i++)
|
||||||
|
if(p->ofile[i])
|
||||||
|
np->ofile[i] = filedup(p->ofile[i]);
|
||||||
|
np->cwd = idup(p->cwd);
|
||||||
|
|
||||||
|
safestrcpy(np->name, p->name, sizeof(p->name));
|
||||||
|
|
||||||
|
pid = np->pid;
|
||||||
|
|
||||||
|
release(&np->lock);
|
||||||
|
|
||||||
|
acquire(&wait_lock);
|
||||||
|
np->parent = p;
|
||||||
|
release(&wait_lock);
|
||||||
|
|
||||||
|
acquire(&np->lock);
|
||||||
|
np->state = RUNNABLE;
|
||||||
|
release(&np->lock);
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass p's abandoned children to init.
|
||||||
|
// Caller must hold wait_lock.
|
||||||
|
void
|
||||||
|
reparent(struct proc *p)
|
||||||
|
{
|
||||||
|
struct proc *pp;
|
||||||
|
|
||||||
|
for(pp = proc; pp < &proc[NPROC]; pp++){
|
||||||
|
if(pp->parent == p){
|
||||||
|
pp->parent = initproc;
|
||||||
|
wakeup(initproc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit the current process. Does not return.
|
||||||
|
// An exited process remains in the zombie state
|
||||||
|
// until its parent calls wait().
|
||||||
|
void
|
||||||
|
exit(int status)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
if(p == initproc)
|
||||||
|
panic("init exiting");
|
||||||
|
|
||||||
|
// Close all open files.
|
||||||
|
for(int fd = 0; fd < NOFILE; fd++){
|
||||||
|
if(p->ofile[fd]){
|
||||||
|
struct file *f = p->ofile[fd];
|
||||||
|
fileclose(f);
|
||||||
|
p->ofile[fd] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
iput(p->cwd);
|
||||||
|
end_op();
|
||||||
|
p->cwd = 0;
|
||||||
|
|
||||||
|
acquire(&wait_lock);
|
||||||
|
|
||||||
|
// Give any children to init.
|
||||||
|
reparent(p);
|
||||||
|
|
||||||
|
// Parent might be sleeping in wait().
|
||||||
|
wakeup(p->parent);
|
||||||
|
|
||||||
|
acquire(&p->lock);
|
||||||
|
|
||||||
|
p->xstate = status;
|
||||||
|
p->state = ZOMBIE;
|
||||||
|
|
||||||
|
release(&wait_lock);
|
||||||
|
|
||||||
|
// Jump into the scheduler, never to return.
|
||||||
|
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(uint64 addr)
|
||||||
|
{
|
||||||
|
struct proc *pp;
|
||||||
|
int havekids, pid;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
acquire(&wait_lock);
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
// Scan through table looking for exited children.
|
||||||
|
havekids = 0;
|
||||||
|
for(pp = proc; pp < &proc[NPROC]; pp++){
|
||||||
|
if(pp->parent == p){
|
||||||
|
// make sure the child isn't still in exit() or swtch().
|
||||||
|
acquire(&pp->lock);
|
||||||
|
|
||||||
|
havekids = 1;
|
||||||
|
if(pp->state == ZOMBIE){
|
||||||
|
// Found one.
|
||||||
|
pid = pp->pid;
|
||||||
|
if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate,
|
||||||
|
sizeof(pp->xstate)) < 0) {
|
||||||
|
release(&pp->lock);
|
||||||
|
release(&wait_lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeproc(pp);
|
||||||
|
release(&pp->lock);
|
||||||
|
release(&wait_lock);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
release(&pp->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No point waiting if we don't have any children.
|
||||||
|
if(!havekids || killed(p)){
|
||||||
|
release(&wait_lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a child to exit.
|
||||||
|
sleep(p, &wait_lock); //DOC: wait-sleep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(;;){
|
||||||
|
// The most recent process to run may have had interrupts
|
||||||
|
// turned off; enable them to avoid a deadlock if all
|
||||||
|
// processes are waiting.
|
||||||
|
intr_on();
|
||||||
|
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++) {
|
||||||
|
acquire(&p->lock);
|
||||||
|
if(p->state == RUNNABLE) {
|
||||||
|
// Switch to chosen process. It is the process's job
|
||||||
|
// to release its lock and then reacquire it
|
||||||
|
// before jumping back to us.
|
||||||
|
p->state = RUNNING;
|
||||||
|
c->proc = p;
|
||||||
|
swtch(&c->context, &p->context);
|
||||||
|
|
||||||
|
// Process is done running for now.
|
||||||
|
// It should have changed its p->state before coming back.
|
||||||
|
c->proc = 0;
|
||||||
|
}
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to scheduler. Must hold only p->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->noff, 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(&p->lock))
|
||||||
|
panic("sched p->lock");
|
||||||
|
if(mycpu()->noff != 1)
|
||||||
|
panic("sched locks");
|
||||||
|
if(p->state == RUNNING)
|
||||||
|
panic("sched running");
|
||||||
|
if(intr_get())
|
||||||
|
panic("sched interruptible");
|
||||||
|
|
||||||
|
intena = mycpu()->intena;
|
||||||
|
swtch(&p->context, &mycpu()->context);
|
||||||
|
mycpu()->intena = intena;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give up the CPU for one scheduling round.
|
||||||
|
void
|
||||||
|
yield(void)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
acquire(&p->lock);
|
||||||
|
p->state = RUNNABLE;
|
||||||
|
sched();
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A fork child's very first scheduling by scheduler()
|
||||||
|
// will swtch to forkret.
|
||||||
|
void
|
||||||
|
forkret(void)
|
||||||
|
{
|
||||||
|
static int first = 1;
|
||||||
|
|
||||||
|
// Still holding p->lock from scheduler.
|
||||||
|
release(&myproc()->lock);
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
// File system initialization must be run in the context of a
|
||||||
|
// regular process (e.g., because it calls sleep), and thus cannot
|
||||||
|
// be run from main().
|
||||||
|
fsinit(ROOTDEV);
|
||||||
|
|
||||||
|
first = 0;
|
||||||
|
// ensure other cores see first=0.
|
||||||
|
__sync_synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
usertrapret();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically release lock and sleep on chan.
|
||||||
|
// Reacquires lock when awakened.
|
||||||
|
void
|
||||||
|
sleep(void *chan, struct spinlock *lk)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
// Must acquire p->lock in order to
|
||||||
|
// change p->state and then call sched.
|
||||||
|
// Once we hold p->lock, we can be
|
||||||
|
// guaranteed that we won't miss any wakeup
|
||||||
|
// (wakeup locks p->lock),
|
||||||
|
// so it's okay to release lk.
|
||||||
|
|
||||||
|
acquire(&p->lock); //DOC: sleeplock1
|
||||||
|
release(lk);
|
||||||
|
|
||||||
|
// Go to sleep.
|
||||||
|
p->chan = chan;
|
||||||
|
p->state = SLEEPING;
|
||||||
|
|
||||||
|
sched();
|
||||||
|
|
||||||
|
// Tidy up.
|
||||||
|
p->chan = 0;
|
||||||
|
|
||||||
|
// Reacquire original lock.
|
||||||
|
release(&p->lock);
|
||||||
|
acquire(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake up all processes sleeping on chan.
|
||||||
|
// Must be called without any p->lock.
|
||||||
|
void
|
||||||
|
wakeup(void *chan)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++) {
|
||||||
|
if(p != myproc()){
|
||||||
|
acquire(&p->lock);
|
||||||
|
if(p->state == SLEEPING && p->chan == chan) {
|
||||||
|
p->state = RUNNABLE;
|
||||||
|
}
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill the process with the given pid.
|
||||||
|
// The victim won't exit until it tries to return
|
||||||
|
// to user space (see usertrap() in trap.c).
|
||||||
|
int
|
||||||
|
kill(int pid)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
|
||||||
|
for(p = proc; p < &proc[NPROC]; p++){
|
||||||
|
acquire(&p->lock);
|
||||||
|
if(p->pid == pid){
|
||||||
|
p->killed = 1;
|
||||||
|
if(p->state == SLEEPING){
|
||||||
|
// Wake process from sleep().
|
||||||
|
p->state = RUNNABLE;
|
||||||
|
}
|
||||||
|
release(&p->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setkilled(struct proc *p)
|
||||||
|
{
|
||||||
|
acquire(&p->lock);
|
||||||
|
p->killed = 1;
|
||||||
|
release(&p->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
killed(struct proc *p)
|
||||||
|
{
|
||||||
|
int k;
|
||||||
|
|
||||||
|
acquire(&p->lock);
|
||||||
|
k = p->killed;
|
||||||
|
release(&p->lock);
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to either a user address, or kernel address,
|
||||||
|
// depending on usr_dst.
|
||||||
|
// Returns 0 on success, -1 on error.
|
||||||
|
int
|
||||||
|
either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
if(user_dst){
|
||||||
|
return copyout(p->pagetable, dst, src, len);
|
||||||
|
} else {
|
||||||
|
memmove((char *)dst, src, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from either a user address, or kernel address,
|
||||||
|
// depending on usr_src.
|
||||||
|
// Returns 0 on success, -1 on error.
|
||||||
|
int
|
||||||
|
either_copyin(void *dst, int user_src, uint64 src, uint64 len)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
if(user_src){
|
||||||
|
return copyin(p->pagetable, dst, src, len);
|
||||||
|
} else {
|
||||||
|
memmove(dst, (char*)src, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
[USED] "used",
|
||||||
|
[SLEEPING] "sleep ",
|
||||||
|
[RUNNABLE] "runble",
|
||||||
|
[RUNNING] "run ",
|
||||||
|
[ZOMBIE] "zombie"
|
||||||
|
};
|
||||||
|
struct proc *p;
|
||||||
|
char *state;
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
for(p = proc; p < &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 = "???";
|
||||||
|
printf("%d %s %s", p->pid, state, p->name);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
107
kernel/proc.h
Normal file
107
kernel/proc.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Saved registers for kernel context switches.
|
||||||
|
struct context {
|
||||||
|
uint64 ra;
|
||||||
|
uint64 sp;
|
||||||
|
|
||||||
|
// callee-saved
|
||||||
|
uint64 s0;
|
||||||
|
uint64 s1;
|
||||||
|
uint64 s2;
|
||||||
|
uint64 s3;
|
||||||
|
uint64 s4;
|
||||||
|
uint64 s5;
|
||||||
|
uint64 s6;
|
||||||
|
uint64 s7;
|
||||||
|
uint64 s8;
|
||||||
|
uint64 s9;
|
||||||
|
uint64 s10;
|
||||||
|
uint64 s11;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Per-CPU state.
|
||||||
|
struct cpu {
|
||||||
|
struct proc *proc; // The process running on this cpu, or null.
|
||||||
|
struct context context; // swtch() here to enter scheduler().
|
||||||
|
int noff; // Depth of push_off() nesting.
|
||||||
|
int intena; // Were interrupts enabled before push_off()?
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct cpu cpus[NCPU];
|
||||||
|
|
||||||
|
// per-process data for the trap handling code in trampoline.S.
|
||||||
|
// sits in a page by itself just under the trampoline page in the
|
||||||
|
// user page table. not specially mapped in the kernel page table.
|
||||||
|
// uservec in trampoline.S saves user registers in the trapframe,
|
||||||
|
// then initializes registers from the trapframe's
|
||||||
|
// kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap.
|
||||||
|
// usertrapret() and userret in trampoline.S set up
|
||||||
|
// the trapframe's kernel_*, restore user registers from the
|
||||||
|
// trapframe, switch to the user page table, and enter user space.
|
||||||
|
// the trapframe includes callee-saved user registers like s0-s11 because the
|
||||||
|
// return-to-user path via usertrapret() doesn't return through
|
||||||
|
// the entire kernel call stack.
|
||||||
|
struct trapframe {
|
||||||
|
/* 0 */ uint64 kernel_satp; // kernel page table
|
||||||
|
/* 8 */ uint64 kernel_sp; // top of process's kernel stack
|
||||||
|
/* 16 */ uint64 kernel_trap; // usertrap()
|
||||||
|
/* 24 */ uint64 epc; // saved user program counter
|
||||||
|
/* 32 */ uint64 kernel_hartid; // saved kernel tp
|
||||||
|
/* 40 */ uint64 ra;
|
||||||
|
/* 48 */ uint64 sp;
|
||||||
|
/* 56 */ uint64 gp;
|
||||||
|
/* 64 */ uint64 tp;
|
||||||
|
/* 72 */ uint64 t0;
|
||||||
|
/* 80 */ uint64 t1;
|
||||||
|
/* 88 */ uint64 t2;
|
||||||
|
/* 96 */ uint64 s0;
|
||||||
|
/* 104 */ uint64 s1;
|
||||||
|
/* 112 */ uint64 a0;
|
||||||
|
/* 120 */ uint64 a1;
|
||||||
|
/* 128 */ uint64 a2;
|
||||||
|
/* 136 */ uint64 a3;
|
||||||
|
/* 144 */ uint64 a4;
|
||||||
|
/* 152 */ uint64 a5;
|
||||||
|
/* 160 */ uint64 a6;
|
||||||
|
/* 168 */ uint64 a7;
|
||||||
|
/* 176 */ uint64 s2;
|
||||||
|
/* 184 */ uint64 s3;
|
||||||
|
/* 192 */ uint64 s4;
|
||||||
|
/* 200 */ uint64 s5;
|
||||||
|
/* 208 */ uint64 s6;
|
||||||
|
/* 216 */ uint64 s7;
|
||||||
|
/* 224 */ uint64 s8;
|
||||||
|
/* 232 */ uint64 s9;
|
||||||
|
/* 240 */ uint64 s10;
|
||||||
|
/* 248 */ uint64 s11;
|
||||||
|
/* 256 */ uint64 t3;
|
||||||
|
/* 264 */ uint64 t4;
|
||||||
|
/* 272 */ uint64 t5;
|
||||||
|
/* 280 */ uint64 t6;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
|
||||||
|
|
||||||
|
// Per-process state
|
||||||
|
struct proc {
|
||||||
|
struct spinlock lock;
|
||||||
|
|
||||||
|
// p->lock must be held when using these:
|
||||||
|
enum procstate state; // Process state
|
||||||
|
void *chan; // If non-zero, sleeping on chan
|
||||||
|
int killed; // If non-zero, have been killed
|
||||||
|
int xstate; // Exit status to be returned to parent's wait
|
||||||
|
int pid; // Process ID
|
||||||
|
|
||||||
|
// wait_lock must be held when using this:
|
||||||
|
struct proc *parent; // Parent process
|
||||||
|
|
||||||
|
// these are private to the process, so p->lock need not be held.
|
||||||
|
uint64 kstack; // Virtual address of kernel stack
|
||||||
|
uint64 sz; // Size of process memory (bytes)
|
||||||
|
pagetable_t pagetable; // User page table
|
||||||
|
struct trapframe *trapframe; // data page for trampoline.S
|
||||||
|
struct context context; // swtch() here to run process
|
||||||
|
struct file *ofile[NOFILE]; // Open files
|
||||||
|
struct inode *cwd; // Current directory
|
||||||
|
char name[16]; // Process name (debugging)
|
||||||
|
};
|
||||||
45
kernel/ramdisk.c
Normal file
45
kernel/ramdisk.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// ramdisk that uses the disk image loaded by qemu -initrd fs.img
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ramdiskinit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
ramdiskrw(struct buf *b)
|
||||||
|
{
|
||||||
|
if(!holdingsleep(&b->lock))
|
||||||
|
panic("ramdiskrw: buf not locked");
|
||||||
|
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
|
||||||
|
panic("ramdiskrw: nothing to do");
|
||||||
|
|
||||||
|
if(b->blockno >= FSSIZE)
|
||||||
|
panic("ramdiskrw: blockno too big");
|
||||||
|
|
||||||
|
uint64 diskaddr = b->blockno * BSIZE;
|
||||||
|
char *addr = (char *)RAMDISK + diskaddr;
|
||||||
|
|
||||||
|
if(b->flags & B_DIRTY){
|
||||||
|
// write
|
||||||
|
memmove(addr, b->data, BSIZE);
|
||||||
|
b->flags &= ~B_DIRTY;
|
||||||
|
} else {
|
||||||
|
// read
|
||||||
|
memmove(b->data, addr, BSIZE);
|
||||||
|
b->flags |= B_VALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
363
kernel/riscv.h
Normal file
363
kernel/riscv.h
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
#ifndef __ASSEMBLER__
|
||||||
|
|
||||||
|
// which hart (core) is this?
|
||||||
|
static inline uint64
|
||||||
|
r_mhartid()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, mhartid" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine Status Register, mstatus
|
||||||
|
|
||||||
|
#define MSTATUS_MPP_MASK (3L << 11) // previous mode.
|
||||||
|
#define MSTATUS_MPP_M (3L << 11)
|
||||||
|
#define MSTATUS_MPP_S (1L << 11)
|
||||||
|
#define MSTATUS_MPP_U (0L << 11)
|
||||||
|
#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable.
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_mstatus()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, mstatus" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_mstatus(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mstatus, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// machine exception program counter, holds the
|
||||||
|
// instruction address to which a return from
|
||||||
|
// exception will go.
|
||||||
|
static inline void
|
||||||
|
w_mepc(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mepc, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Status Register, sstatus
|
||||||
|
|
||||||
|
#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User
|
||||||
|
#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable
|
||||||
|
#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable
|
||||||
|
#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable
|
||||||
|
#define SSTATUS_UIE (1L << 0) // User Interrupt Enable
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_sstatus()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, sstatus" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_sstatus(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw sstatus, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Interrupt Pending
|
||||||
|
static inline uint64
|
||||||
|
r_sip()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, sip" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_sip(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw sip, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Interrupt Enable
|
||||||
|
#define SIE_SEIE (1L << 9) // external
|
||||||
|
#define SIE_STIE (1L << 5) // timer
|
||||||
|
#define SIE_SSIE (1L << 1) // software
|
||||||
|
static inline uint64
|
||||||
|
r_sie()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, sie" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_sie(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw sie, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine-mode Interrupt Enable
|
||||||
|
#define MIE_MEIE (1L << 11) // external
|
||||||
|
#define MIE_MTIE (1L << 7) // timer
|
||||||
|
#define MIE_MSIE (1L << 3) // software
|
||||||
|
static inline uint64
|
||||||
|
r_mie()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, mie" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_mie(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mie, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// supervisor exception program counter, holds the
|
||||||
|
// instruction address to which a return from
|
||||||
|
// exception will go.
|
||||||
|
static inline void
|
||||||
|
w_sepc(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw sepc, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_sepc()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, sepc" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine Exception Delegation
|
||||||
|
static inline uint64
|
||||||
|
r_medeleg()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, medeleg" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_medeleg(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw medeleg, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine Interrupt Delegation
|
||||||
|
static inline uint64
|
||||||
|
r_mideleg()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, mideleg" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_mideleg(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mideleg, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Trap-Vector Base Address
|
||||||
|
// low two bits are mode.
|
||||||
|
static inline void
|
||||||
|
w_stvec(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw stvec, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_stvec()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, stvec" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine-mode interrupt vector
|
||||||
|
static inline void
|
||||||
|
w_mtvec(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mtvec, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Physical Memory Protection
|
||||||
|
static inline void
|
||||||
|
w_pmpcfg0(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw pmpcfg0, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_pmpaddr0(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw pmpaddr0, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// use riscv's sv39 page table scheme.
|
||||||
|
#define SATP_SV39 (8L << 60)
|
||||||
|
|
||||||
|
#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12))
|
||||||
|
|
||||||
|
// supervisor address translation and protection;
|
||||||
|
// holds the address of the page table.
|
||||||
|
static inline void
|
||||||
|
w_satp(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw satp, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_satp()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, satp" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_mscratch(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mscratch, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Trap Cause
|
||||||
|
static inline uint64
|
||||||
|
r_scause()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, scause" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Trap Value
|
||||||
|
static inline uint64
|
||||||
|
r_stval()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, stval" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Machine-mode Counter-Enable
|
||||||
|
static inline void
|
||||||
|
w_mcounteren(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("csrw mcounteren, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_mcounteren()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, mcounteren" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// machine-mode cycle counter
|
||||||
|
static inline uint64
|
||||||
|
r_time()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("csrr %0, time" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable device interrupts
|
||||||
|
static inline void
|
||||||
|
intr_on()
|
||||||
|
{
|
||||||
|
w_sstatus(r_sstatus() | SSTATUS_SIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable device interrupts
|
||||||
|
static inline void
|
||||||
|
intr_off()
|
||||||
|
{
|
||||||
|
w_sstatus(r_sstatus() & ~SSTATUS_SIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// are device interrupts enabled?
|
||||||
|
static inline int
|
||||||
|
intr_get()
|
||||||
|
{
|
||||||
|
uint64 x = r_sstatus();
|
||||||
|
return (x & SSTATUS_SIE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_sp()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("mv %0, sp" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read and write tp, the thread pointer, which xv6 uses to hold
|
||||||
|
// this core's hartid (core number), the index into cpus[].
|
||||||
|
static inline uint64
|
||||||
|
r_tp()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("mv %0, tp" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_tp(uint64 x)
|
||||||
|
{
|
||||||
|
asm volatile("mv tp, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64
|
||||||
|
r_ra()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm volatile("mv %0, ra" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush the TLB.
|
||||||
|
static inline void
|
||||||
|
sfence_vma()
|
||||||
|
{
|
||||||
|
// the zero, zero means flush all TLB entries.
|
||||||
|
asm volatile("sfence.vma zero, zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef uint64 pte_t;
|
||||||
|
typedef uint64 *pagetable_t; // 512 PTEs
|
||||||
|
|
||||||
|
#endif // __ASSEMBLER__
|
||||||
|
|
||||||
|
#define PGSIZE 4096 // bytes per page
|
||||||
|
#define PGSHIFT 12 // bits of offset within a page
|
||||||
|
|
||||||
|
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
|
||||||
|
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
|
||||||
|
|
||||||
|
#define PTE_V (1L << 0) // valid
|
||||||
|
#define PTE_R (1L << 1)
|
||||||
|
#define PTE_W (1L << 2)
|
||||||
|
#define PTE_X (1L << 3)
|
||||||
|
#define PTE_U (1L << 4) // user can access
|
||||||
|
|
||||||
|
// shift a physical address to the right place for a PTE.
|
||||||
|
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
||||||
|
|
||||||
|
#define PTE2PA(pte) (((pte) >> 10) << 12)
|
||||||
|
|
||||||
|
#define PTE_FLAGS(pte) ((pte) & 0x3FF)
|
||||||
|
|
||||||
|
// extract the three 9-bit page table indices from a virtual address.
|
||||||
|
#define PXMASK 0x1FF // 9 bits
|
||||||
|
#define PXSHIFT(level) (PGSHIFT+(9*(level)))
|
||||||
|
#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)
|
||||||
|
|
||||||
|
// one beyond the highest possible virtual address.
|
||||||
|
// MAXVA is actually one bit less than the max allowed by
|
||||||
|
// Sv39, to avoid having to sign-extend virtual addresses
|
||||||
|
// that have the high bit set.
|
||||||
|
#define MAXVA (1L << (9 + 9 + 9 + 12 - 1))
|
||||||
55
kernel/sleeplock.c
Normal file
55
kernel/sleeplock.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Sleeping locks
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.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
kernel/sleeplock.h
Normal file
10
kernel/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
|
||||||
|
};
|
||||||
|
|
||||||
110
kernel/spinlock.c
Normal file
110
kernel/spinlock.c
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Mutual exclusion spin locks.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.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.
|
||||||
|
void
|
||||||
|
acquire(struct spinlock *lk)
|
||||||
|
{
|
||||||
|
push_off(); // disable interrupts to avoid deadlock.
|
||||||
|
if(holding(lk))
|
||||||
|
panic("acquire");
|
||||||
|
|
||||||
|
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
|
||||||
|
// a5 = 1
|
||||||
|
// s1 = &lk->locked
|
||||||
|
// amoswap.w.aq a5, a5, (s1)
|
||||||
|
while(__sync_lock_test_and_set(&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 strictly after the lock is acquired.
|
||||||
|
// On RISC-V, this emits a fence instruction.
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
// Record info about lock acquisition for holding() and debugging.
|
||||||
|
lk->cpu = mycpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the lock.
|
||||||
|
void
|
||||||
|
release(struct spinlock *lk)
|
||||||
|
{
|
||||||
|
if(!holding(lk))
|
||||||
|
panic("release");
|
||||||
|
|
||||||
|
lk->cpu = 0;
|
||||||
|
|
||||||
|
// Tell the C compiler and the CPU to not move loads or stores
|
||||||
|
// past this point, to ensure that all the stores in the critical
|
||||||
|
// section are visible to other CPUs before the lock is released,
|
||||||
|
// and that loads in the critical section occur strictly before
|
||||||
|
// the lock is released.
|
||||||
|
// On RISC-V, this emits a fence instruction.
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
// Release the lock, equivalent to lk->locked = 0.
|
||||||
|
// This code doesn't use a C assignment, since the C standard
|
||||||
|
// implies that an assignment might be implemented with
|
||||||
|
// multiple store instructions.
|
||||||
|
// On RISC-V, sync_lock_release turns into an atomic swap:
|
||||||
|
// s1 = &lk->locked
|
||||||
|
// amoswap.w zero, zero, (s1)
|
||||||
|
__sync_lock_release(&lk->locked);
|
||||||
|
|
||||||
|
pop_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether this cpu is holding the lock.
|
||||||
|
// Interrupts must be off.
|
||||||
|
int
|
||||||
|
holding(struct spinlock *lk)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
r = (lk->locked && lk->cpu == mycpu());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push_off/pop_off are like intr_off()/intr_on() except that they are matched:
|
||||||
|
// it takes two pop_off()s to undo two push_off()s. Also, if interrupts
|
||||||
|
// are initially off, then push_off, pop_off leaves them off.
|
||||||
|
|
||||||
|
void
|
||||||
|
push_off(void)
|
||||||
|
{
|
||||||
|
int old = intr_get();
|
||||||
|
|
||||||
|
intr_off();
|
||||||
|
if(mycpu()->noff == 0)
|
||||||
|
mycpu()->intena = old;
|
||||||
|
mycpu()->noff += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop_off(void)
|
||||||
|
{
|
||||||
|
struct cpu *c = mycpu();
|
||||||
|
if(intr_get())
|
||||||
|
panic("pop_off - interruptible");
|
||||||
|
if(c->noff < 1)
|
||||||
|
panic("pop_off");
|
||||||
|
c->noff -= 1;
|
||||||
|
if(c->noff == 0 && c->intena)
|
||||||
|
intr_on();
|
||||||
|
}
|
||||||
9
kernel/spinlock.h
Normal file
9
kernel/spinlock.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// 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.
|
||||||
|
};
|
||||||
|
|
||||||
89
kernel/start.c
Normal file
89
kernel/start.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
void main();
|
||||||
|
void timerinit();
|
||||||
|
|
||||||
|
// entry.S needs one stack per CPU.
|
||||||
|
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
|
||||||
|
|
||||||
|
// a scratch area per CPU for machine-mode timer interrupts.
|
||||||
|
uint64 timer_scratch[NCPU][5];
|
||||||
|
|
||||||
|
// assembly code in kernelvec.S for machine-mode timer interrupt.
|
||||||
|
extern void timervec();
|
||||||
|
|
||||||
|
// entry.S jumps here in machine mode on stack0.
|
||||||
|
void
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
// set M Previous Privilege mode to Supervisor, for mret.
|
||||||
|
unsigned long x = r_mstatus();
|
||||||
|
x &= ~MSTATUS_MPP_MASK;
|
||||||
|
x |= MSTATUS_MPP_S;
|
||||||
|
w_mstatus(x);
|
||||||
|
|
||||||
|
// set M Exception Program Counter to main, for mret.
|
||||||
|
// requires gcc -mcmodel=medany
|
||||||
|
w_mepc((uint64)main);
|
||||||
|
|
||||||
|
// disable paging for now.
|
||||||
|
w_satp(0);
|
||||||
|
|
||||||
|
// delegate all interrupts and exceptions to supervisor mode.
|
||||||
|
w_medeleg(0xffff);
|
||||||
|
w_mideleg(0xffff);
|
||||||
|
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
||||||
|
|
||||||
|
// configure Physical Memory Protection to give supervisor mode
|
||||||
|
// access to all of physical memory.
|
||||||
|
w_pmpaddr0(0x3fffffffffffffull);
|
||||||
|
w_pmpcfg0(0xf);
|
||||||
|
|
||||||
|
// ask for clock interrupts.
|
||||||
|
timerinit();
|
||||||
|
|
||||||
|
// keep each CPU's hartid in its tp register, for cpuid().
|
||||||
|
int id = r_mhartid();
|
||||||
|
w_tp(id);
|
||||||
|
|
||||||
|
// switch to supervisor mode and jump to main().
|
||||||
|
asm volatile("mret");
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrange to receive timer interrupts.
|
||||||
|
// they will arrive in machine mode at
|
||||||
|
// at timervec in kernelvec.S,
|
||||||
|
// which turns them into software interrupts for
|
||||||
|
// devintr() in trap.c.
|
||||||
|
void
|
||||||
|
timerinit()
|
||||||
|
{
|
||||||
|
// each CPU has a separate source of timer interrupts.
|
||||||
|
int id = r_mhartid();
|
||||||
|
|
||||||
|
// ask the CLINT for a timer interrupt.
|
||||||
|
int interval = 1000000; // cycles; about 1/10th second in qemu.
|
||||||
|
*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;
|
||||||
|
|
||||||
|
// prepare information in scratch[] for timervec.
|
||||||
|
// scratch[0..2] : space for timervec to save registers.
|
||||||
|
// scratch[3] : address of CLINT MTIMECMP register.
|
||||||
|
// scratch[4] : desired interval (in cycles) between timer interrupts.
|
||||||
|
uint64 *scratch = &timer_scratch[id][0];
|
||||||
|
scratch[3] = CLINT_MTIMECMP(id);
|
||||||
|
scratch[4] = interval;
|
||||||
|
w_mscratch((uint64)scratch);
|
||||||
|
|
||||||
|
// set the machine-mode trap handler.
|
||||||
|
w_mtvec((uint64)timervec);
|
||||||
|
|
||||||
|
// enable machine-mode interrupts.
|
||||||
|
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
||||||
|
|
||||||
|
// enable machine-mode timer interrupts.
|
||||||
|
w_mie(r_mie() | MIE_MTIE);
|
||||||
|
}
|
||||||
11
kernel/stat.h
Normal file
11
kernel/stat.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#define T_DIR 1 // Directory
|
||||||
|
#define T_FILE 2 // File
|
||||||
|
#define T_DEVICE 3 // Device
|
||||||
|
|
||||||
|
struct stat {
|
||||||
|
int dev; // File system's disk device
|
||||||
|
uint ino; // Inode number
|
||||||
|
short type; // Type of file
|
||||||
|
short nlink; // Number of links to file
|
||||||
|
uint64 size; // Size of file in bytes
|
||||||
|
};
|
||||||
107
kernel/string.c
Normal file
107
kernel/string.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void*
|
||||||
|
memset(void *dst, int c, uint n)
|
||||||
|
{
|
||||||
|
char *cdst = (char *) dst;
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < n; i++){
|
||||||
|
cdst[i] = c;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
if(n == 0)
|
||||||
|
return dst;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
42
kernel/swtch.S
Normal file
42
kernel/swtch.S
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Context switch
|
||||||
|
#
|
||||||
|
# void swtch(struct context *old, struct context *new);
|
||||||
|
#
|
||||||
|
# Save current registers in old. Load from new.
|
||||||
|
|
||||||
|
|
||||||
|
.globl swtch
|
||||||
|
swtch:
|
||||||
|
sd ra, 0(a0)
|
||||||
|
sd sp, 8(a0)
|
||||||
|
sd s0, 16(a0)
|
||||||
|
sd s1, 24(a0)
|
||||||
|
sd s2, 32(a0)
|
||||||
|
sd s3, 40(a0)
|
||||||
|
sd s4, 48(a0)
|
||||||
|
sd s5, 56(a0)
|
||||||
|
sd s6, 64(a0)
|
||||||
|
sd s7, 72(a0)
|
||||||
|
sd s8, 80(a0)
|
||||||
|
sd s9, 88(a0)
|
||||||
|
sd s10, 96(a0)
|
||||||
|
sd s11, 104(a0)
|
||||||
|
|
||||||
|
ld ra, 0(a1)
|
||||||
|
ld sp, 8(a1)
|
||||||
|
ld s0, 16(a1)
|
||||||
|
ld s1, 24(a1)
|
||||||
|
ld s2, 32(a1)
|
||||||
|
ld s3, 40(a1)
|
||||||
|
ld s4, 48(a1)
|
||||||
|
ld s5, 56(a1)
|
||||||
|
ld s6, 64(a1)
|
||||||
|
ld s7, 72(a1)
|
||||||
|
ld s8, 80(a1)
|
||||||
|
ld s9, 88(a1)
|
||||||
|
ld s10, 96(a1)
|
||||||
|
ld s11, 104(a1)
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
147
kernel/syscall.c
Normal file
147
kernel/syscall.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
// Fetch the uint64 at addr from the current process.
|
||||||
|
int
|
||||||
|
fetchaddr(uint64 addr, uint64 *ip)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow
|
||||||
|
return -1;
|
||||||
|
if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the nul-terminated string at addr from the current process.
|
||||||
|
// Returns length of string, not including nul, or -1 for error.
|
||||||
|
int
|
||||||
|
fetchstr(uint64 addr, char *buf, int max)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
if(copyinstr(p->pagetable, buf, addr, max) < 0)
|
||||||
|
return -1;
|
||||||
|
return strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64
|
||||||
|
argraw(int n)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
switch (n) {
|
||||||
|
case 0:
|
||||||
|
return p->trapframe->a0;
|
||||||
|
case 1:
|
||||||
|
return p->trapframe->a1;
|
||||||
|
case 2:
|
||||||
|
return p->trapframe->a2;
|
||||||
|
case 3:
|
||||||
|
return p->trapframe->a3;
|
||||||
|
case 4:
|
||||||
|
return p->trapframe->a4;
|
||||||
|
case 5:
|
||||||
|
return p->trapframe->a5;
|
||||||
|
}
|
||||||
|
panic("argraw");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the nth 32-bit system call argument.
|
||||||
|
void
|
||||||
|
argint(int n, int *ip)
|
||||||
|
{
|
||||||
|
*ip = argraw(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve an argument as a pointer.
|
||||||
|
// Doesn't check for legality, since
|
||||||
|
// copyin/copyout will do that.
|
||||||
|
void
|
||||||
|
argaddr(int n, uint64 *ip)
|
||||||
|
{
|
||||||
|
*ip = argraw(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the nth word-sized system call argument as a null-terminated string.
|
||||||
|
// Copies into buf, at most max.
|
||||||
|
// Returns string length if OK (including nul), -1 if error.
|
||||||
|
int
|
||||||
|
argstr(int n, char *buf, int max)
|
||||||
|
{
|
||||||
|
uint64 addr;
|
||||||
|
argaddr(n, &addr);
|
||||||
|
return fetchstr(addr, buf, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prototypes for the functions that handle system calls.
|
||||||
|
extern uint64 sys_fork(void);
|
||||||
|
extern uint64 sys_exit(void);
|
||||||
|
extern uint64 sys_wait(void);
|
||||||
|
extern uint64 sys_pipe(void);
|
||||||
|
extern uint64 sys_read(void);
|
||||||
|
extern uint64 sys_kill(void);
|
||||||
|
extern uint64 sys_exec(void);
|
||||||
|
extern uint64 sys_fstat(void);
|
||||||
|
extern uint64 sys_chdir(void);
|
||||||
|
extern uint64 sys_dup(void);
|
||||||
|
extern uint64 sys_getpid(void);
|
||||||
|
extern uint64 sys_sbrk(void);
|
||||||
|
extern uint64 sys_sleep(void);
|
||||||
|
extern uint64 sys_uptime(void);
|
||||||
|
extern uint64 sys_open(void);
|
||||||
|
extern uint64 sys_write(void);
|
||||||
|
extern uint64 sys_mknod(void);
|
||||||
|
extern uint64 sys_unlink(void);
|
||||||
|
extern uint64 sys_link(void);
|
||||||
|
extern uint64 sys_mkdir(void);
|
||||||
|
extern uint64 sys_close(void);
|
||||||
|
|
||||||
|
// An array mapping syscall numbers from syscall.h
|
||||||
|
// to the function that handles the system call.
|
||||||
|
static uint64 (*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_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,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
syscall(void)
|
||||||
|
{
|
||||||
|
int num;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
num = p->trapframe->a7;
|
||||||
|
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
|
||||||
|
// Use num to lookup the system call function for num, call it,
|
||||||
|
// and store its return value in p->trapframe->a0
|
||||||
|
p->trapframe->a0 = syscalls[num]();
|
||||||
|
} else {
|
||||||
|
printf("%d %s: unknown sys call %d\n",
|
||||||
|
p->pid, p->name, num);
|
||||||
|
p->trapframe->a0 = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
kernel/syscall.h
Normal file
22
kernel/syscall.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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
|
||||||
505
kernel/sysfile.c
Normal file
505
kernel/sysfile.c
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
//
|
||||||
|
// 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 "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "fcntl.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;
|
||||||
|
|
||||||
|
argint(n, &fd);
|
||||||
|
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 *p = myproc();
|
||||||
|
|
||||||
|
for(fd = 0; fd < NOFILE; fd++){
|
||||||
|
if(p->ofile[fd] == 0){
|
||||||
|
p->ofile[fd] = f;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_read(void)
|
||||||
|
{
|
||||||
|
struct file *f;
|
||||||
|
int n;
|
||||||
|
uint64 p;
|
||||||
|
|
||||||
|
argaddr(1, &p);
|
||||||
|
argint(2, &n);
|
||||||
|
if(argfd(0, 0, &f) < 0)
|
||||||
|
return -1;
|
||||||
|
return fileread(f, p, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_write(void)
|
||||||
|
{
|
||||||
|
struct file *f;
|
||||||
|
int n;
|
||||||
|
uint64 p;
|
||||||
|
|
||||||
|
argaddr(1, &p);
|
||||||
|
argint(2, &n);
|
||||||
|
if(argfd(0, 0, &f) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return filewrite(f, p, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_close(void)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct file *f;
|
||||||
|
|
||||||
|
if(argfd(0, &fd, &f) < 0)
|
||||||
|
return -1;
|
||||||
|
myproc()->ofile[fd] = 0;
|
||||||
|
fileclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_fstat(void)
|
||||||
|
{
|
||||||
|
struct file *f;
|
||||||
|
uint64 st; // user pointer to struct stat
|
||||||
|
|
||||||
|
argaddr(1, &st);
|
||||||
|
if(argfd(0, 0, &f) < 0)
|
||||||
|
return -1;
|
||||||
|
return filestat(f, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the path new as a link to the same inode as old.
|
||||||
|
uint64
|
||||||
|
sys_link(void)
|
||||||
|
{
|
||||||
|
char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
|
||||||
|
struct inode *dp, *ip;
|
||||||
|
|
||||||
|
if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 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, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
|
||||||
|
panic("isdirempty: readi");
|
||||||
|
if(de.inum != 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_unlink(void)
|
||||||
|
{
|
||||||
|
struct inode *ip, *dp;
|
||||||
|
struct dirent de;
|
||||||
|
char name[DIRSIZ], path[MAXPATH];
|
||||||
|
uint off;
|
||||||
|
|
||||||
|
if(argstr(0, path, MAXPATH) < 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, 0, (uint64)&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)
|
||||||
|
{
|
||||||
|
struct inode *ip, *dp;
|
||||||
|
char name[DIRSIZ];
|
||||||
|
|
||||||
|
if((dp = nameiparent(path, name)) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ilock(dp);
|
||||||
|
|
||||||
|
if((ip = dirlookup(dp, name, 0)) != 0){
|
||||||
|
iunlockput(dp);
|
||||||
|
ilock(ip);
|
||||||
|
if(type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE))
|
||||||
|
return ip;
|
||||||
|
iunlockput(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ip = ialloc(dp->dev, type)) == 0){
|
||||||
|
iunlockput(dp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ilock(ip);
|
||||||
|
ip->major = major;
|
||||||
|
ip->minor = minor;
|
||||||
|
ip->nlink = 1;
|
||||||
|
iupdate(ip);
|
||||||
|
|
||||||
|
if(type == T_DIR){ // Create . and .. entries.
|
||||||
|
// No ip->nlink++ for ".": avoid cyclic ref count.
|
||||||
|
if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dirlink(dp, name, ip->inum) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if(type == T_DIR){
|
||||||
|
// now that success is guaranteed:
|
||||||
|
dp->nlink++; // for ".."
|
||||||
|
iupdate(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
iunlockput(dp);
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
// something went wrong. de-allocate ip.
|
||||||
|
ip->nlink = 0;
|
||||||
|
iupdate(ip);
|
||||||
|
iunlockput(ip);
|
||||||
|
iunlockput(dp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_open(void)
|
||||||
|
{
|
||||||
|
char path[MAXPATH];
|
||||||
|
int fd, omode;
|
||||||
|
struct file *f;
|
||||||
|
struct inode *ip;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
argint(1, &omode);
|
||||||
|
if((n = argstr(0, path, MAXPATH)) < 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(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
|
||||||
|
if(f)
|
||||||
|
fileclose(f);
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ip->type == T_DEVICE){
|
||||||
|
f->type = FD_DEVICE;
|
||||||
|
f->major = ip->major;
|
||||||
|
} else {
|
||||||
|
f->type = FD_INODE;
|
||||||
|
f->off = 0;
|
||||||
|
}
|
||||||
|
f->ip = ip;
|
||||||
|
f->readable = !(omode & O_WRONLY);
|
||||||
|
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
||||||
|
|
||||||
|
if((omode & O_TRUNC) && ip->type == T_FILE){
|
||||||
|
itrunc(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
iunlock(ip);
|
||||||
|
end_op();
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_mkdir(void)
|
||||||
|
{
|
||||||
|
char path[MAXPATH];
|
||||||
|
struct inode *ip;
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
|
||||||
|
end_op();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_mknod(void)
|
||||||
|
{
|
||||||
|
struct inode *ip;
|
||||||
|
char path[MAXPATH];
|
||||||
|
int major, minor;
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
argint(1, &major);
|
||||||
|
argint(2, &minor);
|
||||||
|
if((argstr(0, path, MAXPATH)) < 0 ||
|
||||||
|
(ip = create(path, T_DEVICE, major, minor)) == 0){
|
||||||
|
end_op();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_chdir(void)
|
||||||
|
{
|
||||||
|
char path[MAXPATH];
|
||||||
|
struct inode *ip;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
if(argstr(0, path, MAXPATH) < 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(p->cwd);
|
||||||
|
end_op();
|
||||||
|
p->cwd = ip;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_exec(void)
|
||||||
|
{
|
||||||
|
char path[MAXPATH], *argv[MAXARG];
|
||||||
|
int i;
|
||||||
|
uint64 uargv, uarg;
|
||||||
|
|
||||||
|
argaddr(1, &uargv);
|
||||||
|
if(argstr(0, path, MAXPATH) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(argv, 0, sizeof(argv));
|
||||||
|
for(i=0;; i++){
|
||||||
|
if(i >= NELEM(argv)){
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
if(uarg == 0){
|
||||||
|
argv[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
argv[i] = kalloc();
|
||||||
|
if(argv[i] == 0)
|
||||||
|
goto bad;
|
||||||
|
if(fetchstr(uarg, argv[i], PGSIZE) < 0)
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = exec(path, argv);
|
||||||
|
|
||||||
|
for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
|
||||||
|
kfree(argv[i]);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
bad:
|
||||||
|
for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
|
||||||
|
kfree(argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_pipe(void)
|
||||||
|
{
|
||||||
|
uint64 fdarray; // user pointer to array of two integers
|
||||||
|
struct file *rf, *wf;
|
||||||
|
int fd0, fd1;
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
argaddr(0, &fdarray);
|
||||||
|
if(pipealloc(&rf, &wf) < 0)
|
||||||
|
return -1;
|
||||||
|
fd0 = -1;
|
||||||
|
if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
|
||||||
|
if(fd0 >= 0)
|
||||||
|
p->ofile[fd0] = 0;
|
||||||
|
fileclose(rf);
|
||||||
|
fileclose(wf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||
|
||||||
|
copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){
|
||||||
|
p->ofile[fd0] = 0;
|
||||||
|
p->ofile[fd1] = 0;
|
||||||
|
fileclose(rf);
|
||||||
|
fileclose(wf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
93
kernel/sysproc.c
Normal file
93
kernel/sysproc.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_exit(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
argint(0, &n);
|
||||||
|
exit(n);
|
||||||
|
return 0; // not reached
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_getpid(void)
|
||||||
|
{
|
||||||
|
return myproc()->pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_fork(void)
|
||||||
|
{
|
||||||
|
return fork();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_wait(void)
|
||||||
|
{
|
||||||
|
uint64 p;
|
||||||
|
argaddr(0, &p);
|
||||||
|
return wait(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_sbrk(void)
|
||||||
|
{
|
||||||
|
uint64 addr;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
argint(0, &n);
|
||||||
|
addr = myproc()->sz;
|
||||||
|
if(growproc(n) < 0)
|
||||||
|
return -1;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_sleep(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
uint ticks0;
|
||||||
|
|
||||||
|
argint(0, &n);
|
||||||
|
if(n < 0)
|
||||||
|
n = 0;
|
||||||
|
acquire(&tickslock);
|
||||||
|
ticks0 = ticks;
|
||||||
|
while(ticks - ticks0 < n){
|
||||||
|
if(killed(myproc())){
|
||||||
|
release(&tickslock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sleep(&ticks, &tickslock);
|
||||||
|
}
|
||||||
|
release(&tickslock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64
|
||||||
|
sys_kill(void)
|
||||||
|
{
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
argint(0, &pid);
|
||||||
|
return kill(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return how many clock tick interrupts have occurred
|
||||||
|
// since start.
|
||||||
|
uint64
|
||||||
|
sys_uptime(void)
|
||||||
|
{
|
||||||
|
uint xticks;
|
||||||
|
|
||||||
|
acquire(&tickslock);
|
||||||
|
xticks = ticks;
|
||||||
|
release(&tickslock);
|
||||||
|
return xticks;
|
||||||
|
}
|
||||||
151
kernel/trampoline.S
Normal file
151
kernel/trampoline.S
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#
|
||||||
|
# low-level code to handle traps from user space into
|
||||||
|
# the kernel, and returns from kernel to user.
|
||||||
|
#
|
||||||
|
# the kernel maps the page holding this code
|
||||||
|
# at the same virtual address (TRAMPOLINE)
|
||||||
|
# in user and kernel space so that it continues
|
||||||
|
# to work when it switches page tables.
|
||||||
|
# kernel.ld causes this code to start at
|
||||||
|
# a page boundary.
|
||||||
|
#
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
|
||||||
|
.section trampsec
|
||||||
|
.globl trampoline
|
||||||
|
.globl usertrap
|
||||||
|
trampoline:
|
||||||
|
.align 4
|
||||||
|
.globl uservec
|
||||||
|
uservec:
|
||||||
|
#
|
||||||
|
# trap.c sets stvec to point here, so
|
||||||
|
# traps from user space start here,
|
||||||
|
# in supervisor mode, but with a
|
||||||
|
# user page table.
|
||||||
|
#
|
||||||
|
|
||||||
|
# save user a0 in sscratch so
|
||||||
|
# a0 can be used to get at TRAPFRAME.
|
||||||
|
csrw sscratch, a0
|
||||||
|
|
||||||
|
# each process has a separate p->trapframe memory area,
|
||||||
|
# but it's mapped to the same virtual address
|
||||||
|
# (TRAPFRAME) in every process's user page table.
|
||||||
|
li a0, TRAPFRAME
|
||||||
|
|
||||||
|
# save the user registers in TRAPFRAME
|
||||||
|
sd ra, 40(a0)
|
||||||
|
sd sp, 48(a0)
|
||||||
|
sd gp, 56(a0)
|
||||||
|
sd tp, 64(a0)
|
||||||
|
sd t0, 72(a0)
|
||||||
|
sd t1, 80(a0)
|
||||||
|
sd t2, 88(a0)
|
||||||
|
sd s0, 96(a0)
|
||||||
|
sd s1, 104(a0)
|
||||||
|
sd a1, 120(a0)
|
||||||
|
sd a2, 128(a0)
|
||||||
|
sd a3, 136(a0)
|
||||||
|
sd a4, 144(a0)
|
||||||
|
sd a5, 152(a0)
|
||||||
|
sd a6, 160(a0)
|
||||||
|
sd a7, 168(a0)
|
||||||
|
sd s2, 176(a0)
|
||||||
|
sd s3, 184(a0)
|
||||||
|
sd s4, 192(a0)
|
||||||
|
sd s5, 200(a0)
|
||||||
|
sd s6, 208(a0)
|
||||||
|
sd s7, 216(a0)
|
||||||
|
sd s8, 224(a0)
|
||||||
|
sd s9, 232(a0)
|
||||||
|
sd s10, 240(a0)
|
||||||
|
sd s11, 248(a0)
|
||||||
|
sd t3, 256(a0)
|
||||||
|
sd t4, 264(a0)
|
||||||
|
sd t5, 272(a0)
|
||||||
|
sd t6, 280(a0)
|
||||||
|
|
||||||
|
# save the user a0 in p->trapframe->a0
|
||||||
|
csrr t0, sscratch
|
||||||
|
sd t0, 112(a0)
|
||||||
|
|
||||||
|
# initialize kernel stack pointer, from p->trapframe->kernel_sp
|
||||||
|
ld sp, 8(a0)
|
||||||
|
|
||||||
|
# make tp hold the current hartid, from p->trapframe->kernel_hartid
|
||||||
|
ld tp, 32(a0)
|
||||||
|
|
||||||
|
# load the address of usertrap(), from p->trapframe->kernel_trap
|
||||||
|
ld t0, 16(a0)
|
||||||
|
|
||||||
|
# fetch the kernel page table address, from p->trapframe->kernel_satp.
|
||||||
|
ld t1, 0(a0)
|
||||||
|
|
||||||
|
# wait for any previous memory operations to complete, so that
|
||||||
|
# they use the user page table.
|
||||||
|
sfence.vma zero, zero
|
||||||
|
|
||||||
|
# install the kernel page table.
|
||||||
|
csrw satp, t1
|
||||||
|
|
||||||
|
# flush now-stale user entries from the TLB.
|
||||||
|
sfence.vma zero, zero
|
||||||
|
|
||||||
|
# jump to usertrap(), which does not return
|
||||||
|
jr t0
|
||||||
|
|
||||||
|
.globl userret
|
||||||
|
userret:
|
||||||
|
# userret(pagetable)
|
||||||
|
# called by usertrapret() in trap.c to
|
||||||
|
# switch from kernel to user.
|
||||||
|
# a0: user page table, for satp.
|
||||||
|
|
||||||
|
# switch to the user page table.
|
||||||
|
sfence.vma zero, zero
|
||||||
|
csrw satp, a0
|
||||||
|
sfence.vma zero, zero
|
||||||
|
|
||||||
|
li a0, TRAPFRAME
|
||||||
|
|
||||||
|
# restore all but a0 from TRAPFRAME
|
||||||
|
ld ra, 40(a0)
|
||||||
|
ld sp, 48(a0)
|
||||||
|
ld gp, 56(a0)
|
||||||
|
ld tp, 64(a0)
|
||||||
|
ld t0, 72(a0)
|
||||||
|
ld t1, 80(a0)
|
||||||
|
ld t2, 88(a0)
|
||||||
|
ld s0, 96(a0)
|
||||||
|
ld s1, 104(a0)
|
||||||
|
ld a1, 120(a0)
|
||||||
|
ld a2, 128(a0)
|
||||||
|
ld a3, 136(a0)
|
||||||
|
ld a4, 144(a0)
|
||||||
|
ld a5, 152(a0)
|
||||||
|
ld a6, 160(a0)
|
||||||
|
ld a7, 168(a0)
|
||||||
|
ld s2, 176(a0)
|
||||||
|
ld s3, 184(a0)
|
||||||
|
ld s4, 192(a0)
|
||||||
|
ld s5, 200(a0)
|
||||||
|
ld s6, 208(a0)
|
||||||
|
ld s7, 216(a0)
|
||||||
|
ld s8, 224(a0)
|
||||||
|
ld s9, 232(a0)
|
||||||
|
ld s10, 240(a0)
|
||||||
|
ld s11, 248(a0)
|
||||||
|
ld t3, 256(a0)
|
||||||
|
ld t4, 264(a0)
|
||||||
|
ld t5, 272(a0)
|
||||||
|
ld t6, 280(a0)
|
||||||
|
|
||||||
|
# restore user a0
|
||||||
|
ld a0, 112(a0)
|
||||||
|
|
||||||
|
# return to user mode and user pc.
|
||||||
|
# usertrapret() set up sstatus and sepc.
|
||||||
|
sret
|
||||||
221
kernel/trap.c
Normal file
221
kernel/trap.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
struct spinlock tickslock;
|
||||||
|
uint ticks;
|
||||||
|
|
||||||
|
extern char trampoline[], uservec[], userret[];
|
||||||
|
|
||||||
|
// in kernelvec.S, calls kerneltrap().
|
||||||
|
void kernelvec();
|
||||||
|
|
||||||
|
extern int devintr();
|
||||||
|
|
||||||
|
void
|
||||||
|
trapinit(void)
|
||||||
|
{
|
||||||
|
initlock(&tickslock, "time");
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up to take exceptions and traps while in the kernel.
|
||||||
|
void
|
||||||
|
trapinithart(void)
|
||||||
|
{
|
||||||
|
w_stvec((uint64)kernelvec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// handle an interrupt, exception, or system call from user space.
|
||||||
|
// called from trampoline.S
|
||||||
|
//
|
||||||
|
void
|
||||||
|
usertrap(void)
|
||||||
|
{
|
||||||
|
int which_dev = 0;
|
||||||
|
|
||||||
|
if((r_sstatus() & SSTATUS_SPP) != 0)
|
||||||
|
panic("usertrap: not from user mode");
|
||||||
|
|
||||||
|
// send interrupts and exceptions to kerneltrap(),
|
||||||
|
// since we're now in the kernel.
|
||||||
|
w_stvec((uint64)kernelvec);
|
||||||
|
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
// save user program counter.
|
||||||
|
p->trapframe->epc = r_sepc();
|
||||||
|
|
||||||
|
if(r_scause() == 8){
|
||||||
|
// system call
|
||||||
|
|
||||||
|
if(killed(p))
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
// sepc points to the ecall instruction,
|
||||||
|
// but we want to return to the next instruction.
|
||||||
|
p->trapframe->epc += 4;
|
||||||
|
|
||||||
|
// an interrupt will change sepc, scause, and sstatus,
|
||||||
|
// so enable only now that we're done with those registers.
|
||||||
|
intr_on();
|
||||||
|
|
||||||
|
syscall();
|
||||||
|
} else if((which_dev = devintr()) != 0){
|
||||||
|
// ok
|
||||||
|
} else {
|
||||||
|
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
|
||||||
|
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
|
||||||
|
setkilled(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(killed(p))
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
// give up the CPU if this is a timer interrupt.
|
||||||
|
if(which_dev == 2)
|
||||||
|
yield();
|
||||||
|
|
||||||
|
usertrapret();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return to user space
|
||||||
|
//
|
||||||
|
void
|
||||||
|
usertrapret(void)
|
||||||
|
{
|
||||||
|
struct proc *p = myproc();
|
||||||
|
|
||||||
|
// we're about to switch the destination of traps from
|
||||||
|
// kerneltrap() to usertrap(), so turn off interrupts until
|
||||||
|
// we're back in user space, where usertrap() is correct.
|
||||||
|
intr_off();
|
||||||
|
|
||||||
|
// send syscalls, interrupts, and exceptions to uservec in trampoline.S
|
||||||
|
uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
|
||||||
|
w_stvec(trampoline_uservec);
|
||||||
|
|
||||||
|
// set up trapframe values that uservec will need when
|
||||||
|
// the process next traps into the kernel.
|
||||||
|
p->trapframe->kernel_satp = r_satp(); // kernel page table
|
||||||
|
p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
|
||||||
|
p->trapframe->kernel_trap = (uint64)usertrap;
|
||||||
|
p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
|
||||||
|
|
||||||
|
// set up the registers that trampoline.S's sret will use
|
||||||
|
// to get to user space.
|
||||||
|
|
||||||
|
// set S Previous Privilege mode to User.
|
||||||
|
unsigned long x = r_sstatus();
|
||||||
|
x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
|
||||||
|
x |= SSTATUS_SPIE; // enable interrupts in user mode
|
||||||
|
w_sstatus(x);
|
||||||
|
|
||||||
|
// set S Exception Program Counter to the saved user pc.
|
||||||
|
w_sepc(p->trapframe->epc);
|
||||||
|
|
||||||
|
// tell trampoline.S the user page table to switch to.
|
||||||
|
uint64 satp = MAKE_SATP(p->pagetable);
|
||||||
|
|
||||||
|
// jump to userret in trampoline.S at the top of memory, which
|
||||||
|
// switches to the user page table, restores user registers,
|
||||||
|
// and switches to user mode with sret.
|
||||||
|
uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
|
||||||
|
((void (*)(uint64))trampoline_userret)(satp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// interrupts and exceptions from kernel code go here via kernelvec,
|
||||||
|
// on whatever the current kernel stack is.
|
||||||
|
void
|
||||||
|
kerneltrap()
|
||||||
|
{
|
||||||
|
int which_dev = 0;
|
||||||
|
uint64 sepc = r_sepc();
|
||||||
|
uint64 sstatus = r_sstatus();
|
||||||
|
uint64 scause = r_scause();
|
||||||
|
|
||||||
|
if((sstatus & SSTATUS_SPP) == 0)
|
||||||
|
panic("kerneltrap: not from supervisor mode");
|
||||||
|
if(intr_get() != 0)
|
||||||
|
panic("kerneltrap: interrupts enabled");
|
||||||
|
|
||||||
|
if((which_dev = devintr()) == 0){
|
||||||
|
printf("scause %p\n", scause);
|
||||||
|
printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
|
||||||
|
panic("kerneltrap");
|
||||||
|
}
|
||||||
|
|
||||||
|
// give up the CPU if this is a timer interrupt.
|
||||||
|
if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
|
||||||
|
yield();
|
||||||
|
|
||||||
|
// the yield() may have caused some traps to occur,
|
||||||
|
// so restore trap registers for use by kernelvec.S's sepc instruction.
|
||||||
|
w_sepc(sepc);
|
||||||
|
w_sstatus(sstatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clockintr()
|
||||||
|
{
|
||||||
|
acquire(&tickslock);
|
||||||
|
ticks++;
|
||||||
|
wakeup(&ticks);
|
||||||
|
release(&tickslock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's an external interrupt or software interrupt,
|
||||||
|
// and handle it.
|
||||||
|
// returns 2 if timer interrupt,
|
||||||
|
// 1 if other device,
|
||||||
|
// 0 if not recognized.
|
||||||
|
int
|
||||||
|
devintr()
|
||||||
|
{
|
||||||
|
uint64 scause = r_scause();
|
||||||
|
|
||||||
|
if((scause & 0x8000000000000000L) &&
|
||||||
|
(scause & 0xff) == 9){
|
||||||
|
// this is a supervisor external interrupt, via PLIC.
|
||||||
|
|
||||||
|
// irq indicates which device interrupted.
|
||||||
|
int irq = plic_claim();
|
||||||
|
|
||||||
|
if(irq == UART0_IRQ){
|
||||||
|
uartintr();
|
||||||
|
} else if(irq == VIRTIO0_IRQ){
|
||||||
|
virtio_disk_intr();
|
||||||
|
} else if(irq){
|
||||||
|
printf("unexpected interrupt irq=%d\n", irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the PLIC allows each device to raise at most one
|
||||||
|
// interrupt at a time; tell the PLIC the device is
|
||||||
|
// now allowed to interrupt again.
|
||||||
|
if(irq)
|
||||||
|
plic_complete(irq);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if(scause == 0x8000000000000001L){
|
||||||
|
// software interrupt from a machine-mode timer interrupt,
|
||||||
|
// forwarded by timervec in kernelvec.S.
|
||||||
|
|
||||||
|
if(cpuid() == 0){
|
||||||
|
clockintr();
|
||||||
|
}
|
||||||
|
|
||||||
|
// acknowledge the software interrupt by clearing
|
||||||
|
// the SSIP bit in sip.
|
||||||
|
w_sip(r_sip() & ~2);
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
kernel/types.h
Normal file
10
kernel/types.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
typedef unsigned int uint;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
typedef unsigned char uint8;
|
||||||
|
typedef unsigned short uint16;
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long uint64;
|
||||||
|
|
||||||
|
typedef uint64 pde_t;
|
||||||
190
kernel/uart.c
Normal file
190
kernel/uart.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
//
|
||||||
|
// low-level driver routines for 16550a UART.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
// the UART control registers are memory-mapped
|
||||||
|
// at address UART0. this macro returns the
|
||||||
|
// address of one of the registers.
|
||||||
|
#define Reg(reg) ((volatile unsigned char *)(UART0 + reg))
|
||||||
|
|
||||||
|
// the UART control registers.
|
||||||
|
// some have different meanings for
|
||||||
|
// read vs write.
|
||||||
|
// see http://byterunner.com/16550.html
|
||||||
|
#define RHR 0 // receive holding register (for input bytes)
|
||||||
|
#define THR 0 // transmit holding register (for output bytes)
|
||||||
|
#define IER 1 // interrupt enable register
|
||||||
|
#define IER_RX_ENABLE (1<<0)
|
||||||
|
#define IER_TX_ENABLE (1<<1)
|
||||||
|
#define FCR 2 // FIFO control register
|
||||||
|
#define FCR_FIFO_ENABLE (1<<0)
|
||||||
|
#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
|
||||||
|
#define ISR 2 // interrupt status register
|
||||||
|
#define LCR 3 // line control register
|
||||||
|
#define LCR_EIGHT_BITS (3<<0)
|
||||||
|
#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
|
||||||
|
#define LSR 5 // line status register
|
||||||
|
#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR
|
||||||
|
#define LSR_TX_IDLE (1<<5) // THR can accept another character to send
|
||||||
|
|
||||||
|
#define ReadReg(reg) (*(Reg(reg)))
|
||||||
|
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
|
||||||
|
|
||||||
|
// the transmit output buffer.
|
||||||
|
struct spinlock uart_tx_lock;
|
||||||
|
#define UART_TX_BUF_SIZE 32
|
||||||
|
char uart_tx_buf[UART_TX_BUF_SIZE];
|
||||||
|
uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
|
||||||
|
uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
|
||||||
|
|
||||||
|
extern volatile int panicked; // from printf.c
|
||||||
|
|
||||||
|
void uartstart();
|
||||||
|
|
||||||
|
void
|
||||||
|
uartinit(void)
|
||||||
|
{
|
||||||
|
// disable interrupts.
|
||||||
|
WriteReg(IER, 0x00);
|
||||||
|
|
||||||
|
// special mode to set baud rate.
|
||||||
|
WriteReg(LCR, LCR_BAUD_LATCH);
|
||||||
|
|
||||||
|
// LSB for baud rate of 38.4K.
|
||||||
|
WriteReg(0, 0x03);
|
||||||
|
|
||||||
|
// MSB for baud rate of 38.4K.
|
||||||
|
WriteReg(1, 0x00);
|
||||||
|
|
||||||
|
// leave set-baud mode,
|
||||||
|
// and set word length to 8 bits, no parity.
|
||||||
|
WriteReg(LCR, LCR_EIGHT_BITS);
|
||||||
|
|
||||||
|
// reset and enable FIFOs.
|
||||||
|
WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
|
||||||
|
|
||||||
|
// enable transmit and receive interrupts.
|
||||||
|
WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
|
||||||
|
|
||||||
|
initlock(&uart_tx_lock, "uart");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a character to the output buffer and tell the
|
||||||
|
// UART to start sending if it isn't already.
|
||||||
|
// blocks if the output buffer is full.
|
||||||
|
// because it may block, it can't be called
|
||||||
|
// from interrupts; it's only suitable for use
|
||||||
|
// by write().
|
||||||
|
void
|
||||||
|
uartputc(int c)
|
||||||
|
{
|
||||||
|
acquire(&uart_tx_lock);
|
||||||
|
|
||||||
|
if(panicked){
|
||||||
|
for(;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){
|
||||||
|
// buffer is full.
|
||||||
|
// wait for uartstart() to open up space in the buffer.
|
||||||
|
sleep(&uart_tx_r, &uart_tx_lock);
|
||||||
|
}
|
||||||
|
uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
|
||||||
|
uart_tx_w += 1;
|
||||||
|
uartstart();
|
||||||
|
release(&uart_tx_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// alternate version of uartputc() that doesn't
|
||||||
|
// use interrupts, for use by kernel printf() and
|
||||||
|
// to echo characters. it spins waiting for the uart's
|
||||||
|
// output register to be empty.
|
||||||
|
void
|
||||||
|
uartputc_sync(int c)
|
||||||
|
{
|
||||||
|
push_off();
|
||||||
|
|
||||||
|
if(panicked){
|
||||||
|
for(;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for Transmit Holding Empty to be set in LSR.
|
||||||
|
while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
|
||||||
|
;
|
||||||
|
WriteReg(THR, c);
|
||||||
|
|
||||||
|
pop_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the UART is idle, and a character is waiting
|
||||||
|
// in the transmit buffer, send it.
|
||||||
|
// caller must hold uart_tx_lock.
|
||||||
|
// called from both the top- and bottom-half.
|
||||||
|
void
|
||||||
|
uartstart()
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
if(uart_tx_w == uart_tx_r){
|
||||||
|
// transmit buffer is empty.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ReadReg(LSR) & LSR_TX_IDLE) == 0){
|
||||||
|
// the UART transmit holding register is full,
|
||||||
|
// so we cannot give it another byte.
|
||||||
|
// it will interrupt when it's ready for a new byte.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
|
||||||
|
uart_tx_r += 1;
|
||||||
|
|
||||||
|
// maybe uartputc() is waiting for space in the buffer.
|
||||||
|
wakeup(&uart_tx_r);
|
||||||
|
|
||||||
|
WriteReg(THR, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read one input character from the UART.
|
||||||
|
// return -1 if none is waiting.
|
||||||
|
int
|
||||||
|
uartgetc(void)
|
||||||
|
{
|
||||||
|
if(ReadReg(LSR) & 0x01){
|
||||||
|
// input data is ready.
|
||||||
|
return ReadReg(RHR);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle a uart interrupt, raised because input has
|
||||||
|
// arrived, or the uart is ready for more output, or
|
||||||
|
// both. called from devintr().
|
||||||
|
void
|
||||||
|
uartintr(void)
|
||||||
|
{
|
||||||
|
// read and process incoming characters.
|
||||||
|
while(1){
|
||||||
|
int c = uartgetc();
|
||||||
|
if(c == -1)
|
||||||
|
break;
|
||||||
|
consoleintr(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send buffered characters.
|
||||||
|
acquire(&uart_tx_lock);
|
||||||
|
uartstart();
|
||||||
|
release(&uart_tx_lock);
|
||||||
|
}
|
||||||
96
kernel/virtio.h
Normal file
96
kernel/virtio.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// virtio device definitions.
|
||||||
|
// for both the mmio interface, and virtio descriptors.
|
||||||
|
// only tested with qemu.
|
||||||
|
//
|
||||||
|
// the virtio spec:
|
||||||
|
// https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf
|
||||||
|
//
|
||||||
|
|
||||||
|
// virtio mmio control registers, mapped starting at 0x10001000.
|
||||||
|
// from qemu virtio_mmio.h
|
||||||
|
#define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976
|
||||||
|
#define VIRTIO_MMIO_VERSION 0x004 // version; should be 2
|
||||||
|
#define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk
|
||||||
|
#define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551
|
||||||
|
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
|
||||||
|
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
|
||||||
|
#define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only
|
||||||
|
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only
|
||||||
|
#define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only
|
||||||
|
#define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit
|
||||||
|
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only
|
||||||
|
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only
|
||||||
|
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only
|
||||||
|
#define VIRTIO_MMIO_STATUS 0x070 // read/write
|
||||||
|
#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 // physical address for descriptor table, write-only
|
||||||
|
#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
|
||||||
|
#define VIRTIO_MMIO_DRIVER_DESC_LOW 0x090 // physical address for available ring, write-only
|
||||||
|
#define VIRTIO_MMIO_DRIVER_DESC_HIGH 0x094
|
||||||
|
#define VIRTIO_MMIO_DEVICE_DESC_LOW 0x0a0 // physical address for used ring, write-only
|
||||||
|
#define VIRTIO_MMIO_DEVICE_DESC_HIGH 0x0a4
|
||||||
|
|
||||||
|
// status register bits, from qemu virtio_config.h
|
||||||
|
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
|
||||||
|
#define VIRTIO_CONFIG_S_DRIVER 2
|
||||||
|
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
||||||
|
#define VIRTIO_CONFIG_S_FEATURES_OK 8
|
||||||
|
|
||||||
|
// device feature bits
|
||||||
|
#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
|
||||||
|
#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
|
||||||
|
#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */
|
||||||
|
#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */
|
||||||
|
#define VIRTIO_F_ANY_LAYOUT 27
|
||||||
|
#define VIRTIO_RING_F_INDIRECT_DESC 28
|
||||||
|
#define VIRTIO_RING_F_EVENT_IDX 29
|
||||||
|
|
||||||
|
// this many virtio descriptors.
|
||||||
|
// must be a power of two.
|
||||||
|
#define NUM 8
|
||||||
|
|
||||||
|
// a single descriptor, from the spec.
|
||||||
|
struct virtq_desc {
|
||||||
|
uint64 addr;
|
||||||
|
uint32 len;
|
||||||
|
uint16 flags;
|
||||||
|
uint16 next;
|
||||||
|
};
|
||||||
|
#define VRING_DESC_F_NEXT 1 // chained with another descriptor
|
||||||
|
#define VRING_DESC_F_WRITE 2 // device writes (vs read)
|
||||||
|
|
||||||
|
// the (entire) avail ring, from the spec.
|
||||||
|
struct virtq_avail {
|
||||||
|
uint16 flags; // always zero
|
||||||
|
uint16 idx; // driver will write ring[idx] next
|
||||||
|
uint16 ring[NUM]; // descriptor numbers of chain heads
|
||||||
|
uint16 unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
// one entry in the "used" ring, with which the
|
||||||
|
// device tells the driver about completed requests.
|
||||||
|
struct virtq_used_elem {
|
||||||
|
uint32 id; // index of start of completed descriptor chain
|
||||||
|
uint32 len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct virtq_used {
|
||||||
|
uint16 flags; // always zero
|
||||||
|
uint16 idx; // device increments when it adds a ring[] entry
|
||||||
|
struct virtq_used_elem ring[NUM];
|
||||||
|
};
|
||||||
|
|
||||||
|
// these are specific to virtio block devices, e.g. disks,
|
||||||
|
// described in Section 5.2 of the spec.
|
||||||
|
|
||||||
|
#define VIRTIO_BLK_T_IN 0 // read the disk
|
||||||
|
#define VIRTIO_BLK_T_OUT 1 // write the disk
|
||||||
|
|
||||||
|
// the format of the first descriptor in a disk request.
|
||||||
|
// to be followed by two more descriptors containing
|
||||||
|
// the block, and a one-byte status.
|
||||||
|
struct virtio_blk_req {
|
||||||
|
uint32 type; // VIRTIO_BLK_T_IN or ..._OUT
|
||||||
|
uint32 reserved;
|
||||||
|
uint64 sector;
|
||||||
|
};
|
||||||
327
kernel/virtio_disk.c
Normal file
327
kernel/virtio_disk.c
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
//
|
||||||
|
// driver for qemu's virtio disk device.
|
||||||
|
// uses qemu's mmio interface to virtio.
|
||||||
|
//
|
||||||
|
// qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
#include "virtio.h"
|
||||||
|
|
||||||
|
// the address of virtio mmio register r.
|
||||||
|
#define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))
|
||||||
|
|
||||||
|
static struct disk {
|
||||||
|
// a set (not a ring) of DMA descriptors, with which the
|
||||||
|
// driver tells the device where to read and write individual
|
||||||
|
// disk operations. there are NUM descriptors.
|
||||||
|
// most commands consist of a "chain" (a linked list) of a couple of
|
||||||
|
// these descriptors.
|
||||||
|
struct virtq_desc *desc;
|
||||||
|
|
||||||
|
// a ring in which the driver writes descriptor numbers
|
||||||
|
// that the driver would like the device to process. it only
|
||||||
|
// includes the head descriptor of each chain. the ring has
|
||||||
|
// NUM elements.
|
||||||
|
struct virtq_avail *avail;
|
||||||
|
|
||||||
|
// a ring in which the device writes descriptor numbers that
|
||||||
|
// the device has finished processing (just the head of each chain).
|
||||||
|
// there are NUM used ring entries.
|
||||||
|
struct virtq_used *used;
|
||||||
|
|
||||||
|
// our own book-keeping.
|
||||||
|
char free[NUM]; // is a descriptor free?
|
||||||
|
uint16 used_idx; // we've looked this far in used[2..NUM].
|
||||||
|
|
||||||
|
// track info about in-flight operations,
|
||||||
|
// for use when completion interrupt arrives.
|
||||||
|
// indexed by first descriptor index of chain.
|
||||||
|
struct {
|
||||||
|
struct buf *b;
|
||||||
|
char status;
|
||||||
|
} info[NUM];
|
||||||
|
|
||||||
|
// disk command headers.
|
||||||
|
// one-for-one with descriptors, for convenience.
|
||||||
|
struct virtio_blk_req ops[NUM];
|
||||||
|
|
||||||
|
struct spinlock vdisk_lock;
|
||||||
|
|
||||||
|
} disk;
|
||||||
|
|
||||||
|
void
|
||||||
|
virtio_disk_init(void)
|
||||||
|
{
|
||||||
|
uint32 status = 0;
|
||||||
|
|
||||||
|
initlock(&disk.vdisk_lock, "virtio_disk");
|
||||||
|
|
||||||
|
if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||
|
||||||
|
*R(VIRTIO_MMIO_VERSION) != 2 ||
|
||||||
|
*R(VIRTIO_MMIO_DEVICE_ID) != 2 ||
|
||||||
|
*R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){
|
||||||
|
panic("could not find virtio disk");
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset device
|
||||||
|
*R(VIRTIO_MMIO_STATUS) = status;
|
||||||
|
|
||||||
|
// set ACKNOWLEDGE status bit
|
||||||
|
status |= VIRTIO_CONFIG_S_ACKNOWLEDGE;
|
||||||
|
*R(VIRTIO_MMIO_STATUS) = status;
|
||||||
|
|
||||||
|
// set DRIVER status bit
|
||||||
|
status |= VIRTIO_CONFIG_S_DRIVER;
|
||||||
|
*R(VIRTIO_MMIO_STATUS) = status;
|
||||||
|
|
||||||
|
// negotiate features
|
||||||
|
uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES);
|
||||||
|
features &= ~(1 << VIRTIO_BLK_F_RO);
|
||||||
|
features &= ~(1 << VIRTIO_BLK_F_SCSI);
|
||||||
|
features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);
|
||||||
|
features &= ~(1 << VIRTIO_BLK_F_MQ);
|
||||||
|
features &= ~(1 << VIRTIO_F_ANY_LAYOUT);
|
||||||
|
features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
|
||||||
|
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
|
||||||
|
*R(VIRTIO_MMIO_DRIVER_FEATURES) = features;
|
||||||
|
|
||||||
|
// tell device that feature negotiation is complete.
|
||||||
|
status |= VIRTIO_CONFIG_S_FEATURES_OK;
|
||||||
|
*R(VIRTIO_MMIO_STATUS) = status;
|
||||||
|
|
||||||
|
// re-read status to ensure FEATURES_OK is set.
|
||||||
|
status = *R(VIRTIO_MMIO_STATUS);
|
||||||
|
if(!(status & VIRTIO_CONFIG_S_FEATURES_OK))
|
||||||
|
panic("virtio disk FEATURES_OK unset");
|
||||||
|
|
||||||
|
// initialize queue 0.
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_SEL) = 0;
|
||||||
|
|
||||||
|
// ensure queue 0 is not in use.
|
||||||
|
if(*R(VIRTIO_MMIO_QUEUE_READY))
|
||||||
|
panic("virtio disk should not be ready");
|
||||||
|
|
||||||
|
// check maximum queue size.
|
||||||
|
uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX);
|
||||||
|
if(max == 0)
|
||||||
|
panic("virtio disk has no queue 0");
|
||||||
|
if(max < NUM)
|
||||||
|
panic("virtio disk max queue too short");
|
||||||
|
|
||||||
|
// allocate and zero queue memory.
|
||||||
|
disk.desc = kalloc();
|
||||||
|
disk.avail = kalloc();
|
||||||
|
disk.used = kalloc();
|
||||||
|
if(!disk.desc || !disk.avail || !disk.used)
|
||||||
|
panic("virtio disk kalloc");
|
||||||
|
memset(disk.desc, 0, PGSIZE);
|
||||||
|
memset(disk.avail, 0, PGSIZE);
|
||||||
|
memset(disk.used, 0, PGSIZE);
|
||||||
|
|
||||||
|
// set queue size.
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_NUM) = NUM;
|
||||||
|
|
||||||
|
// write physical addresses.
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc;
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32;
|
||||||
|
*R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail;
|
||||||
|
*R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32;
|
||||||
|
*R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used;
|
||||||
|
*R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32;
|
||||||
|
|
||||||
|
// queue is ready.
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_READY) = 0x1;
|
||||||
|
|
||||||
|
// all NUM descriptors start out unused.
|
||||||
|
for(int i = 0; i < NUM; i++)
|
||||||
|
disk.free[i] = 1;
|
||||||
|
|
||||||
|
// tell device we're completely ready.
|
||||||
|
status |= VIRTIO_CONFIG_S_DRIVER_OK;
|
||||||
|
*R(VIRTIO_MMIO_STATUS) = status;
|
||||||
|
|
||||||
|
// plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a free descriptor, mark it non-free, return its index.
|
||||||
|
static int
|
||||||
|
alloc_desc()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < NUM; i++){
|
||||||
|
if(disk.free[i]){
|
||||||
|
disk.free[i] = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark a descriptor as free.
|
||||||
|
static void
|
||||||
|
free_desc(int i)
|
||||||
|
{
|
||||||
|
if(i >= NUM)
|
||||||
|
panic("free_desc 1");
|
||||||
|
if(disk.free[i])
|
||||||
|
panic("free_desc 2");
|
||||||
|
disk.desc[i].addr = 0;
|
||||||
|
disk.desc[i].len = 0;
|
||||||
|
disk.desc[i].flags = 0;
|
||||||
|
disk.desc[i].next = 0;
|
||||||
|
disk.free[i] = 1;
|
||||||
|
wakeup(&disk.free[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free a chain of descriptors.
|
||||||
|
static void
|
||||||
|
free_chain(int i)
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
int flag = disk.desc[i].flags;
|
||||||
|
int nxt = disk.desc[i].next;
|
||||||
|
free_desc(i);
|
||||||
|
if(flag & VRING_DESC_F_NEXT)
|
||||||
|
i = nxt;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate three descriptors (they need not be contiguous).
|
||||||
|
// disk transfers always use three descriptors.
|
||||||
|
static int
|
||||||
|
alloc3_desc(int *idx)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 3; i++){
|
||||||
|
idx[i] = alloc_desc();
|
||||||
|
if(idx[i] < 0){
|
||||||
|
for(int j = 0; j < i; j++)
|
||||||
|
free_desc(idx[j]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
virtio_disk_rw(struct buf *b, int write)
|
||||||
|
{
|
||||||
|
uint64 sector = b->blockno * (BSIZE / 512);
|
||||||
|
|
||||||
|
acquire(&disk.vdisk_lock);
|
||||||
|
|
||||||
|
// the spec's Section 5.2 says that legacy block operations use
|
||||||
|
// three descriptors: one for type/reserved/sector, one for the
|
||||||
|
// data, one for a 1-byte status result.
|
||||||
|
|
||||||
|
// allocate the three descriptors.
|
||||||
|
int idx[3];
|
||||||
|
while(1){
|
||||||
|
if(alloc3_desc(idx) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(&disk.free[0], &disk.vdisk_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// format the three descriptors.
|
||||||
|
// qemu's virtio-blk.c reads them.
|
||||||
|
|
||||||
|
struct virtio_blk_req *buf0 = &disk.ops[idx[0]];
|
||||||
|
|
||||||
|
if(write)
|
||||||
|
buf0->type = VIRTIO_BLK_T_OUT; // write the disk
|
||||||
|
else
|
||||||
|
buf0->type = VIRTIO_BLK_T_IN; // read the disk
|
||||||
|
buf0->reserved = 0;
|
||||||
|
buf0->sector = sector;
|
||||||
|
|
||||||
|
disk.desc[idx[0]].addr = (uint64) buf0;
|
||||||
|
disk.desc[idx[0]].len = sizeof(struct virtio_blk_req);
|
||||||
|
disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;
|
||||||
|
disk.desc[idx[0]].next = idx[1];
|
||||||
|
|
||||||
|
disk.desc[idx[1]].addr = (uint64) b->data;
|
||||||
|
disk.desc[idx[1]].len = BSIZE;
|
||||||
|
if(write)
|
||||||
|
disk.desc[idx[1]].flags = 0; // device reads b->data
|
||||||
|
else
|
||||||
|
disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data
|
||||||
|
disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT;
|
||||||
|
disk.desc[idx[1]].next = idx[2];
|
||||||
|
|
||||||
|
disk.info[idx[0]].status = 0xff; // device writes 0 on success
|
||||||
|
disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status;
|
||||||
|
disk.desc[idx[2]].len = 1;
|
||||||
|
disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status
|
||||||
|
disk.desc[idx[2]].next = 0;
|
||||||
|
|
||||||
|
// record struct buf for virtio_disk_intr().
|
||||||
|
b->disk = 1;
|
||||||
|
disk.info[idx[0]].b = b;
|
||||||
|
|
||||||
|
// tell the device the first index in our chain of descriptors.
|
||||||
|
disk.avail->ring[disk.avail->idx % NUM] = idx[0];
|
||||||
|
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
// tell the device another avail ring entry is available.
|
||||||
|
disk.avail->idx += 1; // not % NUM ...
|
||||||
|
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
*R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number
|
||||||
|
|
||||||
|
// Wait for virtio_disk_intr() to say request has finished.
|
||||||
|
while(b->disk == 1) {
|
||||||
|
sleep(b, &disk.vdisk_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
disk.info[idx[0]].b = 0;
|
||||||
|
free_chain(idx[0]);
|
||||||
|
|
||||||
|
release(&disk.vdisk_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
virtio_disk_intr()
|
||||||
|
{
|
||||||
|
acquire(&disk.vdisk_lock);
|
||||||
|
|
||||||
|
// the device won't raise another interrupt until we tell it
|
||||||
|
// we've seen this interrupt, which the following line does.
|
||||||
|
// this may race with the device writing new entries to
|
||||||
|
// the "used" ring, in which case we may process the new
|
||||||
|
// completion entries in this interrupt, and have nothing to do
|
||||||
|
// in the next interrupt, which is harmless.
|
||||||
|
*R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3;
|
||||||
|
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
// the device increments disk.used->idx when it
|
||||||
|
// adds an entry to the used ring.
|
||||||
|
|
||||||
|
while(disk.used_idx != disk.used->idx){
|
||||||
|
__sync_synchronize();
|
||||||
|
int id = disk.used->ring[disk.used_idx % NUM].id;
|
||||||
|
|
||||||
|
if(disk.info[id].status != 0)
|
||||||
|
panic("virtio_disk_intr status");
|
||||||
|
|
||||||
|
struct buf *b = disk.info[id].b;
|
||||||
|
b->disk = 0; // disk is done with buf
|
||||||
|
wakeup(b);
|
||||||
|
|
||||||
|
disk.used_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
release(&disk.vdisk_lock);
|
||||||
|
}
|
||||||
451
kernel/vm.c
Normal file
451
kernel/vm.c
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
#include "param.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "elf.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the kernel's page table.
|
||||||
|
*/
|
||||||
|
pagetable_t kernel_pagetable;
|
||||||
|
|
||||||
|
extern char etext[]; // kernel.ld sets this to end of kernel code.
|
||||||
|
|
||||||
|
extern char trampoline[]; // trampoline.S
|
||||||
|
|
||||||
|
// Make a direct-map page table for the kernel.
|
||||||
|
pagetable_t
|
||||||
|
kvmmake(void)
|
||||||
|
{
|
||||||
|
pagetable_t kpgtbl;
|
||||||
|
|
||||||
|
kpgtbl = (pagetable_t) kalloc();
|
||||||
|
memset(kpgtbl, 0, PGSIZE);
|
||||||
|
|
||||||
|
// uart registers
|
||||||
|
kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W);
|
||||||
|
|
||||||
|
// virtio mmio disk interface
|
||||||
|
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
|
||||||
|
|
||||||
|
// PLIC
|
||||||
|
kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
|
||||||
|
|
||||||
|
// map kernel text executable and read-only.
|
||||||
|
kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
|
||||||
|
|
||||||
|
// map kernel data and the physical RAM we'll make use of.
|
||||||
|
kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
|
||||||
|
|
||||||
|
// map the trampoline for trap entry/exit to
|
||||||
|
// the highest virtual address in the kernel.
|
||||||
|
kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
|
||||||
|
|
||||||
|
// allocate and map a kernel stack for each process.
|
||||||
|
proc_mapstacks(kpgtbl);
|
||||||
|
|
||||||
|
return kpgtbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the one kernel_pagetable
|
||||||
|
void
|
||||||
|
kvminit(void)
|
||||||
|
{
|
||||||
|
kernel_pagetable = kvmmake();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch h/w page table register to the kernel's page table,
|
||||||
|
// and enable paging.
|
||||||
|
void
|
||||||
|
kvminithart()
|
||||||
|
{
|
||||||
|
// wait for any previous writes to the page table memory to finish.
|
||||||
|
sfence_vma();
|
||||||
|
|
||||||
|
w_satp(MAKE_SATP(kernel_pagetable));
|
||||||
|
|
||||||
|
// flush stale entries from the TLB.
|
||||||
|
sfence_vma();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the address of the PTE in page table pagetable
|
||||||
|
// that corresponds to virtual address va. If alloc!=0,
|
||||||
|
// create any required page-table pages.
|
||||||
|
//
|
||||||
|
// The risc-v Sv39 scheme has three levels of page-table
|
||||||
|
// pages. A page-table page contains 512 64-bit PTEs.
|
||||||
|
// A 64-bit virtual address is split into five fields:
|
||||||
|
// 39..63 -- must be zero.
|
||||||
|
// 30..38 -- 9 bits of level-2 index.
|
||||||
|
// 21..29 -- 9 bits of level-1 index.
|
||||||
|
// 12..20 -- 9 bits of level-0 index.
|
||||||
|
// 0..11 -- 12 bits of byte offset within the page.
|
||||||
|
pte_t *
|
||||||
|
walk(pagetable_t pagetable, uint64 va, int alloc)
|
||||||
|
{
|
||||||
|
if(va >= MAXVA)
|
||||||
|
panic("walk");
|
||||||
|
|
||||||
|
for(int level = 2; level > 0; level--) {
|
||||||
|
pte_t *pte = &pagetable[PX(level, va)];
|
||||||
|
if(*pte & PTE_V) {
|
||||||
|
pagetable = (pagetable_t)PTE2PA(*pte);
|
||||||
|
} else {
|
||||||
|
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
|
||||||
|
return 0;
|
||||||
|
memset(pagetable, 0, PGSIZE);
|
||||||
|
*pte = PA2PTE(pagetable) | PTE_V;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &pagetable[PX(0, va)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up a virtual address, return the physical address,
|
||||||
|
// or 0 if not mapped.
|
||||||
|
// Can only be used to look up user pages.
|
||||||
|
uint64
|
||||||
|
walkaddr(pagetable_t pagetable, uint64 va)
|
||||||
|
{
|
||||||
|
pte_t *pte;
|
||||||
|
uint64 pa;
|
||||||
|
|
||||||
|
if(va >= MAXVA)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pte = walk(pagetable, va, 0);
|
||||||
|
if(pte == 0)
|
||||||
|
return 0;
|
||||||
|
if((*pte & PTE_V) == 0)
|
||||||
|
return 0;
|
||||||
|
if((*pte & PTE_U) == 0)
|
||||||
|
return 0;
|
||||||
|
pa = PTE2PA(*pte);
|
||||||
|
return pa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a mapping to the kernel page table.
|
||||||
|
// only used when booting.
|
||||||
|
// does not flush TLB or enable paging.
|
||||||
|
void
|
||||||
|
kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm)
|
||||||
|
{
|
||||||
|
if(mappages(kpgtbl, va, sz, pa, perm) != 0)
|
||||||
|
panic("kvmmap");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PTEs for virtual addresses starting at va that refer to
|
||||||
|
// physical addresses starting at pa.
|
||||||
|
// va and size MUST be page-aligned.
|
||||||
|
// Returns 0 on success, -1 if walk() couldn't
|
||||||
|
// allocate a needed page-table page.
|
||||||
|
int
|
||||||
|
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
|
||||||
|
{
|
||||||
|
uint64 a, last;
|
||||||
|
pte_t *pte;
|
||||||
|
|
||||||
|
if((va % PGSIZE) != 0)
|
||||||
|
panic("mappages: va not aligned");
|
||||||
|
|
||||||
|
if((size % PGSIZE) != 0)
|
||||||
|
panic("mappages: size not aligned");
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
panic("mappages: size");
|
||||||
|
|
||||||
|
a = va;
|
||||||
|
last = va + size - PGSIZE;
|
||||||
|
for(;;){
|
||||||
|
if((pte = walk(pagetable, a, 1)) == 0)
|
||||||
|
return -1;
|
||||||
|
if(*pte & PTE_V)
|
||||||
|
panic("mappages: remap");
|
||||||
|
*pte = PA2PTE(pa) | perm | PTE_V;
|
||||||
|
if(a == last)
|
||||||
|
break;
|
||||||
|
a += PGSIZE;
|
||||||
|
pa += PGSIZE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove npages of mappings starting from va. va must be
|
||||||
|
// page-aligned. The mappings must exist.
|
||||||
|
// Optionally free the physical memory.
|
||||||
|
void
|
||||||
|
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
|
||||||
|
{
|
||||||
|
uint64 a;
|
||||||
|
pte_t *pte;
|
||||||
|
|
||||||
|
if((va % PGSIZE) != 0)
|
||||||
|
panic("uvmunmap: not aligned");
|
||||||
|
|
||||||
|
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
|
||||||
|
if((pte = walk(pagetable, a, 0)) == 0)
|
||||||
|
panic("uvmunmap: walk");
|
||||||
|
if((*pte & PTE_V) == 0)
|
||||||
|
panic("uvmunmap: not mapped");
|
||||||
|
if(PTE_FLAGS(*pte) == PTE_V)
|
||||||
|
panic("uvmunmap: not a leaf");
|
||||||
|
if(do_free){
|
||||||
|
uint64 pa = PTE2PA(*pte);
|
||||||
|
kfree((void*)pa);
|
||||||
|
}
|
||||||
|
*pte = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an empty user page table.
|
||||||
|
// returns 0 if out of memory.
|
||||||
|
pagetable_t
|
||||||
|
uvmcreate()
|
||||||
|
{
|
||||||
|
pagetable_t pagetable;
|
||||||
|
pagetable = (pagetable_t) kalloc();
|
||||||
|
if(pagetable == 0)
|
||||||
|
return 0;
|
||||||
|
memset(pagetable, 0, PGSIZE);
|
||||||
|
return pagetable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the user initcode into address 0 of pagetable,
|
||||||
|
// for the very first process.
|
||||||
|
// sz must be less than a page.
|
||||||
|
void
|
||||||
|
uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
|
||||||
|
{
|
||||||
|
char *mem;
|
||||||
|
|
||||||
|
if(sz >= PGSIZE)
|
||||||
|
panic("uvmfirst: more than a page");
|
||||||
|
mem = kalloc();
|
||||||
|
memset(mem, 0, PGSIZE);
|
||||||
|
mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
|
||||||
|
memmove(mem, src, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate PTEs and physical memory to grow process from oldsz to
|
||||||
|
// newsz, which need not be page aligned. Returns new size or 0 on error.
|
||||||
|
uint64
|
||||||
|
uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
|
||||||
|
{
|
||||||
|
char *mem;
|
||||||
|
uint64 a;
|
||||||
|
|
||||||
|
if(newsz < oldsz)
|
||||||
|
return oldsz;
|
||||||
|
|
||||||
|
oldsz = PGROUNDUP(oldsz);
|
||||||
|
for(a = oldsz; a < newsz; a += PGSIZE){
|
||||||
|
mem = kalloc();
|
||||||
|
if(mem == 0){
|
||||||
|
uvmdealloc(pagetable, a, oldsz);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset(mem, 0, PGSIZE);
|
||||||
|
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
|
||||||
|
kfree(mem);
|
||||||
|
uvmdealloc(pagetable, a, oldsz);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate user pages to bring the process size from oldsz to
|
||||||
|
// newsz. oldsz and newsz need not be page-aligned, nor does newsz
|
||||||
|
// need to be less than oldsz. oldsz can be larger than the actual
|
||||||
|
// process size. Returns the new process size.
|
||||||
|
uint64
|
||||||
|
uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
|
||||||
|
{
|
||||||
|
if(newsz >= oldsz)
|
||||||
|
return oldsz;
|
||||||
|
|
||||||
|
if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
|
||||||
|
int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
|
||||||
|
uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively free page-table pages.
|
||||||
|
// All leaf mappings must already have been removed.
|
||||||
|
void
|
||||||
|
freewalk(pagetable_t pagetable)
|
||||||
|
{
|
||||||
|
// there are 2^9 = 512 PTEs in a page table.
|
||||||
|
for(int i = 0; i < 512; i++){
|
||||||
|
pte_t pte = pagetable[i];
|
||||||
|
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
|
||||||
|
// this PTE points to a lower-level page table.
|
||||||
|
uint64 child = PTE2PA(pte);
|
||||||
|
freewalk((pagetable_t)child);
|
||||||
|
pagetable[i] = 0;
|
||||||
|
} else if(pte & PTE_V){
|
||||||
|
panic("freewalk: leaf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kfree((void*)pagetable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free user memory pages,
|
||||||
|
// then free page-table pages.
|
||||||
|
void
|
||||||
|
uvmfree(pagetable_t pagetable, uint64 sz)
|
||||||
|
{
|
||||||
|
if(sz > 0)
|
||||||
|
uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
|
||||||
|
freewalk(pagetable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a parent process's page table, copy
|
||||||
|
// its memory into a child's page table.
|
||||||
|
// Copies both the page table and the
|
||||||
|
// physical memory.
|
||||||
|
// returns 0 on success, -1 on failure.
|
||||||
|
// frees any allocated pages on failure.
|
||||||
|
int
|
||||||
|
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
||||||
|
{
|
||||||
|
pte_t *pte;
|
||||||
|
uint64 pa, i;
|
||||||
|
uint flags;
|
||||||
|
char *mem;
|
||||||
|
|
||||||
|
for(i = 0; i < sz; i += PGSIZE){
|
||||||
|
if((pte = walk(old, i, 0)) == 0)
|
||||||
|
panic("uvmcopy: pte should exist");
|
||||||
|
if((*pte & PTE_V) == 0)
|
||||||
|
panic("uvmcopy: page not present");
|
||||||
|
pa = PTE2PA(*pte);
|
||||||
|
flags = PTE_FLAGS(*pte);
|
||||||
|
if((mem = kalloc()) == 0)
|
||||||
|
goto err;
|
||||||
|
memmove(mem, (char*)pa, PGSIZE);
|
||||||
|
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
|
||||||
|
kfree(mem);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
uvmunmap(new, 0, i / PGSIZE, 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark a PTE invalid for user access.
|
||||||
|
// used by exec for the user stack guard page.
|
||||||
|
void
|
||||||
|
uvmclear(pagetable_t pagetable, uint64 va)
|
||||||
|
{
|
||||||
|
pte_t *pte;
|
||||||
|
|
||||||
|
pte = walk(pagetable, va, 0);
|
||||||
|
if(pte == 0)
|
||||||
|
panic("uvmclear");
|
||||||
|
*pte &= ~PTE_U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from kernel to user.
|
||||||
|
// Copy len bytes from src to virtual address dstva in a given page table.
|
||||||
|
// Return 0 on success, -1 on error.
|
||||||
|
int
|
||||||
|
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
||||||
|
{
|
||||||
|
uint64 n, va0, pa0;
|
||||||
|
pte_t *pte;
|
||||||
|
|
||||||
|
while(len > 0){
|
||||||
|
va0 = PGROUNDDOWN(dstva);
|
||||||
|
if(va0 >= MAXVA)
|
||||||
|
return -1;
|
||||||
|
pte = walk(pagetable, va0, 0);
|
||||||
|
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
|
||||||
|
(*pte & PTE_W) == 0)
|
||||||
|
return -1;
|
||||||
|
pa0 = PTE2PA(*pte);
|
||||||
|
n = PGSIZE - (dstva - va0);
|
||||||
|
if(n > len)
|
||||||
|
n = len;
|
||||||
|
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
||||||
|
|
||||||
|
len -= n;
|
||||||
|
src += n;
|
||||||
|
dstva = va0 + PGSIZE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy from user to kernel.
|
||||||
|
// Copy len bytes to dst from virtual address srcva in a given page table.
|
||||||
|
// Return 0 on success, -1 on error.
|
||||||
|
int
|
||||||
|
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
||||||
|
{
|
||||||
|
uint64 n, va0, pa0;
|
||||||
|
|
||||||
|
while(len > 0){
|
||||||
|
va0 = PGROUNDDOWN(srcva);
|
||||||
|
pa0 = walkaddr(pagetable, va0);
|
||||||
|
if(pa0 == 0)
|
||||||
|
return -1;
|
||||||
|
n = PGSIZE - (srcva - va0);
|
||||||
|
if(n > len)
|
||||||
|
n = len;
|
||||||
|
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
|
||||||
|
|
||||||
|
len -= n;
|
||||||
|
dst += n;
|
||||||
|
srcva = va0 + PGSIZE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a null-terminated string from user to kernel.
|
||||||
|
// Copy bytes to dst from virtual address srcva in a given page table,
|
||||||
|
// until a '\0', or max.
|
||||||
|
// Return 0 on success, -1 on error.
|
||||||
|
int
|
||||||
|
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
||||||
|
{
|
||||||
|
uint64 n, va0, pa0;
|
||||||
|
int got_null = 0;
|
||||||
|
|
||||||
|
while(got_null == 0 && max > 0){
|
||||||
|
va0 = PGROUNDDOWN(srcva);
|
||||||
|
pa0 = walkaddr(pagetable, va0);
|
||||||
|
if(pa0 == 0)
|
||||||
|
return -1;
|
||||||
|
n = PGSIZE - (srcva - va0);
|
||||||
|
if(n > max)
|
||||||
|
n = max;
|
||||||
|
|
||||||
|
char *p = (char *) (pa0 + (srcva - va0));
|
||||||
|
while(n > 0){
|
||||||
|
if(*p == '\0'){
|
||||||
|
*dst = '\0';
|
||||||
|
got_null = 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
*dst = *p;
|
||||||
|
}
|
||||||
|
--n;
|
||||||
|
--max;
|
||||||
|
p++;
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
|
||||||
|
srcva = va0 + PGSIZE;
|
||||||
|
}
|
||||||
|
if(got_null){
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
user/cat.c
Normal file
44
user/cat.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
#include "user/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) {
|
||||||
|
fprintf(2, "cat: write error\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(n < 0){
|
||||||
|
fprintf(2, "cat: read error\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
if(argc <= 1){
|
||||||
|
cat(0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 1; i < argc; i++){
|
||||||
|
if((fd = open(argv[i], O_RDONLY)) < 0){
|
||||||
|
fprintf(2, "cat: cannot open %s\n", argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
cat(fd);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
19
user/echo.c
Normal file
19
user/echo.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 1; i < argc; i++){
|
||||||
|
write(1, argv[i], strlen(argv[i]));
|
||||||
|
if(i + 1 < argc){
|
||||||
|
write(1, " ", 1);
|
||||||
|
} else {
|
||||||
|
write(1, "\n", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
56
user/forktest.c
Normal file
56
user/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 "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
#define N 1000
|
||||||
|
|
||||||
|
void
|
||||||
|
print(const char *s)
|
||||||
|
{
|
||||||
|
write(1, s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
forktest(void)
|
||||||
|
{
|
||||||
|
int n, pid;
|
||||||
|
|
||||||
|
print("fork test\n");
|
||||||
|
|
||||||
|
for(n=0; n<N; n++){
|
||||||
|
pid = fork();
|
||||||
|
if(pid < 0)
|
||||||
|
break;
|
||||||
|
if(pid == 0)
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n == N){
|
||||||
|
print("fork claimed to work N times!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; n > 0; n--){
|
||||||
|
if(wait(0) < 0){
|
||||||
|
print("wait stopped early\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wait(0) != -1){
|
||||||
|
print("wait got too many\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("fork test OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
forktest();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
107
user/grep.c
Normal file
107
user/grep.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Simple grep. Only supports ^ . * $ operators.
|
||||||
|
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
#include "user/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(m > 0){
|
||||||
|
m -= p - buf;
|
||||||
|
memmove(buf, p, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
char *pattern;
|
||||||
|
|
||||||
|
if(argc <= 1){
|
||||||
|
fprintf(2, "usage: grep pattern [file ...]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
pattern = argv[1];
|
||||||
|
|
||||||
|
if(argc <= 2){
|
||||||
|
grep(pattern, 0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 2; i < argc; i++){
|
||||||
|
if((fd = open(argv[i], O_RDONLY)) < 0){
|
||||||
|
printf("grep: cannot open %s\n", argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
grep(pattern, fd);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regexp matcher from Kernighan & Pike,
|
||||||
|
// The Practice of Programming, Chapter 9, or
|
||||||
|
// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
351
user/grind.c
Normal file
351
user/grind.c
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
//
|
||||||
|
// run random system calls in parallel forever.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "kernel/param.h"
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/fs.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
#include "kernel/syscall.h"
|
||||||
|
#include "kernel/memlayout.h"
|
||||||
|
#include "kernel/riscv.h"
|
||||||
|
|
||||||
|
// from FreeBSD.
|
||||||
|
int
|
||||||
|
do_rand(unsigned long *ctx)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Compute x = (7^5 * x) mod (2^31 - 1)
|
||||||
|
* without overflowing 31 bits:
|
||||||
|
* (2^31 - 1) = 127773 * (7^5) + 2836
|
||||||
|
* From "Random number generators: good ones are hard to find",
|
||||||
|
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
|
||||||
|
* October 1988, p. 1195.
|
||||||
|
*/
|
||||||
|
long hi, lo, x;
|
||||||
|
|
||||||
|
/* Transform to [1, 0x7ffffffe] range. */
|
||||||
|
x = (*ctx % 0x7ffffffe) + 1;
|
||||||
|
hi = x / 127773;
|
||||||
|
lo = x % 127773;
|
||||||
|
x = 16807 * lo - 2836 * hi;
|
||||||
|
if (x < 0)
|
||||||
|
x += 0x7fffffff;
|
||||||
|
/* Transform to [0, 0x7ffffffd] range. */
|
||||||
|
x--;
|
||||||
|
*ctx = x;
|
||||||
|
return (x);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long rand_next = 1;
|
||||||
|
|
||||||
|
int
|
||||||
|
rand(void)
|
||||||
|
{
|
||||||
|
return (do_rand(&rand_next));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
go(int which_child)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
static char buf[999];
|
||||||
|
char *break0 = sbrk(0);
|
||||||
|
uint64 iters = 0;
|
||||||
|
|
||||||
|
mkdir("grindir");
|
||||||
|
if(chdir("grindir") != 0){
|
||||||
|
printf("grind: chdir grindir failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
chdir("/");
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
iters++;
|
||||||
|
if((iters % 500) == 0)
|
||||||
|
write(1, which_child?"B":"A", 1);
|
||||||
|
int what = rand() % 23;
|
||||||
|
if(what == 1){
|
||||||
|
close(open("grindir/../a", O_CREATE|O_RDWR));
|
||||||
|
} else if(what == 2){
|
||||||
|
close(open("grindir/../grindir/../b", O_CREATE|O_RDWR));
|
||||||
|
} else if(what == 3){
|
||||||
|
unlink("grindir/../a");
|
||||||
|
} else if(what == 4){
|
||||||
|
if(chdir("grindir") != 0){
|
||||||
|
printf("grind: chdir grindir failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
unlink("../b");
|
||||||
|
chdir("/");
|
||||||
|
} else if(what == 5){
|
||||||
|
close(fd);
|
||||||
|
fd = open("/grindir/../a", O_CREATE|O_RDWR);
|
||||||
|
} else if(what == 6){
|
||||||
|
close(fd);
|
||||||
|
fd = open("/./grindir/./../b", O_CREATE|O_RDWR);
|
||||||
|
} else if(what == 7){
|
||||||
|
write(fd, buf, sizeof(buf));
|
||||||
|
} else if(what == 8){
|
||||||
|
read(fd, buf, sizeof(buf));
|
||||||
|
} else if(what == 9){
|
||||||
|
mkdir("grindir/../a");
|
||||||
|
close(open("a/../a/./a", O_CREATE|O_RDWR));
|
||||||
|
unlink("a/a");
|
||||||
|
} else if(what == 10){
|
||||||
|
mkdir("/../b");
|
||||||
|
close(open("grindir/../b/b", O_CREATE|O_RDWR));
|
||||||
|
unlink("b/b");
|
||||||
|
} else if(what == 11){
|
||||||
|
unlink("b");
|
||||||
|
link("../grindir/./../a", "../b");
|
||||||
|
} else if(what == 12){
|
||||||
|
unlink("../grindir/../a");
|
||||||
|
link(".././b", "/grindir/../a");
|
||||||
|
} else if(what == 13){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 14){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
fork();
|
||||||
|
fork();
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 15){
|
||||||
|
sbrk(6011);
|
||||||
|
} else if(what == 16){
|
||||||
|
if(sbrk(0) > break0)
|
||||||
|
sbrk(-(sbrk(0) - break0));
|
||||||
|
} else if(what == 17){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
close(open("a", O_CREATE|O_RDWR));
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(chdir("../grindir/..") != 0){
|
||||||
|
printf("grind: chdir failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
kill(pid);
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 18){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
kill(getpid());
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 19){
|
||||||
|
int fds[2];
|
||||||
|
if(pipe(fds) < 0){
|
||||||
|
printf("grind: pipe failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
fork();
|
||||||
|
fork();
|
||||||
|
if(write(fds[1], "x", 1) != 1)
|
||||||
|
printf("grind: pipe write failed\n");
|
||||||
|
char c;
|
||||||
|
if(read(fds[0], &c, 1) != 1)
|
||||||
|
printf("grind: pipe read failed\n");
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
close(fds[0]);
|
||||||
|
close(fds[1]);
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 20){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
unlink("a");
|
||||||
|
mkdir("a");
|
||||||
|
chdir("a");
|
||||||
|
unlink("../a");
|
||||||
|
fd = open("x", O_CREATE|O_RDWR);
|
||||||
|
unlink("x");
|
||||||
|
exit(0);
|
||||||
|
} else if(pid < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
wait(0);
|
||||||
|
} else if(what == 21){
|
||||||
|
unlink("c");
|
||||||
|
// should always succeed. check that there are free i-nodes,
|
||||||
|
// file descriptors, blocks.
|
||||||
|
int fd1 = open("c", O_CREATE|O_RDWR);
|
||||||
|
if(fd1 < 0){
|
||||||
|
printf("grind: create c failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(write(fd1, "x", 1) != 1){
|
||||||
|
printf("grind: write c failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
struct stat st;
|
||||||
|
if(fstat(fd1, &st) != 0){
|
||||||
|
printf("grind: fstat failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(st.size != 1){
|
||||||
|
printf("grind: fstat reports wrong size %d\n", (int)st.size);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(st.ino > 200){
|
||||||
|
printf("grind: fstat reports crazy i-number %d\n", st.ino);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
close(fd1);
|
||||||
|
unlink("c");
|
||||||
|
} else if(what == 22){
|
||||||
|
// echo hi | cat
|
||||||
|
int aa[2], bb[2];
|
||||||
|
if(pipe(aa) < 0){
|
||||||
|
fprintf(2, "grind: pipe failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(pipe(bb) < 0){
|
||||||
|
fprintf(2, "grind: pipe failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int pid1 = fork();
|
||||||
|
if(pid1 == 0){
|
||||||
|
close(bb[0]);
|
||||||
|
close(bb[1]);
|
||||||
|
close(aa[0]);
|
||||||
|
close(1);
|
||||||
|
if(dup(aa[1]) != 1){
|
||||||
|
fprintf(2, "grind: dup failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
close(aa[1]);
|
||||||
|
char *args[3] = { "echo", "hi", 0 };
|
||||||
|
exec("grindir/../echo", args);
|
||||||
|
fprintf(2, "grind: echo: not found\n");
|
||||||
|
exit(2);
|
||||||
|
} else if(pid1 < 0){
|
||||||
|
fprintf(2, "grind: fork failed\n");
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
int pid2 = fork();
|
||||||
|
if(pid2 == 0){
|
||||||
|
close(aa[1]);
|
||||||
|
close(bb[0]);
|
||||||
|
close(0);
|
||||||
|
if(dup(aa[0]) != 0){
|
||||||
|
fprintf(2, "grind: dup failed\n");
|
||||||
|
exit(4);
|
||||||
|
}
|
||||||
|
close(aa[0]);
|
||||||
|
close(1);
|
||||||
|
if(dup(bb[1]) != 1){
|
||||||
|
fprintf(2, "grind: dup failed\n");
|
||||||
|
exit(5);
|
||||||
|
}
|
||||||
|
close(bb[1]);
|
||||||
|
char *args[2] = { "cat", 0 };
|
||||||
|
exec("/cat", args);
|
||||||
|
fprintf(2, "grind: cat: not found\n");
|
||||||
|
exit(6);
|
||||||
|
} else if(pid2 < 0){
|
||||||
|
fprintf(2, "grind: fork failed\n");
|
||||||
|
exit(7);
|
||||||
|
}
|
||||||
|
close(aa[0]);
|
||||||
|
close(aa[1]);
|
||||||
|
close(bb[1]);
|
||||||
|
char buf[4] = { 0, 0, 0, 0 };
|
||||||
|
read(bb[0], buf+0, 1);
|
||||||
|
read(bb[0], buf+1, 1);
|
||||||
|
read(bb[0], buf+2, 1);
|
||||||
|
close(bb[0]);
|
||||||
|
int st1, st2;
|
||||||
|
wait(&st1);
|
||||||
|
wait(&st2);
|
||||||
|
if(st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0){
|
||||||
|
printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
iter()
|
||||||
|
{
|
||||||
|
unlink("a");
|
||||||
|
unlink("b");
|
||||||
|
|
||||||
|
int pid1 = fork();
|
||||||
|
if(pid1 < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(pid1 == 0){
|
||||||
|
rand_next ^= 31;
|
||||||
|
go(0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pid2 = fork();
|
||||||
|
if(pid2 < 0){
|
||||||
|
printf("grind: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(pid2 == 0){
|
||||||
|
rand_next ^= 7177;
|
||||||
|
go(1);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int st1 = -1;
|
||||||
|
wait(&st1);
|
||||||
|
if(st1 != 0){
|
||||||
|
kill(pid1);
|
||||||
|
kill(pid2);
|
||||||
|
}
|
||||||
|
int st2 = -1;
|
||||||
|
wait(&st2);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0){
|
||||||
|
iter();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if(pid > 0){
|
||||||
|
wait(0);
|
||||||
|
}
|
||||||
|
sleep(20);
|
||||||
|
rand_next += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
user/init.c
Normal file
54
user/init.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// init: The initial user-level program
|
||||||
|
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/spinlock.h"
|
||||||
|
#include "kernel/sleeplock.h"
|
||||||
|
#include "kernel/fs.h"
|
||||||
|
#include "kernel/file.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
|
||||||
|
char *argv[] = { "sh", 0 };
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
int pid, wpid;
|
||||||
|
|
||||||
|
if(open("console", O_RDWR) < 0){
|
||||||
|
mknod("console", CONSOLE, 0);
|
||||||
|
open("console", O_RDWR);
|
||||||
|
}
|
||||||
|
dup(0); // stdout
|
||||||
|
dup(0); // stderr
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
printf("init: starting sh\n");
|
||||||
|
pid = fork();
|
||||||
|
if(pid < 0){
|
||||||
|
printf("init: fork failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(pid == 0){
|
||||||
|
exec("sh", argv);
|
||||||
|
printf("init: exec sh failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
// this call to wait() returns if the shell exits,
|
||||||
|
// or if a parentless process exits.
|
||||||
|
wpid = wait((int *) 0);
|
||||||
|
if(wpid == pid){
|
||||||
|
// the shell exited; restart it.
|
||||||
|
break;
|
||||||
|
} else if(wpid < 0){
|
||||||
|
printf("init: wait returned an error\n");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
// it was a parentless process; do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
user/initcode.S
Normal file
28
user/initcode.S
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Initial process that execs /init.
|
||||||
|
# This code runs in user space.
|
||||||
|
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
# exec(init, argv)
|
||||||
|
.globl start
|
||||||
|
start:
|
||||||
|
la a0, init
|
||||||
|
la a1, argv
|
||||||
|
li a7, SYS_exec
|
||||||
|
ecall
|
||||||
|
|
||||||
|
# for(;;) exit();
|
||||||
|
exit:
|
||||||
|
li a7, SYS_exit
|
||||||
|
ecall
|
||||||
|
jal exit
|
||||||
|
|
||||||
|
# char init[] = "/init\0";
|
||||||
|
init:
|
||||||
|
.string "/init\0"
|
||||||
|
|
||||||
|
# char *argv[] = { init, 0 };
|
||||||
|
.p2align 2
|
||||||
|
argv:
|
||||||
|
.long init
|
||||||
|
.long 0
|
||||||
17
user/kill.c
Normal file
17
user/kill.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(argc < 2){
|
||||||
|
fprintf(2, "usage: kill pid...\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for(i=1; i<argc; i++)
|
||||||
|
kill(atoi(argv[i]));
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
15
user/ln.c
Normal file
15
user/ln.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if(argc != 3){
|
||||||
|
fprintf(2, "Usage: ln old new\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if(link(argv[1], argv[2]) < 0)
|
||||||
|
fprintf(2, "link %s %s: failed\n", argv[1], argv[2]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
87
user/ls.c
Normal file
87
user/ls.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/fs.h"
|
||||||
|
#include "kernel/fcntl.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, O_RDONLY)) < 0){
|
||||||
|
fprintf(2, "ls: cannot open %s\n", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fstat(fd, &st) < 0){
|
||||||
|
fprintf(2, "ls: cannot stat %s\n", path);
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(st.type){
|
||||||
|
case T_DEVICE:
|
||||||
|
case T_FILE:
|
||||||
|
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_DIR:
|
||||||
|
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
|
||||||
|
printf("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("ls: cannot stat %s\n", buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("%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(0);
|
||||||
|
}
|
||||||
|
for(i=1; i<argc; i++)
|
||||||
|
ls(argv[i]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
23
user/mkdir.c
Normal file
23
user/mkdir.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(argc < 2){
|
||||||
|
fprintf(2, "Usage: mkdir files...\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 1; i < argc; i++){
|
||||||
|
if(mkdir(argv[i]) < 0){
|
||||||
|
fprintf(2, "mkdir: %s failed to create\n", argv[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
113
user/printf.c
Normal file
113
user/printf.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
static char digits[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
static void
|
||||||
|
putc(int fd, char c)
|
||||||
|
{
|
||||||
|
write(fd, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printint(int fd, int xx, int base, int sgn)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printptr(int fd, uint64 x) {
|
||||||
|
int i;
|
||||||
|
putc(fd, '0');
|
||||||
|
putc(fd, 'x');
|
||||||
|
for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
|
||||||
|
putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print to the given fd. Only understands %d, %x, %p, %s.
|
||||||
|
void
|
||||||
|
vprintf(int fd, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
int c, i, state;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
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, va_arg(ap, int), 10, 1);
|
||||||
|
} else if(c == 'l') {
|
||||||
|
printint(fd, va_arg(ap, uint64), 10, 0);
|
||||||
|
} else if(c == 'x') {
|
||||||
|
printint(fd, va_arg(ap, int), 16, 0);
|
||||||
|
} else if(c == 'p') {
|
||||||
|
printptr(fd, va_arg(ap, uint64));
|
||||||
|
} else if(c == 's'){
|
||||||
|
s = va_arg(ap, char*);
|
||||||
|
if(s == 0)
|
||||||
|
s = "(null)";
|
||||||
|
while(*s != 0){
|
||||||
|
putc(fd, *s);
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
} else if(c == 'c'){
|
||||||
|
putc(fd, va_arg(ap, uint));
|
||||||
|
} else if(c == '%'){
|
||||||
|
putc(fd, c);
|
||||||
|
} else {
|
||||||
|
// Unknown % sequence. Print it to draw attention.
|
||||||
|
putc(fd, '%');
|
||||||
|
putc(fd, c);
|
||||||
|
}
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fprintf(int fd, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vprintf(fd, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vprintf(1, fmt, ap);
|
||||||
|
}
|
||||||
23
user/rm.c
Normal file
23
user/rm.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(argc < 2){
|
||||||
|
fprintf(2, "Usage: rm files...\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 1; i < argc; i++){
|
||||||
|
if(unlink(argv[i]) < 0){
|
||||||
|
fprintf(2, "rm: %s failed to delete\n", argv[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
494
user/sh.c
Normal file
494
user/sh.c
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
// Shell.
|
||||||
|
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/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*);
|
||||||
|
void runcmd(struct cmd*) __attribute__((noreturn));
|
||||||
|
|
||||||
|
// 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(1);
|
||||||
|
|
||||||
|
switch(cmd->type){
|
||||||
|
default:
|
||||||
|
panic("runcmd");
|
||||||
|
|
||||||
|
case EXEC:
|
||||||
|
ecmd = (struct execcmd*)cmd;
|
||||||
|
if(ecmd->argv[0] == 0)
|
||||||
|
exit(1);
|
||||||
|
exec(ecmd->argv[0], ecmd->argv);
|
||||||
|
fprintf(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){
|
||||||
|
fprintf(2, "open %s failed\n", rcmd->file);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
runcmd(rcmd->cmd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LIST:
|
||||||
|
lcmd = (struct listcmd*)cmd;
|
||||||
|
if(fork1() == 0)
|
||||||
|
runcmd(lcmd->left);
|
||||||
|
wait(0);
|
||||||
|
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(0);
|
||||||
|
wait(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BACK:
|
||||||
|
bcmd = (struct backcmd*)cmd;
|
||||||
|
if(fork1() == 0)
|
||||||
|
runcmd(bcmd->cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getcmd(char *buf, int nbuf)
|
||||||
|
{
|
||||||
|
write(2, "$ ", 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)
|
||||||
|
fprintf(2, "cannot cd %s\n", buf+3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(fork1() == 0)
|
||||||
|
runcmd(parsecmd(buf));
|
||||||
|
wait(0);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
panic(char *s)
|
||||||
|
{
|
||||||
|
fprintf(2, "%s\n", s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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){
|
||||||
|
fprintf(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|O_TRUNC, 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;
|
||||||
|
}
|
||||||
0
user/sleep.c
Normal file
0
user/sleep.c
Normal file
49
user/stressfs.c
Normal file
49
user/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 "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/fs.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
char path[] = "stressfs0";
|
||||||
|
char data[512];
|
||||||
|
|
||||||
|
printf("stressfs starting\n");
|
||||||
|
memset(data, 'a', sizeof(data));
|
||||||
|
|
||||||
|
for(i = 0; i < 4; i++)
|
||||||
|
if(fork() > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
printf("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("read\n");
|
||||||
|
|
||||||
|
fd = open(path, O_RDONLY);
|
||||||
|
for (i = 0; i < 20; i++)
|
||||||
|
read(fd, data, sizeof(data));
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
wait(0);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
147
user/ulib.c
Normal file
147
user/ulib.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// wrapper so that it's OK if main() does not call exit().
|
||||||
|
//
|
||||||
|
void
|
||||||
|
_main()
|
||||||
|
{
|
||||||
|
extern int main();
|
||||||
|
main();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
char *cdst = (char *) dst;
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < n; i++){
|
||||||
|
cdst[i] = c;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
if (src > dst) {
|
||||||
|
while(n-- > 0)
|
||||||
|
*dst++ = *src++;
|
||||||
|
} else {
|
||||||
|
dst += n;
|
||||||
|
src += n;
|
||||||
|
while(n-- > 0)
|
||||||
|
*--dst = *--src;
|
||||||
|
}
|
||||||
|
return vdst;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
memcmp(const void *s1, const void *s2, uint n)
|
||||||
|
{
|
||||||
|
const char *p1 = s1, *p2 = s2;
|
||||||
|
while (n-- > 0) {
|
||||||
|
if (*p1 != *p2) {
|
||||||
|
return *p1 - *p2;
|
||||||
|
}
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
memcpy(void *dst, const void *src, uint n)
|
||||||
|
{
|
||||||
|
return memmove(dst, src, n);
|
||||||
|
}
|
||||||
90
user/umalloc.c
Normal file
90
user/umalloc.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
#include "kernel/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
user/user.h
Normal file
41
user/user.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
struct stat;
|
||||||
|
|
||||||
|
// system calls
|
||||||
|
int fork(void);
|
||||||
|
int exit(int) __attribute__((noreturn));
|
||||||
|
int wait(int*);
|
||||||
|
int pipe(int*);
|
||||||
|
int write(int, const void*, int);
|
||||||
|
int read(int, void*, int);
|
||||||
|
int close(int);
|
||||||
|
int kill(int);
|
||||||
|
int exec(const 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);
|
||||||
|
|
||||||
|
// 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 fprintf(int, const char*, ...);
|
||||||
|
void printf(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*);
|
||||||
|
int memcmp(const void *, const void *, uint);
|
||||||
|
void *memcpy(void *, const void *, uint);
|
||||||
36
user/user.ld
Normal file
36
user/user.ld
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
ENTRY( _main )
|
||||||
|
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x0;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.text .text.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.sdata .sdata.*) /* do not need to distinguish this from .data */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.data .data.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.sbss .sbss.*) /* do not need to distinguish this from .bss */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.bss .bss.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
PROVIDE(end = .);
|
||||||
|
}
|
||||||
3103
user/usertests.c
Normal file
3103
user/usertests.c
Normal file
File diff suppressed because it is too large
Load Diff
38
user/usys.pl
Executable file
38
user/usys.pl
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
# Generate usys.S, the stubs for syscalls.
|
||||||
|
|
||||||
|
print "# generated by usys.pl - do not edit\n";
|
||||||
|
|
||||||
|
print "#include \"kernel/syscall.h\"\n";
|
||||||
|
|
||||||
|
sub entry {
|
||||||
|
my $name = shift;
|
||||||
|
print ".global $name\n";
|
||||||
|
print "${name}:\n";
|
||||||
|
print " li a7, SYS_${name}\n";
|
||||||
|
print " ecall\n";
|
||||||
|
print " ret\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry("fork");
|
||||||
|
entry("exit");
|
||||||
|
entry("wait");
|
||||||
|
entry("pipe");
|
||||||
|
entry("read");
|
||||||
|
entry("write");
|
||||||
|
entry("close");
|
||||||
|
entry("kill");
|
||||||
|
entry("exec");
|
||||||
|
entry("open");
|
||||||
|
entry("mknod");
|
||||||
|
entry("unlink");
|
||||||
|
entry("fstat");
|
||||||
|
entry("link");
|
||||||
|
entry("mkdir");
|
||||||
|
entry("chdir");
|
||||||
|
entry("dup");
|
||||||
|
entry("getpid");
|
||||||
|
entry("sbrk");
|
||||||
|
entry("sleep");
|
||||||
|
entry("uptime");
|
||||||
55
user/wc.c
Normal file
55
user/wc.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/fcntl.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
void
|
||||||
|
wc(int fd, char *name)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
int l, w, c, inword;
|
||||||
|
|
||||||
|
l = w = c = 0;
|
||||||
|
inword = 0;
|
||||||
|
while((n = read(fd, buf, sizeof(buf))) > 0){
|
||||||
|
for(i=0; i<n; i++){
|
||||||
|
c++;
|
||||||
|
if(buf[i] == '\n')
|
||||||
|
l++;
|
||||||
|
if(strchr(" \r\t\n\v", buf[i]))
|
||||||
|
inword = 0;
|
||||||
|
else if(!inword){
|
||||||
|
w++;
|
||||||
|
inword = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(n < 0){
|
||||||
|
printf("wc: read error\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
printf("%d %d %d %s\n", l, w, c, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
if(argc <= 1){
|
||||||
|
wc(0, "");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 1; i < argc; i++){
|
||||||
|
if((fd = open(argv[i], O_RDONLY)) < 0){
|
||||||
|
printf("wc: cannot open %s\n", argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
wc(fd, argv[i]);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
6
user/xargstest.sh
Normal file
6
user/xargstest.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mkdir a
|
||||||
|
echo hello > a/b
|
||||||
|
mkdir c
|
||||||
|
echo hello > c/b
|
||||||
|
echo hello > b
|
||||||
|
find . b | xargs grep hello
|
||||||
14
user/zombie.c
Normal file
14
user/zombie.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Create a zombie process that
|
||||||
|
// must be reparented at exit.
|
||||||
|
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
if(fork() > 0)
|
||||||
|
sleep(5); // Let child exit before parent.
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user