Compare commits

...

5 Commits
util ... net

Author SHA1 Message Date
ridethepig
dc0f3b33f8 make grade 100/100 2023-02-02 13:17:29 +00:00
Frans Kaashoek
7106151533 Lab net 2022-10-24 07:04:45 -04:00
Frans Kaashoek
4b46c0c6eb Use O_RDONLY instead of 0 2022-09-11 13:51:11 -04:00
Robert Morris
463ae0abc3 handle negative arguments to sleep() a little better 2022-09-09 09:17:37 -04:00
Frans Kaashoek
f5b93ef12f Update acks
61810
2022-08-25 14:20:52 -04:00
37 changed files with 2716 additions and 41 deletions

9
.gitignore vendored
View File

@ -15,3 +15,12 @@ mkfs
kernel/kernel kernel/kernel
user/usys.S user/usys.S
.gdbinit .gdbinit
myapi.key
*-handin.tar.gz
xv6.out*
.vagrant/
submissions/
ph
barrier
/lab-*.json
.DS_Store

259
Makefile
View File

@ -1,14 +1,16 @@
# 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 K=kernel
U=user U=user
OBJS = \ OBJS = \
$K/entry.o \ $K/entry.o \
$K/start.o \
$K/console.o \
$K/printf.o \
$K/uart.o \
$K/kalloc.o \ $K/kalloc.o \
$K/spinlock.o \
$K/string.o \ $K/string.o \
$K/main.o \ $K/main.o \
$K/vm.o \ $K/vm.o \
@ -30,6 +32,34 @@ OBJS = \
$K/plic.o \ $K/plic.o \
$K/virtio_disk.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- # riscv64-unknown-elf- or riscv64-linux-gnu-
# perhaps in /opt/riscv/bin # perhaps in /opt/riscv/bin
#TOOLPREFIX = #TOOLPREFIX =
@ -57,12 +87,28 @@ OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 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 += -MD
CFLAGS += -mcmodel=medany CFLAGS += -mcmodel=medany
CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax
CFLAGS += -I. CFLAGS += -I.
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ifeq ($(LAB),net)
CFLAGS += -DNET_TESTS_PORT=$(SERVERPORT)
endif
ifdef KCSAN
CFLAGS += -DKCSAN
KCSANFLAG = -fsanitize=thread
endif
# Disable PIE when possible (for Ubuntu 16.10 toolchain) # Disable PIE when possible (for Ubuntu 16.10 toolchain)
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
CFLAGS += -fno-pie -no-pie CFLAGS += -fno-pie -no-pie
@ -73,11 +119,17 @@ endif
LDFLAGS = -z max-page-size=4096 LDFLAGS = -z max-page-size=4096
$K/kernel: $(OBJS) $K/kernel.ld $U/initcode $K/kernel: $(OBJS) $(OBJS_KCSAN) $K/kernel.ld $U/initcode
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJS_KCSAN)
$(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -S $K/kernel > $K/kernel.asm
$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym $(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 $U/initcode: $U/initcode.S
$(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o $(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 $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o
@ -89,6 +141,10 @@ tags: $(OBJS) _init
ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o 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) _%: %.o $(ULIB)
$(LD) $(LDFLAGS) -T $U/user.ld -o $@ $^ $(LD) $(LDFLAGS) -T $U/user.ld -o $@ $^
$(OBJDUMP) -S $@ > $*.asm $(OBJDUMP) -S $@ > $*.asm
@ -107,7 +163,7 @@ $U/_forktest: $U/forktest.o $(ULIB)
$(OBJDUMP) -S $U/_forktest > $U/forktest.asm $(OBJDUMP) -S $U/_forktest > $U/forktest.asm
mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h
gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c gcc $(XCFLAGS) -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so # 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 # that disk image changes after first build are persistent until clean. More
@ -133,8 +189,79 @@ UPROGS=\
$U/_wc\ $U/_wc\
$U/_zombie\ $U/_zombie\
fs.img: mkfs/mkfs README $(UPROGS)
mkfs/mkfs fs.img README $(UPROGS)
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 -include kernel/*.d user/*.d
@ -144,7 +271,8 @@ clean:
$U/initcode $U/initcode.out $K/kernel fs.img \ $U/initcode $U/initcode.out $K/kernel fs.img \
mkfs/mkfs .gdbinit \ mkfs/mkfs .gdbinit \
$U/usys.S \ $U/usys.S \
$(UPROGS) $(UPROGS) \
ph barrier
# try to generate a unique GDB port # try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000) GDBPORT = $(shell expr `id -u` % 5000 + 25000)
@ -155,12 +283,22 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
ifndef CPUS ifndef CPUS
CPUS := 3 CPUS := 3
endif 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 = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic
QEMUOPTS += -global virtio-mmio.force-legacy=false QEMUOPTS += -global virtio-mmio.force-legacy=false
QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 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: $K/kernel fs.img
$(QEMU) $(QEMUOPTS) $(QEMU) $(QEMUOPTS)
@ -171,3 +309,102 @@ qemu-gdb: $K/kernel .gdbinit fs.img
@echo "*** Now run 'gdb' in another window." 1>&2 @echo "*** Now run 'gdb' in another window." 1>&2
$(QEMU) $(QEMUOPTS) -S $(QEMUGDB) $(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 web handin
##
WEBSUB := https://6828.scripts.mit.edu/2022/handin.py
handin: tarball-pref myapi.key
@SUF=$(LAB); \
curl -f -F file=@lab-$$SUF-handin.tar.gz -F key=\<myapi.key $(WEBSUB)/upload \
> /dev/null || { \
echo ; \
echo Submit seems to have failed.; \
echo Please go to $(WEBSUB)/ and upload the tarball manually.; }
handin-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
UPSTREAM := $(shell git remote -v | grep -m 1 "xv6-labs-2022" | awk '{split($$0,a," "); print a[1]}')
tarball: handin-check
git archive --format=tar HEAD | gzip > lab-$(LAB)-handin.tar.gz
tarball-pref: handin-check
@SUF=$(LAB); \
git archive --format=tar HEAD > lab-$$SUF-handin.tar; \
git diff $(UPSTREAM)/$(LAB) > /tmp/lab-$$SUF-diff.patch; \
tar -rf lab-$$SUF-handin.tar /tmp/lab-$$SUF-diff.patch; \
gzip -c lab-$$SUF-handin.tar > lab-$$SUF-handin.tar.gz; \
rm lab-$$SUF-handin.tar; \
rm /tmp/lab-$$SUF-diff.patch; \
myapi.key:
@echo Get an API key for yourself by visiting $(WEBSUB)/
@read -p "Please enter your API key: " k; \
if test `echo "$$k" |tr -d '\n' |wc -c` = 32 ; then \
TF=`mktemp -t tmp.XXXXXX`; \
if test "x$$TF" != "x" ; then \
echo "$$k" |tr -d '\n' > $$TF; \
mv -f $$TF $@; \
else \
echo mktemp failed; \
false; \
fi; \
else \
echo Bad API key: $$k; \
echo An API key should be 32 characters long.; \
false; \
fi;
.PHONY: handin tarball tarball-pref clean grade handin-check

36
README
View File

@ -6,7 +6,7 @@ ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer 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, to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.828/, which provides 2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides
pointers to on-line resources for v6. pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching, The following people have made contributions: Russ Cox (context switching,
@ -14,29 +14,31 @@ locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements. Clements.
We are also grateful for the bug reports and patches contributed by We are also grateful for the bug reports and patches contributed by
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, Ian Chen, Dan Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian
Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi, eyalz800, Nelson Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,
Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, flespark, eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel
Peter Froehlich, Yakir Goaron, Shivam Handa, Matt Harvey, Bryan Henry, Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt
jaichenhengjie, Jim Huang, Matúš Jókay, Alexander Kapshuk, Anders Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John
Kaseorg, kehao95, Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt, Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller,
Eddie Kohler, Vadim Kolontsov, Austin Liew, l0stman, Pavan Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin
Maddamsetti, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan
Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider, Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel
OptimisticSide, Greg Price, Jude Rich, Ayan Shafqat, Eldar Sehayek, Nider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude
Yongming Shen, Fumiya Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Rich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya
Stephen Tu, Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, Keiichi Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal,
Watanabe, Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe,
Zheng, ZhUyU1997, and Zou Chang Wei. Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng,
ZhUyU1997, and Zou Chang Wei.
The code in the files that constitute xv6 is The code in the files that constitute xv6 is
Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox. Copyright 2006-2022 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching (kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.S081, so we are more interested in operating system for MIT's 6.1810, so we are more interested in
simplifications and clarifications than new features. simplifications and clarifications than new features.
BUILDING AND RUNNING XV6 BUILDING AND RUNNING XV6

1
conf/lab.mk Normal file
View File

@ -0,0 +1 @@
LAB=net

43
grade-lab-net Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
import re
import subprocess
from gradelib import *
r = Runner(save("xv6.out"))
@test(0, "running nettests")
def test_nettest():
server = subprocess.Popen(["make", "server"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
r.run_qemu(shell_script([
'nettests'
]), timeout=30)
server.terminate()
server.communicate()
@test(40, "nettest: ping", parent=test_nettest)
def test_nettest_():
r.match('^testing ping: OK$')
@test(20, "nettest: single process", parent=test_nettest)
def test_nettest_():
r.match('^testing single-process pings: OK$')
@test(20, "nettest: multi-process", parent=test_nettest)
def test_nettest_fork_test():
r.match('^testing multi-process pings: OK$')
@test(19, "nettest: DNS", parent=test_nettest)
def test_nettest_dns_test():
r.match('^DNS OK$')
#@test(10, "answers-net.txt")
#def test_answers():
# # just a simple sanity check, will be graded manually
# check_answers("answers-net.txt")
@test(1, "time")
def test_time():
check_time()
run_tests()

611
gradelib.py Normal file
View File

@ -0,0 +1,611 @@
from __future__ import print_function
import sys, os, re, time, socket, select, subprocess, errno, shutil, random, string
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
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
# 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
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 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")
(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:
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:
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

View File

@ -8,6 +8,10 @@ struct spinlock;
struct sleeplock; struct sleeplock;
struct stat; struct stat;
struct superblock; struct superblock;
#ifdef LAB_NET
struct mbuf;
struct sock;
#endif
// bio.c // bio.c
void binit(void); void binit(void);
@ -117,6 +121,11 @@ void initlock(struct spinlock*, char*);
void release(struct spinlock*); void release(struct spinlock*);
void push_off(void); void push_off(void);
void pop_off(void); void pop_off(void);
uint64 lockfree_read8(uint64 *addr);
int lockfree_read4(int *addr);
#ifdef LAB_LOCK
void freelock(struct spinlock*);
#endif
// sleeplock.c // sleeplock.c
void acquiresleep(struct sleeplock*); void acquiresleep(struct sleeplock*);
@ -187,3 +196,44 @@ void virtio_disk_intr(void);
// number of elements in fixed-size array // number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0])) #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
#ifdef LAB_PGTBL
// vmcopyin.c
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
#endif
// stats.c
void statsinit(void);
void statsinc(void);
// sprintf.c
int snprintf(char*, int, char*, ...);
#ifdef KCSAN
void kcsaninit();
#endif
#ifdef LAB_NET
// pci.c
void pci_init();
// e1000.c
void e1000_init(uint32 *);
void e1000_intr(void);
int e1000_transmit(struct mbuf*);
// net.c
void net_rx(struct mbuf*);
void net_tx_udp(struct mbuf*, uint32, uint16, uint16);
// sysnet.c
void sockinit(void);
int sockalloc(struct file **, uint32, uint16, uint16);
void sockclose(struct sock *);
int sockread(struct sock *, uint64, int);
int sockwrite(struct sock *, uint64, int);
void sockrecvudp(struct mbuf*, uint32, uint16, uint16);
#endif

162
kernel/e1000.c Normal file
View File

@ -0,0 +1,162 @@
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "defs.h"
#include "e1000_dev.h"
#include "net.h"
#define TX_RING_SIZE 16
static struct tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(16)));
static struct mbuf *tx_mbufs[TX_RING_SIZE];
#define RX_RING_SIZE 16
static struct rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(16)));
static struct mbuf *rx_mbufs[RX_RING_SIZE];
// remember where the e1000's registers live.
static volatile uint32 *regs;
struct spinlock e1000_lock;
// called by pci_init().
// xregs is the memory address at which the
// e1000's registers are mapped.
void
e1000_init(uint32 *xregs)
{
int i;
initlock(&e1000_lock, "e1000");
regs = xregs;
// Reset the device
regs[E1000_IMS] = 0; // disable interrupts
regs[E1000_CTL] |= E1000_CTL_RST;
regs[E1000_IMS] = 0; // redisable interrupts
__sync_synchronize();
// [E1000 14.5] Transmit initialization
memset(tx_ring, 0, sizeof(tx_ring));
for (i = 0; i < TX_RING_SIZE; i++) {
tx_ring[i].status = E1000_TXD_STAT_DD;
tx_mbufs[i] = 0;
}
regs[E1000_TDBAL] = (uint64) tx_ring;
if(sizeof(tx_ring) % 128 != 0)
panic("e1000");
regs[E1000_TDLEN] = sizeof(tx_ring);
regs[E1000_TDH] = regs[E1000_TDT] = 0;
// [E1000 14.4] Receive initialization
memset(rx_ring, 0, sizeof(rx_ring));
for (i = 0; i < RX_RING_SIZE; i++) {
rx_mbufs[i] = mbufalloc(0);
if (!rx_mbufs[i])
panic("e1000");
rx_ring[i].addr = (uint64) rx_mbufs[i]->head;
}
regs[E1000_RDBAL] = (uint64) rx_ring;
if(sizeof(rx_ring) % 128 != 0)
panic("e1000");
regs[E1000_RDH] = 0;
regs[E1000_RDT] = RX_RING_SIZE - 1;
regs[E1000_RDLEN] = sizeof(rx_ring);
// filter by qemu's MAC address, 52:54:00:12:34:56
regs[E1000_RA] = 0x12005452;
regs[E1000_RA+1] = 0x5634 | (1<<31);
// multicast table
for (int i = 0; i < 4096/32; i++)
regs[E1000_MTA + i] = 0;
// transmitter control bits.
regs[E1000_TCTL] = E1000_TCTL_EN | // enable
E1000_TCTL_PSP | // pad short packets
(0x10 << E1000_TCTL_CT_SHIFT) | // collision stuff
(0x40 << E1000_TCTL_COLD_SHIFT);
regs[E1000_TIPG] = 10 | (8<<10) | (6<<20); // inter-pkt gap
// receiver control bits.
regs[E1000_RCTL] = E1000_RCTL_EN | // enable receiver
E1000_RCTL_BAM | // enable broadcast
E1000_RCTL_SZ_2048 | // 2048-byte rx buffers
E1000_RCTL_SECRC; // strip CRC
// ask e1000 for receive interrupts.
regs[E1000_RDTR] = 0; // interrupt after every received packet (no timer)
regs[E1000_RADV] = 0; // interrupt after every packet (no timer)
regs[E1000_IMS] = (1 << 7); // RXDW -- Receiver Descriptor Write Back
}
int
e1000_transmit(struct mbuf *m)
{
//
// Your code here.
//
// the mbuf contains an ethernet frame; program it into
// the TX descriptor ring so that the e1000 sends it. Stash
// a pointer so that it can be freed after sending.
//
acquire(&e1000_lock);
int tdt = regs[E1000_TDT];
if (!(tx_ring[tdt].status & E1000_TXD_STAT_DD)) {
release(&e1000_lock);
return -1;
}
if (tx_mbufs[tdt]) {
mbuffree(tx_mbufs[tdt]);
tx_mbufs[tdt] = 0;
}
tx_mbufs[tdt] = m;
tx_ring[tdt].addr = (uint64)m->head;
tx_ring[tdt].length = m->len;
tx_ring[tdt].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP;
regs[E1000_TDT] = (tdt + 1) % TX_RING_SIZE;
release(&e1000_lock);
return 0;
}
static void
e1000_recv(void)
{
//
// Your code here.
//
// Check for packets that have arrived from the e1000
// Create and deliver an mbuf for each packet (using net_rx()).
//
acquire(&e1000_lock);
int rdt = regs[E1000_RDT];
rdt = (rdt + 1) % RX_RING_SIZE;
while (rx_ring[rdt].status & E1000_RXD_STAT_DD)
{
if (!holding(&e1000_lock)) acquire(&e1000_lock);
rx_mbufs[rdt]->len = rx_ring[rdt].length;
struct mbuf* m = rx_mbufs[rdt];
rx_mbufs[rdt] = mbufalloc(0);
if (!rx_mbufs[rdt]) panic("e1000 recv");
rx_ring[rdt].addr = (uint64)rx_mbufs[rdt]->head;
rx_ring[rdt].status = 0;
regs[E1000_RDT] = rdt;
rdt = (rdt + 1) % RX_RING_SIZE;
release(&e1000_lock);
net_rx(m);
}
}
void
e1000_intr(void)
{
// tell the e1000 we've seen this interrupt;
// without this the e1000 won't raise any
// further interrupts.
regs[E1000_ICR] = 0xffffffff;
e1000_recv();
}

125
kernel/e1000_dev.h Normal file
View File

@ -0,0 +1,125 @@
//
// E1000 hardware definitions: registers and DMA ring format.
// from the Intel 82540EP/EM &c manual.
//
/* Registers */
#define E1000_CTL (0x00000/4) /* Device Control Register - RW */
#define E1000_ICR (0x000C0/4) /* Interrupt Cause Read - R */
#define E1000_IMS (0x000D0/4) /* Interrupt Mask Set - RW */
#define E1000_RCTL (0x00100/4) /* RX Control - RW */
#define E1000_TCTL (0x00400/4) /* TX Control - RW */
#define E1000_TIPG (0x00410/4) /* TX Inter-packet gap -RW */
#define E1000_RDBAL (0x02800/4) /* RX Descriptor Base Address Low - RW */
#define E1000_RDTR (0x02820/4) /* RX Delay Timer */
#define E1000_RADV (0x0282C/4) /* RX Interrupt Absolute Delay Timer */
#define E1000_RDH (0x02810/4) /* RX Descriptor Head - RW */
#define E1000_RDT (0x02818/4) /* RX Descriptor Tail - RW */
#define E1000_RDLEN (0x02808/4) /* RX Descriptor Length - RW */
#define E1000_RSRPD (0x02C00/4) /* RX Small Packet Detect Interrupt */
#define E1000_TDBAL (0x03800/4) /* TX Descriptor Base Address Low - RW */
#define E1000_TDLEN (0x03808/4) /* TX Descriptor Length - RW */
#define E1000_TDH (0x03810/4) /* TX Descriptor Head - RW */
#define E1000_TDT (0x03818/4) /* TX Descripotr Tail - RW */
#define E1000_MTA (0x05200/4) /* Multicast Table Array - RW Array */
#define E1000_RA (0x05400/4) /* Receive Address - RW Array */
/* Device Control */
#define E1000_CTL_SLU 0x00000040 /* set link up */
#define E1000_CTL_FRCSPD 0x00000800 /* force speed */
#define E1000_CTL_FRCDPLX 0x00001000 /* force duplex */
#define E1000_CTL_RST 0x00400000 /* full reset */
/* Transmit Control */
#define E1000_TCTL_RST 0x00000001 /* software reset */
#define E1000_TCTL_EN 0x00000002 /* enable tx */
#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
#define E1000_TCTL_CT_SHIFT 4
#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
#define E1000_TCTL_COLD_SHIFT 12
#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */
/* Receive Control */
#define E1000_RCTL_RST 0x00000001 /* Software reset */
#define E1000_RCTL_EN 0x00000002 /* enable */
#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */
#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */
#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */
#define DATA_MAX 1518
/* Transmit Descriptor command definitions [E1000 3.3.3.1] */
#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */
#define E1000_TXD_CMD_RS 0x08 /* Report Status */
/* Transmit Descriptor status definitions [E1000 3.3.3.2] */
#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
// [E1000 3.3.3]
struct tx_desc
{
uint64 addr;
uint16 length;
uint8 cso;
uint8 cmd;
uint8 status;
uint8 css;
uint16 special;
};
/* Receive Descriptor bit definitions [E1000 3.2.3.1] */
#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */
// [E1000 3.2.3]
struct rx_desc
{
uint64 addr; /* Address of the descriptor's data buffer */
uint16 length; /* Length of data DMAed into data buffer */
uint16 csum; /* Packet checksum */
uint8 status; /* Descriptor status */
uint8 errors; /* Descriptor Errors */
uint16 special;
};

View File

@ -80,6 +80,11 @@ fileclose(struct file *f)
iput(ff.ip); iput(ff.ip);
end_op(); end_op();
} }
#ifdef LAB_NET
else if(ff.type == FD_SOCK){
sockclose(ff.sock);
}
#endif
} }
// Get metadata about file f. // Get metadata about file f.
@ -122,7 +127,13 @@ fileread(struct file *f, uint64 addr, int n)
if((r = readi(f->ip, 1, addr, f->off, n)) > 0) if((r = readi(f->ip, 1, addr, f->off, n)) > 0)
f->off += r; f->off += r;
iunlock(f->ip); iunlock(f->ip);
} else { }
#ifdef LAB_NET
else if(f->type == FD_SOCK){
r = sockread(f->sock, addr, n);
}
#endif
else {
panic("fileread"); panic("fileread");
} }
@ -173,7 +184,13 @@ filewrite(struct file *f, uint64 addr, int n)
i += r; i += r;
} }
ret = (i == n ? n : -1); ret = (i == n ? n : -1);
} else { }
#ifdef LAB_NET
else if(f->type == FD_SOCK){
ret = sockwrite(f->sock, addr, n);
}
#endif
else {
panic("filewrite"); panic("filewrite");
} }

View File

@ -1,10 +1,17 @@
struct file { struct file {
#ifdef LAB_NET
enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE, FD_SOCK } type;
#else
enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type; enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type;
#endif
int ref; // reference count int ref; // reference count
char readable; char readable;
char writable; char writable;
struct pipe *pipe; // FD_PIPE struct pipe *pipe; // FD_PIPE
struct inode *ip; // FD_INODE and FD_DEVICE struct inode *ip; // FD_INODE and FD_DEVICE
#ifdef LAB_NET
struct sock *sock; // FD_SOCK
#endif
uint off; // FD_INODE uint off; // FD_INODE
short major; // FD_DEVICE short major; // FD_DEVICE
}; };
@ -38,3 +45,4 @@ struct devsw {
extern struct devsw devsw[]; extern struct devsw devsw[];
#define CONSOLE 1 #define CONSOLE 1
#define STATS 2

View File

@ -12,6 +12,9 @@ main()
{ {
if(cpuid() == 0){ if(cpuid() == 0){
consoleinit(); consoleinit();
#if defined(LAB_LOCK)
statsinit();
#endif
printfinit(); printfinit();
printf("\n"); printf("\n");
printf("xv6 kernel is booting\n"); printf("xv6 kernel is booting\n");
@ -28,11 +31,18 @@ main()
iinit(); // inode table iinit(); // inode table
fileinit(); // file table fileinit(); // file table
virtio_disk_init(); // emulated hard disk virtio_disk_init(); // emulated hard disk
#ifdef LAB_NET
pci_init();
sockinit();
#endif
userinit(); // first user process userinit(); // first user process
#ifdef KCSAN
kcsaninit();
#endif
__sync_synchronize(); __sync_synchronize();
started = 1; started = 1;
} else { } else {
while(started == 0) while(lockfree_read4((int *) &started) == 0)
; ;
__sync_synchronize(); __sync_synchronize();
printf("hart %d starting\n", cpuid()); printf("hart %d starting\n", cpuid());

View File

@ -25,6 +25,10 @@
#define VIRTIO0 0x10001000 #define VIRTIO0 0x10001000
#define VIRTIO0_IRQ 1 #define VIRTIO0_IRQ 1
#ifdef LAB_NET
#define E1000_IRQ 33
#endif
// core local interruptor (CLINT), which contains the timer. // core local interruptor (CLINT), which contains the timer.
#define CLINT 0x2000000L #define CLINT 0x2000000L
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) #define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
@ -53,7 +57,7 @@
// map kernel stacks beneath the trampoline, // map kernel stacks beneath the trampoline,
// each surrounded by invalid guard pages. // each surrounded by invalid guard pages.
#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE) #define KSTACK(p) (TRAMPOLINE - (p)*2*PGSIZE - 3*PGSIZE)
// User memory layout. // User memory layout.
// Address zero first: // Address zero first:
@ -62,6 +66,14 @@
// fixed-size stack // fixed-size stack
// expandable heap // expandable heap
// ... // ...
// USYSCALL (shared with kernel)
// TRAPFRAME (p->trapframe, used by the trampoline) // TRAPFRAME (p->trapframe, used by the trampoline)
// TRAMPOLINE (the same page as in the kernel) // TRAMPOLINE (the same page as in the kernel)
#define TRAPFRAME (TRAMPOLINE - PGSIZE) #define TRAPFRAME (TRAMPOLINE - PGSIZE)
#ifdef LAB_PGTBL
#define USYSCALL (TRAPFRAME - PGSIZE)
struct usyscall {
int pid; // Process ID
};
#endif

374
kernel/net.c Normal file
View File

@ -0,0 +1,374 @@
//
// networking protocol support (IP, UDP, ARP, etc.).
//
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "net.h"
#include "defs.h"
static uint32 local_ip = MAKE_IP_ADDR(10, 0, 2, 15); // qemu's idea of the guest IP
static uint8 local_mac[ETHADDR_LEN] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
static uint8 broadcast_mac[ETHADDR_LEN] = { 0xFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF };
// Strips data from the start of the buffer and returns a pointer to it.
// Returns 0 if less than the full requested length is available.
char *
mbufpull(struct mbuf *m, unsigned int len)
{
char *tmp = m->head;
if (m->len < len)
return 0;
m->len -= len;
m->head += len;
return tmp;
}
// Prepends data to the beginning of the buffer and returns a pointer to it.
char *
mbufpush(struct mbuf *m, unsigned int len)
{
m->head -= len;
if (m->head < m->buf)
panic("mbufpush");
m->len += len;
return m->head;
}
// Appends data to the end of the buffer and returns a pointer to it.
char *
mbufput(struct mbuf *m, unsigned int len)
{
char *tmp = m->head + m->len;
m->len += len;
if (m->len > MBUF_SIZE)
panic("mbufput");
return tmp;
}
// Strips data from the end of the buffer and returns a pointer to it.
// Returns 0 if less than the full requested length is available.
char *
mbuftrim(struct mbuf *m, unsigned int len)
{
if (len > m->len)
return 0;
m->len -= len;
return m->head + m->len;
}
// Allocates a packet buffer.
struct mbuf *
mbufalloc(unsigned int headroom)
{
struct mbuf *m;
if (headroom > MBUF_SIZE)
return 0;
m = kalloc();
if (m == 0)
return 0;
m->next = 0;
m->head = (char *)m->buf + headroom;
m->len = 0;
memset(m->buf, 0, sizeof(m->buf));
return m;
}
// Frees a packet buffer.
void
mbuffree(struct mbuf *m)
{
kfree(m);
}
// Pushes an mbuf to the end of the queue.
void
mbufq_pushtail(struct mbufq *q, struct mbuf *m)
{
m->next = 0;
if (!q->head){
q->head = q->tail = m;
return;
}
q->tail->next = m;
q->tail = m;
}
// Pops an mbuf from the start of the queue.
struct mbuf *
mbufq_pophead(struct mbufq *q)
{
struct mbuf *head = q->head;
if (!head)
return 0;
q->head = head->next;
return head;
}
// Returns one (nonzero) if the queue is empty.
int
mbufq_empty(struct mbufq *q)
{
return q->head == 0;
}
// Intializes a queue of mbufs.
void
mbufq_init(struct mbufq *q)
{
q->head = 0;
}
// This code is lifted from FreeBSD's ping.c, and is copyright by the Regents
// of the University of California.
static unsigned short
in_cksum(const unsigned char *addr, int len)
{
int nleft = len;
const unsigned short *w = (const unsigned short *)addr;
unsigned int sum = 0;
unsigned short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(const unsigned char *)w;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum & 0xffff) + (sum >> 16);
sum += (sum >> 16);
/* guaranteed now that the lower 16 bits of sum are correct */
answer = ~sum; /* truncate to 16 bits */
return answer;
}
// sends an ethernet packet
static void
net_tx_eth(struct mbuf *m, uint16 ethtype)
{
struct eth *ethhdr;
ethhdr = mbufpushhdr(m, *ethhdr);
memmove(ethhdr->shost, local_mac, ETHADDR_LEN);
// In a real networking stack, dhost would be set to the address discovered
// through ARP. Because we don't support enough of the ARP protocol, set it
// to broadcast instead.
memmove(ethhdr->dhost, broadcast_mac, ETHADDR_LEN);
ethhdr->type = htons(ethtype);
if (e1000_transmit(m)) {
mbuffree(m);
}
}
// sends an IP packet
static void
net_tx_ip(struct mbuf *m, uint8 proto, uint32 dip)
{
struct ip *iphdr;
// push the IP header
iphdr = mbufpushhdr(m, *iphdr);
memset(iphdr, 0, sizeof(*iphdr));
iphdr->ip_vhl = (4 << 4) | (20 >> 2);
iphdr->ip_p = proto;
iphdr->ip_src = htonl(local_ip);
iphdr->ip_dst = htonl(dip);
iphdr->ip_len = htons(m->len);
iphdr->ip_ttl = 100;
iphdr->ip_sum = in_cksum((unsigned char *)iphdr, sizeof(*iphdr));
// now on to the ethernet layer
net_tx_eth(m, ETHTYPE_IP);
}
// sends a UDP packet
void
net_tx_udp(struct mbuf *m, uint32 dip,
uint16 sport, uint16 dport)
{
struct udp *udphdr;
// put the UDP header
udphdr = mbufpushhdr(m, *udphdr);
udphdr->sport = htons(sport);
udphdr->dport = htons(dport);
udphdr->ulen = htons(m->len);
udphdr->sum = 0; // zero means no checksum is provided
// now on to the IP layer
net_tx_ip(m, IPPROTO_UDP, dip);
}
// sends an ARP packet
static int
net_tx_arp(uint16 op, uint8 dmac[ETHADDR_LEN], uint32 dip)
{
struct mbuf *m;
struct arp *arphdr;
m = mbufalloc(MBUF_DEFAULT_HEADROOM);
if (!m)
return -1;
// generic part of ARP header
arphdr = mbufputhdr(m, *arphdr);
arphdr->hrd = htons(ARP_HRD_ETHER);
arphdr->pro = htons(ETHTYPE_IP);
arphdr->hln = ETHADDR_LEN;
arphdr->pln = sizeof(uint32);
arphdr->op = htons(op);
// ethernet + IP part of ARP header
memmove(arphdr->sha, local_mac, ETHADDR_LEN);
arphdr->sip = htonl(local_ip);
memmove(arphdr->tha, dmac, ETHADDR_LEN);
arphdr->tip = htonl(dip);
// header is ready, send the packet
net_tx_eth(m, ETHTYPE_ARP);
return 0;
}
// receives an ARP packet
static void
net_rx_arp(struct mbuf *m)
{
struct arp *arphdr;
uint8 smac[ETHADDR_LEN];
uint32 sip, tip;
arphdr = mbufpullhdr(m, *arphdr);
if (!arphdr)
goto done;
// validate the ARP header
if (ntohs(arphdr->hrd) != ARP_HRD_ETHER ||
ntohs(arphdr->pro) != ETHTYPE_IP ||
arphdr->hln != ETHADDR_LEN ||
arphdr->pln != sizeof(uint32)) {
goto done;
}
// only requests are supported so far
// check if our IP was solicited
tip = ntohl(arphdr->tip); // target IP address
if (ntohs(arphdr->op) != ARP_OP_REQUEST || tip != local_ip)
goto done;
// handle the ARP request
memmove(smac, arphdr->sha, ETHADDR_LEN); // sender's ethernet address
sip = ntohl(arphdr->sip); // sender's IP address (qemu's slirp)
net_tx_arp(ARP_OP_REPLY, smac, sip);
done:
mbuffree(m);
}
// receives a UDP packet
static void
net_rx_udp(struct mbuf *m, uint16 len, struct ip *iphdr)
{
struct udp *udphdr;
uint32 sip;
uint16 sport, dport;
udphdr = mbufpullhdr(m, *udphdr);
if (!udphdr)
goto fail;
// TODO: validate UDP checksum
// validate lengths reported in headers
if (ntohs(udphdr->ulen) != len)
goto fail;
len -= sizeof(*udphdr);
if (len > m->len)
goto fail;
// minimum packet size could be larger than the payload
mbuftrim(m, m->len - len);
// parse the necessary fields
sip = ntohl(iphdr->ip_src);
sport = ntohs(udphdr->sport);
dport = ntohs(udphdr->dport);
sockrecvudp(m, sip, dport, sport);
return;
fail:
mbuffree(m);
}
// receives an IP packet
static void
net_rx_ip(struct mbuf *m)
{
struct ip *iphdr;
uint16 len;
iphdr = mbufpullhdr(m, *iphdr);
if (!iphdr)
goto fail;
// check IP version and header len
if (iphdr->ip_vhl != ((4 << 4) | (20 >> 2)))
goto fail;
// validate IP checksum
if (in_cksum((unsigned char *)iphdr, sizeof(*iphdr)))
goto fail;
// can't support fragmented IP packets
if (htons(iphdr->ip_off) != 0)
goto fail;
// is the packet addressed to us?
if (htonl(iphdr->ip_dst) != local_ip)
goto fail;
// can only support UDP
if (iphdr->ip_p != IPPROTO_UDP)
goto fail;
len = ntohs(iphdr->ip_len) - sizeof(*iphdr);
net_rx_udp(m, len, iphdr);
return;
fail:
mbuffree(m);
}
// called by e1000 driver's interrupt handler to deliver a packet to the
// networking stack
void net_rx(struct mbuf *m)
{
struct eth *ethhdr;
uint16 type;
ethhdr = mbufpullhdr(m, *ethhdr);
if (!ethhdr) {
mbuffree(m);
return;
}
type = ntohs(ethhdr->type);
if (type == ETHTYPE_IP)
net_rx_ip(m);
else if (type == ETHTYPE_ARP)
net_rx_arp(m);
else
mbuffree(m);
}

173
kernel/net.h Normal file
View File

@ -0,0 +1,173 @@
//
// packet buffer management
//
#define MBUF_SIZE 2048
#define MBUF_DEFAULT_HEADROOM 128
struct mbuf {
struct mbuf *next; // the next mbuf in the chain
char *head; // the current start position of the buffer
unsigned int len; // the length of the buffer
char buf[MBUF_SIZE]; // the backing store
};
char *mbufpull(struct mbuf *m, unsigned int len);
char *mbufpush(struct mbuf *m, unsigned int len);
char *mbufput(struct mbuf *m, unsigned int len);
char *mbuftrim(struct mbuf *m, unsigned int len);
// The above functions manipulate the size and position of the buffer:
// <- push <- trim
// -> pull -> put
// [-headroom-][------buffer------][-tailroom-]
// |----------------MBUF_SIZE-----------------|
//
// These marcos automatically typecast and determine the size of header structs.
// In most situations you should use these instead of the raw ops above.
#define mbufpullhdr(mbuf, hdr) (typeof(hdr)*)mbufpull(mbuf, sizeof(hdr))
#define mbufpushhdr(mbuf, hdr) (typeof(hdr)*)mbufpush(mbuf, sizeof(hdr))
#define mbufputhdr(mbuf, hdr) (typeof(hdr)*)mbufput(mbuf, sizeof(hdr))
#define mbuftrimhdr(mbuf, hdr) (typeof(hdr)*)mbuftrim(mbuf, sizeof(hdr))
struct mbuf *mbufalloc(unsigned int headroom);
void mbuffree(struct mbuf *m);
struct mbufq {
struct mbuf *head; // the first element in the queue
struct mbuf *tail; // the last element in the queue
};
void mbufq_pushtail(struct mbufq *q, struct mbuf *m);
struct mbuf *mbufq_pophead(struct mbufq *q);
int mbufq_empty(struct mbufq *q);
void mbufq_init(struct mbufq *q);
//
// endianness support
//
static inline uint16 bswaps(uint16 val)
{
return (((val & 0x00ffU) << 8) |
((val & 0xff00U) >> 8));
}
static inline uint32 bswapl(uint32 val)
{
return (((val & 0x000000ffUL) << 24) |
((val & 0x0000ff00UL) << 8) |
((val & 0x00ff0000UL) >> 8) |
((val & 0xff000000UL) >> 24));
}
// Use these macros to convert network bytes to the native byte order.
// Note that Risc-V uses little endian while network order is big endian.
#define ntohs bswaps
#define ntohl bswapl
#define htons bswaps
#define htonl bswapl
//
// useful networking headers
//
#define ETHADDR_LEN 6
// an Ethernet packet header (start of the packet).
struct eth {
uint8 dhost[ETHADDR_LEN];
uint8 shost[ETHADDR_LEN];
uint16 type;
} __attribute__((packed));
#define ETHTYPE_IP 0x0800 // Internet protocol
#define ETHTYPE_ARP 0x0806 // Address resolution protocol
// an IP packet header (comes after an Ethernet header).
struct ip {
uint8 ip_vhl; // version << 4 | header length >> 2
uint8 ip_tos; // type of service
uint16 ip_len; // total length
uint16 ip_id; // identification
uint16 ip_off; // fragment offset field
uint8 ip_ttl; // time to live
uint8 ip_p; // protocol
uint16 ip_sum; // checksum
uint32 ip_src, ip_dst;
};
#define IPPROTO_ICMP 1 // Control message protocol
#define IPPROTO_TCP 6 // Transmission control protocol
#define IPPROTO_UDP 17 // User datagram protocol
#define MAKE_IP_ADDR(a, b, c, d) \
(((uint32)a << 24) | ((uint32)b << 16) | \
((uint32)c << 8) | (uint32)d)
// a UDP packet header (comes after an IP header).
struct udp {
uint16 sport; // source port
uint16 dport; // destination port
uint16 ulen; // length, including udp header, not including IP header
uint16 sum; // checksum
};
// an ARP packet (comes after an Ethernet header).
struct arp {
uint16 hrd; // format of hardware address
uint16 pro; // format of protocol address
uint8 hln; // length of hardware address
uint8 pln; // length of protocol address
uint16 op; // operation
char sha[ETHADDR_LEN]; // sender hardware address
uint32 sip; // sender IP address
char tha[ETHADDR_LEN]; // target hardware address
uint32 tip; // target IP address
} __attribute__((packed));
#define ARP_HRD_ETHER 1 // Ethernet
enum {
ARP_OP_REQUEST = 1, // requests hw addr given protocol addr
ARP_OP_REPLY = 2, // replies a hw addr given protocol addr
};
// an DNS packet (comes after an UDP header).
struct dns {
uint16 id; // request ID
uint8 rd: 1; // recursion desired
uint8 tc: 1; // truncated
uint8 aa: 1; // authoritive
uint8 opcode: 4;
uint8 qr: 1; // query/response
uint8 rcode: 4; // response code
uint8 cd: 1; // checking disabled
uint8 ad: 1; // authenticated data
uint8 z: 1;
uint8 ra: 1; // recursion available
uint16 qdcount; // number of question entries
uint16 ancount; // number of resource records in answer section
uint16 nscount; // number of NS resource records in authority section
uint16 arcount; // number of resource records in additional records
} __attribute__((packed));
struct dns_question {
uint16 qtype;
uint16 qclass;
} __attribute__((packed));
#define ARECORD (0x0001)
#define QCLASS (0x0001)
struct dns_data {
uint16 type;
uint16 class;
uint32 ttl;
uint16 len;
} __attribute__((packed));

61
kernel/pci.c Normal file
View File

@ -0,0 +1,61 @@
//
// simple PCI-Express initialization, only
// works for qemu and its e1000 card.
//
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "defs.h"
void
pci_init()
{
// we'll place the e1000 registers at this address.
// vm.c maps this range.
uint64 e1000_regs = 0x40000000L;
// qemu -machine virt puts PCIe config space here.
// vm.c maps this range.
uint32 *ecam = (uint32 *) 0x30000000L;
// look at each possible PCI device on bus 0.
for(int dev = 0; dev < 32; dev++){
int bus = 0;
int func = 0;
int offset = 0;
uint32 off = (bus << 16) | (dev << 11) | (func << 8) | (offset);
volatile uint32 *base = ecam + off;
uint32 id = base[0];
// 100e:8086 is an e1000
if(id == 0x100e8086){
// command and status register.
// bit 0 : I/O access enable
// bit 1 : memory access enable
// bit 2 : enable mastering
base[1] = 7;
__sync_synchronize();
for(int i = 0; i < 6; i++){
uint32 old = base[4+i];
// writing all 1's to the BAR causes it to be
// replaced with its size.
base[4+i] = 0xffffffff;
__sync_synchronize();
base[4+i] = old;
}
// tell the e1000 to reveal its registers at
// physical address 0x40000000.
base[4+0] = e1000_regs;
e1000_init((uint32*)e1000_regs);
}
}
}

View File

@ -14,6 +14,13 @@ plicinit(void)
// set desired IRQ priorities non-zero (otherwise disabled). // set desired IRQ priorities non-zero (otherwise disabled).
*(uint32*)(PLIC + UART0_IRQ*4) = 1; *(uint32*)(PLIC + UART0_IRQ*4) = 1;
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1; *(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
#ifdef LAB_NET
// PCIE IRQs are 32 to 35
for(int irq = 1; irq < 0x35; irq++){
*(uint32*)(PLIC + irq*4) = 1;
}
#endif
} }
void void
@ -25,6 +32,11 @@ plicinithart(void)
// for the uart and virtio disk. // for the uart and virtio disk.
*(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
#ifdef LAB_NET
// hack to get at next 32 IRQs for e1000
*(uint32*)(PLIC_SENABLE(hart)+4) = 0xffffffff;
#endif
// set this hart's S-mode priority threshold to 0. // set this hart's S-mode priority threshold to 0.
*(uint32*)PLIC_SPRIORITY(hart) = 0; *(uint32*)PLIC_SPRIORITY(hart) = 0;
} }

View File

@ -8,12 +8,52 @@
#include "proc.h" #include "proc.h"
#include "defs.h" #include "defs.h"
#ifdef LAB_LOCK
#define NLOCK 500
static struct spinlock *locks[NLOCK];
struct spinlock lock_locks;
void
freelock(struct spinlock *lk)
{
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == lk) {
locks[i] = 0;
break;
}
}
release(&lock_locks);
}
static void
findslot(struct spinlock *lk) {
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == 0) {
locks[i] = lk;
release(&lock_locks);
return;
}
}
panic("findslot");
}
#endif
void void
initlock(struct spinlock *lk, char *name) initlock(struct spinlock *lk, char *name)
{ {
lk->name = name; lk->name = name;
lk->locked = 0; lk->locked = 0;
lk->cpu = 0; lk->cpu = 0;
#ifdef LAB_LOCK
lk->nts = 0;
lk->n = 0;
findslot(lk);
#endif
} }
// Acquire the lock. // Acquire the lock.
@ -25,12 +65,21 @@ acquire(struct spinlock *lk)
if(holding(lk)) if(holding(lk))
panic("acquire"); panic("acquire");
#ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->n), 1);
#endif
// On RISC-V, sync_lock_test_and_set turns into an atomic swap: // On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1 // a5 = 1
// s1 = &lk->locked // s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1) // amoswap.w.aq a5, a5, (s1)
while(__sync_lock_test_and_set(&lk->locked, 1) != 0) while(__sync_lock_test_and_set(&lk->locked, 1) != 0) {
; #ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->nts), 1);
#else
;
#endif
}
// Tell the C compiler and the processor to not move loads or stores // Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory // past this point, to ensure that the critical section's memory
@ -108,3 +157,69 @@ pop_off(void)
if(c->noff == 0 && c->intena) if(c->noff == 0 && c->intena)
intr_on(); intr_on();
} }
// Read a shared 64-bit value without holding a lock
uint64
lockfree_read8(uint64 *addr) {
uint64 val;
__atomic_load(addr, &val, __ATOMIC_SEQ_CST);
return val;
}
// Read a shared 32-bit value without holding a lock
int
lockfree_read4(int *addr) {
uint32 val;
__atomic_load(addr, &val, __ATOMIC_SEQ_CST);
return val;
}
#ifdef LAB_LOCK
int
snprint_lock(char *buf, int sz, struct spinlock *lk)
{
int n = 0;
if(lk->n > 0) {
n = snprintf(buf, sz, "lock: %s: #test-and-set %d #acquire() %d\n",
lk->name, lk->nts, lk->n);
}
return n;
}
int
statslock(char *buf, int sz) {
int n;
int tot = 0;
acquire(&lock_locks);
n = snprintf(buf, sz, "--- lock kmem/bcache stats\n");
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(strncmp(locks[i]->name, "bcache", strlen("bcache")) == 0 ||
strncmp(locks[i]->name, "kmem", strlen("kmem")) == 0) {
tot += locks[i]->nts;
n += snprint_lock(buf +n, sz-n, locks[i]);
}
}
n += snprintf(buf+n, sz-n, "--- top 5 contended locks:\n");
int last = 100000000;
// stupid way to compute top 5 contended locks
for(int t = 0; t < 5; t++) {
int top = 0;
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(locks[i]->nts > locks[top]->nts && locks[i]->nts < last) {
top = i;
}
}
n += snprint_lock(buf+n, sz-n, locks[top]);
last = locks[top]->nts;
}
n += snprintf(buf+n, sz-n, "tot= %d\n", tot);
release(&lock_locks);
return n;
}
#endif

View File

@ -102,6 +102,13 @@ extern uint64 sys_link(void);
extern uint64 sys_mkdir(void); extern uint64 sys_mkdir(void);
extern uint64 sys_close(void); extern uint64 sys_close(void);
#ifdef LAB_NET
extern uint64 sys_connect(void);
#endif
#ifdef LAB_PGTBL
extern uint64 sys_pgaccess(void);
#endif
// An array mapping syscall numbers from syscall.h // An array mapping syscall numbers from syscall.h
// to the function that handles the system call. // to the function that handles the system call.
static uint64 (*syscalls[])(void) = { static uint64 (*syscalls[])(void) = {
@ -126,8 +133,16 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link, [SYS_link] sys_link,
[SYS_mkdir] sys_mkdir, [SYS_mkdir] sys_mkdir,
[SYS_close] sys_close, [SYS_close] sys_close,
#ifdef LAB_NET
[SYS_connect] sys_connect,
#endif
#ifdef LAB_PGTBL
[SYS_pgaccess] sys_pgaccess,
#endif
}; };
void void
syscall(void) syscall(void)
{ {

View File

@ -20,3 +20,14 @@
#define SYS_link 19 #define SYS_link 19
#define SYS_mkdir 20 #define SYS_mkdir 20
#define SYS_close 21 #define SYS_close 21
// System calls for labs
#define SYS_trace 22
#define SYS_sysinfo 23
#define SYS_sigalarm 24
#define SYS_sigreturn 25
#define SYS_symlink 26
#define SYS_mmap 27
#define SYS_munmap 28
#define SYS_connect 29
#define SYS_pgaccess 30

View File

@ -503,3 +503,29 @@ sys_pipe(void)
} }
return 0; return 0;
} }
#ifdef LAB_NET
int
sys_connect(void)
{
struct file *f;
int fd;
uint32 raddr;
uint32 rport;
uint32 lport;
argint(0, (int*)&raddr);
argint(1, (int*)&lport);
argint(2, (int*)&rport);
if(sockalloc(&f, raddr, lport, rport) < 0)
return -1;
if((fd=fdalloc(f)) < 0){
fileclose(f);
return -1;
}
return fd;
}
#endif

185
kernel/sysnet.c Normal file
View File

@ -0,0 +1,185 @@
//
// network system calls.
//
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "defs.h"
#include "fs.h"
#include "sleeplock.h"
#include "file.h"
#include "net.h"
struct sock {
struct sock *next; // the next socket in the list
uint32 raddr; // the remote IPv4 address
uint16 lport; // the local UDP port number
uint16 rport; // the remote UDP port number
struct spinlock lock; // protects the rxq
struct mbufq rxq; // a queue of packets waiting to be received
};
static struct spinlock lock;
static struct sock *sockets;
void
sockinit(void)
{
initlock(&lock, "socktbl");
}
int
sockalloc(struct file **f, uint32 raddr, uint16 lport, uint16 rport)
{
struct sock *si, *pos;
si = 0;
*f = 0;
if ((*f = filealloc()) == 0)
goto bad;
if ((si = (struct sock*)kalloc()) == 0)
goto bad;
// initialize objects
si->raddr = raddr;
si->lport = lport;
si->rport = rport;
initlock(&si->lock, "sock");
mbufq_init(&si->rxq);
(*f)->type = FD_SOCK;
(*f)->readable = 1;
(*f)->writable = 1;
(*f)->sock = si;
// add to list of sockets
acquire(&lock);
pos = sockets;
while (pos) {
if (pos->raddr == raddr &&
pos->lport == lport &&
pos->rport == rport) {
release(&lock);
goto bad;
}
pos = pos->next;
}
si->next = sockets;
sockets = si;
release(&lock);
return 0;
bad:
if (si)
kfree((char*)si);
if (*f)
fileclose(*f);
return -1;
}
void
sockclose(struct sock *si)
{
struct sock **pos;
struct mbuf *m;
// remove from list of sockets
acquire(&lock);
pos = &sockets;
while (*pos) {
if (*pos == si){
*pos = si->next;
break;
}
pos = &(*pos)->next;
}
release(&lock);
// free any pending mbufs
while (!mbufq_empty(&si->rxq)) {
m = mbufq_pophead(&si->rxq);
mbuffree(m);
}
kfree((char*)si);
}
int
sockread(struct sock *si, uint64 addr, int n)
{
struct proc *pr = myproc();
struct mbuf *m;
int len;
acquire(&si->lock);
while (mbufq_empty(&si->rxq) && !pr->killed) {
sleep(&si->rxq, &si->lock);
}
if (pr->killed) {
release(&si->lock);
return -1;
}
m = mbufq_pophead(&si->rxq);
release(&si->lock);
len = m->len;
if (len > n)
len = n;
if (copyout(pr->pagetable, addr, m->head, len) == -1) {
mbuffree(m);
return -1;
}
mbuffree(m);
return len;
}
int
sockwrite(struct sock *si, uint64 addr, int n)
{
struct proc *pr = myproc();
struct mbuf *m;
m = mbufalloc(MBUF_DEFAULT_HEADROOM);
if (!m)
return -1;
if (copyin(pr->pagetable, mbufput(m, n), addr, n) == -1) {
mbuffree(m);
return -1;
}
net_tx_udp(m, si->raddr, si->lport, si->rport);
return n;
}
// called by protocol handler layer to deliver UDP packets
void
sockrecvudp(struct mbuf *m, uint32 raddr, uint16 lport, uint16 rport)
{
//
// Find the socket that handles this mbuf and deliver it, waking
// any sleeping reader. Free the mbuf if there are no sockets
// registered to handle it.
//
struct sock *si;
acquire(&lock);
si = sockets;
while (si) {
if (si->raddr == raddr && si->lport == lport && si->rport == rport)
goto found;
si = si->next;
}
release(&lock);
mbuffree(m);
return;
found:
acquire(&si->lock);
mbufq_pushtail(&si->rxq, m);
wakeup(&si->rxq);
release(&si->lock);
release(&lock);
}

View File

@ -55,6 +55,8 @@ sys_sleep(void)
uint ticks0; uint ticks0;
argint(0, &n); argint(0, &n);
if(n < 0)
n = 0;
acquire(&tickslock); acquire(&tickslock);
ticks0 = ticks; ticks0 = ticks;
while(ticks - ticks0 < n){ while(ticks - ticks0 < n){

View File

@ -68,6 +68,8 @@ usertrap(void)
} else if((which_dev = devintr()) != 0){ } else if((which_dev = devintr()) != 0){
// ok // ok
} else { } else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
setkilled(p); setkilled(p);
@ -76,6 +78,7 @@ usertrap(void)
if(killed(p)) if(killed(p))
exit(-1); exit(-1);
// give up the CPU if this is a timer interrupt. // give up the CPU if this is a timer interrupt.
if(which_dev == 2) if(which_dev == 2)
yield(); yield();
@ -190,7 +193,13 @@ devintr()
uartintr(); uartintr();
} else if(irq == VIRTIO0_IRQ){ } else if(irq == VIRTIO0_IRQ){
virtio_disk_intr(); virtio_disk_intr();
} else if(irq){ }
#ifdef LAB_NET
else if(irq == E1000_IRQ){
e1000_intr();
}
#endif
else if(irq){
printf("unexpected interrupt irq=%d\n", irq); printf("unexpected interrupt irq=%d\n", irq);
} }

View File

@ -4,6 +4,8 @@
#include "elf.h" #include "elf.h"
#include "riscv.h" #include "riscv.h"
#include "defs.h" #include "defs.h"
#include "spinlock.h"
#include "proc.h"
#include "fs.h" #include "fs.h"
/* /*
@ -30,6 +32,14 @@ kvmmake(void)
// virtio mmio disk interface // virtio mmio disk interface
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
#ifdef LAB_NET
// PCI-E ECAM (configuration space), for pci.c
kvmmap(kpgtbl, 0x30000000L, 0x30000000L, 0x10000000, PTE_R | PTE_W);
// pci.c maps the e1000's registers here.
kvmmap(kpgtbl, 0x40000000L, 0x40000000L, 0x20000, PTE_R | PTE_W);
#endif
// PLIC // PLIC
kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W); kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
@ -179,8 +189,10 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if((pte = walk(pagetable, a, 0)) == 0) if((pte = walk(pagetable, a, 0)) == 0)
panic("uvmunmap: walk"); panic("uvmunmap: walk");
if((*pte & PTE_V) == 0) if((*pte & PTE_V) == 0) {
printf("va=%p pte=%p\n", a, *pte);
panic("uvmunmap: not mapped"); panic("uvmunmap: not mapped");
}
if(PTE_FLAGS(*pte) == PTE_V) if(PTE_FLAGS(*pte) == PTE_V)
panic("uvmunmap: not a leaf"); panic("uvmunmap: not a leaf");
if(do_free){ if(do_free){
@ -437,3 +449,6 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
return -1; return -1;
} }
} }

BIN
packets.pcap Normal file

Binary file not shown.

12
ping.py Normal file
View File

@ -0,0 +1,12 @@
import socket
import sys
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('localhost', int(sys.argv[1]))
buf = "this is a ping!".encode('utf-8')
while True:
print("pinging...", file=sys.stderr)
sock.sendto(buf, ("127.0.0.1", int(sys.argv[1])))
time.sleep(1)

13
server.py Normal file
View File

@ -0,0 +1,13 @@
import socket
import sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('localhost', int(sys.argv[1]))
print('listening on %s port %s' % addr, file=sys.stderr)
sock.bind(addr)
while True:
buf, raddr = sock.recvfrom(4096)
print(buf.decode("utf-8"), file=sys.stderr)
if buf:
sent = sock.sendto(b'this is the host!', raddr)

1
time.txt Normal file
View File

@ -0,0 +1 @@
5

View File

@ -1,5 +1,6 @@
#include "kernel/types.h" #include "kernel/types.h"
#include "kernel/stat.h" #include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h" #include "user/user.h"
char buf[512]; char buf[512];
@ -32,7 +33,7 @@ main(int argc, char *argv[])
} }
for(i = 1; i < argc; i++){ for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){ if((fd = open(argv[i], O_RDONLY)) < 0){
fprintf(2, "cat: cannot open %s\n", argv[i]); fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }

View File

@ -2,6 +2,7 @@
#include "kernel/types.h" #include "kernel/types.h"
#include "kernel/stat.h" #include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h" #include "user/user.h"
char buf[1024]; char buf[1024];
@ -51,7 +52,7 @@ main(int argc, char *argv[])
} }
for(i = 2; i < argc; i++){ for(i = 2; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){ if((fd = open(argv[i], O_RDONLY)) < 0){
printf("grep: cannot open %s\n", argv[i]); printf("grep: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }

View File

@ -2,6 +2,7 @@
#include "kernel/stat.h" #include "kernel/stat.h"
#include "user/user.h" #include "user/user.h"
#include "kernel/fs.h" #include "kernel/fs.h"
#include "kernel/fcntl.h"
char* char*
fmtname(char *path) fmtname(char *path)
@ -30,7 +31,7 @@ ls(char *path)
struct dirent de; struct dirent de;
struct stat st; struct stat st;
if((fd = open(path, 0)) < 0){ if((fd = open(path, O_RDONLY)) < 0){
fprintf(2, "ls: cannot open %s\n", path); fprintf(2, "ls: cannot open %s\n", path);
return; return;
} }

297
user/nettests.c Normal file
View File

@ -0,0 +1,297 @@
#include "kernel/types.h"
#include "kernel/net.h"
#include "kernel/stat.h"
#include "user/user.h"
//
// send a UDP packet to the localhost (outside of qemu),
// and receive a response.
//
static void
ping(uint16 sport, uint16 dport, int attempts)
{
int fd;
char *obuf = "a message from xv6!";
uint32 dst;
// 10.0.2.2, which qemu remaps to the external host,
// i.e. the machine you're running qemu on.
dst = (10 << 24) | (0 << 16) | (2 << 8) | (2 << 0);
// you can send a UDP packet to any Internet address
// by using a different dst.
if((fd = connect(dst, sport, dport)) < 0){
fprintf(2, "ping: connect() failed\n");
exit(1);
}
for(int i = 0; i < attempts; i++) {
if(write(fd, obuf, strlen(obuf)) < 0){
fprintf(2, "ping: send() failed\n");
exit(1);
}
}
char ibuf[128];
int cc = read(fd, ibuf, sizeof(ibuf)-1);
if(cc < 0){
fprintf(2, "ping: recv() failed\n");
exit(1);
}
close(fd);
ibuf[cc] = '\0';
if(strcmp(ibuf, "this is the host!") != 0){
fprintf(2, "ping didn't receive correct payload\n");
exit(1);
}
}
// Encode a DNS name
static void
encode_qname(char *qn, char *host)
{
char *l = host;
for(char *c = host; c < host+strlen(host)+1; c++) {
if(*c == '.') {
*qn++ = (char) (c-l);
for(char *d = l; d < c; d++) {
*qn++ = *d;
}
l = c+1; // skip .
}
}
*qn = '\0';
}
// Decode a DNS name
static void
decode_qname(char *qn, int max)
{
char *qnMax = qn + max;
while(1){
if(qn >= qnMax){
printf("invalid DNS reply\n");
exit(1);
}
int l = *qn;
if(l == 0)
break;
for(int i = 0; i < l; i++) {
*qn = *(qn+1);
qn++;
}
*qn++ = '.';
}
}
// Make a DNS request
static int
dns_req(uint8 *obuf)
{
int len = 0;
struct dns *hdr = (struct dns *) obuf;
hdr->id = htons(6828);
hdr->rd = 1;
hdr->qdcount = htons(1);
len += sizeof(struct dns);
// qname part of question
char *qname = (char *) (obuf + sizeof(struct dns));
char *s = "pdos.csail.mit.edu.";
encode_qname(qname, s);
len += strlen(qname) + 1;
// constants part of question
struct dns_question *h = (struct dns_question *) (qname+strlen(qname)+1);
h->qtype = htons(0x1);
h->qclass = htons(0x1);
len += sizeof(struct dns_question);
return len;
}
// Process DNS response
static void
dns_rep(uint8 *ibuf, int cc)
{
struct dns *hdr = (struct dns *) ibuf;
int len;
char *qname = 0;
int record = 0;
if(cc < sizeof(struct dns)){
printf("DNS reply too short\n");
exit(1);
}
if(!hdr->qr) {
printf("Not a DNS reply for %d\n", ntohs(hdr->id));
exit(1);
}
if(hdr->id != htons(6828)){
printf("DNS wrong id: %d\n", ntohs(hdr->id));
exit(1);
}
if(hdr->rcode != 0) {
printf("DNS rcode error: %x\n", hdr->rcode);
exit(1);
}
//printf("qdcount: %x\n", ntohs(hdr->qdcount));
//printf("ancount: %x\n", ntohs(hdr->ancount));
//printf("nscount: %x\n", ntohs(hdr->nscount));
//printf("arcount: %x\n", ntohs(hdr->arcount));
len = sizeof(struct dns);
for(int i =0; i < ntohs(hdr->qdcount); i++) {
char *qn = (char *) (ibuf+len);
qname = qn;
decode_qname(qn, cc - len);
len += strlen(qn)+1;
len += sizeof(struct dns_question);
}
for(int i = 0; i < ntohs(hdr->ancount); i++) {
if(len >= cc){
printf("invalid DNS reply\n");
exit(1);
}
char *qn = (char *) (ibuf+len);
if((int) qn[0] > 63) { // compression?
qn = (char *)(ibuf+qn[1]);
len += 2;
} else {
decode_qname(qn, cc - len);
len += strlen(qn)+1;
}
struct dns_data *d = (struct dns_data *) (ibuf+len);
len += sizeof(struct dns_data);
//printf("type %d ttl %d len %d\n", ntohs(d->type), ntohl(d->ttl), ntohs(d->len));
if(ntohs(d->type) == ARECORD && ntohs(d->len) == 4) {
record = 1;
printf("DNS arecord for %s is ", qname ? qname : "" );
uint8 *ip = (ibuf+len);
printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
if(ip[0] != 128 || ip[1] != 52 || ip[2] != 129 || ip[3] != 126) {
printf("wrong ip address");
exit(1);
}
len += 4;
}
}
// needed for DNS servers with EDNS support
for(int i = 0; i < ntohs(hdr->arcount); i++) {
char *qn = (char *) (ibuf+len);
if(*qn != 0) {
printf("invalid name for EDNS\n");
exit(1);
}
len += 1;
struct dns_data *d = (struct dns_data *) (ibuf+len);
len += sizeof(struct dns_data);
if(ntohs(d->type) != 41) {
printf("invalid type for EDNS\n");
exit(1);
}
len += ntohs(d->len);
}
if(len != cc) {
printf("Processed %d data bytes but received %d\n", len, cc);
exit(1);
}
if(!record) {
printf("Didn't receive an arecord\n");
exit(1);
}
}
static void
dns()
{
#define N 1000
uint8 obuf[N];
uint8 ibuf[N];
uint32 dst;
int fd;
int len;
memset(obuf, 0, N);
memset(ibuf, 0, N);
// 8.8.8.8: google's name server
dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0);
if((fd = connect(dst, 10000, 53)) < 0){
fprintf(2, "ping: connect() failed\n");
exit(1);
}
len = dns_req(obuf);
if(write(fd, obuf, len) < 0){
fprintf(2, "dns: send() failed\n");
exit(1);
}
int cc = read(fd, ibuf, sizeof(ibuf));
if(cc < 0){
fprintf(2, "dns: recv() failed\n");
exit(1);
}
dns_rep(ibuf, cc);
close(fd);
}
int
main(int argc, char *argv[])
{
int i, ret;
uint16 dport = NET_TESTS_PORT;
printf("nettests running on port %d\n", dport);
printf("testing ping: ");
ping(2000, dport, 1);
printf("OK\n");
printf("testing single-process pings: ");
for (i = 0; i < 100; i++)
ping(2000, dport, 1);
printf("OK\n");
printf("testing multi-process pings: ");
for (i = 0; i < 10; i++){
int pid = fork();
if (pid == 0){
ping(2000 + i + 1, dport, 1);
exit(0);
}
}
for (i = 0; i < 10; i++){
wait(&ret);
if (ret != 0)
exit(1);
}
printf("OK\n");
printf("testing DNS\n");
dns();
printf("DNS OK\n");
printf("all tests passed.\n");
exit(0);
}

52
user/pingpong.c Normal file
View File

@ -0,0 +1,52 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define N 5
char buf[N];
void
pong(int *parent_to_child, int *child_to_parent) {
if (read(parent_to_child[0], buf, N) < 0) {
printf("read failed\n");
}
printf("%d: received %s\n", getpid(), buf);
if (write(child_to_parent[1], "pong", 4) != 4) {
printf("write failed\n");
}
}
void
ping(int *parent_to_child, int *child_to_parent) {
if (write(parent_to_child[1], "ping", 4) != 4) {
printf("write failed\n");
}
if (read(child_to_parent[0], buf, N) < 0) {
printf("read failed\n");
}
printf("%d: received %s\n", getpid(), buf);
}
int
main(int argc, char *argv[])
{
int parent_to_child[2];
int child_to_parent[2];
int pid;
if (pipe(parent_to_child) < 0 || pipe(child_to_parent) < 0) {
printf("pipe failed\n");
}
if ((pid = fork()) < 0) {
printf("fork failed\n");
}
if (pid == 0) {
pong(parent_to_child, child_to_parent);
} else {
ping(parent_to_child, child_to_parent);
}
exit(0);
}

View File

@ -22,6 +22,14 @@ int getpid(void);
char* sbrk(int); char* sbrk(int);
int sleep(int); int sleep(int);
int uptime(void); int uptime(void);
#ifdef LAB_NET
int connect(uint32, uint16, uint16);
#endif
#ifdef LAB_PGTBL
int pgaccess(void *base, int len, void *mask);
// usyscall region
int ugetpid(void);
#endif
// ulib.c // ulib.c
int stat(const char*, struct stat*); int stat(const char*, struct stat*);
@ -39,3 +47,4 @@ void free(void*);
int atoi(const char*); int atoi(const char*);
int memcmp(const void *, const void *, uint); int memcmp(const void *, const void *, uint);
void *memcpy(void *, const void *, uint); void *memcpy(void *, const void *, uint);
int statistics(void*, int);

View File

@ -36,3 +36,5 @@ entry("getpid");
entry("sbrk"); entry("sbrk");
entry("sleep"); entry("sleep");
entry("uptime"); entry("uptime");
entry("connect");
entry("pgaccess");

View File

@ -1,5 +1,6 @@
#include "kernel/types.h" #include "kernel/types.h"
#include "kernel/stat.h" #include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h" #include "user/user.h"
char buf[512]; char buf[512];
@ -43,7 +44,7 @@ main(int argc, char *argv[])
} }
for(i = 1; i < argc; i++){ for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){ if((fd = open(argv[i], O_RDONLY)) < 0){
printf("wc: cannot open %s\n", argv[i]); printf("wc: cannot open %s\n", argv[i]);
exit(1); exit(1);
} }