mirror of
https://github.com/SmallPond/MIT6.828_OS.git
synced 2026-02-03 11:03:16 +08:00
LAB 4 IS DONE.
This commit is contained in:
37
lab/CODING
Normal file
37
lab/CODING
Normal file
@@ -0,0 +1,37 @@
|
||||
JOS CODING STANDARDS
|
||||
|
||||
It's easier on everyone if all authors working on a shared
|
||||
code base are consistent in the way they write their programs.
|
||||
We have the following conventions in our code:
|
||||
|
||||
* No space after the name of a function in a call
|
||||
For example, printf("hello") not printf ("hello").
|
||||
|
||||
* One space after keywords "if", "for", "while", "switch".
|
||||
For example, if (x) not if(x).
|
||||
|
||||
* Space before braces.
|
||||
For example, if (x) { not if (x){.
|
||||
|
||||
* Function names are all lower-case separated by underscores.
|
||||
|
||||
* Beginning-of-line indentation via tabs, not spaces.
|
||||
|
||||
* Preprocessor macros are always UPPERCASE.
|
||||
There are a few grandfathered exceptions: assert, panic,
|
||||
static_assert, offsetof.
|
||||
|
||||
* Pointer types have spaces: (uint16_t *) not (uint16_t*).
|
||||
|
||||
* Multi-word names are lower_case_with_underscores.
|
||||
|
||||
* Comments in imported code are usually C /* ... */ comments.
|
||||
Comments in new code are C++ style //.
|
||||
|
||||
* In a function definition, the function name starts a new line.
|
||||
Then you can grep -n '^foo' */*.c to find the definition of foo.
|
||||
|
||||
* Functions that take no arguments are declared f(void) not f().
|
||||
|
||||
The included .dir-locals.el file will automatically set up the basic
|
||||
indentation style in Emacs.
|
||||
338
lab/GNUmakefile
Normal file
338
lab/GNUmakefile
Normal file
@@ -0,0 +1,338 @@
|
||||
#
|
||||
# This makefile system follows the structuring conventions
|
||||
# recommended by Peter Miller in his excellent paper:
|
||||
#
|
||||
# Recursive Make Considered Harmful
|
||||
# http://aegis.sourceforge.net/auug97.pdf
|
||||
#
|
||||
OBJDIR := obj
|
||||
|
||||
# Run 'make V=1' to turn on verbose commands, or 'make V=0' to turn them off.
|
||||
ifeq ($(V),1)
|
||||
override V =
|
||||
endif
|
||||
ifeq ($(V),0)
|
||||
override V = @
|
||||
endif
|
||||
|
||||
-include conf/lab.mk
|
||||
|
||||
-include conf/env.mk
|
||||
|
||||
LABSETUP ?= ./
|
||||
|
||||
TOP = .
|
||||
|
||||
# Cross-compiler jos toolchain
|
||||
#
|
||||
# This Makefile will automatically use the cross-compiler toolchain
|
||||
# installed as 'i386-jos-elf-*', if one exists. If the host tools ('gcc',
|
||||
# 'objdump', and so forth) compile for a 32-bit x86 ELF target, that will
|
||||
# be detected as well. If you have the right compiler toolchain installed
|
||||
# using a different name, set GCCPREFIX explicitly in conf/env.mk
|
||||
|
||||
# try to infer the correct GCCPREFIX
|
||||
ifndef GCCPREFIX
|
||||
GCCPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
|
||||
then echo 'i386-jos-elf-'; \
|
||||
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
|
||||
then echo ''; \
|
||||
else echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
|
||||
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
|
||||
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
|
||||
echo "*** prefix other than 'i386-jos-elf-', set your GCCPREFIX" 1>&2; \
|
||||
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
|
||||
echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \
|
||||
echo "***" 1>&2; exit 1; fi)
|
||||
endif
|
||||
|
||||
# try to infer the correct QEMU
|
||||
ifndef QEMU
|
||||
QEMU := $(shell if which qemu >/dev/null 2>&1; \
|
||||
then echo qemu; exit; \
|
||||
elif which qemu-system-i386 >/dev/null 2>&1; \
|
||||
then echo qemu-system-i386; exit; \
|
||||
else \
|
||||
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
|
||||
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
|
||||
echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
|
||||
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
|
||||
echo "*** or have you tried setting the QEMU variable in conf/env.mk?" 1>&2; \
|
||||
echo "***" 1>&2; exit 1)
|
||||
endif
|
||||
|
||||
# try to generate a unique GDB port
|
||||
GDBPORT := $(shell expr `id -u` % 5000 + 25000)
|
||||
|
||||
CC := $(GCCPREFIX)gcc -pipe
|
||||
GDB := $(GCCPREFIX)gdb
|
||||
AS := $(GCCPREFIX)as
|
||||
AR := $(GCCPREFIX)ar
|
||||
LD := $(GCCPREFIX)ld
|
||||
OBJCOPY := $(GCCPREFIX)objcopy
|
||||
OBJDUMP := $(GCCPREFIX)objdump
|
||||
NM := $(GCCPREFIX)nm
|
||||
|
||||
# Native commands
|
||||
NCC := gcc $(CC_VER) -pipe
|
||||
NATIVE_CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -I$(TOP) -MD -Wall
|
||||
TAR := gtar
|
||||
PERL := perl
|
||||
|
||||
# Compiler flags
|
||||
# -fno-builtin is required to avoid refs to undefined functions in the kernel.
|
||||
# Only optimize to -O1 to discourage inlining, which complicates backtraces.
|
||||
CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -O1 -fno-builtin -I$(TOP) -MD
|
||||
CFLAGS += -fno-omit-frame-pointer
|
||||
CFLAGS += -std=gnu99
|
||||
CFLAGS += -static
|
||||
CFLAGS += -fno-pie
|
||||
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
||||
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
|
||||
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
|
||||
CFLAGS += -fno-tree-ch
|
||||
|
||||
# Add -fno-stack-protector if the option exists.
|
||||
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||
|
||||
# Common linker flags
|
||||
LDFLAGS := -m elf_i386
|
||||
|
||||
# Linker flags for JOS user programs
|
||||
ULDFLAGS := -T user/user.ld
|
||||
|
||||
GCC_LIB := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
|
||||
# Lists that the */Makefrag makefile fragments will add to
|
||||
OBJDIRS :=
|
||||
|
||||
# Make sure that 'all' is the first target
|
||||
all:
|
||||
|
||||
# Eliminate default suffix rules
|
||||
.SUFFIXES:
|
||||
|
||||
# Delete target files if there is an error (or make is interrupted)
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
# make it so that no intermediate .o files are ever deleted
|
||||
.PRECIOUS: %.o $(OBJDIR)/boot/%.o $(OBJDIR)/kern/%.o \
|
||||
$(OBJDIR)/lib/%.o $(OBJDIR)/fs/%.o $(OBJDIR)/net/%.o \
|
||||
$(OBJDIR)/user/%.o
|
||||
|
||||
KERN_CFLAGS := $(CFLAGS) -DJOS_KERNEL -gstabs
|
||||
USER_CFLAGS := $(CFLAGS) -DJOS_USER -gstabs
|
||||
|
||||
# Update .vars.X if variable X has changed since the last make run.
|
||||
#
|
||||
# Rules that use variable X should depend on $(OBJDIR)/.vars.X. If
|
||||
# the variable's value has changed, this will update the vars file and
|
||||
# force a rebuild of the rule that depends on it.
|
||||
$(OBJDIR)/.vars.%: FORCE
|
||||
$(V)echo "$($*)" | cmp -s $@ || echo "$($*)" > $@
|
||||
.PRECIOUS: $(OBJDIR)/.vars.%
|
||||
.PHONY: FORCE
|
||||
|
||||
|
||||
# Include Makefrags for subdirectories
|
||||
include boot/Makefrag
|
||||
include kern/Makefrag
|
||||
include lib/Makefrag
|
||||
include user/Makefrag
|
||||
|
||||
|
||||
CPUS ?= 1
|
||||
|
||||
QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT)
|
||||
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
|
||||
IMAGES = $(OBJDIR)/kern/kernel.img
|
||||
QEMUOPTS += -smp $(CPUS)
|
||||
QEMUOPTS += $(QEMUEXTRA)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl
|
||||
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||
|
||||
gdb:
|
||||
$(GDB) -n -x .gdbinit
|
||||
|
||||
pre-qemu: .gdbinit
|
||||
|
||||
qemu: $(IMAGES) pre-qemu
|
||||
$(QEMU) $(QEMUOPTS)
|
||||
|
||||
qemu-nox: $(IMAGES) pre-qemu
|
||||
@echo "***"
|
||||
@echo "*** Use Ctrl-a x to exit qemu"
|
||||
@echo "***"
|
||||
$(QEMU) -nographic $(QEMUOPTS)
|
||||
|
||||
qemu-gdb: $(IMAGES) pre-qemu
|
||||
@echo "***"
|
||||
@echo "*** Now run 'make gdb'." 1>&2
|
||||
@echo "***"
|
||||
$(QEMU) $(QEMUOPTS) -S
|
||||
|
||||
qemu-nox-gdb: $(IMAGES) pre-qemu
|
||||
@echo "***"
|
||||
@echo "*** Now run 'make gdb'." 1>&2
|
||||
@echo "***"
|
||||
$(QEMU) -nographic $(QEMUOPTS) -S
|
||||
|
||||
print-qemu:
|
||||
@echo $(QEMU)
|
||||
|
||||
print-gdbport:
|
||||
@echo $(GDBPORT)
|
||||
|
||||
# For deleting the build
|
||||
clean:
|
||||
rm -rf $(OBJDIR) .gdbinit jos.in qemu.log
|
||||
|
||||
realclean: clean
|
||||
rm -rf lab$(LAB).tar.gz \
|
||||
jos.out $(wildcard jos.out.*) \
|
||||
qemu.pcap $(wildcard qemu.pcap.*) \
|
||||
myapi.key
|
||||
|
||||
distclean: realclean
|
||||
rm -rf conf/gcc.mk
|
||||
|
||||
ifneq ($(V),@)
|
||||
GRADEFLAGS += -v
|
||||
endif
|
||||
|
||||
grade:
|
||||
@echo $(MAKE) clean
|
||||
@$(MAKE) clean || \
|
||||
(echo "'make clean' failed. HINT: Do you have another running instance of JOS?" && exit 1)
|
||||
./grade-lab$(LAB) $(GRADEFLAGS)
|
||||
|
||||
git-handin: handin-check
|
||||
@if test -n "`git config remote.handin.url`"; then \
|
||||
echo "Hand in to remote repository using 'git push handin HEAD' ..."; \
|
||||
if ! git push -f handin HEAD; then \
|
||||
echo ; \
|
||||
echo "Hand in failed."; \
|
||||
echo "As an alternative, please run 'make tarball'"; \
|
||||
echo "and visit http://pdos.csail.mit.edu/6.828/submit/"; \
|
||||
echo "to upload lab$(LAB)-handin.tar.gz. Thanks!"; \
|
||||
false; \
|
||||
fi; \
|
||||
else \
|
||||
echo "Hand-in repository is not configured."; \
|
||||
echo "Please run 'make handin-prep' first. Thanks!"; \
|
||||
false; \
|
||||
fi
|
||||
|
||||
WEBSUB := https://6828.scripts.mit.edu/2018/handin.py
|
||||
|
||||
handin: tarball-pref myapi.key
|
||||
@SUF=$(LAB); \
|
||||
test -f .suf && SUF=`cat .suf`; \
|
||||
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$(LAB); then \
|
||||
git branch; \
|
||||
read -p "You are not on the lab$(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 "pdos.csail.mit.edu/6.828/2018/jos.git (fetch)" | awk '{split($$0,a," "); print a[1]}')
|
||||
|
||||
tarball-pref: handin-check
|
||||
@SUF=$(LAB); \
|
||||
if test $(LAB) -eq 3 -o $(LAB) -eq 4; then \
|
||||
read -p "Which part would you like to submit? [a, b, c (c for lab 4 only)]" p; \
|
||||
if test "$$p" != a -a "$$p" != b; then \
|
||||
if test ! $(LAB) -eq 4 -o ! "$$p" = c; then \
|
||||
echo "Bad part \"$$p\""; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
SUF="$(LAB)$$p"; \
|
||||
echo $$SUF > .suf; \
|
||||
else \
|
||||
rm -f .suf; \
|
||||
fi; \
|
||||
git archive --format=tar HEAD > lab$$SUF-handin.tar; \
|
||||
git diff $(UPSTREAM)/lab$(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;
|
||||
|
||||
#handin-prep:
|
||||
# @./handin-prep
|
||||
|
||||
# For test runs
|
||||
|
||||
prep-%:
|
||||
$(V)$(MAKE) "INIT_CFLAGS=${INIT_CFLAGS} -DTEST=`case $* in *_*) echo $*;; *) echo user_$*;; esac`" $(IMAGES)
|
||||
|
||||
run-%-nox-gdb: prep-% pre-qemu
|
||||
$(QEMU) -nographic $(QEMUOPTS) -S
|
||||
|
||||
run-%-gdb: prep-% pre-qemu
|
||||
$(QEMU) $(QEMUOPTS) -S
|
||||
|
||||
run-%-nox: prep-% pre-qemu
|
||||
$(QEMU) -nographic $(QEMUOPTS)
|
||||
|
||||
run-%: prep-% pre-qemu
|
||||
$(QEMU) $(QEMUOPTS)
|
||||
|
||||
# This magic automatically generates makefile dependencies
|
||||
# for header files included from C source files we compile,
|
||||
# and keeps those dependencies up-to-date every time we recompile.
|
||||
# See 'mergedep.pl' for more information.
|
||||
$(OBJDIR)/.deps: $(foreach dir, $(OBJDIRS), $(wildcard $(OBJDIR)/$(dir)/*.d))
|
||||
@mkdir -p $(@D)
|
||||
@$(PERL) mergedep.pl $@ $^
|
||||
|
||||
-include $(OBJDIR)/.deps
|
||||
|
||||
always:
|
||||
@:
|
||||
|
||||
.PHONY: all always \
|
||||
handin git-handin tarball tarball-pref clean realclean distclean grade handin-prep handin-check
|
||||
32
lab/boot/Makefrag
Normal file
32
lab/boot/Makefrag
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Makefile fragment for the JOS kernel.
|
||||
# This is NOT a complete makefile;
|
||||
# you must run GNU make in the top-level directory
|
||||
# where the GNUmakefile is located.
|
||||
#
|
||||
|
||||
OBJDIRS += boot
|
||||
|
||||
BOOT_OBJS := $(OBJDIR)/boot/boot.o $(OBJDIR)/boot/main.o
|
||||
|
||||
$(OBJDIR)/boot/%.o: boot/%.c
|
||||
@echo + cc -Os $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/boot/%.o: boot/%.S
|
||||
@echo + as $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/boot/main.o: boot/main.c
|
||||
@echo + cc -Os $<
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $(OBJDIR)/boot/main.o boot/main.c
|
||||
|
||||
$(OBJDIR)/boot/boot: $(BOOT_OBJS)
|
||||
@echo + ld boot/boot
|
||||
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^
|
||||
$(V)$(OBJDUMP) -S $@.out >$@.asm
|
||||
$(V)$(OBJCOPY) -S -O binary -j .text $@.out $@
|
||||
$(V)perl boot/sign.pl $(OBJDIR)/boot/boot
|
||||
|
||||
85
lab/boot/boot.S
Normal file
85
lab/boot/boot.S
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <inc/mmu.h>
|
||||
|
||||
# Start the CPU: switch to 32-bit protected mode, jump into C.
|
||||
# The BIOS loads this code from the first sector of the hard disk into
|
||||
# memory at physical address 0x7c00 and starts executing in real mode
|
||||
# with %cs=0 %ip=7c00.
|
||||
|
||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
||||
.set CR0_PE_ON, 0x1 # protected mode enable flag
|
||||
|
||||
.globl start
|
||||
start:
|
||||
.code16 # Assemble for 16-bit mode
|
||||
cli # Disable interrupts
|
||||
cld # String operations increment
|
||||
|
||||
# Set up the important data segment registers (DS, ES, SS).
|
||||
xorw %ax,%ax # Segment number zero
|
||||
movw %ax,%ds # -> Data Segment
|
||||
movw %ax,%es # -> Extra Segment
|
||||
movw %ax,%ss # -> Stack Segment
|
||||
|
||||
# Enable A20:
|
||||
# For backwards compatibility with the earliest PCs, physical
|
||||
# address line 20 is tied low, so that addresses higher than
|
||||
# 1MB wrap around to zero by default. This code undoes this.
|
||||
seta20.1:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.1
|
||||
|
||||
movb $0xd1,%al # 0xd1 -> port 0x64
|
||||
outb %al,$0x64
|
||||
|
||||
seta20.2:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.2
|
||||
|
||||
movb $0xdf,%al # 0xdf -> port 0x60
|
||||
outb %al,$0x60
|
||||
|
||||
# Switch from real to protected mode, using a bootstrap GDT
|
||||
# and segment translation that makes virtual addresses
|
||||
# identical to their physical addresses, so that the
|
||||
# effective memory map does not change during the switch.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE_ON, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Jump to next instruction, but in 32-bit code segment.
|
||||
# Switches processor into 32-bit mode.
|
||||
ljmp $PROT_MODE_CSEG, $protcseg
|
||||
|
||||
.code32 # Assemble for 32-bit mode
|
||||
protcseg:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $PROT_MODE_DSEG, %ax # Our data segment selector
|
||||
movw %ax, %ds # -> DS: Data Segment
|
||||
movw %ax, %es # -> ES: Extra Segment
|
||||
movw %ax, %fs # -> FS
|
||||
movw %ax, %gs # -> GS
|
||||
movw %ax, %ss # -> SS: Stack Segment
|
||||
|
||||
# Set up the stack pointer and call into C.
|
||||
movl $start, %esp
|
||||
call bootmain
|
||||
|
||||
# If bootmain returns (it shouldn't), loop.
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
gdt:
|
||||
SEG_NULL # null seg
|
||||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||
SEG(STA_W, 0x0, 0xffffffff) # data seg
|
||||
|
||||
gdtdesc:
|
||||
.word 0x17 # sizeof(gdt) - 1
|
||||
.long gdt # address gdt
|
||||
|
||||
130
lab/boot/main.c
Normal file
130
lab/boot/main.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <inc/x86.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
/**********************************************************************
|
||||
* This a dirt simple boot loader, whose sole job is to boot
|
||||
* an ELF kernel image from the first IDE hard disk.
|
||||
*
|
||||
* DISK LAYOUT
|
||||
* * This program(boot.S and main.c) is the bootloader. It should
|
||||
* be stored in the first sector of the disk.
|
||||
*
|
||||
* * The 2nd sector onward holds the kernel image.
|
||||
*
|
||||
* * The kernel image must be in ELF format.
|
||||
*
|
||||
* BOOT UP STEPS
|
||||
* * when the CPU boots it loads the BIOS into memory and executes it
|
||||
*
|
||||
* * the BIOS intializes devices, sets of the interrupt routines, and
|
||||
* reads the first sector of the boot device(e.g., hard-drive)
|
||||
* into memory and jumps to it.
|
||||
*
|
||||
* * Assuming this boot loader is stored in the first sector of the
|
||||
* hard-drive, this code takes over...
|
||||
*
|
||||
* * control starts in boot.S -- which sets up protected mode,
|
||||
* and a stack so C code then run, then calls bootmain()
|
||||
*
|
||||
* * bootmain() in this file takes over, reads in the kernel and jumps to it.
|
||||
**********************************************************************/
|
||||
|
||||
#define SECTSIZE 512
|
||||
#define ELFHDR ((struct Elf *) 0x10000) // scratch space
|
||||
|
||||
void readsect(void*, uint32_t);
|
||||
void readseg(uint32_t, uint32_t, uint32_t);
|
||||
|
||||
void
|
||||
bootmain(void)
|
||||
{
|
||||
struct Proghdr *ph, *eph;
|
||||
int i;
|
||||
|
||||
// read 1st page off disk
|
||||
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
||||
|
||||
// is this a valid ELF?
|
||||
if (ELFHDR->e_magic != ELF_MAGIC)
|
||||
goto bad;
|
||||
|
||||
// load each program segment (ignores ph flags)
|
||||
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
||||
eph = ph + ELFHDR->e_phnum;
|
||||
for (; ph < eph; ph++) {
|
||||
// p_pa is the load address of this segment (as well
|
||||
// as the physical address)
|
||||
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
|
||||
for (i = 0; i < ph->p_memsz - ph->p_filesz; i++) {
|
||||
*((char *) ph->p_pa + ph->p_filesz + i) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// call the entry point from the ELF header
|
||||
// note: does not return!
|
||||
((void (*)(void)) (ELFHDR->e_entry))();
|
||||
|
||||
bad:
|
||||
outw(0x8A00, 0x8A00);
|
||||
outw(0x8A00, 0x8E00);
|
||||
while (1)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
|
||||
// Might copy more than asked
|
||||
void
|
||||
readseg(uint32_t pa, uint32_t count, uint32_t offset)
|
||||
{
|
||||
uint32_t end_pa;
|
||||
|
||||
end_pa = pa + count;
|
||||
|
||||
// round down to sector boundary
|
||||
pa &= ~(SECTSIZE - 1);
|
||||
|
||||
// translate from bytes to sectors, and kernel starts at sector 1
|
||||
offset = (offset / SECTSIZE) + 1;
|
||||
|
||||
// If this is too slow, we could read lots of sectors at a time.
|
||||
// We'd write more to memory than asked, but it doesn't matter --
|
||||
// we load in increasing order.
|
||||
while (pa < end_pa) {
|
||||
// Since we haven't enabled paging yet and we're using
|
||||
// an identity segment mapping (see boot.S), we can
|
||||
// use physical addresses directly. This won't be the
|
||||
// case once JOS enables the MMU.
|
||||
readsect((uint8_t*) pa, offset);
|
||||
pa += SECTSIZE;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
waitdisk(void)
|
||||
{
|
||||
// wait for disk reaady
|
||||
while ((inb(0x1F7) & 0xC0) != 0x40)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
void
|
||||
readsect(void *dst, uint32_t offset)
|
||||
{
|
||||
// wait for disk to be ready
|
||||
waitdisk();
|
||||
|
||||
outb(0x1F2, 1); // count = 1
|
||||
outb(0x1F3, offset);
|
||||
outb(0x1F4, offset >> 8);
|
||||
outb(0x1F5, offset >> 16);
|
||||
outb(0x1F6, (offset >> 24) | 0xE0);
|
||||
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
||||
|
||||
// wait for disk to be ready
|
||||
waitdisk();
|
||||
|
||||
// read a sector
|
||||
insl(0x1F0, dst, SECTSIZE/4);
|
||||
}
|
||||
|
||||
23
lab/boot/sign.pl
Normal file
23
lab/boot/sign.pl
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
open(BB, $ARGV[0]) || die "open $ARGV[0]: $!";
|
||||
|
||||
binmode BB;
|
||||
my $buf;
|
||||
read(BB, $buf, 1000);
|
||||
$n = length($buf);
|
||||
|
||||
if($n > 510){
|
||||
print STDERR "boot block too large: $n bytes (max 510)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print STDERR "boot block is $n bytes (max 510)\n";
|
||||
|
||||
$buf .= "\0" x (510-$n);
|
||||
$buf .= "\x55\xAA";
|
||||
|
||||
open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
|
||||
binmode BB;
|
||||
print BB $buf;
|
||||
close BB;
|
||||
20
lab/conf/env.mk
Normal file
20
lab/conf/env.mk
Normal file
@@ -0,0 +1,20 @@
|
||||
# env.mk - configuration variables for the JOS lab
|
||||
|
||||
# '$(V)' controls whether the lab makefiles print verbose commands (the
|
||||
# actual shell commands run by Make), as well as the "overview" commands
|
||||
# (such as '+ cc lib/readline.c').
|
||||
#
|
||||
# For overview commands only, the line should read 'V = @'.
|
||||
# For overview and verbose commands, the line should read 'V ='.
|
||||
V = @
|
||||
|
||||
# If your system-standard GNU toolchain is ELF-compatible, then comment
|
||||
# out the following line to use those tools (as opposed to the i386-jos-elf
|
||||
# tools that the 6.828 make system looks for by default).
|
||||
#
|
||||
# GCCPREFIX=''
|
||||
|
||||
# If the makefile cannot find your QEMU binary, uncomment the
|
||||
# following line and set it to the full path to QEMU.
|
||||
#
|
||||
# QEMU=
|
||||
2
lab/conf/lab.mk
Normal file
2
lab/conf/lab.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
LAB=4
|
||||
PACKAGEDATE=Mon Oct 8 21:31:51 PDT 2018
|
||||
67
lab/fs/test.c
Normal file
67
lab/fs/test.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <inc/x86.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
static char *msg = "This is the NEW message of the day!\n\n";
|
||||
|
||||
void
|
||||
fs_test(void)
|
||||
{
|
||||
struct File *f;
|
||||
int r;
|
||||
char *blk;
|
||||
uint32_t *bits;
|
||||
|
||||
// back up bitmap
|
||||
if ((r = sys_page_alloc(0, (void*) PGSIZE, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
bits = (uint32_t*) PGSIZE;
|
||||
memmove(bits, bitmap, PGSIZE);
|
||||
// allocate block
|
||||
if ((r = alloc_block()) < 0)
|
||||
panic("alloc_block: %e", r);
|
||||
// check that block was free
|
||||
assert(bits[r/32] & (1 << (r%32)));
|
||||
// and is not free any more
|
||||
assert(!(bitmap[r/32] & (1 << (r%32))));
|
||||
cprintf("alloc_block is good\n");
|
||||
|
||||
if ((r = file_open("/not-found", &f)) < 0 && r != -E_NOT_FOUND)
|
||||
panic("file_open /not-found: %e", r);
|
||||
else if (r == 0)
|
||||
panic("file_open /not-found succeeded!");
|
||||
if ((r = file_open("/newmotd", &f)) < 0)
|
||||
panic("file_open /newmotd: %e", r);
|
||||
cprintf("file_open is good\n");
|
||||
|
||||
if ((r = file_get_block(f, 0, &blk)) < 0)
|
||||
panic("file_get_block: %e", r);
|
||||
if (strcmp(blk, msg) != 0)
|
||||
panic("file_get_block returned wrong data");
|
||||
cprintf("file_get_block is good\n");
|
||||
|
||||
*(volatile char*)blk = *(volatile char*)blk;
|
||||
assert((uvpt[PGNUM(blk)] & PTE_D));
|
||||
file_flush(f);
|
||||
assert(!(uvpt[PGNUM(blk)] & PTE_D));
|
||||
cprintf("file_flush is good\n");
|
||||
|
||||
if ((r = file_set_size(f, 0)) < 0)
|
||||
panic("file_set_size: %e", r);
|
||||
assert(f->f_direct[0] == 0);
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
cprintf("file_truncate is good\n");
|
||||
|
||||
if ((r = file_set_size(f, strlen(msg))) < 0)
|
||||
panic("file_set_size 2: %e", r);
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
if ((r = file_get_block(f, 0, &blk)) < 0)
|
||||
panic("file_get_block 2: %e", r);
|
||||
strcpy(blk, msg);
|
||||
assert((uvpt[PGNUM(blk)] & PTE_D));
|
||||
file_flush(f);
|
||||
assert(!(uvpt[PGNUM(blk)] & PTE_D));
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
cprintf("file rewrite is good\n");
|
||||
}
|
||||
70
lab/fs/testshell.key
Normal file
70
lab/fs/testshell.key
Normal file
@@ -0,0 +1,70 @@
|
||||
# echo hello world | cat
|
||||
hello world
|
||||
# cat lorem
|
||||
Lorem ipsum dolor sit amet, consectetur
|
||||
adipisicing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit
|
||||
in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim
|
||||
id est laborum.
|
||||
# cat lorem |num
|
||||
1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 adipisicing elit, sed do eiusmod tempor
|
||||
3 incididunt ut labore et dolore magna
|
||||
4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 nostrud exercitation ullamco laboris
|
||||
6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 Duis aute irure dolor in reprehenderit
|
||||
8 in voluptate velit esse cillum dolore eu
|
||||
9 fugiat nulla pariatur. Excepteur sint
|
||||
10 occaecat cupidatat non proident, sunt in
|
||||
11 culpa qui officia deserunt mollit anim
|
||||
12 id est laborum.
|
||||
# cat lorem |num |num |num |num |num
|
||||
1 1 1 1 1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 2 2 2 2 adipisicing elit, sed do eiusmod tempor
|
||||
3 3 3 3 3 incididunt ut labore et dolore magna
|
||||
4 4 4 4 4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 5 5 5 5 nostrud exercitation ullamco laboris
|
||||
6 6 6 6 6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 7 7 7 7 Duis aute irure dolor in reprehenderit
|
||||
8 8 8 8 8 in voluptate velit esse cillum dolore eu
|
||||
9 9 9 9 9 fugiat nulla pariatur. Excepteur sint
|
||||
10 10 10 10 10 occaecat cupidatat non proident, sunt in
|
||||
11 11 11 11 11 culpa qui officia deserunt mollit anim
|
||||
12 12 12 12 12 id est laborum.
|
||||
# lsfd -1
|
||||
fd 0: name testshell.sh isdir 0 size 113 dev file
|
||||
fd 1: name <pipe> isdir 0 size 32 dev pipe
|
||||
fd 3: name <pipe> isdir 0 size 32 dev pipe
|
||||
# cat script
|
||||
echo This is from the script.
|
||||
cat lorem | num | cat
|
||||
echo These are my file descriptors.
|
||||
lsfd -1
|
||||
echo This is the end of the script.
|
||||
# sh <script
|
||||
This is from the script.
|
||||
1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 adipisicing elit, sed do eiusmod tempor
|
||||
3 incididunt ut labore et dolore magna
|
||||
4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 nostrud exercitation ullamco laboris
|
||||
6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 Duis aute irure dolor in reprehenderit
|
||||
8 in voluptate velit esse cillum dolore eu
|
||||
9 fugiat nulla pariatur. Excepteur sint
|
||||
10 occaecat cupidatat non proident, sunt in
|
||||
11 culpa qui officia deserunt mollit anim
|
||||
12 id est laborum.
|
||||
These are my file descriptors.
|
||||
fd 0: name script isdir 0 size 132 dev file
|
||||
fd 1: name <pipe> isdir 0 size 32 dev pipe
|
||||
fd 3: name <pipe> isdir 0 size 32 dev pipe
|
||||
This is the end of the script.
|
||||
44
lab/grade-lab1
Normal file
44
lab/grade-lab1
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
@test(0, "running JOS")
|
||||
def test_jos():
|
||||
r.run_qemu()
|
||||
|
||||
@test(20, parent=test_jos)
|
||||
def test_printf():
|
||||
r.match("6828 decimal is 15254 octal!")
|
||||
|
||||
BACKTRACE_RE = r"^ *ebp +f01[0-9a-z]{5} +eip +f0100[0-9a-z]{3} +args +([0-9a-z]+)"
|
||||
|
||||
@test(10, parent=test_jos)
|
||||
def test_backtrace_count():
|
||||
matches = re.findall(BACKTRACE_RE, r.qemu.output, re.MULTILINE)
|
||||
assert_equal(len(matches), 8)
|
||||
|
||||
@test(10, parent=test_jos)
|
||||
def test_backtrace_arguments():
|
||||
matches = re.findall(BACKTRACE_RE, r.qemu.output, re.MULTILINE)
|
||||
assert_equal("\n".join(matches[:7]),
|
||||
"\n".join("%08x" % n for n in [0,0,1,2,3,4,5]))
|
||||
|
||||
@test(5, parent=test_jos)
|
||||
def test_backtrace_symbols():
|
||||
matches = re.findall(r"kern/init.c:[0-9]+: +([^+]*)\+", r.qemu.output)
|
||||
assert_equal("\n".join(matches[:7]),
|
||||
"\n".join(["test_backtrace"] * 6 + ["i386_init"]))
|
||||
|
||||
@test(5, parent=test_jos)
|
||||
def test_backtrace_lines():
|
||||
matches = re.findall(r"([^ ]*init.c:([0-9]+):) +test_backtrace\+", r.qemu.output)
|
||||
assert matches, "No line numbers"
|
||||
if any(int(m[1]) < 5 or int(m[1]) > 50 for m in matches):
|
||||
assert_equal("\n".join(m[0] for m in matches),
|
||||
"Line numbers between 5 and 50")
|
||||
|
||||
run_tests()
|
||||
28
lab/grade-lab2
Normal file
28
lab/grade-lab2
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
@test(0, "running JOS")
|
||||
def test_jos():
|
||||
r.run_qemu()
|
||||
|
||||
@test(20, "Physical page allocator", parent=test_jos)
|
||||
def test_check_page_alloc():
|
||||
r.match(r"check_page_alloc\(\) succeeded!")
|
||||
|
||||
@test(20, "Page management", parent=test_jos)
|
||||
def test_check_page():
|
||||
r.match(r"check_page\(\) succeeded!")
|
||||
|
||||
@test(20, "Kernel page directory", parent=test_jos)
|
||||
def test_check_kern_pgdir():
|
||||
r.match(r"check_kern_pgdir\(\) succeeded!")
|
||||
|
||||
@test(10, "Page management 2", parent=test_jos)
|
||||
def test_check_page_installed_pgdir():
|
||||
r.match(r"check_page_installed_pgdir\(\) succeeded!")
|
||||
|
||||
run_tests()
|
||||
135
lab/grade-lab3
Normal file
135
lab/grade-lab3
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
@test(10)
|
||||
def test_divzero():
|
||||
r.user_test("divzero")
|
||||
r.match('Incoming TRAP frame at 0xefffff..',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x00000000 Divide error',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 00001000',
|
||||
no=['1/0 is ........!'])
|
||||
|
||||
@test(10)
|
||||
def test_softint():
|
||||
r.user_test("softint")
|
||||
r.match('Welcome to the JOS kernel monitor!',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000d General Protection',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(10)
|
||||
def test_badsegment():
|
||||
r.user_test("badsegment")
|
||||
r.match('Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000d General Protection',
|
||||
' err 0x00000028',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
end_part("A")
|
||||
|
||||
@test(5)
|
||||
def test_faultread():
|
||||
r.user_test("faultread")
|
||||
r.match('.00001000. user fault va 00000000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000004.*',
|
||||
'.00001000. free env 0000100',
|
||||
no=['I read ........ from location 0!'])
|
||||
|
||||
@test(5)
|
||||
def test_faultreadkernel():
|
||||
r.user_test("faultreadkernel")
|
||||
r.match('.00001000. user fault va f0100000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000005.*',
|
||||
'.00001000. free env 00001000',
|
||||
no=['I read ........ from location 0xf0100000!'])
|
||||
|
||||
@test(5)
|
||||
def test_faultwrite():
|
||||
r.user_test("faultwrite")
|
||||
r.match('.00001000. user fault va 00000000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000006.*',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_faultwritekernel():
|
||||
r.user_test("faultwritekernel")
|
||||
r.match('.00001000. user fault va f0100000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000007.*',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_breakpoint():
|
||||
r.user_test("breakpoint")
|
||||
r.match('Welcome to the JOS kernel monitor!',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x00000003 Breakpoint',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
no=['.00001000. free env 00001000'])
|
||||
|
||||
@test(5)
|
||||
def test_testbss():
|
||||
r.user_test("testbss")
|
||||
r.match('Making sure bss works right...',
|
||||
'Yes, good. Now doing a wild write off the end...',
|
||||
'.00001000. user fault va 00c..... ip 008.....',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_hello():
|
||||
r.user_test("hello")
|
||||
r.match('.00000000. new env 00001000',
|
||||
'hello, world',
|
||||
'i am environment 00001000',
|
||||
'.00001000. exiting gracefully',
|
||||
'.00001000. free env 00001000',
|
||||
'Destroyed the only environment - nothing more to do!')
|
||||
|
||||
@test(5)
|
||||
def test_buggyhello():
|
||||
r.user_test("buggyhello")
|
||||
r.match('.00001000. user_mem_check assertion failure for va 00000001',
|
||||
'.00001000. free env 00001000')
|
||||
|
||||
@test(5)
|
||||
def test_buggyhello2():
|
||||
r.user_test("buggyhello2")
|
||||
r.match('.00001000. user_mem_check assertion failure for va 0....000',
|
||||
'.00001000. free env 00001000',
|
||||
no=['hello, world'])
|
||||
|
||||
@test(5)
|
||||
def test_evilhello():
|
||||
r.user_test("evilhello")
|
||||
r.match('.00001000. user_mem_check assertion failure for va f0100...',
|
||||
'.00001000. free env 00001000')
|
||||
|
||||
end_part("B")
|
||||
|
||||
run_tests()
|
||||
190
lab/grade-lab4
Normal file
190
lab/grade-lab4
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
def E(s, trim=False):
|
||||
"""Expand $En in s to the environment ID of the n'th user
|
||||
environment."""
|
||||
|
||||
tmpl = "%x" if trim else "%08x"
|
||||
return re.sub(r"\$E([0-9]+)",
|
||||
lambda m: tmpl % (0x1000 + int(m.group(1))-1), s)
|
||||
|
||||
@test(5)
|
||||
def test_dumbfork():
|
||||
r.user_test("dumbfork")
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
"0: I am the parent.",
|
||||
"9: I am the parent.",
|
||||
"0: I am the child.",
|
||||
"9: I am the child.",
|
||||
"19: I am the child.",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
end_part("A")
|
||||
|
||||
@test(5)
|
||||
def test_faultread():
|
||||
r.user_test("faultread")
|
||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
||||
"TRAP frame at 0xf....... from CPU .",
|
||||
" trap 0x0000000e Page Fault",
|
||||
" err 0x00000004.*",
|
||||
E(".$E1. free env $E1"),
|
||||
no=["I read ........ from location 0."])
|
||||
|
||||
@test(5)
|
||||
def test_faultwrite():
|
||||
r.user_test("faultwrite")
|
||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
||||
"TRAP frame at 0xf....... from CPU .",
|
||||
" trap 0x0000000e Page Fault",
|
||||
" err 0x00000006.*",
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultdie():
|
||||
r.user_test("faultdie")
|
||||
r.match("i faulted at va deadbeef, err 6",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultregs():
|
||||
r.user_test("faultregs")
|
||||
r.match("Registers in UTrapframe OK",
|
||||
"Registers after page-fault OK",
|
||||
no=["Registers in UTrapframe MISMATCH",
|
||||
"Registers after page-fault MISMATCH"])
|
||||
|
||||
@test(5)
|
||||
def test_faultalloc():
|
||||
r.user_test("faultalloc")
|
||||
r.match("fault deadbeef",
|
||||
"this string was faulted in at deadbeef",
|
||||
"fault cafebffe",
|
||||
"fault cafec000",
|
||||
"this string was faulted in at cafebffe",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultallocbad():
|
||||
r.user_test("faultallocbad")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va deadbeef"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultnostack():
|
||||
r.user_test("faultnostack")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va eebfff.."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultbadhandler():
|
||||
r.user_test("faultbadhandler")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va (deadb|eebfe)..."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultevilhandler():
|
||||
r.user_test("faultevilhandler")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va (f0100|eebfe)..."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_forktree():
|
||||
r.user_test("forktree")
|
||||
r.match("....: I am .0.",
|
||||
"....: I am .1.",
|
||||
"....: I am .000.",
|
||||
"....: I am .100.",
|
||||
"....: I am .110.",
|
||||
"....: I am .111.",
|
||||
"....: I am .011.",
|
||||
"....: I am .001.",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
".0000200.. exiting gracefully",
|
||||
".0000200.. free env 0000200.")
|
||||
|
||||
end_part("B")
|
||||
|
||||
@test(5)
|
||||
def test_spin():
|
||||
r.user_test("spin")
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
"I am the parent. Forking the child...",
|
||||
E(".$E1. new env $E2"),
|
||||
"I am the parent. Running the child...",
|
||||
"I am the child. Spinning...",
|
||||
"I am the parent. Killing the child...",
|
||||
E(".$E1. destroying $E2"),
|
||||
E(".$E1. free env $E2"),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_stresssched():
|
||||
r.user_test("stresssched", make_args=["CPUS=4"])
|
||||
r.match(".000010... stresssched on CPU 0",
|
||||
".000010... stresssched on CPU 1",
|
||||
".000010... stresssched on CPU 2",
|
||||
".000010... stresssched on CPU 3",
|
||||
no=[".*ran on two CPUs at once"])
|
||||
|
||||
@test(5)
|
||||
def test_sendpage():
|
||||
r.user_test("sendpage", make_args=["CPUS=2"])
|
||||
r.match(".00000000. new env 00001000",
|
||||
E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("$E1 got message: hello child environment! how are you?", trim=True),
|
||||
E("child received correct message", trim=True),
|
||||
E("$E2 got message: hello parent environment! I'm good", trim=True),
|
||||
E("parent received correct message", trim=True),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
@test(5)
|
||||
def test_pingpong():
|
||||
r.user_test("pingpong", make_args=["CPUS=4"])
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("send 0 from $E1 to $E2", trim=True),
|
||||
E("$E2 got 0 from $E1", trim=True),
|
||||
E("$E1 got 1 from $E2", trim=True),
|
||||
E("$E2 got 8 from $E1", trim=True),
|
||||
E("$E1 got 9 from $E2", trim=True),
|
||||
E("$E2 got 10 from $E1", trim=True),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
@test(5)
|
||||
def test_primes():
|
||||
r.user_test("primes", stop_on_line("CPU .: 1877"), stop_on_line(".*panic"),
|
||||
make_args=["CPUS=4"], timeout=60)
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("CPU .: 2 .$E2. new env $E3"),
|
||||
E("CPU .: 3 .$E3. new env $E4"),
|
||||
E("CPU .: 5 .$E4. new env $E5"),
|
||||
E("CPU .: 7 .$E5. new env $E6"),
|
||||
E("CPU .: 11 .$E6. new env $E7"),
|
||||
E("CPU .: 1877 .$E289. new env $E290"))
|
||||
|
||||
end_part("C")
|
||||
|
||||
run_tests()
|
||||
154
lab/inc/COPYRIGHT
Normal file
154
lab/inc/COPYRIGHT
Normal file
@@ -0,0 +1,154 @@
|
||||
The files in this directory are:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders. See the file AUTHORS which should have accompanied this software
|
||||
* for a list of all copyright holders.
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by the copyright
|
||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
||||
* the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
queue.h is:
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
stdarg.h is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)stdarg.h 8.1 (Berkeley) 6/10/93
|
||||
*/
|
||||
|
||||
types.h is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1982, 1986, 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
* All or some portions of this file are derived from material licensed
|
||||
* to the University of California by American Telephone and Telegraph
|
||||
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||
* the permission of UNIX System Laboratories, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)types.h 8.4 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
||||
20
lab/inc/assert.h
Normal file
20
lab/inc/assert.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_INC_ASSERT_H
|
||||
#define JOS_INC_ASSERT_H
|
||||
|
||||
#include <inc/stdio.h>
|
||||
|
||||
void _warn(const char*, int, const char*, ...);
|
||||
void _panic(const char*, int, const char*, ...) __attribute__((noreturn));
|
||||
|
||||
#define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define assert(x) \
|
||||
do { if (!(x)) panic("assertion failed: %s", #x); } while (0)
|
||||
|
||||
// static_assert(x) will generate a compile-time error if 'x' is false.
|
||||
#define static_assert(x) switch (x) case 0: case (x):
|
||||
|
||||
#endif /* !JOS_INC_ASSERT_H */
|
||||
65
lab/inc/elf.h
Normal file
65
lab/inc/elf.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef JOS_INC_ELF_H
|
||||
#define JOS_INC_ELF_H
|
||||
|
||||
#define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */
|
||||
|
||||
struct Elf {
|
||||
uint32_t e_magic; // must equal ELF_MAGIC
|
||||
uint8_t e_elf[12];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry;
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct Proghdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_va;
|
||||
uint32_t p_pa;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
struct Secthdr {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint32_t sh_flags;
|
||||
uint32_t sh_addr;
|
||||
uint32_t sh_offset;
|
||||
uint32_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint32_t sh_addralign;
|
||||
uint32_t sh_entsize;
|
||||
};
|
||||
|
||||
// Values for Proghdr::p_type
|
||||
#define ELF_PROG_LOAD 1
|
||||
|
||||
// Flag bits for Proghdr::p_flags
|
||||
#define ELF_PROG_FLAG_EXEC 1
|
||||
#define ELF_PROG_FLAG_WRITE 2
|
||||
#define ELF_PROG_FLAG_READ 4
|
||||
|
||||
// Values for Secthdr::sh_type
|
||||
#define ELF_SHT_NULL 0
|
||||
#define ELF_SHT_PROGBITS 1
|
||||
#define ELF_SHT_SYMTAB 2
|
||||
#define ELF_SHT_STRTAB 3
|
||||
|
||||
// Values for Secthdr::sh_name
|
||||
#define ELF_SHN_UNDEF 0
|
||||
|
||||
#endif /* !JOS_INC_ELF_H */
|
||||
72
lab/inc/env.h
Normal file
72
lab/inc/env.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_INC_ENV_H
|
||||
#define JOS_INC_ENV_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/trap.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
typedef int32_t envid_t;
|
||||
|
||||
// An environment ID 'envid_t' has three parts:
|
||||
//
|
||||
// +1+---------------21-----------------+--------10--------+
|
||||
// |0| Uniqueifier | Environment |
|
||||
// | | | Index |
|
||||
// +------------------------------------+------------------+
|
||||
// \--- ENVX(eid) --/
|
||||
//
|
||||
// The environment index ENVX(eid) equals the environment's index in the
|
||||
// 'envs[]' array. The uniqueifier distinguishes environments that were
|
||||
// created at different times, but share the same environment index.
|
||||
//
|
||||
// All real environments are greater than 0 (so the sign bit is zero).
|
||||
// envid_ts less than 0 signify errors. The envid_t == 0 is special, and
|
||||
// stands for the current environment.
|
||||
|
||||
#define LOG2NENV 10
|
||||
#define NENV (1 << LOG2NENV)
|
||||
#define ENVX(envid) ((envid) & (NENV - 1))
|
||||
|
||||
// Values of env_status in struct Env
|
||||
enum {
|
||||
ENV_FREE = 0,
|
||||
ENV_DYING,
|
||||
ENV_RUNNABLE,
|
||||
ENV_RUNNING,
|
||||
ENV_NOT_RUNNABLE
|
||||
};
|
||||
|
||||
// Special environment types
|
||||
enum EnvType {
|
||||
ENV_TYPE_USER = 0,
|
||||
};
|
||||
|
||||
struct Env {
|
||||
struct Trapframe env_tf; // Saved registers
|
||||
struct Env *env_link; // Next free Env
|
||||
envid_t env_id; // Unique environment identifier
|
||||
envid_t env_parent_id; // env_id of this env's parent
|
||||
enum EnvType env_type; // Indicates special system environments
|
||||
unsigned env_status; // Status of the environment
|
||||
uint32_t env_runs; // Number of times environment has run
|
||||
int env_cpunum; // The CPU that the env is running on
|
||||
|
||||
// Address space
|
||||
pde_t *env_pgdir; // Kernel virtual address of page dir
|
||||
|
||||
// Exception handling
|
||||
void *env_pgfault_upcall; // Page fault upcall entry point
|
||||
|
||||
// Lab 4 challenge
|
||||
// uint32_t env_priority;
|
||||
// Lab 4 IPC
|
||||
bool env_ipc_recving; // Env is blocked receiving
|
||||
void *env_ipc_dstva; // VA at which to map received page
|
||||
uint32_t env_ipc_value; // Data value sent to us
|
||||
envid_t env_ipc_from; // envid of the sender
|
||||
int env_ipc_perm; // Perm of page mapping received
|
||||
};
|
||||
|
||||
#endif // !JOS_INC_ENV_H
|
||||
23
lab/inc/error.h
Normal file
23
lab/inc/error.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_INC_ERROR_H
|
||||
#define JOS_INC_ERROR_H
|
||||
|
||||
enum {
|
||||
// Kernel error codes -- keep in sync with list in lib/printfmt.c.
|
||||
E_UNSPECIFIED = 1, // Unspecified or unknown problem
|
||||
E_BAD_ENV , // Environment doesn't exist or otherwise
|
||||
// cannot be used in requested action
|
||||
E_INVAL , // Invalid parameter
|
||||
E_NO_MEM , // Request failed due to memory shortage
|
||||
E_NO_FREE_ENV , // Attempt to create a new environment beyond
|
||||
// the maximum allowed
|
||||
E_FAULT , // Memory fault
|
||||
|
||||
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
||||
E_EOF , // Unexpected end of file
|
||||
|
||||
MAXERROR
|
||||
};
|
||||
|
||||
#endif // !JOS_INC_ERROR_H */
|
||||
83
lab/inc/kbdreg.h
Normal file
83
lab/inc/kbdreg.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef JOS_KBDREG_H
|
||||
#define JOS_KBDREG_H
|
||||
|
||||
// Special keycodes
|
||||
#define KEY_HOME 0xE0
|
||||
#define KEY_END 0xE1
|
||||
#define KEY_UP 0xE2
|
||||
#define KEY_DN 0xE3
|
||||
#define KEY_LF 0xE4
|
||||
#define KEY_RT 0xE5
|
||||
#define KEY_PGUP 0xE6
|
||||
#define KEY_PGDN 0xE7
|
||||
#define KEY_INS 0xE8
|
||||
#define KEY_DEL 0xE9
|
||||
|
||||
|
||||
/* This is i8042reg.h + kbdreg.h from NetBSD. */
|
||||
|
||||
#define KBSTATP 0x64 /* kbd controller status port(I) */
|
||||
#define KBS_DIB 0x01 /* kbd data in buffer */
|
||||
#define KBS_IBF 0x02 /* kbd input buffer low */
|
||||
#define KBS_WARM 0x04 /* kbd input buffer low */
|
||||
#define KBS_OCMD 0x08 /* kbd output buffer has command */
|
||||
#define KBS_NOSEC 0x10 /* kbd security lock not engaged */
|
||||
#define KBS_TERR 0x20 /* kbd transmission error or from mouse */
|
||||
#define KBS_RERR 0x40 /* kbd receive error */
|
||||
#define KBS_PERR 0x80 /* kbd parity error */
|
||||
|
||||
#define KBCMDP 0x64 /* kbd controller port(O) */
|
||||
#define KBC_RAMREAD 0x20 /* read from RAM */
|
||||
#define KBC_RAMWRITE 0x60 /* write to RAM */
|
||||
#define KBC_AUXDISABLE 0xa7 /* disable auxiliary port */
|
||||
#define KBC_AUXENABLE 0xa8 /* enable auxiliary port */
|
||||
#define KBC_AUXTEST 0xa9 /* test auxiliary port */
|
||||
#define KBC_KBDECHO 0xd2 /* echo to keyboard port */
|
||||
#define KBC_AUXECHO 0xd3 /* echo to auxiliary port */
|
||||
#define KBC_AUXWRITE 0xd4 /* write to auxiliary port */
|
||||
#define KBC_SELFTEST 0xaa /* start self-test */
|
||||
#define KBC_KBDTEST 0xab /* test keyboard port */
|
||||
#define KBC_KBDDISABLE 0xad /* disable keyboard port */
|
||||
#define KBC_KBDENABLE 0xae /* enable keyboard port */
|
||||
#define KBC_PULSE0 0xfe /* pulse output bit 0 */
|
||||
#define KBC_PULSE1 0xfd /* pulse output bit 1 */
|
||||
#define KBC_PULSE2 0xfb /* pulse output bit 2 */
|
||||
#define KBC_PULSE3 0xf7 /* pulse output bit 3 */
|
||||
|
||||
#define KBDATAP 0x60 /* kbd data port(I) */
|
||||
#define KBOUTP 0x60 /* kbd data port(O) */
|
||||
|
||||
#define K_RDCMDBYTE 0x20
|
||||
#define K_LDCMDBYTE 0x60
|
||||
|
||||
#define KC8_TRANS 0x40 /* convert to old scan codes */
|
||||
#define KC8_MDISABLE 0x20 /* disable mouse */
|
||||
#define KC8_KDISABLE 0x10 /* disable keyboard */
|
||||
#define KC8_IGNSEC 0x08 /* ignore security lock */
|
||||
#define KC8_CPU 0x04 /* exit from protected mode reset */
|
||||
#define KC8_MENABLE 0x02 /* enable mouse interrupt */
|
||||
#define KC8_KENABLE 0x01 /* enable keyboard interrupt */
|
||||
#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE)
|
||||
|
||||
/* keyboard commands */
|
||||
#define KBC_RESET 0xFF /* reset the keyboard */
|
||||
#define KBC_RESEND 0xFE /* request the keyboard resend the last byte */
|
||||
#define KBC_SETDEFAULT 0xF6 /* resets keyboard to its power-on defaults */
|
||||
#define KBC_DISABLE 0xF5 /* as per KBC_SETDEFAULT, but also disable key scanning */
|
||||
#define KBC_ENABLE 0xF4 /* enable key scanning */
|
||||
#define KBC_TYPEMATIC 0xF3 /* set typematic rate and delay */
|
||||
#define KBC_SETTABLE 0xF0 /* set scancode translation table */
|
||||
#define KBC_MODEIND 0xED /* set mode indicators(i.e. LEDs) */
|
||||
#define KBC_ECHO 0xEE /* request an echo from the keyboard */
|
||||
|
||||
/* keyboard responses */
|
||||
#define KBR_EXTENDED 0xE0 /* extended key sequence */
|
||||
#define KBR_RESEND 0xFE /* needs resend of command */
|
||||
#define KBR_ACK 0xFA /* received a valid command */
|
||||
#define KBR_OVERRUN 0x00 /* flooded */
|
||||
#define KBR_FAILURE 0xFD /* diagnosic failure */
|
||||
#define KBR_BREAK 0xF0 /* break code prefix - sent on key release */
|
||||
#define KBR_RSTDONE 0xAA /* reset complete */
|
||||
#define KBR_ECHO 0xEE /* echo response */
|
||||
|
||||
#endif /* !JOS_KBDREG_H */
|
||||
91
lab/inc/lib.h
Normal file
91
lab/inc/lib.h
Normal file
@@ -0,0 +1,91 @@
|
||||
// Main public header file for our user-land support library,
|
||||
// whose code lives in the lib directory.
|
||||
// This library is roughly our OS's version of a standard C library,
|
||||
// and is intended to be linked into all user-mode applications
|
||||
// (NOT the kernel or boot loader).
|
||||
|
||||
#ifndef JOS_INC_LIB_H
|
||||
#define JOS_INC_LIB_H 1
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/env.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/syscall.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#define USED(x) (void)(x)
|
||||
|
||||
// main user program
|
||||
void umain(int argc, char **argv);
|
||||
|
||||
// libmain.c or entry.S
|
||||
extern const char *binaryname;
|
||||
extern const volatile struct Env *thisenv;
|
||||
extern const volatile struct Env envs[NENV];
|
||||
extern const volatile struct PageInfo pages[];
|
||||
|
||||
// exit.c
|
||||
void exit(void);
|
||||
|
||||
// pgfault.c
|
||||
void set_pgfault_handler(void (*handler)(struct UTrapframe *utf));
|
||||
|
||||
// readline.c
|
||||
char* readline(const char *buf);
|
||||
|
||||
// syscall.c
|
||||
void sys_cputs(const char *string, size_t len);
|
||||
int sys_cgetc(void);
|
||||
envid_t sys_getenvid(void);
|
||||
int sys_env_destroy(envid_t);
|
||||
void sys_yield(void);
|
||||
static envid_t sys_exofork(void);
|
||||
int sys_env_set_status(envid_t env, int status);
|
||||
int sys_env_set_pgfault_upcall(envid_t env, void *upcall);
|
||||
int sys_page_alloc(envid_t env, void *pg, int perm);
|
||||
int sys_page_map(envid_t src_env, void *src_pg,
|
||||
envid_t dst_env, void *dst_pg, int perm);
|
||||
int sys_page_unmap(envid_t env, void *pg);
|
||||
int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
||||
int sys_ipc_recv(void *rcv_pg);
|
||||
|
||||
// This must be inlined. Exercise for reader: why?
|
||||
static inline envid_t __attribute__((always_inline))
|
||||
sys_exofork(void)
|
||||
{
|
||||
envid_t ret;
|
||||
asm volatile("int %2"
|
||||
: "=a" (ret)
|
||||
: "a" (SYS_exofork), "i" (T_SYSCALL));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ipc.c
|
||||
void ipc_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
||||
int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store);
|
||||
envid_t ipc_find_env(enum EnvType type);
|
||||
|
||||
// fork.c
|
||||
#define PTE_SHARE 0x400
|
||||
envid_t fork(void);
|
||||
envid_t sfork(void); // Challenge!
|
||||
|
||||
|
||||
|
||||
/* File open modes */
|
||||
#define O_RDONLY 0x0000 /* open for reading only */
|
||||
#define O_WRONLY 0x0001 /* open for writing only */
|
||||
#define O_RDWR 0x0002 /* open for reading and writing */
|
||||
#define O_ACCMODE 0x0003 /* mask for above modes */
|
||||
|
||||
#define O_CREAT 0x0100 /* create if nonexistent */
|
||||
#define O_TRUNC 0x0200 /* truncate to zero length */
|
||||
#define O_EXCL 0x0400 /* error if already exists */
|
||||
#define O_MKDIR 0x0800 /* create directory, not regular file */
|
||||
|
||||
#endif // !JOS_INC_LIB_H
|
||||
191
lab/inc/memlayout.h
Normal file
191
lab/inc/memlayout.h
Normal file
@@ -0,0 +1,191 @@
|
||||
#ifndef JOS_INC_MEMLAYOUT_H
|
||||
#define JOS_INC_MEMLAYOUT_H
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#include <inc/types.h>
|
||||
#include <inc/mmu.h>
|
||||
#endif /* not __ASSEMBLER__ */
|
||||
|
||||
/*
|
||||
* This file contains definitions for memory management in our OS,
|
||||
* which are relevant to both the kernel and user-mode software.
|
||||
*/
|
||||
|
||||
// Global descriptor numbers
|
||||
#define GD_KT 0x08 // kernel text
|
||||
#define GD_KD 0x10 // kernel data
|
||||
#define GD_UT 0x18 // user text
|
||||
#define GD_UD 0x20 // user data
|
||||
#define GD_TSS0 0x28 // Task segment selector for CPU 0
|
||||
|
||||
/*
|
||||
* Virtual memory map: Permissions
|
||||
* kernel/user
|
||||
*
|
||||
* 4 Gig --------> +------------------------------+
|
||||
* | | RW/--
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* : . :
|
||||
* : . :
|
||||
* : . :
|
||||
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/--
|
||||
* | | RW/--
|
||||
* | Remapped Physical Memory | RW/--
|
||||
* | | RW/--
|
||||
* KERNBASE, ----> +------------------------------+ 0xf0000000 --+
|
||||
* KSTACKTOP | CPU0's Kernel Stack | RW/-- KSTKSIZE |
|
||||
* | - - - - - - - - - - - - - - -| |
|
||||
* | Invalid Memory (*) | --/-- KSTKGAP |
|
||||
* +------------------------------+ |
|
||||
* | CPU1's Kernel Stack | RW/-- KSTKSIZE |
|
||||
* | - - - - - - - - - - - - - - -| PTSIZE
|
||||
* | Invalid Memory (*) | --/-- KSTKGAP |
|
||||
* +------------------------------+ |
|
||||
* : . : |
|
||||
* : . : |
|
||||
* MMIOLIM ------> +------------------------------+ 0xefc00000 --+
|
||||
* | Memory-mapped I/O | RW/-- PTSIZE
|
||||
* ULIM, MMIOBASE --> +------------------------------+ 0xef800000
|
||||
* | Cur. Page Table (User R-) | R-/R- PTSIZE
|
||||
* UVPT ----> +------------------------------+ 0xef400000
|
||||
* | RO PAGES | R-/R- PTSIZE
|
||||
* UPAGES ----> +------------------------------+ 0xef000000
|
||||
* | RO ENVS | R-/R- PTSIZE
|
||||
* UTOP,UENVS ------> +------------------------------+ 0xeec00000
|
||||
* UXSTACKTOP -/ | User Exception Stack | RW/RW PGSIZE
|
||||
* +------------------------------+ 0xeebff000
|
||||
* | Empty Memory (*) | --/-- PGSIZE
|
||||
* USTACKTOP ---> +------------------------------+ 0xeebfe000
|
||||
* | Normal User Stack | RW/RW PGSIZE
|
||||
* +------------------------------+ 0xeebfd000
|
||||
* | |
|
||||
* | |
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* . .
|
||||
* . .
|
||||
* . .
|
||||
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
|
||||
* | Program Data & Heap |
|
||||
* UTEXT --------> +------------------------------+ 0x00800000
|
||||
* PFTEMP -------> | Empty Memory (*) | PTSIZE
|
||||
* | |
|
||||
* UTEMP --------> +------------------------------+ 0x00400000 --+
|
||||
* | Empty Memory (*) | |
|
||||
* | - - - - - - - - - - - - - - -| |
|
||||
* | User STAB Data (optional) | PTSIZE
|
||||
* USTABDATA ----> +------------------------------+ 0x00200000 |
|
||||
* | Empty Memory (*) | |
|
||||
* 0 ------------> +------------------------------+ --+
|
||||
*
|
||||
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
|
||||
* "Empty Memory" is normally unmapped, but user programs may map pages
|
||||
* there if desired. JOS user programs map pages temporarily at UTEMP.
|
||||
*/
|
||||
|
||||
|
||||
// All physical memory mapped at this address
|
||||
#define KERNBASE 0xF0000000
|
||||
|
||||
// At IOPHYSMEM (640K) there is a 384K hole for I/O. From the kernel,
|
||||
// IOPHYSMEM can be addressed at KERNBASE + IOPHYSMEM. The hole ends
|
||||
// at physical address EXTPHYSMEM.
|
||||
#define IOPHYSMEM 0x0A0000
|
||||
#define EXTPHYSMEM 0x100000
|
||||
|
||||
// Kernel stack.
|
||||
#define KSTACKTOP KERNBASE
|
||||
#define KSTKSIZE (8*PGSIZE) // size of a kernel stack
|
||||
#define KSTKGAP (8*PGSIZE) // size of a kernel stack guard
|
||||
|
||||
// Memory-mapped IO.
|
||||
#define MMIOLIM (KSTACKTOP - PTSIZE)
|
||||
#define MMIOBASE (MMIOLIM - PTSIZE)
|
||||
|
||||
#define ULIM (MMIOBASE)
|
||||
|
||||
/*
|
||||
* User read-only mappings! Anything below here til UTOP are readonly to user.
|
||||
* They are global pages mapped in at env allocation time.
|
||||
*/
|
||||
|
||||
// User read-only virtual page table (see 'uvpt' below)
|
||||
#define UVPT (ULIM - PTSIZE)
|
||||
// Read-only copies of the Page structures
|
||||
#define UPAGES (UVPT - PTSIZE)
|
||||
// Read-only copies of the global env structures
|
||||
#define UENVS (UPAGES - PTSIZE)
|
||||
|
||||
/*
|
||||
* Top of user VM. User can manipulate VA from UTOP-1 and down!
|
||||
*/
|
||||
|
||||
// Top of user-accessible VM
|
||||
#define UTOP UENVS
|
||||
// Top of one-page user exception stack
|
||||
#define UXSTACKTOP UTOP
|
||||
// Next page left invalid to guard against exception stack overflow; then:
|
||||
// Top of normal user stack
|
||||
#define USTACKTOP (UTOP - 2*PGSIZE)
|
||||
|
||||
// Where user programs generally begin
|
||||
#define UTEXT (2*PTSIZE)
|
||||
|
||||
// Used for temporary page mappings. Typed 'void*' for convenience
|
||||
#define UTEMP ((void*) PTSIZE)
|
||||
// Used for temporary page mappings for the user page-fault handler
|
||||
// (should not conflict with other temporary page mappings)
|
||||
#define PFTEMP (UTEMP + PTSIZE - PGSIZE)
|
||||
// The location of the user-level STABS data structure
|
||||
#define USTABDATA (PTSIZE / 2)
|
||||
|
||||
// Physical address of startup code for non-boot CPUs (APs)
|
||||
#define MPENTRY_PADDR 0x7000
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef uint32_t pte_t;
|
||||
typedef uint32_t pde_t;
|
||||
|
||||
#if JOS_USER
|
||||
/*
|
||||
* The page directory entry corresponding to the virtual address range
|
||||
* [UVPT, UVPT + PTSIZE) points to the page directory itself. Thus, the page
|
||||
* directory is treated as a page table as well as a page directory.
|
||||
*
|
||||
* One result of treating the page directory as a page table is that all PTEs
|
||||
* can be accessed through a "virtual page table" at virtual address UVPT (to
|
||||
* which uvpt is set in lib/entry.S). The PTE for page number N is stored in
|
||||
* uvpt[N]. (It's worth drawing a diagram of this!)
|
||||
*
|
||||
* A second consequence is that the contents of the current page directory
|
||||
* will always be available at virtual address (UVPT + (UVPT >> PGSHIFT)), to
|
||||
* which uvpd is set in lib/entry.S.
|
||||
*/
|
||||
extern volatile pte_t uvpt[]; // VA of "virtual page table"
|
||||
extern volatile pde_t uvpd[]; // VA of current page directory
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Page descriptor structures, mapped at UPAGES.
|
||||
* Read/write to the kernel, read-only to user programs.
|
||||
*
|
||||
* Each struct PageInfo stores metadata for one physical page.
|
||||
* Is it NOT the physical page itself, but there is a one-to-one
|
||||
* correspondence between physical pages and struct PageInfo's.
|
||||
* You can map a struct PageInfo * to the corresponding physical address
|
||||
* with page2pa() in kern/pmap.h.
|
||||
*/
|
||||
struct PageInfo {
|
||||
// Next page on the free list.
|
||||
struct PageInfo *pp_link;
|
||||
|
||||
// pp_ref is the count of pointers (usually in page table entries)
|
||||
// to this page, for pages allocated using page_alloc.
|
||||
// Pages allocated at boot time using pmap.c's
|
||||
// boot_alloc do not have valid reference count fields.
|
||||
|
||||
uint16_t pp_ref;
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* !JOS_INC_MEMLAYOUT_H */
|
||||
317
lab/inc/mmu.h
Normal file
317
lab/inc/mmu.h
Normal file
@@ -0,0 +1,317 @@
|
||||
#ifndef JOS_INC_MMU_H
|
||||
#define JOS_INC_MMU_H
|
||||
|
||||
/*
|
||||
* This file contains definitions for the x86 memory management unit (MMU),
|
||||
* including paging- and segmentation-related data structures and constants,
|
||||
* the %cr0, %cr4, and %eflags registers, and traps.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 1. Paging data structures and constants.
|
||||
*
|
||||
*/
|
||||
|
||||
// A linear address 'la' has a three-part structure as follows:
|
||||
//
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
// | Page Directory | Page Table | Offset within Page |
|
||||
// | Index | Index | |
|
||||
// +----------------+----------------+---------------------+
|
||||
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
|
||||
// \---------- PGNUM(la) ----------/
|
||||
//
|
||||
// The PDX, PTX, PGOFF, and PGNUM macros decompose linear addresses as shown.
|
||||
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
|
||||
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
|
||||
|
||||
// page number field of address
|
||||
#define PGNUM(la) (((uintptr_t) (la)) >> PTXSHIFT)
|
||||
|
||||
// page directory index
|
||||
#define PDX(la) ((((uintptr_t) (la)) >> PDXSHIFT) & 0x3FF)
|
||||
|
||||
// page table index
|
||||
#define PTX(la) ((((uintptr_t) (la)) >> PTXSHIFT) & 0x3FF)
|
||||
|
||||
// offset in page
|
||||
#define PGOFF(la) (((uintptr_t) (la)) & 0xFFF)
|
||||
|
||||
// construct linear address from indexes and offset
|
||||
#define PGADDR(d, t, o) ((void*) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
|
||||
// Page directory and page table constants.
|
||||
#define NPDENTRIES 1024 // page directory entries per page directory
|
||||
#define NPTENTRIES 1024 // page table entries per page table
|
||||
|
||||
#define PGSIZE 4096 // bytes mapped by a page
|
||||
#define PGSHIFT 12 // log2(PGSIZE)
|
||||
|
||||
#define PTSIZE (PGSIZE*NPTENTRIES) // bytes mapped by a page directory entry
|
||||
#define PTSHIFT 22 // log2(PTSIZE)
|
||||
|
||||
#define PTXSHIFT 12 // offset of PTX in a linear address
|
||||
#define PDXSHIFT 22 // offset of PDX in a linear address
|
||||
|
||||
// Page table/directory entry flags.
|
||||
#define PTE_P 0x001 // Present
|
||||
#define PTE_W 0x002 // Writeable
|
||||
#define PTE_U 0x004 // User
|
||||
#define PTE_PWT 0x008 // Write-Through
|
||||
#define PTE_PCD 0x010 // Cache-Disable
|
||||
#define PTE_A 0x020 // Accessed
|
||||
#define PTE_D 0x040 // Dirty
|
||||
#define PTE_PS 0x080 // Page Size
|
||||
#define PTE_G 0x100 // Global
|
||||
|
||||
// The PTE_AVAIL bits aren't used by the kernel or interpreted by the
|
||||
// hardware, so user processes are allowed to set them arbitrarily.
|
||||
#define PTE_AVAIL 0xE00 // Available for software use
|
||||
|
||||
// Flags in PTE_SYSCALL may be used in system calls. (Others may not.)
|
||||
#define PTE_SYSCALL (PTE_AVAIL | PTE_P | PTE_W | PTE_U)
|
||||
|
||||
// Address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((physaddr_t) (pte) & ~0xFFF)
|
||||
|
||||
// Control Register flags
|
||||
#define CR0_PE 0x00000001 // Protection Enable
|
||||
#define CR0_MP 0x00000002 // Monitor coProcessor
|
||||
#define CR0_EM 0x00000004 // Emulation
|
||||
#define CR0_TS 0x00000008 // Task Switched
|
||||
#define CR0_ET 0x00000010 // Extension Type
|
||||
#define CR0_NE 0x00000020 // Numeric Errror
|
||||
#define CR0_WP 0x00010000 // Write Protect
|
||||
#define CR0_AM 0x00040000 // Alignment Mask
|
||||
#define CR0_NW 0x20000000 // Not Writethrough
|
||||
#define CR0_CD 0x40000000 // Cache Disable
|
||||
#define CR0_PG 0x80000000 // Paging
|
||||
|
||||
#define CR4_PCE 0x00000100 // Performance counter enable
|
||||
#define CR4_MCE 0x00000040 // Machine Check Enable
|
||||
#define CR4_PSE 0x00000010 // Page Size Extensions
|
||||
#define CR4_DE 0x00000008 // Debugging Extensions
|
||||
#define CR4_TSD 0x00000004 // Time Stamp Disable
|
||||
#define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts
|
||||
#define CR4_VME 0x00000001 // V86 Mode Extensions
|
||||
|
||||
// Eflags register
|
||||
#define FL_CF 0x00000001 // Carry Flag
|
||||
#define FL_PF 0x00000004 // Parity Flag
|
||||
#define FL_AF 0x00000010 // Auxiliary carry Flag
|
||||
#define FL_ZF 0x00000040 // Zero Flag
|
||||
#define FL_SF 0x00000080 // Sign Flag
|
||||
#define FL_TF 0x00000100 // Trap Flag
|
||||
#define FL_IF 0x00000200 // Interrupt Flag
|
||||
#define FL_DF 0x00000400 // Direction Flag
|
||||
#define FL_OF 0x00000800 // Overflow Flag
|
||||
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
|
||||
#define FL_IOPL_0 0x00000000 // IOPL == 0
|
||||
#define FL_IOPL_1 0x00001000 // IOPL == 1
|
||||
#define FL_IOPL_2 0x00002000 // IOPL == 2
|
||||
#define FL_IOPL_3 0x00003000 // IOPL == 3
|
||||
#define FL_NT 0x00004000 // Nested Task
|
||||
#define FL_RF 0x00010000 // Resume Flag
|
||||
#define FL_VM 0x00020000 // Virtual 8086 mode
|
||||
#define FL_AC 0x00040000 // Alignment Check
|
||||
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
|
||||
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
|
||||
#define FL_ID 0x00200000 // ID flag
|
||||
|
||||
// Page fault error codes
|
||||
#define FEC_PR 0x1 // Page fault caused by protection violation
|
||||
#define FEC_WR 0x2 // Page fault caused by a write
|
||||
#define FEC_U 0x4 // Page fault occured while in user mode
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 2. Segmentation data structures and constants.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __ASSEMBLER__
|
||||
|
||||
/*
|
||||
* Macros to build GDT entries in assembly.
|
||||
*/
|
||||
#define SEG_NULL \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
#define SEG(type,base,lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
#else // not __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Segment Descriptors
|
||||
struct Segdesc {
|
||||
unsigned sd_lim_15_0 : 16; // Low bits of segment limit
|
||||
unsigned sd_base_15_0 : 16; // Low bits of segment base address
|
||||
unsigned sd_base_23_16 : 8; // Middle bits of segment base address
|
||||
unsigned sd_type : 4; // Segment type (see STS_ constants)
|
||||
unsigned sd_s : 1; // 0 = system, 1 = application
|
||||
unsigned sd_dpl : 2; // Descriptor Privilege Level
|
||||
unsigned sd_p : 1; // Present
|
||||
unsigned sd_lim_19_16 : 4; // High bits of segment limit
|
||||
unsigned sd_avl : 1; // Unused (available for software use)
|
||||
unsigned sd_rsv1 : 1; // Reserved
|
||||
unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||
unsigned sd_g : 1; // Granularity: limit scaled by 4K when set
|
||||
unsigned sd_base_31_24 : 8; // High bits of segment base address
|
||||
};
|
||||
// Null segment
|
||||
#define SEG_NULL { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
// Segment that is loadable but faults when used
|
||||
#define SEG_FAULT { 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }
|
||||
// Normal segment
|
||||
#define SEG(type, base, lim, dpl) \
|
||||
{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \
|
||||
type, 1, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1, \
|
||||
(unsigned) (base) >> 24 }
|
||||
#define SEG16(type, base, lim, dpl) (struct Segdesc) \
|
||||
{ (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \
|
||||
type, 1, dpl, 1, (unsigned) (lim) >> 16, 0, 0, 1, 0, \
|
||||
(unsigned) (base) >> 24 }
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
// Application segment type bits
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_E 0x4 // Expand down (non-executable segments)
|
||||
#define STA_C 0x4 // Conforming code segment (executable only)
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
// System segment type bits
|
||||
#define STS_T16A 0x1 // Available 16-bit TSS
|
||||
#define STS_LDT 0x2 // Local Descriptor Table
|
||||
#define STS_T16B 0x3 // Busy 16-bit TSS
|
||||
#define STS_CG16 0x4 // 16-bit Call Gate
|
||||
#define STS_TG 0x5 // Task Gate / Coum Transmitions
|
||||
#define STS_IG16 0x6 // 16-bit Interrupt Gate
|
||||
#define STS_TG16 0x7 // 16-bit Trap Gate
|
||||
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||
#define STS_T32B 0xB // Busy 32-bit TSS
|
||||
#define STS_CG32 0xC // 32-bit Call Gate
|
||||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 3. Traps.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
// Task state segment format (as described by the Pentium architecture book)
|
||||
struct Taskstate {
|
||||
uint32_t ts_link; // Old ts selector
|
||||
uintptr_t ts_esp0; // Stack pointers and segment selectors
|
||||
uint16_t ts_ss0; // after an increase in privilege level
|
||||
uint16_t ts_padding1;
|
||||
uintptr_t ts_esp1;
|
||||
uint16_t ts_ss1;
|
||||
uint16_t ts_padding2;
|
||||
uintptr_t ts_esp2;
|
||||
uint16_t ts_ss2;
|
||||
uint16_t ts_padding3;
|
||||
physaddr_t ts_cr3; // Page directory base
|
||||
uintptr_t ts_eip; // Saved state from last task switch
|
||||
uint32_t ts_eflags;
|
||||
uint32_t ts_eax; // More saved state (registers)
|
||||
uint32_t ts_ecx;
|
||||
uint32_t ts_edx;
|
||||
uint32_t ts_ebx;
|
||||
uintptr_t ts_esp;
|
||||
uintptr_t ts_ebp;
|
||||
uint32_t ts_esi;
|
||||
uint32_t ts_edi;
|
||||
uint16_t ts_es; // Even more saved state (segment selectors)
|
||||
uint16_t ts_padding4;
|
||||
uint16_t ts_cs;
|
||||
uint16_t ts_padding5;
|
||||
uint16_t ts_ss;
|
||||
uint16_t ts_padding6;
|
||||
uint16_t ts_ds;
|
||||
uint16_t ts_padding7;
|
||||
uint16_t ts_fs;
|
||||
uint16_t ts_padding8;
|
||||
uint16_t ts_gs;
|
||||
uint16_t ts_padding9;
|
||||
uint16_t ts_ldt;
|
||||
uint16_t ts_padding10;
|
||||
uint16_t ts_t; // Trap on task switch
|
||||
uint16_t ts_iomb; // I/O map base address
|
||||
};
|
||||
|
||||
// Gate descriptors for interrupts and traps
|
||||
struct Gatedesc {
|
||||
unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment
|
||||
unsigned gd_sel : 16; // segment selector
|
||||
unsigned gd_args : 5; // # args, 0 for interrupt/trap gates
|
||||
unsigned gd_rsv1 : 3; // reserved(should be zero I guess)
|
||||
unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})
|
||||
unsigned gd_s : 1; // must be 0 (system)
|
||||
unsigned gd_dpl : 2; // descriptor(meaning new) privilege level
|
||||
unsigned gd_p : 1; // Present
|
||||
unsigned gd_off_31_16 : 16; // high bits of offset in segment
|
||||
};
|
||||
|
||||
// Set up a normal interrupt/trap gate descriptor.
|
||||
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
|
||||
// see section 9.6.1.3 of the i386 reference: "The difference between
|
||||
// an interrupt gate and a trap gate is in the effect on IF (the
|
||||
// interrupt-enable flag). An interrupt that vectors through an
|
||||
// interrupt gate resets IF, thereby preventing other interrupts from
|
||||
// interfering with the current interrupt handler. A subsequent IRET
|
||||
// instruction restores IF to the value in the EFLAGS image on the
|
||||
// stack. An interrupt through a trap gate does not change IF."
|
||||
// - sel: Code segment selector for interrupt/trap handler
|
||||
// - off: Offset in code segment for interrupt/trap handler
|
||||
// - dpl: Descriptor Privilege Level -
|
||||
// the privilege level required for software to invoke
|
||||
// this interrupt/trap gate explicitly using an int instruction.
|
||||
#define SETGATE(gate, istrap, sel, off, dpl) \
|
||||
{ \
|
||||
(gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \
|
||||
(gate).gd_sel = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t) (off) >> 16; \
|
||||
}
|
||||
|
||||
// Set up a call gate descriptor.
|
||||
#define SETCALLGATE(gate, sel, off, dpl) \
|
||||
{ \
|
||||
(gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \
|
||||
(gate).gd_sel = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = STS_CG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t) (off) >> 16; \
|
||||
}
|
||||
|
||||
// Pseudo-descriptors used for LGDT, LLDT and LIDT instructions.
|
||||
struct Pseudodesc {
|
||||
uint16_t pd_lim; // Limit
|
||||
uint32_t pd_base; // Base address
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !JOS_INC_MMU_H */
|
||||
51
lab/inc/stab.h
Normal file
51
lab/inc/stab.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef JOS_STAB_H
|
||||
#define JOS_STAB_H
|
||||
#include <inc/types.h>
|
||||
|
||||
// <inc/stab.h>
|
||||
// STABS debugging info
|
||||
|
||||
// The JOS kernel debugger can understand some debugging information
|
||||
// in the STABS format. For more information on this format, see
|
||||
// http://sourceware.org/gdb/onlinedocs/stabs.html
|
||||
|
||||
// The constants below define some symbol types used by various debuggers
|
||||
// and compilers. JOS uses the N_SO, N_SOL, N_FUN, and N_SLINE types.
|
||||
|
||||
#define N_GSYM 0x20 // global symbol
|
||||
#define N_FNAME 0x22 // F77 function name
|
||||
#define N_FUN 0x24 // procedure name
|
||||
#define N_STSYM 0x26 // data segment variable
|
||||
#define N_LCSYM 0x28 // bss segment variable
|
||||
#define N_MAIN 0x2a // main function name
|
||||
#define N_PC 0x30 // global Pascal symbol
|
||||
#define N_RSYM 0x40 // register variable
|
||||
#define N_SLINE 0x44 // text segment line number
|
||||
#define N_DSLINE 0x46 // data segment line number
|
||||
#define N_BSLINE 0x48 // bss segment line number
|
||||
#define N_SSYM 0x60 // structure/union element
|
||||
#define N_SO 0x64 // main source file name
|
||||
#define N_LSYM 0x80 // stack variable
|
||||
#define N_BINCL 0x82 // include file beginning
|
||||
#define N_SOL 0x84 // included source file name
|
||||
#define N_PSYM 0xa0 // parameter variable
|
||||
#define N_EINCL 0xa2 // include file end
|
||||
#define N_ENTRY 0xa4 // alternate entry point
|
||||
#define N_LBRAC 0xc0 // left bracket
|
||||
#define N_EXCL 0xc2 // deleted include file
|
||||
#define N_RBRAC 0xe0 // right bracket
|
||||
#define N_BCOMM 0xe2 // begin common
|
||||
#define N_ECOMM 0xe4 // end common
|
||||
#define N_ECOML 0xe8 // end common (local name)
|
||||
#define N_LENG 0xfe // length of preceding entry
|
||||
|
||||
// Entries in the STABS table are formatted as follows.
|
||||
struct Stab {
|
||||
uint32_t n_strx; // index into string table of name
|
||||
uint8_t n_type; // type of symbol
|
||||
uint8_t n_other; // misc info (usually empty)
|
||||
uint16_t n_desc; // description field
|
||||
uintptr_t n_value; // value of symbol
|
||||
};
|
||||
|
||||
#endif /* !JOS_STAB_H */
|
||||
14
lab/inc/stdarg.h
Normal file
14
lab/inc/stdarg.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* $NetBSD: stdarg.h,v 1.12 1995/12/25 23:15:31 mycroft Exp $ */
|
||||
|
||||
#ifndef JOS_INC_STDARG_H
|
||||
#define JOS_INC_STDARG_H
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(ap, last) __builtin_va_start(ap, last)
|
||||
|
||||
#define va_arg(ap, type) __builtin_va_arg(ap, type)
|
||||
|
||||
#define va_end(ap) __builtin_va_end(ap)
|
||||
|
||||
#endif /* !JOS_INC_STDARG_H */
|
||||
33
lab/inc/stdio.h
Normal file
33
lab/inc/stdio.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef JOS_INC_STDIO_H
|
||||
#define JOS_INC_STDIO_H
|
||||
|
||||
#include <inc/stdarg.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *) 0)
|
||||
#endif /* !NULL */
|
||||
|
||||
// lib/console.c
|
||||
void cputchar(int c);
|
||||
int getchar(void);
|
||||
int iscons(int fd);
|
||||
|
||||
// lib/printfmt.c
|
||||
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
|
||||
void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list);
|
||||
int snprintf(char *str, int size, const char *fmt, ...);
|
||||
int vsnprintf(char *str, int size, const char *fmt, va_list);
|
||||
|
||||
// lib/printf.c
|
||||
int cprintf(const char *fmt, ...);
|
||||
int vcprintf(const char *fmt, va_list);
|
||||
|
||||
// lib/fprintf.c
|
||||
int printf(const char *fmt, ...);
|
||||
int fprintf(int fd, const char *fmt, ...);
|
||||
int vfprintf(int fd, const char *fmt, va_list);
|
||||
|
||||
// lib/readline.c
|
||||
char* readline(const char *prompt);
|
||||
|
||||
#endif /* !JOS_INC_STDIO_H */
|
||||
25
lab/inc/string.h
Normal file
25
lab/inc/string.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef JOS_INC_STRING_H
|
||||
#define JOS_INC_STRING_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
int strlen(const char *s);
|
||||
int strnlen(const char *s, size_t size);
|
||||
char * strcpy(char *dst, const char *src);
|
||||
char * strncpy(char *dst, const char *src, size_t size);
|
||||
char * strcat(char *dst, const char *src);
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
int strncmp(const char *s1, const char *s2, size_t size);
|
||||
char * strchr(const char *s, char c);
|
||||
char * strfind(const char *s, char c);
|
||||
|
||||
void * memset(void *dst, int c, size_t len);
|
||||
void * memcpy(void *dst, const void *src, size_t len);
|
||||
void * memmove(void *dst, const void *src, size_t len);
|
||||
int memcmp(const void *s1, const void *s2, size_t len);
|
||||
void * memfind(const void *s, int c, size_t len);
|
||||
|
||||
long strtol(const char *s, char **endptr, int base);
|
||||
|
||||
#endif /* not JOS_INC_STRING_H */
|
||||
22
lab/inc/syscall.h
Normal file
22
lab/inc/syscall.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef JOS_INC_SYSCALL_H
|
||||
#define JOS_INC_SYSCALL_H
|
||||
|
||||
/* system call numbers */
|
||||
enum {
|
||||
SYS_cputs = 0,
|
||||
SYS_cgetc,
|
||||
SYS_getenvid,
|
||||
SYS_env_destroy,
|
||||
SYS_page_alloc,
|
||||
SYS_page_map,
|
||||
SYS_page_unmap,
|
||||
SYS_exofork,
|
||||
SYS_env_set_status,
|
||||
SYS_env_set_pgfault_upcall,
|
||||
SYS_yield,
|
||||
SYS_ipc_try_send,
|
||||
SYS_ipc_recv,
|
||||
NSYSCALLS
|
||||
};
|
||||
|
||||
#endif /* !JOS_INC_SYSCALL_H */
|
||||
91
lab/inc/trap.h
Normal file
91
lab/inc/trap.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef JOS_INC_TRAP_H
|
||||
#define JOS_INC_TRAP_H
|
||||
|
||||
// Trap numbers
|
||||
// These are processor defined:
|
||||
#define T_DIVIDE 0 // divide error
|
||||
#define T_DEBUG 1 // debug exception
|
||||
#define T_NMI 2 // non-maskable interrupt
|
||||
#define T_BRKPT 3 // breakpoint
|
||||
#define T_OFLOW 4 // overflow
|
||||
#define T_BOUND 5 // bounds check
|
||||
#define T_ILLOP 6 // illegal opcode
|
||||
#define T_DEVICE 7 // device not available
|
||||
#define T_DBLFLT 8 // double fault
|
||||
/* #define T_COPROC 9 */ // reserved (not generated by recent processors)
|
||||
#define T_TSS 10 // invalid task switch segment
|
||||
#define T_SEGNP 11 // segment not present
|
||||
#define T_STACK 12 // stack exception
|
||||
#define T_GPFLT 13 // general protection fault
|
||||
#define T_PGFLT 14 // page fault
|
||||
/* #define T_RES 15 */ // reserved
|
||||
#define T_FPERR 16 // floating point error
|
||||
#define T_ALIGN 17 // aligment check
|
||||
#define T_MCHK 18 // machine check
|
||||
#define T_SIMDERR 19 // SIMD floating point error
|
||||
|
||||
// These are arbitrarily chosen, but with care not to overlap
|
||||
// processor defined exceptions or interrupt vectors.
|
||||
#define T_SYSCALL 48 // system call
|
||||
#define T_DEFAULT 500 // catchall
|
||||
|
||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET
|
||||
|
||||
// Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER)
|
||||
#define IRQ_TIMER 0
|
||||
#define IRQ_KBD 1
|
||||
#define IRQ_SERIAL 4
|
||||
#define IRQ_SPURIOUS 7
|
||||
#define IRQ_IDE 14
|
||||
#define IRQ_ERROR 19
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
struct PushRegs {
|
||||
/* registers as pushed by pusha */
|
||||
uint32_t reg_edi;
|
||||
uint32_t reg_esi;
|
||||
uint32_t reg_ebp;
|
||||
uint32_t reg_oesp; /* Useless */
|
||||
uint32_t reg_ebx;
|
||||
uint32_t reg_edx;
|
||||
uint32_t reg_ecx;
|
||||
uint32_t reg_eax;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Trapframe {
|
||||
struct PushRegs tf_regs;
|
||||
uint16_t tf_es;
|
||||
uint16_t tf_padding1;
|
||||
uint16_t tf_ds;
|
||||
uint16_t tf_padding2;
|
||||
uint32_t tf_trapno;
|
||||
/* below here defined by x86 hardware */
|
||||
uint32_t tf_err;
|
||||
uintptr_t tf_eip;
|
||||
uint16_t tf_cs;
|
||||
uint16_t tf_padding3;
|
||||
uint32_t tf_eflags;
|
||||
/* below here only when crossing rings, such as from user to kernel */
|
||||
uintptr_t tf_esp;
|
||||
uint16_t tf_ss;
|
||||
uint16_t tf_padding4;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct UTrapframe {
|
||||
/* information about the fault */
|
||||
uint32_t utf_fault_va; /* va for T_PGFLT, 0 otherwise */
|
||||
uint32_t utf_err;
|
||||
/* trap-time return state */
|
||||
struct PushRegs utf_regs;
|
||||
uintptr_t utf_eip;
|
||||
uint32_t utf_eflags;
|
||||
/* the trap-time stack to return to */
|
||||
uintptr_t utf_esp;
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !JOS_INC_TRAP_H */
|
||||
75
lab/inc/types.h
Normal file
75
lab/inc/types.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef JOS_INC_TYPES_H
|
||||
#define JOS_INC_TYPES_H
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*) 0)
|
||||
#endif
|
||||
|
||||
// Represents true-or-false values
|
||||
typedef _Bool bool;
|
||||
enum { false, true };
|
||||
|
||||
// Explicitly-sized versions of integer types
|
||||
typedef __signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
// Pointers and addresses are 32 bits long.
|
||||
// We use pointer types to represent virtual addresses,
|
||||
// uintptr_t to represent the numerical values of virtual addresses,
|
||||
// and physaddr_t to represent physical addresses.
|
||||
typedef int32_t intptr_t;
|
||||
typedef uint32_t uintptr_t;
|
||||
typedef uint32_t physaddr_t;
|
||||
|
||||
// Page numbers are 32 bits long.
|
||||
typedef uint32_t ppn_t;
|
||||
|
||||
// size_t is used for memory object sizes.
|
||||
typedef uint32_t size_t;
|
||||
// ssize_t is a signed version of ssize_t, used in case there might be an
|
||||
// error return.
|
||||
typedef int32_t ssize_t;
|
||||
|
||||
// off_t is used for file offsets and lengths.
|
||||
typedef int32_t off_t;
|
||||
|
||||
// Efficient min and max operations
|
||||
#define MIN(_a, _b) \
|
||||
({ \
|
||||
typeof(_a) __a = (_a); \
|
||||
typeof(_b) __b = (_b); \
|
||||
__a <= __b ? __a : __b; \
|
||||
})
|
||||
#define MAX(_a, _b) \
|
||||
({ \
|
||||
typeof(_a) __a = (_a); \
|
||||
typeof(_b) __b = (_b); \
|
||||
__a >= __b ? __a : __b; \
|
||||
})
|
||||
|
||||
// Rounding operations (efficient when n is a power of 2)
|
||||
// Round down to the nearest multiple of n
|
||||
#define ROUNDDOWN(a, n) \
|
||||
({ \
|
||||
uint32_t __a = (uint32_t) (a); \
|
||||
(typeof(a)) (__a - __a % (n)); \
|
||||
})
|
||||
// Round up to the nearest multiple of n
|
||||
#define ROUNDUP(a, n) \
|
||||
({ \
|
||||
uint32_t __n = (uint32_t) (n); \
|
||||
(typeof(a)) (ROUNDDOWN((uint32_t) (a) + __n - 1, __n)); \
|
||||
})
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
// Return the offset of 'member' relative to the beginning of a struct type
|
||||
#define offsetof(type, member) ((size_t) (&((type*)0)->member))
|
||||
|
||||
#endif /* !JOS_INC_TYPES_H */
|
||||
264
lab/inc/x86.h
Normal file
264
lab/inc/x86.h
Normal file
@@ -0,0 +1,264 @@
|
||||
#ifndef JOS_INC_X86_H
|
||||
#define JOS_INC_X86_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
static inline void
|
||||
breakpoint(void)
|
||||
{
|
||||
asm volatile("int3");
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
inb(int port)
|
||||
{
|
||||
uint8_t data;
|
||||
asm volatile("inb %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insb(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsb"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
inw(int port)
|
||||
{
|
||||
uint16_t data;
|
||||
asm volatile("inw %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insw(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsw"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
inl(int port)
|
||||
{
|
||||
uint32_t data;
|
||||
asm volatile("inl %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insl(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsl"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outb(int port, uint8_t data)
|
||||
{
|
||||
asm volatile("outb %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsb(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsb"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outw(int port, uint16_t data)
|
||||
{
|
||||
asm volatile("outw %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsw(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsw"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsl(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsl"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outl(int port, uint32_t data)
|
||||
{
|
||||
asm volatile("outl %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
invlpg(void *addr)
|
||||
{
|
||||
asm volatile("invlpg (%0)" : : "r" (addr) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
lidt(void *p)
|
||||
{
|
||||
asm volatile("lidt (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lgdt(void *p)
|
||||
{
|
||||
asm volatile("lgdt (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lldt(uint16_t sel)
|
||||
{
|
||||
asm volatile("lldt %0" : : "r" (sel));
|
||||
}
|
||||
|
||||
static inline void
|
||||
ltr(uint16_t sel)
|
||||
{
|
||||
asm volatile("ltr %0" : : "r" (sel));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr0(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr0" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr0(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr0,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr2(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr2,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr3(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr3" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr3(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr3,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr4(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr4" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr4(void)
|
||||
{
|
||||
uint32_t cr4;
|
||||
asm volatile("movl %%cr4,%0" : "=r" (cr4));
|
||||
return cr4;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tlbflush(void)
|
||||
{
|
||||
uint32_t cr3;
|
||||
asm volatile("movl %%cr3,%0" : "=r" (cr3));
|
||||
asm volatile("movl %0,%%cr3" : : "r" (cr3));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_eflags(void)
|
||||
{
|
||||
uint32_t eflags;
|
||||
asm volatile("pushfl; popl %0" : "=r" (eflags));
|
||||
return eflags;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_eflags(uint32_t eflags)
|
||||
{
|
||||
asm volatile("pushl %0; popfl" : : "r" (eflags));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_ebp(void)
|
||||
{
|
||||
uint32_t ebp;
|
||||
asm volatile("movl %%ebp,%0" : "=r" (ebp));
|
||||
return ebp;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_esp(void)
|
||||
{
|
||||
uint32_t esp;
|
||||
asm volatile("movl %%esp,%0" : "=r" (esp));
|
||||
return esp;
|
||||
}
|
||||
|
||||
static inline void
|
||||
cpuid(uint32_t info, uint32_t *eaxp, uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
asm volatile("cpuid"
|
||||
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
||||
: "a" (info));
|
||||
if (eaxp)
|
||||
*eaxp = eax;
|
||||
if (ebxp)
|
||||
*ebxp = ebx;
|
||||
if (ecxp)
|
||||
*ecxp = ecx;
|
||||
if (edxp)
|
||||
*edxp = edx;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
read_tsc(void)
|
||||
{
|
||||
uint64_t tsc;
|
||||
asm volatile("rdtsc" : "=A" (tsc));
|
||||
return tsc;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xchg(volatile uint32_t *addr, uint32_t newval)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
// The + in "+m" denotes a read-modify-write operand.
|
||||
asm volatile("lock; xchgl %0, %1"
|
||||
: "+m" (*addr), "=a" (result)
|
||||
: "1" (newval)
|
||||
: "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* !JOS_INC_X86_H */
|
||||
155
lab/kern/COPYRIGHT
Normal file
155
lab/kern/COPYRIGHT
Normal file
@@ -0,0 +1,155 @@
|
||||
Most of the source files in this directory are derived from the Exokernel,
|
||||
which is:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders. See the file AUTHORS which should have accompanied this software
|
||||
* for a list of all copyright holders.
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by the copyright
|
||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
||||
* the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
Console.c was created consulting the NetBSD pccons driver which is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved.
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* William Jolitz and Don Ahn.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
Kclock.h, sched.h, and printf.h are copyright:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1998 Exotec, Inc.
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with Exotec, Inc..
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by Exotec, Inc. The rest
|
||||
* of this file is covered by the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
Printf.c is copyright:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1986, 1988, 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
* All or some portions of this file are derived from material licensed
|
||||
* to the University of California by American Telephone and Telegraph
|
||||
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||
* the permission of UNIX System Laboratories, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
||||
127
lab/kern/Makefrag
Normal file
127
lab/kern/Makefrag
Normal file
@@ -0,0 +1,127 @@
|
||||
#
|
||||
# Makefile fragment for JOS kernel.
|
||||
# This is NOT a complete makefile;
|
||||
# you must run GNU make in the top-level directory
|
||||
# where the GNUmakefile is located.
|
||||
#
|
||||
|
||||
OBJDIRS += kern
|
||||
|
||||
KERN_LDFLAGS := $(LDFLAGS) -T kern/kernel.ld -nostdlib
|
||||
|
||||
# entry.S must be first, so that it's the first code in the text segment!!!
|
||||
#
|
||||
# We also snatch the use of a couple handy source files
|
||||
# from the lib directory, to avoid gratuitous code duplication.
|
||||
KERN_SRCFILES := kern/entry.S \
|
||||
kern/entrypgdir.c \
|
||||
kern/init.c \
|
||||
kern/console.c \
|
||||
kern/monitor.c \
|
||||
kern/pmap.c \
|
||||
kern/env.c \
|
||||
kern/kclock.c \
|
||||
kern/picirq.c \
|
||||
kern/printf.c \
|
||||
kern/trap.c \
|
||||
kern/trapentry.S \
|
||||
kern/sched.c \
|
||||
kern/syscall.c \
|
||||
kern/kdebug.c \
|
||||
lib/printfmt.c \
|
||||
lib/readline.c \
|
||||
lib/string.c
|
||||
|
||||
# Source files for LAB4
|
||||
KERN_SRCFILES += kern/mpentry.S \
|
||||
kern/mpconfig.c \
|
||||
kern/lapic.c \
|
||||
kern/spinlock.c
|
||||
|
||||
# Only build files if they exist.
|
||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
||||
|
||||
# Binary program images to embed within the kernel.
|
||||
# Binary files for LAB3
|
||||
KERN_BINFILES := user/hello \
|
||||
user/buggyhello \
|
||||
user/buggyhello2 \
|
||||
user/evilhello \
|
||||
user/testbss \
|
||||
user/divzero \
|
||||
user/breakpoint \
|
||||
user/softint \
|
||||
user/badsegment \
|
||||
user/faultread \
|
||||
user/faultreadkernel \
|
||||
user/faultwrite \
|
||||
user/faultwritekernel
|
||||
|
||||
# Binary files for LAB4
|
||||
KERN_BINFILES += user/idle \
|
||||
user/yield \
|
||||
user/dumbfork \
|
||||
user/stresssched \
|
||||
user/faultdie \
|
||||
user/faultregs \
|
||||
user/faultalloc \
|
||||
user/faultallocbad \
|
||||
user/faultnostack \
|
||||
user/faultbadhandler \
|
||||
user/faultevilhandler \
|
||||
user/forktree \
|
||||
user/sendpage \
|
||||
user/spin \
|
||||
user/fairness \
|
||||
user/pingpong \
|
||||
user/pingpongs \
|
||||
user/primes
|
||||
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
||||
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
|
||||
|
||||
KERN_BINFILES := $(patsubst %, $(OBJDIR)/%, $(KERN_BINFILES))
|
||||
|
||||
# How to build kernel object files
|
||||
$(OBJDIR)/kern/%.o: kern/%.c $(OBJDIR)/.vars.KERN_CFLAGS
|
||||
@echo + cc $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/kern/%.o: kern/%.S $(OBJDIR)/.vars.KERN_CFLAGS
|
||||
@echo + as $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/kern/%.o: lib/%.c $(OBJDIR)/.vars.KERN_CFLAGS
|
||||
@echo + cc $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
|
||||
|
||||
# Special flags for kern/init
|
||||
$(OBJDIR)/kern/init.o: override KERN_CFLAGS+=$(INIT_CFLAGS)
|
||||
$(OBJDIR)/kern/init.o: $(OBJDIR)/.vars.INIT_CFLAGS
|
||||
|
||||
# How to build the kernel itself
|
||||
$(OBJDIR)/kern/kernel: $(KERN_OBJFILES) $(KERN_BINFILES) kern/kernel.ld \
|
||||
$(OBJDIR)/.vars.KERN_LDFLAGS
|
||||
@echo + ld $@
|
||||
$(V)$(LD) -o $@ $(KERN_LDFLAGS) $(KERN_OBJFILES) $(GCC_LIB) -b binary $(KERN_BINFILES)
|
||||
$(V)$(OBJDUMP) -S $@ > $@.asm
|
||||
$(V)$(NM) -n $@ > $@.sym
|
||||
|
||||
# How to build the kernel disk image
|
||||
$(OBJDIR)/kern/kernel.img: $(OBJDIR)/kern/kernel $(OBJDIR)/boot/boot
|
||||
@echo + mk $@
|
||||
$(V)dd if=/dev/zero of=$(OBJDIR)/kern/kernel.img~ count=10000 2>/dev/null
|
||||
$(V)dd if=$(OBJDIR)/boot/boot of=$(OBJDIR)/kern/kernel.img~ conv=notrunc 2>/dev/null
|
||||
$(V)dd if=$(OBJDIR)/kern/kernel of=$(OBJDIR)/kern/kernel.img~ seek=1 conv=notrunc 2>/dev/null
|
||||
$(V)mv $(OBJDIR)/kern/kernel.img~ $(OBJDIR)/kern/kernel.img
|
||||
|
||||
all: $(OBJDIR)/kern/kernel.img
|
||||
|
||||
grub: $(OBJDIR)/jos-grub
|
||||
|
||||
$(OBJDIR)/jos-grub: $(OBJDIR)/kern/kernel
|
||||
@echo + oc $@
|
||||
$(V)$(OBJCOPY) --adjust-vma=0x10000000 $^ $@
|
||||
481
lab/kern/console.c
Normal file
481
lab/kern/console.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/kbdreg.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/console.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/picirq.h>
|
||||
|
||||
static void cons_intr(int (*proc)(void));
|
||||
static void cons_putc(int c);
|
||||
|
||||
// Stupid I/O delay routine necessitated by historical PC design flaws
|
||||
static void
|
||||
delay(void)
|
||||
{
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
}
|
||||
|
||||
/***** Serial I/O code *****/
|
||||
|
||||
#define COM1 0x3F8
|
||||
|
||||
#define COM_RX 0 // In: Receive buffer (DLAB=0)
|
||||
#define COM_TX 0 // Out: Transmit buffer (DLAB=0)
|
||||
#define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
|
||||
#define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
|
||||
#define COM_IER 1 // Out: Interrupt Enable Register
|
||||
#define COM_IER_RDI 0x01 // Enable receiver data interrupt
|
||||
#define COM_IIR 2 // In: Interrupt ID Register
|
||||
#define COM_FCR 2 // Out: FIFO Control Register
|
||||
#define COM_LCR 3 // Out: Line Control Register
|
||||
#define COM_LCR_DLAB 0x80 // Divisor latch access bit
|
||||
#define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
|
||||
#define COM_MCR 4 // Out: Modem Control Register
|
||||
#define COM_MCR_RTS 0x02 // RTS complement
|
||||
#define COM_MCR_DTR 0x01 // DTR complement
|
||||
#define COM_MCR_OUT2 0x08 // Out2 complement
|
||||
#define COM_LSR 5 // In: Line Status Register
|
||||
#define COM_LSR_DATA 0x01 // Data available
|
||||
#define COM_LSR_TXRDY 0x20 // Transmit buffer avail
|
||||
#define COM_LSR_TSRE 0x40 // Transmitter off
|
||||
|
||||
static bool serial_exists;
|
||||
|
||||
static int
|
||||
serial_proc_data(void)
|
||||
{
|
||||
if (!(inb(COM1+COM_LSR) & COM_LSR_DATA))
|
||||
return -1;
|
||||
return inb(COM1+COM_RX);
|
||||
}
|
||||
|
||||
void
|
||||
serial_intr(void)
|
||||
{
|
||||
if (serial_exists)
|
||||
cons_intr(serial_proc_data);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_putc(int c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0;
|
||||
!(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800;
|
||||
i++)
|
||||
delay();
|
||||
|
||||
outb(COM1 + COM_TX, c);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_init(void)
|
||||
{
|
||||
// Turn off the FIFO
|
||||
outb(COM1+COM_FCR, 0);
|
||||
|
||||
// Set speed; requires DLAB latch
|
||||
outb(COM1+COM_LCR, COM_LCR_DLAB);
|
||||
outb(COM1+COM_DLL, (uint8_t) (115200 / 9600));
|
||||
outb(COM1+COM_DLM, 0);
|
||||
|
||||
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||
outb(COM1+COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
|
||||
|
||||
// No modem controls
|
||||
outb(COM1+COM_MCR, 0);
|
||||
// Enable rcv interrupts
|
||||
outb(COM1+COM_IER, COM_IER_RDI);
|
||||
|
||||
// Clear any preexisting overrun indications and interrupts
|
||||
// Serial port doesn't exist if COM_LSR returns 0xFF
|
||||
serial_exists = (inb(COM1+COM_LSR) != 0xFF);
|
||||
(void) inb(COM1+COM_IIR);
|
||||
(void) inb(COM1+COM_RX);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** Parallel port output code *****/
|
||||
// For information on PC parallel port programming, see the class References
|
||||
// page.
|
||||
|
||||
static void
|
||||
lpt_putc(int c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
|
||||
delay();
|
||||
outb(0x378+0, c);
|
||||
outb(0x378+2, 0x08|0x04|0x01);
|
||||
outb(0x378+2, 0x08);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***** Text-mode CGA/VGA display output *****/
|
||||
|
||||
static unsigned addr_6845;
|
||||
static uint16_t *crt_buf;
|
||||
static uint16_t crt_pos;
|
||||
|
||||
static void
|
||||
cga_init(void)
|
||||
{
|
||||
volatile uint16_t *cp;
|
||||
uint16_t was;
|
||||
unsigned pos;
|
||||
|
||||
cp = (uint16_t*) (KERNBASE + CGA_BUF);
|
||||
was = *cp;
|
||||
*cp = (uint16_t) 0xA55A;
|
||||
if (*cp != 0xA55A) {
|
||||
cp = (uint16_t*) (KERNBASE + MONO_BUF);
|
||||
addr_6845 = MONO_BASE;
|
||||
} else {
|
||||
*cp = was;
|
||||
addr_6845 = CGA_BASE;
|
||||
}
|
||||
|
||||
/* Extract cursor location */
|
||||
outb(addr_6845, 14);
|
||||
pos = inb(addr_6845 + 1) << 8;
|
||||
outb(addr_6845, 15);
|
||||
pos |= inb(addr_6845 + 1);
|
||||
|
||||
crt_buf = (uint16_t*) cp;
|
||||
crt_pos = pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
cga_putc(int c)
|
||||
{
|
||||
// if no attribute given, then use black on white
|
||||
if (!(c & ~0xFF))
|
||||
c |= 0x0700;
|
||||
|
||||
switch (c & 0xff) {
|
||||
case '\b':
|
||||
if (crt_pos > 0) {
|
||||
crt_pos--;
|
||||
crt_buf[crt_pos] = (c & ~0xff) | ' ';
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
crt_pos += CRT_COLS;
|
||||
/* fallthru */
|
||||
case '\r':
|
||||
crt_pos -= (crt_pos % CRT_COLS);
|
||||
break;
|
||||
case '\t':
|
||||
cons_putc(' ');
|
||||
cons_putc(' ');
|
||||
cons_putc(' ');
|
||||
cons_putc(' ');
|
||||
cons_putc(' ');
|
||||
break;
|
||||
default:
|
||||
crt_buf[crt_pos++] = c; /* write the character */
|
||||
break;
|
||||
}
|
||||
|
||||
// What is the purpose of this?
|
||||
if (crt_pos >= CRT_SIZE) {
|
||||
int i;
|
||||
|
||||
memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
|
||||
for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
|
||||
crt_buf[i] = 0x0700 | ' ';
|
||||
crt_pos -= CRT_COLS;
|
||||
}
|
||||
|
||||
/* move that little blinky thing */
|
||||
outb(addr_6845, 14);
|
||||
outb(addr_6845 + 1, crt_pos >> 8);
|
||||
outb(addr_6845, 15);
|
||||
outb(addr_6845 + 1, crt_pos);
|
||||
}
|
||||
|
||||
|
||||
/***** Keyboard input code *****/
|
||||
|
||||
#define NO 0
|
||||
|
||||
#define SHIFT (1<<0)
|
||||
#define CTL (1<<1)
|
||||
#define ALT (1<<2)
|
||||
|
||||
#define CAPSLOCK (1<<3)
|
||||
#define NUMLOCK (1<<4)
|
||||
#define SCROLLLOCK (1<<5)
|
||||
|
||||
#define E0ESC (1<<6)
|
||||
|
||||
static uint8_t shiftcode[256] =
|
||||
{
|
||||
[0x1D] = CTL,
|
||||
[0x2A] = SHIFT,
|
||||
[0x36] = SHIFT,
|
||||
[0x38] = ALT,
|
||||
[0x9D] = CTL,
|
||||
[0xB8] = ALT
|
||||
};
|
||||
|
||||
static uint8_t togglecode[256] =
|
||||
{
|
||||
[0x3A] = CAPSLOCK,
|
||||
[0x45] = NUMLOCK,
|
||||
[0x46] = SCROLLLOCK
|
||||
};
|
||||
|
||||
static uint8_t normalmap[256] =
|
||||
{
|
||||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
||||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
||||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
||||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] = KEY_HOME, [0x9C] = '\n' /*KP_Enter*/,
|
||||
[0xB5] = '/' /*KP_Div*/, [0xC8] = KEY_UP,
|
||||
[0xC9] = KEY_PGUP, [0xCB] = KEY_LF,
|
||||
[0xCD] = KEY_RT, [0xCF] = KEY_END,
|
||||
[0xD0] = KEY_DN, [0xD1] = KEY_PGDN,
|
||||
[0xD2] = KEY_INS, [0xD3] = KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t shiftmap[256] =
|
||||
{
|
||||
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
|
||||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
||||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
||||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
||||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] = KEY_HOME, [0x9C] = '\n' /*KP_Enter*/,
|
||||
[0xB5] = '/' /*KP_Div*/, [0xC8] = KEY_UP,
|
||||
[0xC9] = KEY_PGUP, [0xCB] = KEY_LF,
|
||||
[0xCD] = KEY_RT, [0xCF] = KEY_END,
|
||||
[0xD0] = KEY_DN, [0xD1] = KEY_PGDN,
|
||||
[0xD2] = KEY_INS, [0xD3] = KEY_DEL
|
||||
};
|
||||
|
||||
#define C(x) (x - '@')
|
||||
|
||||
static uint8_t ctlmap[256] =
|
||||
{
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
|
||||
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
|
||||
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
|
||||
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
|
||||
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
|
||||
[0x97] = KEY_HOME,
|
||||
[0xB5] = C('/'), [0xC8] = KEY_UP,
|
||||
[0xC9] = KEY_PGUP, [0xCB] = KEY_LF,
|
||||
[0xCD] = KEY_RT, [0xCF] = KEY_END,
|
||||
[0xD0] = KEY_DN, [0xD1] = KEY_PGDN,
|
||||
[0xD2] = KEY_INS, [0xD3] = KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t *charcode[4] = {
|
||||
normalmap,
|
||||
shiftmap,
|
||||
ctlmap,
|
||||
ctlmap
|
||||
};
|
||||
|
||||
/*
|
||||
* Get data from the keyboard. If we finish a character, return it. Else 0.
|
||||
* Return -1 if no data.
|
||||
*/
|
||||
static int
|
||||
kbd_proc_data(void)
|
||||
{
|
||||
int c;
|
||||
uint8_t stat, data;
|
||||
static uint32_t shift;
|
||||
|
||||
stat = inb(KBSTATP);
|
||||
if ((stat & KBS_DIB) == 0)
|
||||
return -1;
|
||||
// Ignore data from mouse.
|
||||
if (stat & KBS_TERR)
|
||||
return -1;
|
||||
|
||||
data = inb(KBDATAP);
|
||||
|
||||
if (data == 0xE0) {
|
||||
// E0 escape character
|
||||
shift |= E0ESC;
|
||||
return 0;
|
||||
} else if (data & 0x80) {
|
||||
// Key released
|
||||
data = (shift & E0ESC ? data : data & 0x7F);
|
||||
shift &= ~(shiftcode[data] | E0ESC);
|
||||
return 0;
|
||||
} else if (shift & E0ESC) {
|
||||
// Last character was an E0 escape; or with 0x80
|
||||
data |= 0x80;
|
||||
shift &= ~E0ESC;
|
||||
}
|
||||
|
||||
shift |= shiftcode[data];
|
||||
shift ^= togglecode[data];
|
||||
|
||||
c = charcode[shift & (CTL | SHIFT)][data];
|
||||
if (shift & CAPSLOCK) {
|
||||
if ('a' <= c && c <= 'z')
|
||||
c += 'A' - 'a';
|
||||
else if ('A' <= c && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
|
||||
// Process special keys
|
||||
// Ctrl-Alt-Del: reboot
|
||||
if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
|
||||
cprintf("Rebooting!\n");
|
||||
outb(0x92, 0x3); // courtesy of Chris Frost
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
kbd_intr(void)
|
||||
{
|
||||
cons_intr(kbd_proc_data);
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_init(void)
|
||||
{
|
||||
// Drain the kbd buffer so that QEMU generates interrupts.
|
||||
kbd_intr();
|
||||
irq_setmask_8259A(irq_mask_8259A & ~(1<<IRQ_KBD));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** General device-independent console code *****/
|
||||
// Here we manage the console input buffer,
|
||||
// where we stash characters received from the keyboard or serial port
|
||||
// whenever the corresponding interrupt occurs.
|
||||
|
||||
#define CONSBUFSIZE 512
|
||||
|
||||
static struct {
|
||||
uint8_t buf[CONSBUFSIZE];
|
||||
uint32_t rpos;
|
||||
uint32_t wpos;
|
||||
} cons;
|
||||
|
||||
// called by device interrupt routines to feed input characters
|
||||
// into the circular console input buffer.
|
||||
static void
|
||||
cons_intr(int (*proc)(void))
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = (*proc)()) != -1) {
|
||||
if (c == 0)
|
||||
continue;
|
||||
cons.buf[cons.wpos++] = c;
|
||||
if (cons.wpos == CONSBUFSIZE)
|
||||
cons.wpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// return the next input character from the console, or 0 if none waiting
|
||||
int
|
||||
cons_getc(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
// poll for any pending input characters,
|
||||
// so that this function works even when interrupts are disabled
|
||||
// (e.g., when called from the kernel monitor).
|
||||
serial_intr();
|
||||
kbd_intr();
|
||||
|
||||
// grab the next character from the input buffer.
|
||||
if (cons.rpos != cons.wpos) {
|
||||
c = cons.buf[cons.rpos++];
|
||||
if (cons.rpos == CONSBUFSIZE)
|
||||
cons.rpos = 0;
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// output a character to the console
|
||||
static void
|
||||
cons_putc(int c)
|
||||
{
|
||||
serial_putc(c);
|
||||
lpt_putc(c);
|
||||
cga_putc(c);
|
||||
}
|
||||
|
||||
// initialize the console devices
|
||||
void
|
||||
cons_init(void)
|
||||
{
|
||||
cga_init();
|
||||
kbd_init();
|
||||
serial_init();
|
||||
|
||||
if (!serial_exists)
|
||||
cprintf("Serial port does not exist!\n");
|
||||
}
|
||||
|
||||
|
||||
// `High'-level console I/O. Used by readline and cprintf.
|
||||
|
||||
void
|
||||
cputchar(int c)
|
||||
{
|
||||
cons_putc(c);
|
||||
}
|
||||
|
||||
int
|
||||
getchar(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = cons_getc()) == 0)
|
||||
/* do nothing */;
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
iscons(int fdnum)
|
||||
{
|
||||
// used by readline
|
||||
return 1;
|
||||
}
|
||||
26
lab/kern/console.h
Normal file
26
lab/kern/console.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef _CONSOLE_H_
|
||||
#define _CONSOLE_H_
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
#define MONO_BASE 0x3B4
|
||||
#define MONO_BUF 0xB0000
|
||||
#define CGA_BASE 0x3D4
|
||||
#define CGA_BUF 0xB8000
|
||||
|
||||
#define CRT_ROWS 25
|
||||
#define CRT_COLS 80
|
||||
#define CRT_SIZE (CRT_ROWS * CRT_COLS)
|
||||
|
||||
void cons_init(void);
|
||||
int cons_getc(void);
|
||||
|
||||
void kbd_intr(void); // irq 1
|
||||
void serial_intr(void); // irq 4
|
||||
|
||||
#endif /* _CONSOLE_H_ */
|
||||
46
lab/kern/cpu.h
Normal file
46
lab/kern/cpu.h
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
#ifndef JOS_INC_CPU_H
|
||||
#define JOS_INC_CPU_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/env.h>
|
||||
|
||||
// Maximum number of CPUs
|
||||
#define NCPU 8
|
||||
|
||||
// Values of status in struct Cpu
|
||||
enum {
|
||||
CPU_UNUSED = 0,
|
||||
CPU_STARTED,
|
||||
CPU_HALTED,
|
||||
};
|
||||
|
||||
// Per-CPU state
|
||||
struct CpuInfo {
|
||||
uint8_t cpu_id; // Local APIC ID; index into cpus[] below
|
||||
volatile unsigned cpu_status; // The status of the CPU
|
||||
struct Env *cpu_env; // The currently-running environment.
|
||||
struct Taskstate cpu_ts; // Used by x86 to find stack for interrupt
|
||||
};
|
||||
|
||||
// Initialized in mpconfig.c
|
||||
extern struct CpuInfo cpus[NCPU];
|
||||
extern int ncpu; // Total number of CPUs in the system
|
||||
extern struct CpuInfo *bootcpu; // The boot-strap processor (BSP)
|
||||
extern physaddr_t lapicaddr; // Physical MMIO address of the local APIC
|
||||
|
||||
// Per-CPU kernel stacks
|
||||
extern unsigned char percpu_kstacks[NCPU][KSTKSIZE];
|
||||
|
||||
int cpunum(void);
|
||||
#define thiscpu (&cpus[cpunum()])
|
||||
|
||||
void mp_init(void);
|
||||
void lapic_init(void);
|
||||
void lapic_startap(uint8_t apicid, uint32_t addr);
|
||||
void lapic_eoi(void);
|
||||
void lapic_ipi(int vector);
|
||||
|
||||
#endif
|
||||
97
lab/kern/entry.S
Normal file
97
lab/kern/entry.S
Normal file
@@ -0,0 +1,97 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
# Shift Right Logical
|
||||
#define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
|
||||
|
||||
|
||||
###################################################################
|
||||
# The kernel (this code) is linked at address ~(KERNBASE + 1 Meg),
|
||||
# but the bootloader loads it at address ~1 Meg.
|
||||
#
|
||||
# RELOC(x) maps a symbol x from its link address to its actual
|
||||
# location in physical memory (its load address).
|
||||
###################################################################
|
||||
|
||||
#define RELOC(x) ((x) - KERNBASE)
|
||||
|
||||
#define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
|
||||
#define MULTIBOOT_HEADER_FLAGS (0)
|
||||
#define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS))
|
||||
|
||||
###################################################################
|
||||
# entry point
|
||||
###################################################################
|
||||
|
||||
.text
|
||||
|
||||
# The Multiboot header
|
||||
.align 4
|
||||
.long MULTIBOOT_HEADER_MAGIC
|
||||
.long MULTIBOOT_HEADER_FLAGS
|
||||
.long CHECKSUM
|
||||
|
||||
# '_start' specifies the ELF entry point. Since we haven't set up
|
||||
# virtual memory when the bootloader enters this code, we need the
|
||||
# bootloader to jump to the *physical* address of the entry point.
|
||||
.globl _start
|
||||
_start = RELOC(entry)
|
||||
|
||||
.globl entry
|
||||
entry:
|
||||
movw $0x1234,0x472 # warm boot
|
||||
|
||||
# We haven't set up virtual memory yet, so we're running from
|
||||
# the physical address the boot loader loaded the kernel at: 1MB
|
||||
# (plus a few bytes). However, the C code is linked to run at
|
||||
# KERNBASE+1MB. Hence, we set up a trivial page directory that
|
||||
# translates virtual addresses [KERNBASE, KERNBASE+4MB) to
|
||||
# physical addresses [0, 4MB). This 4MB region will be
|
||||
# sufficient until we set up our real page table in mem_init
|
||||
# in lab 2.
|
||||
|
||||
# Load the physical address of entry_pgdir into cr3. entry_pgdir
|
||||
# is defined in entrypgdir.c.
|
||||
movl $(RELOC(entry_pgdir)), %eax
|
||||
movl %eax, %cr3
|
||||
# Turn on paging.
|
||||
movl %cr0, %eax
|
||||
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Now paging is enabled, but we're still running at a low EIP
|
||||
# (why is this okay?). Jump up above KERNBASE before entering
|
||||
# C code.
|
||||
mov $relocated, %eax
|
||||
jmp *%eax
|
||||
relocated:
|
||||
|
||||
# Clear the frame pointer register (EBP)
|
||||
# so that once we get into debugging C code,
|
||||
# stack backtraces will be terminated properly.
|
||||
movl $0x0,%ebp # nuke frame pointer
|
||||
|
||||
# Set the stack pointer
|
||||
movl $(bootstacktop),%esp
|
||||
|
||||
# now to C code
|
||||
call i386_init
|
||||
|
||||
# Should never get here, but in case we do, just spin.
|
||||
spin: jmp spin
|
||||
|
||||
|
||||
.data
|
||||
###################################################################
|
||||
# boot stack
|
||||
###################################################################
|
||||
.p2align PGSHIFT # force page alignment
|
||||
.globl bootstack
|
||||
bootstack:
|
||||
.space KSTKSIZE
|
||||
.globl bootstacktop
|
||||
bootstacktop:
|
||||
|
||||
1059
lab/kern/entrypgdir.c
Normal file
1059
lab/kern/entrypgdir.c
Normal file
File diff suppressed because it is too large
Load Diff
572
lab/kern/env.c
Normal file
572
lab/kern/env.c
Normal file
@@ -0,0 +1,572 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
struct Env *envs = NULL; // All environments
|
||||
static struct Env *env_free_list; // Free environment list
|
||||
// (linked by Env->env_link)
|
||||
|
||||
#define ENVGENSHIFT 12 // >= LOGNENV
|
||||
|
||||
// Global descriptor table.
|
||||
//
|
||||
// Set up global descriptor table (GDT) with separate segments for
|
||||
// kernel mode and user mode. Segments serve many purposes on the x86.
|
||||
// We don't use any of their memory-mapping capabilities, but we need
|
||||
// them to switch privilege levels.
|
||||
//
|
||||
// The kernel and user segments are identical except for the DPL.
|
||||
// To load the SS register, the CPL must equal the DPL. Thus,
|
||||
// we must duplicate the segments for the user and the kernel.
|
||||
//
|
||||
// In particular, the last argument to the SEG macro used in the
|
||||
// definition of gdt specifies the Descriptor Privilege Level (DPL)
|
||||
// of that descriptor: 0 for kernel and 3 for user.
|
||||
//
|
||||
struct Segdesc gdt[NCPU + 5] =
|
||||
{
|
||||
// 0x0 - unused (always faults -- for trapping NULL far pointers)
|
||||
SEG_NULL,
|
||||
|
||||
// 0x8 - kernel code segment
|
||||
[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
|
||||
|
||||
// 0x10 - kernel data segment
|
||||
[GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
|
||||
|
||||
// 0x18 - user code segment
|
||||
[GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
|
||||
|
||||
// 0x20 - user data segment
|
||||
[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
|
||||
|
||||
// Per-CPU TSS descriptors (starting from GD_TSS0) are initialized
|
||||
// in trap_init_percpu()
|
||||
[GD_TSS0 >> 3] = SEG_NULL
|
||||
};
|
||||
|
||||
struct Pseudodesc gdt_pd = {
|
||||
sizeof(gdt) - 1, (unsigned long) gdt
|
||||
};
|
||||
|
||||
//
|
||||
// Converts an envid to an env pointer.
|
||||
// If checkperm is set, the specified environment must be either the
|
||||
// current environment or an immediate child of the current environment.
|
||||
//
|
||||
// RETURNS
|
||||
// 0 on success, -E_BAD_ENV on error.
|
||||
// On success, sets *env_store to the environment.
|
||||
// On error, sets *env_store to NULL.
|
||||
//
|
||||
int
|
||||
envid2env(envid_t envid, struct Env **env_store, bool checkperm)
|
||||
{
|
||||
struct Env *e;
|
||||
|
||||
// If envid is zero, return the current environment.
|
||||
if (envid == 0) {
|
||||
*env_store = curenv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Look up the Env structure via the index part of the envid,
|
||||
// then check the env_id field in that struct Env
|
||||
// to ensure that the envid is not stale
|
||||
// (i.e., does not refer to a _previous_ environment
|
||||
// that used the same slot in the envs[] array).
|
||||
e = &envs[ENVX(envid)];
|
||||
if (e->env_status == ENV_FREE || e->env_id != envid) {
|
||||
*env_store = 0;
|
||||
return -E_BAD_ENV;
|
||||
}
|
||||
|
||||
// Check that the calling environment has legitimate permission
|
||||
// to manipulate the specified environment.
|
||||
// If checkperm is set, the specified environment
|
||||
// must be either the current environment
|
||||
// or an immediate child of the current environment.
|
||||
if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) {
|
||||
*env_store = 0;
|
||||
return -E_BAD_ENV;
|
||||
}
|
||||
|
||||
*env_store = e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mark all environments in 'envs' as free, set their env_ids to 0,
|
||||
// and insert them into the env_free_list.
|
||||
// Make sure the environments are in the free list in the same order
|
||||
// they are in the envs array (i.e., so that the first call to
|
||||
// env_alloc() returns envs[0]).
|
||||
//
|
||||
void
|
||||
env_init(void)
|
||||
{
|
||||
// Set up envs array
|
||||
// LAB 3: Your code here.
|
||||
int i;
|
||||
// 确保最小的env在最前端
|
||||
for (i = NENV-1; i >= 0; --i) {
|
||||
envs[i].env_id = 0;
|
||||
|
||||
envs[i].env_link = env_free_list;
|
||||
env_free_list = &envs[i];
|
||||
}
|
||||
|
||||
// Per-CPU part of the initialization
|
||||
env_init_percpu();
|
||||
}
|
||||
|
||||
// Load GDT and segment descriptors.
|
||||
void
|
||||
env_init_percpu(void)
|
||||
{
|
||||
lgdt(&gdt_pd);
|
||||
// The kernel never uses GS or FS, so we leave those set to
|
||||
// the user data segment.
|
||||
asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3));
|
||||
asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3));
|
||||
// The kernel does use ES, DS, and SS. We'll change between
|
||||
// the kernel and user data segments as needed.
|
||||
asm volatile("movw %%ax,%%es" : : "a" (GD_KD));
|
||||
asm volatile("movw %%ax,%%ds" : : "a" (GD_KD));
|
||||
asm volatile("movw %%ax,%%ss" : : "a" (GD_KD));
|
||||
// Load the kernel text segment into CS.
|
||||
asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT));
|
||||
// For good measure, clear the local descriptor table (LDT),
|
||||
// since we don't use it.
|
||||
lldt(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the kernel virtual memory layout for environment e.
|
||||
// Allocate a page directory, set e->env_pgdir accordingly,
|
||||
// and initialize the kernel portion of the new environment's address space.
|
||||
// Do NOT (yet) map anything into the user portion
|
||||
// of the environment's virtual address space.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors include:
|
||||
// -E_NO_MEM if page directory or table could not be allocated.
|
||||
// 这里又为每个环境分配一个页目录,有点晕了。
|
||||
static int
|
||||
env_setup_vm(struct Env *e)
|
||||
{
|
||||
int i;
|
||||
struct PageInfo *p = NULL;
|
||||
|
||||
// Allocate a page for the page directory
|
||||
if (!(p = page_alloc(ALLOC_ZERO)))
|
||||
return -E_NO_MEM;
|
||||
|
||||
// Now, set e->env_pgdir and initialize the page directory.
|
||||
//
|
||||
// Hint:
|
||||
// - The VA space of all envs is identical above UTOP
|
||||
// (except at UVPT, which we've set below).
|
||||
// See inc/memlayout.h for permissions and layout.
|
||||
// Can you use kern_pgdir as a template? Hint: Yes.
|
||||
// (Make sure you got the permissions right in Lab 2.)
|
||||
// - The initial VA below UTOP is empty.
|
||||
// - You do not need to make any more calls to page_alloc.
|
||||
// - Note: In general, pp_ref is not maintained for
|
||||
// physical pages mapped only above UTOP, but env_pgdir
|
||||
// is an exception -- you need to increment env_pgdir's
|
||||
// pp_ref for env_free to work correctly.
|
||||
// - The functions in kern/pmap.h are handy.
|
||||
|
||||
|
||||
// LAB 3: Your code here.
|
||||
// 申请一个页表存用户环境的页目录
|
||||
|
||||
e->env_pgdir = page2kva(p);
|
||||
// why ?因为在UTOP之上都是一样的,所以可以直接把kern_pgdir的内容全部拷贝过来
|
||||
|
||||
memcpy(e->env_pgdir, kern_pgdir, PGSIZE);
|
||||
p->pp_ref++;
|
||||
// UVPT maps the env's own page table read-only.
|
||||
// Permissions: kernel R, user R
|
||||
// 但是唯独UVPT这个地方是不一样的,因为要放的是自己的页表目录
|
||||
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates and initializes a new environment.
|
||||
// On success, the new environment is stored in *newenv_store.
|
||||
//
|
||||
// Returns 0 on success, < 0 on failure. Errors include:
|
||||
// -E_NO_FREE_ENV if all NENV environments are allocated
|
||||
// -E_NO_MEM on memory exhaustion
|
||||
//
|
||||
int
|
||||
env_alloc(struct Env **newenv_store, envid_t parent_id)
|
||||
{
|
||||
int32_t generation;
|
||||
int r;
|
||||
struct Env *e;
|
||||
|
||||
if (!(e = env_free_list))
|
||||
return -E_NO_FREE_ENV;
|
||||
|
||||
// Allocate and set up the page directory for this environment.
|
||||
if ((r = env_setup_vm(e)) < 0)
|
||||
return r;
|
||||
|
||||
// Generate an env_id for this environment.
|
||||
generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1);
|
||||
if (generation <= 0) // Don't create a negative env_id.
|
||||
generation = 1 << ENVGENSHIFT;
|
||||
e->env_id = generation | (e - envs);
|
||||
|
||||
// Set the basic status variables.
|
||||
e->env_parent_id = parent_id;
|
||||
e->env_type = ENV_TYPE_USER;
|
||||
e->env_status = ENV_RUNNABLE;
|
||||
e->env_runs = 0;
|
||||
|
||||
// Clear out all the saved register state,
|
||||
// to prevent the register values
|
||||
// of a prior environment inhabiting this Env structure
|
||||
// from "leaking" into our new environment.
|
||||
memset(&e->env_tf, 0, sizeof(e->env_tf));
|
||||
|
||||
// Set up appropriate initial values for the segment registers.
|
||||
// GD_UD is the user data segment selector in the GDT, and
|
||||
// GD_UT is the user text segment selector (see inc/memlayout.h).
|
||||
// The low 2 bits of each segment register contains the
|
||||
// Requestor Privilege Level (RPL); 3 means user mode. When
|
||||
// we switch privilege levels, the hardware does various
|
||||
// checks involving the RPL and the Descriptor Privilege Level
|
||||
// (DPL) stored in the descriptors themselves.
|
||||
e->env_tf.tf_ds = GD_UD | 3;
|
||||
e->env_tf.tf_es = GD_UD | 3;
|
||||
e->env_tf.tf_ss = GD_UD | 3;
|
||||
e->env_tf.tf_esp = USTACKTOP;
|
||||
e->env_tf.tf_cs = GD_UT | 3;
|
||||
// You will set e->env_tf.tf_eip later.
|
||||
|
||||
// Enable interrupts while in user mode.
|
||||
// LAB 4: Your code here.
|
||||
e->env_tf.tf_eflags |= FL_IF;
|
||||
// Clear the page fault handler until user installs one.
|
||||
e->env_pgfault_upcall = 0;
|
||||
|
||||
// Also clear the IPC receiving flag.
|
||||
e->env_ipc_recving = 0;
|
||||
|
||||
// commit the allocation
|
||||
env_free_list = e->env_link;
|
||||
*newenv_store = e;
|
||||
|
||||
cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate len bytes of physical memory for environment env,
|
||||
// and map it at virtual address va in the environment's address space.
|
||||
// Does not zero or otherwise initialize the mapped pages in any way.
|
||||
// Pages should be writable by user and kernel.
|
||||
// Panic if any allocation attempt fails.
|
||||
//
|
||||
static void
|
||||
region_alloc(struct Env *e, void *va, size_t len)
|
||||
{
|
||||
// LAB 3: Your code here.
|
||||
// (But only if you need it for load_icode.)
|
||||
//
|
||||
struct PageInfo *pp;
|
||||
size_t i;
|
||||
size_t pgs = ROUNDUP(len, PGSIZE)/PGSIZE;
|
||||
size_t pva = ROUNDDOWN((size_t)va, PGSIZE);
|
||||
|
||||
|
||||
for(i=0; i < pgs; i++) {
|
||||
if (!(pp = page_alloc(ALLOC_ZERO))) {
|
||||
int ex = -E_NO_MEM;
|
||||
panic("region_alloc: %e", ex);
|
||||
}
|
||||
page_insert(e->env_pgdir, pp, (void *)pva, PTE_U|PTE_W|PTE_P);
|
||||
pva += PGSIZE;
|
||||
|
||||
}
|
||||
// Hint: It is easier to use region_alloc if the caller can pass
|
||||
// 'va' and 'len' values that are not page-aligned.
|
||||
// You should round va down, and round (va + len) up.
|
||||
// (Watch out for corner-cases!)
|
||||
}
|
||||
|
||||
//
|
||||
// Set up the initial program binary, stack, and processor flags
|
||||
// for a user process.
|
||||
// This function is ONLY called during kernel initialization,
|
||||
// before running the first user-mode environment.
|
||||
//
|
||||
// This function loads all loadable segments from the ELF binary image
|
||||
// into the environment's user memory, starting at the appropriate
|
||||
// virtual addresses indicated in the ELF program header.
|
||||
// At the same time it clears to zero any portions of these segments
|
||||
// that are marked in the program header as being mapped
|
||||
// but not actually present in the ELF file - i.e., the program's bss section.
|
||||
//
|
||||
// All this is very similar to what our boot loader does, except the boot
|
||||
// loader also needs to read the code from disk. Take a look at
|
||||
// boot/main.c to get ideas.
|
||||
//
|
||||
// Finally, this function maps one page for the program's initial stack.
|
||||
//
|
||||
// load_icode panics if it encounters problems.
|
||||
// - How might load_icode fail? What might be wrong with the given input?
|
||||
//
|
||||
static void
|
||||
load_icode(struct Env *e, uint8_t *binary)
|
||||
{
|
||||
// Hints:
|
||||
// Load each program segment into virtual memory
|
||||
// at the address specified in the ELF segment header.
|
||||
// You should only load segments with ph->p_type == ELF_PROG_LOAD.
|
||||
// Each segment's virtual address can be found in ph->p_va
|
||||
// and its size in memory can be found in ph->p_memsz.
|
||||
// The ph->p_filesz bytes from the ELF binary, starting at
|
||||
// 'binary + ph->p_offset', should be copied to virtual address
|
||||
// ph->p_va. Any remaining memory bytes should be cleared to zero.
|
||||
// (The ELF header should have ph->p_filesz <= ph->p_memsz.)
|
||||
// Use functions from the previous lab to allocate and map pages.
|
||||
//
|
||||
// All page protection bits should be user read/write for now.
|
||||
// ELF segments are not necessarily page-aligned, but you can
|
||||
// assume for this function that no two segments will touch
|
||||
// the same virtual page.
|
||||
//
|
||||
// You may find a function like region_alloc useful.
|
||||
//
|
||||
// Loading the segments is much simpler if you can move data
|
||||
// directly into the virtual addresses stored in the ELF binary.
|
||||
// So which page directory should be in force during
|
||||
// this function?
|
||||
//
|
||||
// You must also do something with the program's entry point,
|
||||
// to make sure that the environment starts executing there.
|
||||
// What? (See env_run() and env_pop_tf() below.)
|
||||
|
||||
// LAB 3: Your code here.
|
||||
// 怎么得到program的ELF地址? 就是bianry
|
||||
struct Proghdr *ph, *eph;
|
||||
struct Elf *elfhdr = (struct Elf *)binary;
|
||||
|
||||
// is this a valid ELF?
|
||||
if (elfhdr->e_magic != ELF_MAGIC)
|
||||
panic("elf header's magic is not correct\n");
|
||||
|
||||
// 所有程序段
|
||||
ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff);
|
||||
eph = ph + elfhdr->e_phnum;
|
||||
// 转换到用户页目录,用户空间映射
|
||||
lcr3(PADDR(e->env_pgdir));
|
||||
|
||||
for(; ph < eph; ph++) {
|
||||
|
||||
if (ph->p_type != ELF_PROG_LOAD)
|
||||
continue;
|
||||
if (ph->p_filesz > ph->p_memsz)
|
||||
panic("file size is great than memmory size\n");
|
||||
|
||||
region_alloc(e, (void *)ph->p_va, ph->p_memsz);
|
||||
memcpy((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz);
|
||||
// clear bss section
|
||||
memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz));
|
||||
|
||||
}
|
||||
e->env_tf.tf_eip = elfhdr->e_entry;
|
||||
// Now map one page for the program's initial stack
|
||||
// at virtual address USTACKTOP - PGSIZE.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
|
||||
// map the user stack
|
||||
region_alloc(e, (void *)USTACKTOP-PGSIZE, PGSIZE);
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates a new env with env_alloc, loads the named elf
|
||||
// binary into it with load_icode, and sets its env_type.
|
||||
// This function is ONLY called during kernel initialization,
|
||||
// before running the first user-mode environment.
|
||||
// The new env's parent ID is set to 0.
|
||||
//
|
||||
void
|
||||
env_create(uint8_t *binary, enum EnvType type)
|
||||
{
|
||||
// LAB 3: Your code here.
|
||||
struct Env *newenv;
|
||||
int ret = 0;
|
||||
if ((ret = env_alloc(&newenv, 0)) < 0) {
|
||||
panic("env_create: %e\n", ret);
|
||||
}
|
||||
newenv->env_type = type;
|
||||
load_icode(newenv, binary);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Frees env e and all memory it uses.
|
||||
//
|
||||
void
|
||||
env_free(struct Env *e)
|
||||
{
|
||||
pte_t *pt;
|
||||
uint32_t pdeno, pteno;
|
||||
physaddr_t pa;
|
||||
|
||||
// If freeing the current environment, switch to kern_pgdir
|
||||
// before freeing the page directory, just in case the page
|
||||
// gets reused.
|
||||
if (e == curenv)
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
|
||||
// Note the environment's demise.
|
||||
cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
|
||||
// Flush all mapped pages in the user portion of the address space
|
||||
static_assert(UTOP % PTSIZE == 0);
|
||||
for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) {
|
||||
|
||||
// only look at mapped page tables
|
||||
if (!(e->env_pgdir[pdeno] & PTE_P))
|
||||
continue;
|
||||
|
||||
// find the pa and va of the page table
|
||||
pa = PTE_ADDR(e->env_pgdir[pdeno]);
|
||||
pt = (pte_t*) KADDR(pa);
|
||||
|
||||
// unmap all PTEs in this page table
|
||||
for (pteno = 0; pteno <= PTX(~0); pteno++) {
|
||||
if (pt[pteno] & PTE_P)
|
||||
page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
|
||||
}
|
||||
|
||||
// free the page table itself
|
||||
e->env_pgdir[pdeno] = 0;
|
||||
page_decref(pa2page(pa));
|
||||
}
|
||||
|
||||
// free the page directory
|
||||
pa = PADDR(e->env_pgdir);
|
||||
e->env_pgdir = 0;
|
||||
page_decref(pa2page(pa));
|
||||
|
||||
// return the environment to the free list
|
||||
e->env_status = ENV_FREE;
|
||||
e->env_link = env_free_list;
|
||||
env_free_list = e;
|
||||
}
|
||||
|
||||
//
|
||||
// Frees environment e.
|
||||
// If e was the current env, then runs a new environment (and does not return
|
||||
// to the caller).
|
||||
//
|
||||
void
|
||||
env_destroy(struct Env *e)
|
||||
{
|
||||
// If e is currently running on other CPUs, we change its state to
|
||||
// ENV_DYING. A zombie environment will be freed the next time
|
||||
// it traps to the kernel.
|
||||
if (e->env_status == ENV_RUNNING && curenv != e) {
|
||||
e->env_status = ENV_DYING;
|
||||
return;
|
||||
}
|
||||
|
||||
env_free(e);
|
||||
|
||||
if (curenv == e) {
|
||||
curenv = NULL;
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Restores the register values in the Trapframe with the 'iret' instruction.
|
||||
// This exits the kernel and starts executing some environment's code.
|
||||
//
|
||||
// This function does not return.
|
||||
//
|
||||
void
|
||||
env_pop_tf(struct Trapframe *tf)
|
||||
{
|
||||
// Record the CPU we are running on for user-space debugging
|
||||
curenv->env_cpunum = cpunum();
|
||||
|
||||
asm volatile(
|
||||
"\tmovl %0,%%esp\n"
|
||||
"\tpopal\n"
|
||||
"\tpopl %%es\n"
|
||||
"\tpopl %%ds\n"
|
||||
"\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */
|
||||
"\tiret\n"
|
||||
: : "g" (tf) : "memory");
|
||||
panic("iret failed"); /* mostly to placate the compiler */
|
||||
}
|
||||
|
||||
//
|
||||
// Context switch from curenv to env e.
|
||||
// Note: if this is the first call to env_run, curenv is NULL.
|
||||
//
|
||||
// This function does not return.
|
||||
//
|
||||
void
|
||||
env_run(struct Env *e)
|
||||
{
|
||||
// Step 1: If this is a context switch (a new environment is running):
|
||||
// 1. Set the current environment (if any) back to
|
||||
// ENV_RUNNABLE if it is ENV_RUNNING (think about
|
||||
// what other states it can be in),
|
||||
// 2. Set 'curenv' to the new environment,
|
||||
// 3. Set its status to ENV_RUNNING,
|
||||
// 4. Update its 'env_runs' counter,
|
||||
// 5. Use lcr3() to switch to its address space.
|
||||
// Step 2: Use env_pop_tf() to restore the environment's
|
||||
// registers and drop into user mode in the
|
||||
// environment.
|
||||
|
||||
// Hint: This function loads the new environment's state from
|
||||
// e->env_tf. Go back through the code you wrote above
|
||||
// and make sure you have set the relevant parts of
|
||||
// e->env_tf to sensible values.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
if (curenv && curenv->env_status == ENV_RUNNING) {
|
||||
curenv->env_status = ENV_RUNNABLE;
|
||||
}
|
||||
curenv = e;
|
||||
curenv->env_status = ENV_RUNNING;
|
||||
curenv->env_runs++;
|
||||
lcr3(PADDR(curenv->env_pgdir));
|
||||
|
||||
unlock_kernel();
|
||||
// iret退出内核, 回到用户环境执行,
|
||||
// 在load_icode() 中 env_tf保存了可执行文件的eip等信息
|
||||
env_pop_tf(&(curenv->env_tf));
|
||||
|
||||
|
||||
// panic("env_run not yet implemented");
|
||||
}
|
||||
|
||||
36
lab/kern/env.h
Normal file
36
lab/kern/env.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_ENV_H
|
||||
#define JOS_KERN_ENV_H
|
||||
|
||||
#include <inc/env.h>
|
||||
#include <kern/cpu.h>
|
||||
|
||||
extern struct Env *envs; // All environments
|
||||
#define curenv (thiscpu->cpu_env) // Current environment
|
||||
extern struct Segdesc gdt[];
|
||||
|
||||
void env_init(void);
|
||||
void env_init_percpu(void);
|
||||
int env_alloc(struct Env **e, envid_t parent_id);
|
||||
void env_free(struct Env *e);
|
||||
void env_create(uint8_t *binary, enum EnvType type);
|
||||
void env_destroy(struct Env *e); // Does not return if e == curenv
|
||||
|
||||
int envid2env(envid_t envid, struct Env **env_store, bool checkperm);
|
||||
// The following two functions do not return
|
||||
void env_run(struct Env *e) __attribute__((noreturn));
|
||||
void env_pop_tf(struct Trapframe *tf) __attribute__((noreturn));
|
||||
|
||||
// Without this extra macro, we couldn't pass macros like TEST to
|
||||
// ENV_CREATE because of the C pre-processor's argument prescan rule.
|
||||
#define ENV_PASTE3(x, y, z) x ## y ## z
|
||||
|
||||
#define ENV_CREATE(x, type) \
|
||||
do { \
|
||||
extern uint8_t ENV_PASTE3(_binary_obj_, x, _start)[]; \
|
||||
env_create(ENV_PASTE3(_binary_obj_, x, _start), \
|
||||
type); \
|
||||
} while (0)
|
||||
|
||||
#endif // !JOS_KERN_ENV_H
|
||||
170
lab/kern/init.c
Normal file
170
lab/kern/init.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
static void boot_aps(void);
|
||||
|
||||
|
||||
void
|
||||
i386_init(void)
|
||||
{
|
||||
// Initialize the console.
|
||||
// Can't call cprintf until after we do this!
|
||||
cons_init();
|
||||
|
||||
cprintf("6828 decimal is %o octal!\n", 6828);
|
||||
|
||||
// Lab 2 memory management initialization functions
|
||||
mem_init();
|
||||
|
||||
// Lab 3 user environment initialization functions
|
||||
env_init();
|
||||
trap_init();
|
||||
|
||||
// Lab 4 multiprocessor initialization functions
|
||||
mp_init();
|
||||
lapic_init();
|
||||
|
||||
// Lab 4 multitasking initialization functions
|
||||
pic_init();
|
||||
|
||||
// Acquire the big kernel lock before waking up APs
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
// Starting non-boot CPUs
|
||||
boot_aps();
|
||||
|
||||
#if defined(TEST)
|
||||
// Don't touch -- used by grading script!
|
||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||
#else
|
||||
// Touch all you want.
|
||||
//ENV_CREATE(user_primes, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
|
||||
|
||||
#endif // TEST*
|
||||
|
||||
// Schedule and run the first user environment!
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// While boot_aps is booting a given CPU, it communicates the per-core
|
||||
// stack pointer that should be loaded by mpentry.S to that CPU in
|
||||
// this variable.
|
||||
void *mpentry_kstack;
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
boot_aps(void)
|
||||
{
|
||||
extern unsigned char mpentry_start[], mpentry_end[];
|
||||
void *code;
|
||||
struct CpuInfo *c;
|
||||
|
||||
// Write entry code to unused memory at MPENTRY_PADDR
|
||||
code = KADDR(MPENTRY_PADDR);
|
||||
memmove(code, mpentry_start, mpentry_end - mpentry_start);
|
||||
|
||||
// Boot each AP one at a time
|
||||
for (c = cpus; c < cpus + ncpu; c++) {
|
||||
if (c == cpus + cpunum()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell mpentry.S what stack to use
|
||||
mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE;
|
||||
// Start the CPU at mpentry_start
|
||||
lapic_startap(c->cpu_id, PADDR(code));
|
||||
// Wait for the CPU to finish some basic setup in mp_main()
|
||||
while(c->cpu_status != CPU_STARTED)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup code for APs
|
||||
void
|
||||
mp_main(void)
|
||||
{
|
||||
// We are in high EIP now, safe to switch to kern_pgdir
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
cprintf("SMP: CPU %d starting\n", cpunum());
|
||||
|
||||
lapic_init();
|
||||
env_init_percpu();
|
||||
trap_init_percpu();
|
||||
xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up
|
||||
|
||||
// Now that we have finished some basic setup, call sched_yield()
|
||||
// to start running processes on this CPU. But make sure that
|
||||
// only one CPU can enter the scheduler at a time!
|
||||
//
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
|
||||
sched_yield();
|
||||
// Remove this after you finish Exercise 6
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable panicstr contains argument to first call to panic; used as flag
|
||||
* to indicate that the kernel has already called panic.
|
||||
*/
|
||||
const char *panicstr;
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors.
|
||||
* It prints "panic: mesg", and then enters the kernel monitor.
|
||||
*/
|
||||
void
|
||||
_panic(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (panicstr)
|
||||
goto dead;
|
||||
panicstr = fmt;
|
||||
|
||||
// Be extra sure that the machine is in as reasonable state
|
||||
asm volatile("cli; cld");
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
||||
dead:
|
||||
/* break into the kernel monitor */
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
/* like panic, but don't */
|
||||
void
|
||||
_warn(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel warning at %s:%d: ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
22
lab/kern/kclock.c
Normal file
22
lab/kern/kclock.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
/* Support for reading the NVRAM from the real-time clock. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
|
||||
#include <kern/kclock.h>
|
||||
|
||||
|
||||
unsigned
|
||||
mc146818_read(unsigned reg)
|
||||
{
|
||||
outb(IO_RTC, reg);
|
||||
return inb(IO_RTC+1);
|
||||
}
|
||||
|
||||
void
|
||||
mc146818_write(unsigned reg, unsigned datum)
|
||||
{
|
||||
outb(IO_RTC, reg);
|
||||
outb(IO_RTC+1, datum);
|
||||
}
|
||||
29
lab/kern/kclock.h
Normal file
29
lab/kern/kclock.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_KCLOCK_H
|
||||
#define JOS_KERN_KCLOCK_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#define IO_RTC 0x070 /* RTC port */
|
||||
|
||||
#define MC_NVRAM_START 0xe /* start of NVRAM: offset 14 */
|
||||
#define MC_NVRAM_SIZE 50 /* 50 bytes of NVRAM */
|
||||
|
||||
/* NVRAM bytes 7 & 8: base memory size */
|
||||
#define NVRAM_BASELO (MC_NVRAM_START + 7) /* low byte; RTC off. 0x15 */
|
||||
#define NVRAM_BASEHI (MC_NVRAM_START + 8) /* high byte; RTC off. 0x16 */
|
||||
|
||||
/* NVRAM bytes 9 & 10: extended memory size (between 1MB and 16MB) */
|
||||
#define NVRAM_EXTLO (MC_NVRAM_START + 9) /* low byte; RTC off. 0x17 */
|
||||
#define NVRAM_EXTHI (MC_NVRAM_START + 10) /* high byte; RTC off. 0x18 */
|
||||
|
||||
/* NVRAM bytes 38 and 39: extended memory size (between 16MB and 4G) */
|
||||
#define NVRAM_EXT16LO (MC_NVRAM_START + 38) /* low byte; RTC off. 0x34 */
|
||||
#define NVRAM_EXT16HI (MC_NVRAM_START + 39) /* high byte; RTC off. 0x35 */
|
||||
|
||||
unsigned mc146818_read(unsigned reg);
|
||||
void mc146818_write(unsigned reg, unsigned datum);
|
||||
|
||||
#endif // !JOS_KERN_KCLOCK_H
|
||||
236
lab/kern/kdebug.c
Normal file
236
lab/kern/kdebug.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <inc/stab.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/kdebug.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/env.h>
|
||||
|
||||
extern const struct Stab __STAB_BEGIN__[]; // Beginning of stabs table
|
||||
extern const struct Stab __STAB_END__[]; // End of stabs table
|
||||
extern const char __STABSTR_BEGIN__[]; // Beginning of string table
|
||||
extern const char __STABSTR_END__[]; // End of string table
|
||||
|
||||
struct UserStabData {
|
||||
const struct Stab *stabs;
|
||||
const struct Stab *stab_end;
|
||||
const char *stabstr;
|
||||
const char *stabstr_end;
|
||||
};
|
||||
|
||||
|
||||
// stab_binsearch(stabs, region_left, region_right, type, addr)
|
||||
//
|
||||
// Some stab types are arranged in increasing order by instruction
|
||||
// address. For example, N_FUN stabs (stab entries with n_type ==
|
||||
// N_FUN), which mark functions, and N_SO stabs, which mark source files.
|
||||
//
|
||||
// Given an instruction address, this function finds the single stab
|
||||
// entry of type 'type' that contains that address.
|
||||
//
|
||||
// The search takes place within the range [*region_left, *region_right].
|
||||
// Thus, to search an entire set of N stabs, you might do:
|
||||
//
|
||||
// left = 0;
|
||||
// right = N - 1; /* rightmost stab */
|
||||
// stab_binsearch(stabs, &left, &right, type, addr);
|
||||
//
|
||||
// The search modifies *region_left and *region_right to bracket the
|
||||
// 'addr'. *region_left points to the matching stab that contains
|
||||
// 'addr', and *region_right points just before the next stab. If
|
||||
// *region_left > *region_right, then 'addr' is not contained in any
|
||||
// matching stab.
|
||||
//
|
||||
// For example, given these N_SO stabs:
|
||||
// Index Type Address
|
||||
// 0 SO f0100000
|
||||
// 13 SO f0100040
|
||||
// 117 SO f0100176
|
||||
// 118 SO f0100178
|
||||
// 555 SO f0100652
|
||||
// 556 SO f0100654
|
||||
// 657 SO f0100849
|
||||
// this code:
|
||||
// left = 0, right = 657;
|
||||
// stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
|
||||
// will exit setting left = 118, right = 554.
|
||||
//
|
||||
static void
|
||||
stab_binsearch(const struct Stab *stabs, int *region_left, int *region_right,
|
||||
int type, uintptr_t addr)
|
||||
{
|
||||
int l = *region_left, r = *region_right, any_matches = 0;
|
||||
|
||||
while (l <= r) {
|
||||
int true_m = (l + r) / 2, m = true_m;
|
||||
|
||||
// search for earliest stab with right type
|
||||
while (m >= l && stabs[m].n_type != type)
|
||||
m--;
|
||||
if (m < l) { // no match in [l, m]
|
||||
l = true_m + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// actual binary search
|
||||
any_matches = 1;
|
||||
if (stabs[m].n_value < addr) {
|
||||
*region_left = m;
|
||||
l = true_m + 1;
|
||||
} else if (stabs[m].n_value > addr) {
|
||||
*region_right = m - 1;
|
||||
r = m - 1;
|
||||
} else {
|
||||
// exact match for 'addr', but continue loop to find
|
||||
// *region_right
|
||||
*region_left = m;
|
||||
l = m;
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_matches)
|
||||
*region_right = *region_left - 1;
|
||||
else {
|
||||
// find rightmost region containing 'addr'
|
||||
for (l = *region_right;
|
||||
l > *region_left && stabs[l].n_type != type;
|
||||
l--)
|
||||
/* do nothing */;
|
||||
*region_left = l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// debuginfo_eip(addr, info)
|
||||
//
|
||||
// Fill in the 'info' structure with information about the specified
|
||||
// instruction address, 'addr'. Returns 0 if information was found, and
|
||||
// negative if not. But even if it returns negative it has stored some
|
||||
// information into '*info'.
|
||||
//
|
||||
int
|
||||
debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
|
||||
{
|
||||
const struct Stab *stabs, *stab_end;
|
||||
const char *stabstr, *stabstr_end;
|
||||
int lfile, rfile, lfun, rfun, lline, rline;
|
||||
|
||||
// Initialize *info
|
||||
info->eip_file = "<unknown>";
|
||||
info->eip_line = 0;
|
||||
info->eip_fn_name = "<unknown>";
|
||||
info->eip_fn_namelen = 9;
|
||||
info->eip_fn_addr = addr;
|
||||
info->eip_fn_narg = 0;
|
||||
|
||||
// Find the relevant set of stabs
|
||||
if (addr >= ULIM) {
|
||||
stabs = __STAB_BEGIN__;
|
||||
stab_end = __STAB_END__;
|
||||
stabstr = __STABSTR_BEGIN__;
|
||||
stabstr_end = __STABSTR_END__;
|
||||
} else {
|
||||
// The user-application linker script, user/user.ld,
|
||||
// puts information about the application's stabs (equivalent
|
||||
// to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
|
||||
// __STABSTR_END__) in a structure located at virtual address
|
||||
// USTABDATA.
|
||||
const struct UserStabData *usd = (const struct UserStabData *) USTABDATA;
|
||||
|
||||
// Make sure this memory is valid.
|
||||
// Return -1 if it is not. Hint: Call user_mem_check.
|
||||
// LAB 3: Your code here.
|
||||
if (user_mem_check(curenv, usd, sizeof(struct UserStabData), PTE_U))
|
||||
return -1;
|
||||
stabs = usd->stabs;
|
||||
stab_end = usd->stab_end;
|
||||
stabstr = usd->stabstr;
|
||||
stabstr_end = usd->stabstr_end;
|
||||
|
||||
// Make sure the STABS and string table memory is valid.
|
||||
// LAB 3: Your code here.
|
||||
if (user_mem_check(curenv, stabs, sizeof(struct Stab), PTE_U))
|
||||
return -1;
|
||||
if (user_mem_check(curenv, stabstr, stabstr_end-stabstr, PTE_U))
|
||||
return -1;
|
||||
}
|
||||
|
||||
// String table validity checks
|
||||
if (stabstr_end <= stabstr || stabstr_end[-1] != 0)
|
||||
return -1;
|
||||
|
||||
// Now we find the right stabs that define the function containing
|
||||
// 'eip'. First, we find the basic source file containing 'eip'.
|
||||
// Then, we look in that source file for the function. Then we look
|
||||
// for the line number.
|
||||
|
||||
// Search the entire set of stabs for the source file (type N_SO).
|
||||
lfile = 0;
|
||||
rfile = (stab_end - stabs) - 1;
|
||||
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
|
||||
if (lfile == 0)
|
||||
return -1;
|
||||
|
||||
// Search within that file's stabs for the function definition
|
||||
// (N_FUN).
|
||||
lfun = lfile;
|
||||
rfun = rfile;
|
||||
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
|
||||
|
||||
if (lfun <= rfun) {
|
||||
// stabs[lfun] points to the function name
|
||||
// in the string table, but check bounds just in case.
|
||||
if (stabs[lfun].n_strx < stabstr_end - stabstr)
|
||||
info->eip_fn_name = stabstr + stabs[lfun].n_strx;
|
||||
info->eip_fn_addr = stabs[lfun].n_value;
|
||||
addr -= info->eip_fn_addr;
|
||||
// Search within the function definition for the line number.
|
||||
lline = lfun;
|
||||
rline = rfun;
|
||||
} else {
|
||||
// Couldn't find function stab! Maybe we're in an assembly
|
||||
// file. Search the whole file for the line number.
|
||||
info->eip_fn_addr = addr;
|
||||
lline = lfile;
|
||||
rline = rfile;
|
||||
}
|
||||
// Ignore stuff after the colon.
|
||||
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
|
||||
|
||||
|
||||
// Search within [lline, rline] for the line number stab.
|
||||
// If found, set info->eip_line to the right line number.
|
||||
// If not found, return -1.
|
||||
//
|
||||
// Hint:
|
||||
// There's a particular stabs type used for line numbers.
|
||||
// Look at the STABS documentation and <inc/stab.h> to find
|
||||
// which one.
|
||||
// Your code here.
|
||||
|
||||
|
||||
// Search backwards from the line number for the relevant filename
|
||||
// stab.
|
||||
// We can't just use the "lfile" stab because inlined functions
|
||||
// can interpolate code from a different file!
|
||||
// Such included source files use the N_SOL stab type.
|
||||
while (lline >= lfile
|
||||
&& stabs[lline].n_type != N_SOL
|
||||
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value))
|
||||
lline--;
|
||||
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr)
|
||||
info->eip_file = stabstr + stabs[lline].n_strx;
|
||||
|
||||
|
||||
// Set eip_fn_narg to the number of arguments taken by the function,
|
||||
// or 0 if there was no containing function.
|
||||
if (lfun < rfun)
|
||||
for (lline = lfun + 1;
|
||||
lline < rfun && stabs[lline].n_type == N_PSYM;
|
||||
lline++)
|
||||
info->eip_fn_narg++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
lab/kern/kdebug.h
Normal file
20
lab/kern/kdebug.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef JOS_KERN_KDEBUG_H
|
||||
#define JOS_KERN_KDEBUG_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Debug information about a particular instruction pointer
|
||||
struct Eipdebuginfo {
|
||||
const char *eip_file; // Source code filename for EIP
|
||||
int eip_line; // Source code linenumber for EIP
|
||||
|
||||
const char *eip_fn_name; // Name of function containing EIP
|
||||
// - Note: not null terminated!
|
||||
int eip_fn_namelen; // Length of function name
|
||||
uintptr_t eip_fn_addr; // Address of start of function
|
||||
int eip_fn_narg; // Number of function arguments
|
||||
};
|
||||
|
||||
int debuginfo_eip(uintptr_t eip, struct Eipdebuginfo *info);
|
||||
|
||||
#endif
|
||||
62
lab/kern/kernel.ld
Normal file
62
lab/kern/kernel.ld
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Simple linker script for the JOS kernel.
|
||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
|
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Link the kernel at this address: "." means the current address */
|
||||
. = 0xF0100000;
|
||||
|
||||
/* AT(...) gives the load address of this section, which tells
|
||||
the boot loader where to load the kernel in physical memory */
|
||||
.text : AT(0x100000) {
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/* Include debugging information in kernel memory */
|
||||
.stab : {
|
||||
PROVIDE(__STAB_BEGIN__ = .);
|
||||
*(.stab);
|
||||
PROVIDE(__STAB_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
.stabstr : {
|
||||
PROVIDE(__STABSTR_BEGIN__ = .);
|
||||
*(.stabstr);
|
||||
PROVIDE(__STABSTR_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
/* Adjust the address for the data segment to the next page */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/* The data segment */
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
PROVIDE(edata = .);
|
||||
*(.dynbss)
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
PROVIDE(end = .);
|
||||
}
|
||||
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack .comment .note)
|
||||
}
|
||||
}
|
||||
182
lab/kern/lapic.c
Normal file
182
lab/kern/lapic.c
Normal file
@@ -0,0 +1,182 @@
|
||||
// The local APIC manages internal (non-I/O) interrupts.
|
||||
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/x86.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/cpu.h>
|
||||
|
||||
// Local APIC registers, divided by 4 for use as uint32_t[] indices.
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define OTHERS 0x000C0000 // Send to all APICs, excluding self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
physaddr_t lapicaddr; // Initialized in mpconfig.c
|
||||
volatile uint32_t *lapic;
|
||||
|
||||
static void
|
||||
lapicw(int index, int value)
|
||||
{
|
||||
lapic[index] = value;
|
||||
lapic[ID]; // wait for write to finish, by reading
|
||||
}
|
||||
|
||||
void
|
||||
lapic_init(void)
|
||||
{
|
||||
if (!lapicaddr)
|
||||
return;
|
||||
|
||||
// lapicaddr is the physical address of the LAPIC's 4K MMIO
|
||||
// region. Map it in to virtual memory so we can access it.
|
||||
lapic = mmio_map_region(lapicaddr, 4096);
|
||||
|
||||
// Enable local APIC; set spurious interrupt vector.
|
||||
lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS));
|
||||
|
||||
// The timer repeatedly counts down at bus frequency
|
||||
// from lapic[TICR] and then issues an interrupt.
|
||||
// If we cared more about precise timekeeping,
|
||||
// TICR would be calibrated using an external time source.
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
|
||||
// Leave LINT0 of the BSP enabled so that it can get
|
||||
// interrupts from the 8259A chip.
|
||||
//
|
||||
// According to Intel MP Specification, the BIOS should initialize
|
||||
// BSP's local APIC in Virtual Wire Mode, in which 8259A's
|
||||
// INTR is virtually connected to BSP's LINTIN0. In this mode,
|
||||
// we do not need to program the IOAPIC.
|
||||
if (thiscpu != bootcpu)
|
||||
lapicw(LINT0, MASKED);
|
||||
|
||||
// Disable NMI (LINT1) on all CPUs
|
||||
lapicw(LINT1, MASKED);
|
||||
|
||||
// Disable performance counter overflow interrupts
|
||||
// on machines that provide that interrupt entry.
|
||||
if (((lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
|
||||
// Map error interrupt to IRQ_ERROR.
|
||||
lapicw(ERROR, IRQ_OFFSET + IRQ_ERROR);
|
||||
|
||||
// Clear error status register (requires back-to-back writes).
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
|
||||
// Ack any outstanding interrupts.
|
||||
lapicw(EOI, 0);
|
||||
|
||||
// Send an Init Level De-Assert to synchronize arbitration ID's.
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
|
||||
// Enable interrupts on the APIC (but not on the processor).
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
int
|
||||
cpunum(void)
|
||||
{
|
||||
if (lapic)
|
||||
return lapic[ID] >> 24;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Acknowledge interrupt.
|
||||
void
|
||||
lapic_eoi(void)
|
||||
{
|
||||
if (lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
// Spin for a given number of microseconds.
|
||||
// On real hardware would want to tune this dynamically.
|
||||
static void
|
||||
microdelay(int us)
|
||||
{
|
||||
}
|
||||
|
||||
#define IO_RTC 0x70
|
||||
|
||||
// Start additional processor running entry code at addr.
|
||||
// See Appendix B of MultiProcessor Specification.
|
||||
void
|
||||
lapic_startap(uint8_t apicid, uint32_t addr)
|
||||
{
|
||||
int i;
|
||||
uint16_t *wrv;
|
||||
|
||||
// "The BSP must initialize CMOS shutdown code to 0AH
|
||||
// and the warm reset vector (DWORD based at 40:67) to point at
|
||||
// the AP startup code prior to the [universal startup algorithm]."
|
||||
outb(IO_RTC, 0xF); // offset 0xF is shutdown code
|
||||
outb(IO_RTC+1, 0x0A);
|
||||
wrv = (uint16_t *)KADDR((0x40 << 4 | 0x67)); // Warm reset vector
|
||||
wrv[0] = 0;
|
||||
wrv[1] = addr >> 4;
|
||||
|
||||
// "Universal startup algorithm."
|
||||
// Send INIT (level-triggered) interrupt to reset other CPU.
|
||||
lapicw(ICRHI, apicid << 24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
microdelay(200);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
microdelay(100); // should be 10ms, but too slow in Bochs!
|
||||
|
||||
// Send startup IPI (twice!) to enter code.
|
||||
// Regular hardware is supposed to only accept a STARTUP
|
||||
// when it is in the halted state due to an INIT. So the second
|
||||
// should be ignored, but it is part of the official Intel algorithm.
|
||||
// Bochs complains about the second one. Too bad for Bochs.
|
||||
for (i = 0; i < 2; i++) {
|
||||
lapicw(ICRHI, apicid << 24);
|
||||
lapicw(ICRLO, STARTUP | (addr >> 12));
|
||||
microdelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lapic_ipi(int vector)
|
||||
{
|
||||
lapicw(ICRLO, OTHERS | FIXED | vector);
|
||||
while (lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
}
|
||||
128
lab/kern/monitor.c
Normal file
128
lab/kern/monitor.c
Normal file
@@ -0,0 +1,128 @@
|
||||
// Simple command-line kernel monitor useful for
|
||||
// controlling the kernel and exploring the system interactively.
|
||||
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/kdebug.h>
|
||||
#include <kern/trap.h>
|
||||
|
||||
#define CMDBUF_SIZE 80 // enough for one VGA text line
|
||||
|
||||
|
||||
struct Command {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
// return -1 to force monitor to exit
|
||||
int (*func)(int argc, char** argv, struct Trapframe* tf);
|
||||
};
|
||||
|
||||
static struct Command commands[] = {
|
||||
{ "help", "Display this list of commands", mon_help },
|
||||
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
|
||||
};
|
||||
|
||||
/***** Implementations of basic kernel monitor commands *****/
|
||||
|
||||
int
|
||||
mon_help(int argc, char **argv, struct Trapframe *tf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(commands); i++)
|
||||
cprintf("%s - %s\n", commands[i].name, commands[i].desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mon_kerninfo(int argc, char **argv, struct Trapframe *tf)
|
||||
{
|
||||
extern char _start[], entry[], etext[], edata[], end[];
|
||||
|
||||
cprintf("Special kernel symbols:\n");
|
||||
cprintf(" _start %08x (phys)\n", _start);
|
||||
cprintf(" entry %08x (virt) %08x (phys)\n", entry, entry - KERNBASE);
|
||||
cprintf(" etext %08x (virt) %08x (phys)\n", etext, etext - KERNBASE);
|
||||
cprintf(" edata %08x (virt) %08x (phys)\n", edata, edata - KERNBASE);
|
||||
cprintf(" end %08x (virt) %08x (phys)\n", end, end - KERNBASE);
|
||||
cprintf("Kernel executable memory footprint: %dKB\n",
|
||||
ROUNDUP(end - entry, 1024) / 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
|
||||
{
|
||||
// Your code here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** Kernel monitor command interpreter *****/
|
||||
|
||||
#define WHITESPACE "\t\r\n "
|
||||
#define MAXARGS 16
|
||||
|
||||
static int
|
||||
runcmd(char *buf, struct Trapframe *tf)
|
||||
{
|
||||
int argc;
|
||||
char *argv[MAXARGS];
|
||||
int i;
|
||||
|
||||
// Parse the command buffer into whitespace-separated arguments
|
||||
argc = 0;
|
||||
argv[argc] = 0;
|
||||
while (1) {
|
||||
// gobble whitespace
|
||||
while (*buf && strchr(WHITESPACE, *buf))
|
||||
*buf++ = 0;
|
||||
if (*buf == 0)
|
||||
break;
|
||||
|
||||
// save and scan past next arg
|
||||
if (argc == MAXARGS-1) {
|
||||
cprintf("Too many arguments (max %d)\n", MAXARGS);
|
||||
return 0;
|
||||
}
|
||||
argv[argc++] = buf;
|
||||
while (*buf && !strchr(WHITESPACE, *buf))
|
||||
buf++;
|
||||
}
|
||||
argv[argc] = 0;
|
||||
|
||||
// Lookup and invoke the command
|
||||
if (argc == 0)
|
||||
return 0;
|
||||
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||
if (strcmp(argv[0], commands[i].name) == 0)
|
||||
return commands[i].func(argc, argv, tf);
|
||||
}
|
||||
cprintf("Unknown command '%s'\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
monitor(struct Trapframe *tf)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
cprintf("Welcome to the JOS kernel monitor!\n");
|
||||
cprintf("Type 'help' for a list of commands.\n");
|
||||
|
||||
if (tf != NULL)
|
||||
print_trapframe(tf);
|
||||
|
||||
while (1) {
|
||||
buf = readline("K> ");
|
||||
if (buf != NULL)
|
||||
if (runcmd(buf, tf) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
19
lab/kern/monitor.h
Normal file
19
lab/kern/monitor.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef JOS_KERN_MONITOR_H
|
||||
#define JOS_KERN_MONITOR_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
struct Trapframe;
|
||||
|
||||
// Activate the kernel monitor,
|
||||
// optionally providing a trap frame indicating the current state
|
||||
// (NULL if none).
|
||||
void monitor(struct Trapframe *tf);
|
||||
|
||||
// Functions implementing monitor commands.
|
||||
int mon_help(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_kerninfo(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
|
||||
|
||||
#endif // !JOS_KERN_MONITOR_H
|
||||
225
lab/kern/mpconfig.c
Normal file
225
lab/kern/mpconfig.c
Normal file
@@ -0,0 +1,225 @@
|
||||
// Search for and parse the multiprocessor configuration table
|
||||
// See http://developer.intel.com/design/pentium/datashts/24201606.pdf
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/env.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/pmap.h>
|
||||
|
||||
struct CpuInfo cpus[NCPU];
|
||||
struct CpuInfo *bootcpu;
|
||||
int ismp;
|
||||
int ncpu;
|
||||
|
||||
// Per-CPU kernel stacks
|
||||
unsigned char percpu_kstacks[NCPU][KSTKSIZE]
|
||||
__attribute__ ((aligned(PGSIZE)));
|
||||
|
||||
|
||||
// See MultiProcessor Specification Version 1.[14]
|
||||
|
||||
struct mp { // floating pointer [MP 4.1]
|
||||
uint8_t signature[4]; // "_MP_"
|
||||
physaddr_t physaddr; // phys addr of MP config table
|
||||
uint8_t length; // 1
|
||||
uint8_t specrev; // [14]
|
||||
uint8_t checksum; // all bytes must add up to 0
|
||||
uint8_t type; // MP system config type
|
||||
uint8_t imcrp;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mpconf { // configuration table header [MP 4.2]
|
||||
uint8_t signature[4]; // "PCMP"
|
||||
uint16_t length; // total table length
|
||||
uint8_t version; // [14]
|
||||
uint8_t checksum; // all bytes must add up to 0
|
||||
uint8_t product[20]; // product id
|
||||
physaddr_t oemtable; // OEM table pointer
|
||||
uint16_t oemlength; // OEM table length
|
||||
uint16_t entry; // entry count
|
||||
physaddr_t lapicaddr; // address of local APIC
|
||||
uint16_t xlength; // extended table length
|
||||
uint8_t xchecksum; // extended table checksum
|
||||
uint8_t reserved;
|
||||
uint8_t entries[0]; // table entries
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mpproc { // processor table entry [MP 4.3.1]
|
||||
uint8_t type; // entry type (0)
|
||||
uint8_t apicid; // local APIC id
|
||||
uint8_t version; // local APIC version
|
||||
uint8_t flags; // CPU flags
|
||||
uint8_t signature[4]; // CPU signature
|
||||
uint32_t feature; // feature flags from CPUID instruction
|
||||
uint8_t reserved[8];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
// mpproc flags
|
||||
#define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor
|
||||
|
||||
// Table entry types
|
||||
#define MPPROC 0x00 // One per processor
|
||||
#define MPBUS 0x01 // One per bus
|
||||
#define MPIOAPIC 0x02 // One per I/O APIC
|
||||
#define MPIOINTR 0x03 // One per bus interrupt source
|
||||
#define MPLINTR 0x04 // One per system interrupt source
|
||||
|
||||
static uint8_t
|
||||
sum(void *addr, int len)
|
||||
{
|
||||
int i, sum;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
sum += ((uint8_t *)addr)[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Look for an MP structure in the len bytes at physical address addr.
|
||||
static struct mp *
|
||||
mpsearch1(physaddr_t a, int len)
|
||||
{
|
||||
struct mp *mp = KADDR(a), *end = KADDR(a + len);
|
||||
|
||||
for (; mp < end; mp++)
|
||||
if (memcmp(mp->signature, "_MP_", 4) == 0 &&
|
||||
sum(mp, sizeof(*mp)) == 0)
|
||||
return mp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search for the MP Floating Pointer Structure, which according to
|
||||
// [MP 4] is in one of the following three locations:
|
||||
// 1) in the first KB of the EBDA;
|
||||
// 2) if there is no EBDA, in the last KB of system base memory;
|
||||
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
|
||||
static struct mp *
|
||||
mpsearch(void)
|
||||
{
|
||||
uint8_t *bda;
|
||||
uint32_t p;
|
||||
struct mp *mp;
|
||||
|
||||
static_assert(sizeof(*mp) == 16);
|
||||
|
||||
// The BIOS data area lives in 16-bit segment 0x40.
|
||||
bda = (uint8_t *) KADDR(0x40 << 4);
|
||||
|
||||
// [MP 4] The 16-bit segment of the EBDA is in the two bytes
|
||||
// starting at byte 0x0E of the BDA. 0 if not present.
|
||||
if ((p = *(uint16_t *) (bda + 0x0E))) {
|
||||
p <<= 4; // Translate from segment to PA
|
||||
if ((mp = mpsearch1(p, 1024)))
|
||||
return mp;
|
||||
} else {
|
||||
// The size of base memory, in KB is in the two bytes
|
||||
// starting at 0x13 of the BDA.
|
||||
p = *(uint16_t *) (bda + 0x13) * 1024;
|
||||
if ((mp = mpsearch1(p - 1024, 1024)))
|
||||
return mp;
|
||||
}
|
||||
return mpsearch1(0xF0000, 0x10000);
|
||||
}
|
||||
|
||||
// Search for an MP configuration table. For now, don't accept the
|
||||
// default configurations (physaddr == 0).
|
||||
// Check for the correct signature, checksum, and version.
|
||||
static struct mpconf *
|
||||
mpconfig(struct mp **pmp)
|
||||
{
|
||||
struct mpconf *conf;
|
||||
struct mp *mp;
|
||||
|
||||
if ((mp = mpsearch()) == 0)
|
||||
return NULL;
|
||||
if (mp->physaddr == 0 || mp->type != 0) {
|
||||
cprintf("SMP: Default configurations not implemented\n");
|
||||
return NULL;
|
||||
}
|
||||
conf = (struct mpconf *) KADDR(mp->physaddr);
|
||||
if (memcmp(conf, "PCMP", 4) != 0) {
|
||||
cprintf("SMP: Incorrect MP configuration table signature\n");
|
||||
return NULL;
|
||||
}
|
||||
if (sum(conf, conf->length) != 0) {
|
||||
cprintf("SMP: Bad MP configuration checksum\n");
|
||||
return NULL;
|
||||
}
|
||||
if (conf->version != 1 && conf->version != 4) {
|
||||
cprintf("SMP: Unsupported MP version %d\n", conf->version);
|
||||
return NULL;
|
||||
}
|
||||
if ((sum((uint8_t *)conf + conf->length, conf->xlength) + conf->xchecksum) & 0xff) {
|
||||
cprintf("SMP: Bad MP configuration extended checksum\n");
|
||||
return NULL;
|
||||
}
|
||||
*pmp = mp;
|
||||
return conf;
|
||||
}
|
||||
|
||||
void
|
||||
mp_init(void)
|
||||
{
|
||||
struct mp *mp;
|
||||
struct mpconf *conf;
|
||||
struct mpproc *proc;
|
||||
uint8_t *p;
|
||||
unsigned int i;
|
||||
|
||||
bootcpu = &cpus[0];
|
||||
if ((conf = mpconfig(&mp)) == 0)
|
||||
return;
|
||||
ismp = 1;
|
||||
lapicaddr = conf->lapicaddr;
|
||||
|
||||
for (p = conf->entries, i = 0; i < conf->entry; i++) {
|
||||
switch (*p) {
|
||||
case MPPROC:
|
||||
proc = (struct mpproc *)p;
|
||||
if (proc->flags & MPPROC_BOOT)
|
||||
bootcpu = &cpus[ncpu];
|
||||
if (ncpu < NCPU) {
|
||||
cpus[ncpu].cpu_id = ncpu;
|
||||
ncpu++;
|
||||
} else {
|
||||
cprintf("SMP: too many CPUs, CPU %d disabled\n",
|
||||
proc->apicid);
|
||||
}
|
||||
p += sizeof(struct mpproc);
|
||||
continue;
|
||||
case MPBUS:
|
||||
case MPIOAPIC:
|
||||
case MPIOINTR:
|
||||
case MPLINTR:
|
||||
p += 8;
|
||||
continue;
|
||||
default:
|
||||
cprintf("mpinit: unknown config type %x\n", *p);
|
||||
ismp = 0;
|
||||
i = conf->entry;
|
||||
}
|
||||
}
|
||||
|
||||
bootcpu->cpu_status = CPU_STARTED;
|
||||
if (!ismp) {
|
||||
// Didn't like what we found; fall back to no MP.
|
||||
ncpu = 1;
|
||||
lapicaddr = 0;
|
||||
cprintf("SMP: configuration not found, SMP disabled\n");
|
||||
return;
|
||||
}
|
||||
cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu);
|
||||
|
||||
if (mp->imcrp) {
|
||||
// [MP 3.2.6.1] If the hardware implements PIC mode,
|
||||
// switch to getting interrupts from the LAPIC.
|
||||
cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n");
|
||||
outb(0x22, 0x70); // Select IMCR
|
||||
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
|
||||
}
|
||||
}
|
||||
97
lab/kern/mpentry.S
Normal file
97
lab/kern/mpentry.S
Normal file
@@ -0,0 +1,97 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
###################################################################
|
||||
# entry point for APs
|
||||
###################################################################
|
||||
|
||||
# Each non-boot CPU ("AP") is started up in response to a STARTUP
|
||||
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
|
||||
# Specification says that the AP will start in real mode with CS:IP
|
||||
# set to XY00:0000, where XY is an 8-bit value sent with the
|
||||
# STARTUP. Thus this code must start at a 4096-byte boundary.
|
||||
#
|
||||
# Because this code sets DS to zero, it must run from an address in
|
||||
# the low 2^16 bytes of physical memory.
|
||||
#
|
||||
# boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which
|
||||
# satisfies the above restrictions). Then, for each AP, it stores the
|
||||
# address of the pre-allocated per-core stack in mpentry_kstack, sends
|
||||
# the STARTUP IPI, and waits for this code to acknowledge that it has
|
||||
# started (which happens in mp_main in init.c).
|
||||
#
|
||||
# This code is similar to boot/boot.S except that
|
||||
# - it does not need to enable A20
|
||||
# - it uses MPBOOTPHYS to calculate absolute addresses of its
|
||||
# symbols, rather than relying on the linker to fill them
|
||||
|
||||
#define RELOC(x) ((x) - KERNBASE)
|
||||
#define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR)
|
||||
|
||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
||||
|
||||
.code16
|
||||
.globl mpentry_start
|
||||
mpentry_start:
|
||||
cli
|
||||
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
lgdt MPBOOTPHYS(gdtdesc)
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
ljmpl $(PROT_MODE_CSEG), $(MPBOOTPHYS(start32))
|
||||
|
||||
.code32
|
||||
start32:
|
||||
movw $(PROT_MODE_DSEG), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
movw $0, %ax
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
# Set up initial page table. We cannot use kern_pgdir yet because
|
||||
# we are still running at a low EIP.
|
||||
movl $(RELOC(entry_pgdir)), %eax
|
||||
movl %eax, %cr3
|
||||
# Turn on paging.
|
||||
movl %cr0, %eax
|
||||
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Switch to the per-cpu stack allocated in boot_aps()
|
||||
movl mpentry_kstack, %esp
|
||||
movl $0x0, %ebp # nuke frame pointer
|
||||
|
||||
# Call mp_main(). (Exercise for the reader: why the indirect call?)
|
||||
movl $mp_main, %eax
|
||||
call *%eax
|
||||
|
||||
# If mp_main returns (it shouldn't), loop.
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
gdt:
|
||||
SEG_NULL # null seg
|
||||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||
SEG(STA_W, 0x0, 0xffffffff) # data seg
|
||||
|
||||
gdtdesc:
|
||||
.word 0x17 # sizeof(gdt) - 1
|
||||
.long MPBOOTPHYS(gdt) # address gdt
|
||||
|
||||
.globl mpentry_end
|
||||
mpentry_end:
|
||||
nop
|
||||
86
lab/kern/picirq.c
Normal file
86
lab/kern/picirq.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/assert.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#include <kern/picirq.h>
|
||||
|
||||
|
||||
// Current IRQ mask.
|
||||
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
|
||||
uint16_t irq_mask_8259A = 0xFFFF & ~(1<<IRQ_SLAVE);
|
||||
static bool didinit;
|
||||
|
||||
/* Initialize the 8259A interrupt controllers. */
|
||||
void
|
||||
pic_init(void)
|
||||
{
|
||||
didinit = 1;
|
||||
|
||||
// mask all interrupts
|
||||
outb(IO_PIC1+1, 0xFF);
|
||||
outb(IO_PIC2+1, 0xFF);
|
||||
|
||||
// Set up master (8259A-1)
|
||||
// Interrupt Control Word
|
||||
// ICW1: 0001g0hi
|
||||
// g: 0 = edge triggering, 1 = level triggering
|
||||
// h: 0 = cascaded PICs, 1 = master only
|
||||
// i: 0 = no ICW4, 1 = ICW4 required
|
||||
outb(IO_PIC1, 0x11);
|
||||
|
||||
// ICW2: Vector offset
|
||||
outb(IO_PIC1+1, IRQ_OFFSET);
|
||||
|
||||
// ICW3: bit mask of IR lines connected to slave PICs (master PIC),
|
||||
// 3-bit No of IR line at which slave connects to master(slave PIC).
|
||||
outb(IO_PIC1+1, 1<<IRQ_SLAVE);
|
||||
|
||||
// ICW4: 000nbmap
|
||||
// n: 1 = special fully nested mode
|
||||
// b: 1 = buffered mode
|
||||
// m: 0 = slave PIC, 1 = master PIC
|
||||
// (ignored when b is 0, as the master/slave role
|
||||
// can be hardwired).
|
||||
// a: 1 = Automatic EOI mode
|
||||
// p: 0 = MCS-80/85 mode, 1 = intel x86 mode
|
||||
outb(IO_PIC1+1, 0x3);
|
||||
|
||||
// Set up slave (8259A-2)
|
||||
outb(IO_PIC2, 0x11); // ICW1
|
||||
outb(IO_PIC2+1, IRQ_OFFSET + 8); // ICW2
|
||||
outb(IO_PIC2+1, IRQ_SLAVE); // ICW3
|
||||
// NB Automatic EOI mode doesn't tend to work on the slave.
|
||||
// Linux source code says it's "to be investigated".
|
||||
outb(IO_PIC2+1, 0x01); // ICW4
|
||||
|
||||
// OCW3: 0ef01prs
|
||||
// ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask
|
||||
// p: 0 = no polling, 1 = polling mode
|
||||
// rs: 0x = NOP, 10 = read IRR, 11 = read ISR
|
||||
outb(IO_PIC1, 0x68); /* clear specific mask */
|
||||
outb(IO_PIC1, 0x0a); /* read IRR by default */
|
||||
|
||||
outb(IO_PIC2, 0x68); /* OCW3 */
|
||||
outb(IO_PIC2, 0x0a); /* OCW3 */
|
||||
|
||||
if (irq_mask_8259A != 0xFFFF)
|
||||
irq_setmask_8259A(irq_mask_8259A);
|
||||
}
|
||||
|
||||
void
|
||||
irq_setmask_8259A(uint16_t mask)
|
||||
{
|
||||
int i;
|
||||
irq_mask_8259A = mask;
|
||||
if (!didinit)
|
||||
return;
|
||||
outb(IO_PIC1+1, (char)mask);
|
||||
outb(IO_PIC2+1, (char)(mask >> 8));
|
||||
cprintf("enabled interrupts:");
|
||||
for (i = 0; i < 16; i++)
|
||||
if (~mask & (1<<i))
|
||||
cprintf(" %d", i);
|
||||
cprintf("\n");
|
||||
}
|
||||
|
||||
28
lab/kern/picirq.h
Normal file
28
lab/kern/picirq.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_PICIRQ_H
|
||||
#define JOS_KERN_PICIRQ_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#define MAX_IRQS 16 // Number of IRQs
|
||||
|
||||
// I/O Addresses of the two 8259A programmable interrupt controllers
|
||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
||||
|
||||
#define IRQ_SLAVE 2 // IRQ at which slave connects to master
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
extern uint16_t irq_mask_8259A;
|
||||
void pic_init(void);
|
||||
void irq_setmask_8259A(uint16_t mask);
|
||||
#endif // !__ASSEMBLER__
|
||||
|
||||
#endif // !JOS_KERN_PICIRQ_H
|
||||
1260
lab/kern/pmap.c
Normal file
1260
lab/kern/pmap.c
Normal file
File diff suppressed because it is too large
Load Diff
93
lab/kern/pmap.h
Normal file
93
lab/kern/pmap.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_PMAP_H
|
||||
#define JOS_KERN_PMAP_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/assert.h>
|
||||
struct Env;
|
||||
|
||||
extern char bootstacktop[], bootstack[];
|
||||
|
||||
extern struct PageInfo *pages;
|
||||
extern size_t npages;
|
||||
|
||||
extern pde_t *kern_pgdir;
|
||||
|
||||
|
||||
/* This macro takes a kernel virtual address -- an address that points above
|
||||
* KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
|
||||
* and returns the corresponding physical address. It panics if you pass it a
|
||||
* non-kernel virtual address.
|
||||
*/
|
||||
#define PADDR(kva) _paddr(__FILE__, __LINE__, kva)
|
||||
|
||||
static inline physaddr_t
|
||||
_paddr(const char *file, int line, void *kva)
|
||||
{
|
||||
if ((uint32_t)kva < KERNBASE)
|
||||
_panic(file, line, "PADDR called with invalid kva %08lx", kva);
|
||||
return (physaddr_t)kva - KERNBASE;
|
||||
}
|
||||
|
||||
/* This macro takes a physical address and returns the corresponding kernel
|
||||
* virtual address. It panics if you pass an invalid physical address. */
|
||||
#define KADDR(pa) _kaddr(__FILE__, __LINE__, pa)
|
||||
|
||||
static inline void*
|
||||
_kaddr(const char *file, int line, physaddr_t pa)
|
||||
{
|
||||
if (PGNUM(pa) >= npages)
|
||||
_panic(file, line, "KADDR called with invalid pa %08lx", pa);
|
||||
return (void *)(pa + KERNBASE);
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
// For page_alloc, zero the returned physical page.
|
||||
ALLOC_ZERO = 1<<0,
|
||||
};
|
||||
|
||||
void mem_init(void);
|
||||
|
||||
void page_init(void);
|
||||
struct PageInfo *page_alloc(int alloc_flags);
|
||||
void page_free(struct PageInfo *pp);
|
||||
int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm);
|
||||
void page_remove(pde_t *pgdir, void *va);
|
||||
struct PageInfo *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store);
|
||||
void page_decref(struct PageInfo *pp);
|
||||
|
||||
void tlb_invalidate(pde_t *pgdir, void *va);
|
||||
|
||||
void * mmio_map_region(physaddr_t pa, size_t size);
|
||||
|
||||
int user_mem_check(struct Env *env, const void *va, size_t len, int perm);
|
||||
void user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
|
||||
|
||||
static inline physaddr_t
|
||||
page2pa(struct PageInfo *pp)
|
||||
{
|
||||
return (pp - pages) << PGSHIFT;
|
||||
}
|
||||
|
||||
static inline struct PageInfo*
|
||||
pa2page(physaddr_t pa)
|
||||
{
|
||||
if (PGNUM(pa) >= npages)
|
||||
panic("pa2page called with invalid pa");
|
||||
return &pages[PGNUM(pa)];
|
||||
}
|
||||
|
||||
static inline void*
|
||||
page2kva(struct PageInfo *pp)
|
||||
{
|
||||
return KADDR(page2pa(pp));
|
||||
}
|
||||
|
||||
pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create);
|
||||
|
||||
#endif /* !JOS_KERN_PMAP_H */
|
||||
37
lab/kern/printf.c
Normal file
37
lab/kern/printf.c
Normal file
@@ -0,0 +1,37 @@
|
||||
// Simple implementation of cprintf console output for the kernel,
|
||||
// based on printfmt() and the kernel console's cputchar().
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
|
||||
|
||||
static void
|
||||
putch(int ch, int *cnt)
|
||||
{
|
||||
cputchar(ch);
|
||||
*cnt++;
|
||||
}
|
||||
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
vprintfmt((void*)putch, &cnt, fmt, ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int
|
||||
cprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
99
lab/kern/sched.c
Normal file
99
lab/kern/sched.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
#include <kern/spinlock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/monitor.h>
|
||||
|
||||
void sched_halt(void);
|
||||
|
||||
// Choose a user environment to run and run it.
|
||||
void
|
||||
sched_yield(void)
|
||||
{
|
||||
struct Env *idle;
|
||||
|
||||
// Implement simple round-robin scheduling.
|
||||
//
|
||||
// Search through 'envs' for an ENV_RUNNABLE environment in
|
||||
// circular fashion starting just after the env this CPU was
|
||||
// last running. Switch to the first such environment found.
|
||||
//
|
||||
// If no envs are runnable, but the environment previously
|
||||
// running on this CPU is still ENV_RUNNING, it's okay to
|
||||
// choose that environment.
|
||||
//
|
||||
// Never choose an environment that's currently running on
|
||||
// another CPU (env_status == ENV_RUNNING). If there are
|
||||
// no runnable environments, simply drop through to the code
|
||||
// below to halt the cpu.
|
||||
|
||||
// LAB 4: Your code here.
|
||||
|
||||
idle = curenv;
|
||||
int start_envid = idle ? ENVX(idle->env_id)+1 : 0;
|
||||
|
||||
for (int i = 0; i < NENV; i++) {
|
||||
int j = (start_envid + i) % NENV;
|
||||
if (envs[j].env_status == ENV_RUNNABLE) {
|
||||
env_run(&envs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (idle && idle->env_status == ENV_RUNNING) {
|
||||
env_run(idle);
|
||||
}
|
||||
|
||||
// sched_halt never returns
|
||||
sched_halt();
|
||||
|
||||
}
|
||||
|
||||
// Halt this CPU when there is nothing to do. Wait until the
|
||||
// timer interrupt wakes it up. This function never returns.
|
||||
//
|
||||
void
|
||||
sched_halt(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
// For debugging and testing purposes, if there are no runnable
|
||||
// environments in the system, then drop into the kernel monitor.
|
||||
for (i = 0; i < NENV; i++) {
|
||||
if ((envs[i].env_status == ENV_RUNNABLE ||
|
||||
envs[i].env_status == ENV_RUNNING ||
|
||||
envs[i].env_status == ENV_DYING))
|
||||
break;
|
||||
}
|
||||
if (i == NENV) {
|
||||
cprintf("No runnable environments in the system!\n");
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
// Mark that no environment is running on this CPU
|
||||
curenv = NULL;
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
|
||||
// Mark that this CPU is in the HALT state, so that when
|
||||
// timer interupts come in, we know we should re-acquire the
|
||||
// big kernel lock
|
||||
xchg(&thiscpu->cpu_status, CPU_HALTED);
|
||||
|
||||
// Release the big kernel lock as if we were "leaving" the kernel
|
||||
unlock_kernel();
|
||||
|
||||
// Reset stack pointer, enable interrupts and then halt.
|
||||
asm volatile (
|
||||
"movl $0, %%ebp\n"
|
||||
"movl %0, %%esp\n"
|
||||
"pushl $0\n"
|
||||
"pushl $0\n"
|
||||
// Uncomment the following line after completing exercise 13
|
||||
"sti\n"
|
||||
"1:\n"
|
||||
"hlt\n"
|
||||
"jmp 1b\n"
|
||||
: : "a" (thiscpu->cpu_ts.ts_esp0));
|
||||
}
|
||||
|
||||
12
lab/kern/sched.h
Normal file
12
lab/kern/sched.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_SCHED_H
|
||||
#define JOS_KERN_SCHED_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
// This function does not return.
|
||||
void sched_yield(void) __attribute__((noreturn));
|
||||
|
||||
#endif // !JOS_KERN_SCHED_H
|
||||
116
lab/kern/spinlock.c
Normal file
116
lab/kern/spinlock.c
Normal file
@@ -0,0 +1,116 @@
|
||||
// Mutual exclusion spin locks.
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/string.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
#include <kern/kdebug.h>
|
||||
|
||||
// The big kernel lock
|
||||
struct spinlock kernel_lock = {
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
.name = "kernel_lock"
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
// Record the current call stack in pcs[] by following the %ebp chain.
|
||||
static void
|
||||
get_caller_pcs(uint32_t pcs[])
|
||||
{
|
||||
uint32_t *ebp;
|
||||
int i;
|
||||
|
||||
ebp = (uint32_t *)read_ebp();
|
||||
for (i = 0; i < 10; i++){
|
||||
if (ebp == 0 || ebp < (uint32_t *)ULIM)
|
||||
break;
|
||||
pcs[i] = ebp[1]; // saved %eip
|
||||
ebp = (uint32_t *)ebp[0]; // saved %ebp
|
||||
}
|
||||
for (; i < 10; i++)
|
||||
pcs[i] = 0;
|
||||
}
|
||||
|
||||
// Check whether this CPU is holding the lock.
|
||||
static int
|
||||
holding(struct spinlock *lock)
|
||||
{
|
||||
return lock->locked && lock->cpu == thiscpu;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
__spin_initlock(struct spinlock *lk, char *name)
|
||||
{
|
||||
lk->locked = 0;
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
lk->name = name;
|
||||
lk->cpu = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Acquire the lock.
|
||||
// Loops (spins) until the lock is acquired.
|
||||
// Holding a lock for a long time may cause
|
||||
// other CPUs to waste time spinning to acquire it.
|
||||
void
|
||||
spin_lock(struct spinlock *lk)
|
||||
{
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
if (holding(lk))
|
||||
panic("CPU %d cannot acquire %s: already holding", cpunum(), lk->name);
|
||||
#endif
|
||||
|
||||
// The xchg is atomic.
|
||||
// It also serializes, so that reads after acquire are not
|
||||
// reordered before it.
|
||||
while (xchg(&lk->locked, 1) != 0)
|
||||
asm volatile ("pause");
|
||||
|
||||
// Record info about lock acquisition for debugging.
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
lk->cpu = thiscpu;
|
||||
get_caller_pcs(lk->pcs);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Release the lock.
|
||||
void
|
||||
spin_unlock(struct spinlock *lk)
|
||||
{
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
if (!holding(lk)) {
|
||||
int i;
|
||||
uint32_t pcs[10];
|
||||
// Nab the acquiring EIP chain before it gets released
|
||||
memmove(pcs, lk->pcs, sizeof pcs);
|
||||
cprintf("CPU %d cannot release %s: held by CPU %d\nAcquired at:",
|
||||
cpunum(), lk->name, lk->cpu->cpu_id);
|
||||
for (i = 0; i < 10 && pcs[i]; i++) {
|
||||
struct Eipdebuginfo info;
|
||||
if (debuginfo_eip(pcs[i], &info) >= 0)
|
||||
cprintf(" %08x %s:%d: %.*s+%x\n", pcs[i],
|
||||
info.eip_file, info.eip_line,
|
||||
info.eip_fn_namelen, info.eip_fn_name,
|
||||
pcs[i] - info.eip_fn_addr);
|
||||
else
|
||||
cprintf(" %08x\n", pcs[i]);
|
||||
}
|
||||
panic("spin_unlock");
|
||||
}
|
||||
|
||||
lk->pcs[0] = 0;
|
||||
lk->cpu = 0;
|
||||
#endif
|
||||
|
||||
// The xchg instruction is atomic (i.e. uses the "lock" prefix) with
|
||||
// respect to any other instruction which references the same memory.
|
||||
// x86 CPUs will not reorder loads/stores across locked instructions
|
||||
// (vol 3, 8.2.2). Because xchg() is implemented using asm volatile,
|
||||
// gcc will not reorder C statements across the xchg.
|
||||
xchg(&lk->locked, 0);
|
||||
}
|
||||
48
lab/kern/spinlock.h
Normal file
48
lab/kern/spinlock.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef JOS_INC_SPINLOCK_H
|
||||
#define JOS_INC_SPINLOCK_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Comment this to disable spinlock debugging
|
||||
#define DEBUG_SPINLOCK
|
||||
|
||||
// Mutual exclusion lock.
|
||||
struct spinlock {
|
||||
unsigned locked; // Is the lock held?
|
||||
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
struct CpuInfo *cpu; // The CPU holding the lock.
|
||||
uintptr_t pcs[10]; // The call stack (an array of program counters)
|
||||
// that locked the lock.
|
||||
#endif
|
||||
};
|
||||
|
||||
void __spin_initlock(struct spinlock *lk, char *name);
|
||||
void spin_lock(struct spinlock *lk);
|
||||
void spin_unlock(struct spinlock *lk);
|
||||
|
||||
#define spin_initlock(lock) __spin_initlock(lock, #lock)
|
||||
|
||||
extern struct spinlock kernel_lock;
|
||||
|
||||
static inline void
|
||||
lock_kernel(void)
|
||||
{
|
||||
spin_lock(&kernel_lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
unlock_kernel(void)
|
||||
{
|
||||
spin_unlock(&kernel_lock);
|
||||
|
||||
// Normally we wouldn't need to do this, but QEMU only runs
|
||||
// one CPU at a time and has a long time-slice. Without the
|
||||
// pause, this CPU is likely to reacquire the lock before
|
||||
// another CPU has even been given a chance to acquire it.
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
#endif
|
||||
447
lab/kern/syscall.c
Normal file
447
lab/kern/syscall.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/sched.h>
|
||||
|
||||
// Print a string to the system console.
|
||||
// The string is exactly 'len' characters long.
|
||||
// Destroys the environment on memory errors.
|
||||
static void
|
||||
sys_cputs(const char *s, size_t len)
|
||||
{
|
||||
// Check that the user has permission to read memory [s, s+len).
|
||||
// Destroy the environment if not.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
user_mem_assert(curenv, (const void *) s, len, 0);
|
||||
// Print the string supplied by the user.
|
||||
cprintf("%.*s", len, s);
|
||||
}
|
||||
|
||||
// Read a character from the system console without blocking.
|
||||
// Returns the character, or 0 if there is no input waiting.
|
||||
static int
|
||||
sys_cgetc(void)
|
||||
{
|
||||
return cons_getc();
|
||||
}
|
||||
|
||||
// Returns the current environment's envid.
|
||||
static envid_t
|
||||
sys_getenvid(void)
|
||||
{
|
||||
return curenv->env_id;
|
||||
}
|
||||
|
||||
// Destroy a given environment (possibly the currently running environment).
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
static int
|
||||
sys_env_destroy(envid_t envid)
|
||||
{
|
||||
int r;
|
||||
struct Env *e;
|
||||
|
||||
if ((r = envid2env(envid, &e, 1)) < 0)
|
||||
return r;
|
||||
if (e == curenv)
|
||||
cprintf("[%08x] exiting gracefully\n", curenv->env_id);
|
||||
else
|
||||
cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
|
||||
env_destroy(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Deschedule current environment and pick a different one to run.
|
||||
static void
|
||||
sys_yield(void)
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
|
||||
// Allocate a new environment.
|
||||
// Returns envid of new environment, or < 0 on error. Errors are:
|
||||
// -E_NO_FREE_ENV if no free environment is available.
|
||||
// -E_NO_MEM on memory exhaustion.
|
||||
static envid_t
|
||||
sys_exofork(void)
|
||||
{
|
||||
// Create the new environment with env_alloc(), from kern/env.c.
|
||||
// It should be left as env_alloc created it, except that
|
||||
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
|
||||
// from the current environment -- but tweaked so sys_exofork
|
||||
// will appear to return 0.
|
||||
// LAB 4: Your code here.
|
||||
struct Env *newenv;
|
||||
int32_t ret;
|
||||
if ((ret = env_alloc(&newenv, sys_getenvid())) < 0) {
|
||||
// 两个函数的返回值是一样的
|
||||
return ret;
|
||||
}
|
||||
newenv->env_status = ENV_NOT_RUNNABLE;
|
||||
newenv->env_tf = curenv->env_tf;
|
||||
// newenv的返回值为0, 实现子进程返回0值
|
||||
newenv->env_tf.tf_regs.reg_eax = 0;
|
||||
|
||||
// 返回值存放在eax中
|
||||
return newenv->env_id;
|
||||
|
||||
// panic("sys_exofork not implemented");
|
||||
}
|
||||
|
||||
// Set envid's env_status to status, which must be ENV_RUNNABLE
|
||||
// or ENV_NOT_RUNNABLE.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if status is not a valid status for an environment.
|
||||
static int
|
||||
sys_env_set_status(envid_t envid, int status)
|
||||
{
|
||||
// Hint: Use the 'envid2env' function from kern/env.c to translate an
|
||||
// envid to a struct Env.
|
||||
// You should set envid2env's third argument to 1, which will
|
||||
// check whether the current environment has permission to set
|
||||
// envid's status.
|
||||
|
||||
// LAB 4: Your code here.
|
||||
struct Env *e;
|
||||
if (envid2env(envid, &e, 1)) return -E_BAD_ENV;
|
||||
|
||||
if (status != ENV_NOT_RUNNABLE && status != ENV_RUNNABLE)
|
||||
return -E_INVAL;
|
||||
|
||||
e->env_status = status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the page fault upcall for 'envid' by modifying the corresponding struct
|
||||
// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
|
||||
// kernel will push a fault record onto the exception stack, then branch to
|
||||
// 'func'.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
static int
|
||||
sys_env_set_pgfault_upcall(envid_t envid, void *func)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
struct Env *e;
|
||||
if (envid2env(envid, &e, 1))
|
||||
return -E_BAD_ENV;
|
||||
// cprintf("set pgfault upcall\n");
|
||||
e->env_pgfault_upcall = func;
|
||||
return 0;
|
||||
// panic("sys_env_set_pgfault_upcall not implemented");
|
||||
}
|
||||
|
||||
// Allocate a page of memory and map it at 'va' with permission
|
||||
// 'perm' in the address space of 'envid'.
|
||||
// The page's contents are set to 0.
|
||||
// If a page is already mapped at 'va', that page is unmapped as a
|
||||
// side effect.
|
||||
//
|
||||
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
|
||||
// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
||||
// -E_INVAL if perm is inappropriate (see above).
|
||||
// -E_NO_MEM if there's no memory to allocate the new page,
|
||||
// or to allocate any necessary page tables.
|
||||
static int
|
||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_alloc() and
|
||||
// page_insert() from kern/pmap.c.
|
||||
// Most of the new code you write should be to check the
|
||||
// parameters for correctness.
|
||||
// If page_insert() fails, remember to free the page you
|
||||
// allocated!
|
||||
|
||||
// LAB 4: Your code here.
|
||||
int ret = 0;
|
||||
struct Env *env;
|
||||
|
||||
if ((ret = envid2env(envid, &env, 1)) < 0)
|
||||
return -E_BAD_ENV;
|
||||
|
||||
if((uintptr_t)va >= UTOP || PGOFF(va))
|
||||
return -E_INVAL;
|
||||
if ((perm & PTE_U) == 0 || (perm & PTE_P) == 0)
|
||||
return -E_INVAL;
|
||||
if (perm & ~PTE_SYSCALL)
|
||||
return -E_INVAL;
|
||||
|
||||
struct PageInfo *pp = page_alloc(ALLOC_ZERO);
|
||||
if(!pp)
|
||||
return -E_NO_MEM;
|
||||
|
||||
if (page_insert(env->env_pgdir, pp, va, perm) < 0)
|
||||
return -E_NO_MEM;
|
||||
|
||||
return 0;
|
||||
// panic("sys_page_alloc not implemented");
|
||||
}
|
||||
|
||||
// Map the page of memory at 'srcva' in srcenvid's address space
|
||||
// at 'dstva' in dstenvid's address space with permission 'perm'.
|
||||
// Perm has the same restrictions as in sys_page_alloc, except
|
||||
// that it also must not grant write access to a read-only
|
||||
// page.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change one of them.
|
||||
// -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
|
||||
// or dstva >= UTOP or dstva is not page-aligned.
|
||||
// -E_INVAL is srcva is not mapped in srcenvid's address space.
|
||||
// -E_INVAL if perm is inappropriate (see sys_page_alloc).
|
||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
|
||||
// address space.
|
||||
// -E_NO_MEM if there's no memory to allocate any necessary page tables.
|
||||
static int
|
||||
sys_page_map(envid_t srcenvid, void *srcva,
|
||||
envid_t dstenvid, void *dstva, int perm)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_lookup() and
|
||||
// page_insert() from kern/pmap.c.
|
||||
// Again, most of the new code you write should be to check the
|
||||
// parameters for correctness.
|
||||
// Use the third argument to page_lookup() to
|
||||
// check the current permissions on the page.
|
||||
|
||||
// LAB 4: Your code here.
|
||||
int ret = 0;
|
||||
struct Env *srcenv, *dstenv;
|
||||
struct PageInfo *srcpp, *dstpp;
|
||||
pte_t *pte;
|
||||
if ((envid2env(srcenvid, &srcenv, 1) < 0 )|| ( envid2env(dstenvid, &dstenv, 1) < 0))
|
||||
return -E_BAD_ENV;
|
||||
if ((uintptr_t)srcva >= UTOP || PGOFF(srcva) || (uintptr_t)dstva >= UTOP || PGOFF(dstva))
|
||||
return -E_INVAL;
|
||||
if ( (perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_SYSCALL))
|
||||
return -E_INVAL;
|
||||
if (!(srcpp = page_lookup(srcenv->env_pgdir, srcva, &pte)))
|
||||
return -E_INVAL;
|
||||
if ((perm & PTE_W) && ((*pte & PTE_W) == 0))
|
||||
return -E_INVAL;
|
||||
if (page_insert(dstenv->env_pgdir, srcpp, dstva, perm) < 0)
|
||||
return -E_NO_MEM;
|
||||
|
||||
return 0;
|
||||
// panic("sys_page_map not implemented");
|
||||
}
|
||||
|
||||
// Unmap the page of memory at 'va' in the address space of 'envid'.
|
||||
// If no page is mapped, the function silently succeeds.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
||||
static int
|
||||
sys_page_unmap(envid_t envid, void *va)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_remove().
|
||||
|
||||
// LAB 4: Your code here.
|
||||
int ret = 0;
|
||||
struct Env *env;
|
||||
|
||||
if ((ret = envid2env(envid, &env, 1)) < 0)
|
||||
return -E_BAD_ENV;
|
||||
if ((uintptr_t)va >= UTOP || PGOFF(va))
|
||||
return -E_INVAL;
|
||||
page_remove(env->env_pgdir, va);
|
||||
return 0;
|
||||
// panic("sys_page_unmap not implemented");
|
||||
}
|
||||
|
||||
// Try to send 'value' to the target env 'envid'.
|
||||
// If srcva < UTOP, then also send page currently mapped at 'srcva',
|
||||
// so that receiver gets a duplicate mapping of the same page.
|
||||
//
|
||||
// The send fails with a return value of -E_IPC_NOT_RECV if the
|
||||
// target is not blocked, waiting for an IPC.
|
||||
//
|
||||
// The send also can fail for the other reasons listed below.
|
||||
//
|
||||
// Otherwise, the send succeeds, and the target's ipc fields are
|
||||
// updated as follows:
|
||||
// env_ipc_recving is set to 0 to block future sends;
|
||||
// env_ipc_from is set to the sending envid;
|
||||
// env_ipc_value is set to the 'value' parameter;
|
||||
// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
|
||||
// The target environment is marked runnable again, returning 0
|
||||
// from the paused sys_ipc_recv system call. (Hint: does the
|
||||
// sys_ipc_recv function ever actually return?)
|
||||
//
|
||||
// If the sender wants to send a page but the receiver isn't asking for one,
|
||||
// then no page mapping is transferred, but no error occurs.
|
||||
// The ipc only happens when no errors occur.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error.
|
||||
// Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist.
|
||||
// (No need to check permissions.)
|
||||
// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
|
||||
// or another environment managed to send first.
|
||||
// -E_INVAL if srcva < UTOP but srcva is not page-aligned.
|
||||
// -E_INVAL if srcva < UTOP and perm is inappropriate
|
||||
// (see sys_page_alloc).
|
||||
// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
|
||||
// address space.
|
||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in the
|
||||
// current environment's address space.
|
||||
// -E_NO_MEM if there's not enough memory to map srcva in envid's
|
||||
// address space.
|
||||
|
||||
static int
|
||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
struct Env *dstenv;
|
||||
pte_t *pte;
|
||||
struct PageInfo *pp;
|
||||
int r;
|
||||
if ( (r = envid2env( envid, &dstenv, 0)) < 0)
|
||||
return r;
|
||||
|
||||
// 不处于等待接收状态, 或有进程已经请求发送数据
|
||||
if ( (dstenv->env_ipc_recving != true) || dstenv->env_ipc_from != 0)
|
||||
return -E_IPC_NOT_RECV;
|
||||
|
||||
if ((uint32_t)srcva < UTOP) {
|
||||
if ( PGOFF(srcva))
|
||||
return -E_INVAL;
|
||||
if ( !(perm & PTE_P ) || !(perm & PTE_U) )
|
||||
return -E_INVAL;
|
||||
if (perm & (~ PTE_SYSCALL))
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((pp = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL )
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((perm & PTE_W) && !(*pte & PTE_W) )
|
||||
return -E_INVAL;
|
||||
|
||||
// 接收进程愿意接收一个页
|
||||
if (dstenv->env_ipc_dstva) {
|
||||
// 开始映射
|
||||
if( (r = page_insert(dstenv->env_pgdir, pp, dstenv->env_ipc_dstva, perm)) < 0)
|
||||
return r;
|
||||
dstenv->env_ipc_perm = perm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
dstenv->env_ipc_from = curenv->env_id;
|
||||
dstenv->env_ipc_recving = false;
|
||||
dstenv->env_ipc_value = value;
|
||||
dstenv->env_status = ENV_RUNNABLE;
|
||||
// 返回值
|
||||
dstenv->env_tf.tf_regs.reg_eax = 0;
|
||||
return 0;
|
||||
|
||||
//panic("sys_ipc_try_send not implemented");
|
||||
}
|
||||
|
||||
|
||||
// Block until a value is ready. Record that you want to receive
|
||||
// using the env_ipc_recving and env_ipc_dstva fields of struct Env,
|
||||
// mark yourself not runnable, and then give up the CPU.
|
||||
//
|
||||
// If 'dstva' is < UTOP, then you are willing to receive a page of data.
|
||||
// 'dstva' is the virtual address at which the sent page should be mapped.
|
||||
//
|
||||
// This function only returns on error, but the system call will eventually
|
||||
// return 0 on success.
|
||||
// Return < 0 on error. Errors are:
|
||||
// -E_INVAL if dstva < UTOP but dstva is not page-aligned.
|
||||
static int
|
||||
sys_ipc_recv(void *dstva)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
if ((uint32_t) dstva < UTOP ) {
|
||||
if (PGOFF(dstva))
|
||||
return -E_INVAL;
|
||||
|
||||
}
|
||||
// 大于小于都可以赋值为desva。
|
||||
curenv->env_ipc_dstva = dstva;
|
||||
curenv->env_status = ENV_NOT_RUNNABLE;
|
||||
curenv->env_ipc_recving = true;
|
||||
curenv->env_ipc_from = 0;
|
||||
sched_yield();
|
||||
// panic("sys_ipc_recv not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dispatches to the correct kernel function, passing the arguments.
|
||||
int32_t
|
||||
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||
{
|
||||
// Call the function corresponding to the 'syscallno' parameter.
|
||||
// Return any appropriate return value.
|
||||
// LAB 3: Your code here.
|
||||
switch (syscallno) {
|
||||
case SYS_cputs:
|
||||
sys_cputs((const char*)a1, a2);
|
||||
break;
|
||||
case SYS_cgetc:
|
||||
return sys_cgetc();
|
||||
case SYS_getenvid:
|
||||
return sys_getenvid();
|
||||
case SYS_env_destroy:
|
||||
return sys_env_destroy(a1);
|
||||
case SYS_yield:
|
||||
sys_yield();
|
||||
break;
|
||||
case SYS_page_alloc:
|
||||
return sys_page_alloc((envid_t)a1, (void * )a2, (int )a3);
|
||||
case SYS_env_set_pgfault_upcall:
|
||||
return sys_env_set_pgfault_upcall((envid_t) a1, (void *) a2);
|
||||
case SYS_page_map:
|
||||
return sys_page_map((envid_t) a1, (void *) a2, (envid_t) a3, (void *) a4, (int) a5);
|
||||
|
||||
case SYS_page_unmap:
|
||||
return sys_page_unmap((envid_t) a1, (void *) a2);
|
||||
|
||||
case SYS_exofork:
|
||||
return sys_exofork();
|
||||
|
||||
case SYS_env_set_status:
|
||||
return sys_env_set_status((envid_t) a1, (int) a2);
|
||||
case SYS_ipc_recv:
|
||||
return sys_ipc_recv( (void *) a1);
|
||||
case SYS_ipc_try_send:
|
||||
return sys_ipc_try_send((envid_t) a1, (uint32_t) a2, (void *) a3, (int) a4);
|
||||
case NSYSCALLS:
|
||||
return -E_INVAL;
|
||||
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
11
lab/kern/syscall.h
Normal file
11
lab/kern/syscall.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef JOS_KERN_SYSCALL_H
|
||||
#define JOS_KERN_SYSCALL_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/syscall.h>
|
||||
|
||||
int32_t syscall(uint32_t num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5);
|
||||
|
||||
#endif /* !JOS_KERN_SYSCALL_H */
|
||||
422
lab/kern/trap.c
Normal file
422
lab/kern/trap.c
Normal file
@@ -0,0 +1,422 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
static struct Taskstate ts;
|
||||
|
||||
/* For debugging, so print_trapframe can distinguish between printing
|
||||
* a saved trapframe and printing the current trapframe and print some
|
||||
* additional information in the latter case.
|
||||
*/
|
||||
static struct Trapframe *last_tf;
|
||||
|
||||
/* Interrupt descriptor table. (Must be built at run time because
|
||||
* shifted function addresses can't be represented in relocation records.)
|
||||
*/
|
||||
struct Gatedesc idt[256] = { { 0 } };
|
||||
struct Pseudodesc idt_pd = {
|
||||
sizeof(idt) - 1, (uint32_t) idt
|
||||
};
|
||||
|
||||
|
||||
static const char *trapname(int trapno)
|
||||
{
|
||||
static const char * const excnames[] = {
|
||||
"Divide error",
|
||||
"Debug",
|
||||
"Non-Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Overflow",
|
||||
"BOUND Range Exceeded",
|
||||
"Invalid Opcode",
|
||||
"Device Not Available",
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Invalid TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection",
|
||||
"Page Fault",
|
||||
"(unknown trap)",
|
||||
"x87 FPU Floating-Point Error",
|
||||
"Alignment Check",
|
||||
"Machine-Check",
|
||||
"SIMD Floating-Point Exception"
|
||||
};
|
||||
|
||||
if (trapno < ARRAY_SIZE(excnames))
|
||||
return excnames[trapno];
|
||||
if (trapno == T_SYSCALL)
|
||||
return "System call";
|
||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16)
|
||||
return "Hardware Interrupt";
|
||||
return "(unknown trap)";
|
||||
}
|
||||
|
||||
// You will also need to modify trap_init() to initialize the idt to
|
||||
// point to each of these entry points defined in trapentry.S;
|
||||
// the SETGATE macro will be helpful here
|
||||
void
|
||||
trap_init(void)
|
||||
{
|
||||
|
||||
extern struct Segdesc gdt[];
|
||||
void divide_handler();
|
||||
void debug_handler();
|
||||
void nmi_handler();
|
||||
void brkpt_handler();
|
||||
void oflow_handler();
|
||||
void bound_handler();
|
||||
void device_handler();
|
||||
void illop_handler();
|
||||
void tss_handler();
|
||||
void segnp_handler();
|
||||
void stack_handler();
|
||||
void gpflt_handler();
|
||||
void pgflt_handler();
|
||||
void fperr_handler();
|
||||
void align_handler();
|
||||
void mchk_handler();
|
||||
void simderr_handler();
|
||||
void syscall_handler();
|
||||
void dblflt_handler();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_handler();
|
||||
|
||||
|
||||
// LAB 3: Your code here.
|
||||
// GD_KT 全局描述符, kernel text
|
||||
SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0);
|
||||
SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0);
|
||||
SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0);
|
||||
SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3);
|
||||
SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0);
|
||||
SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0);
|
||||
SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0);
|
||||
SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0);
|
||||
SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0);
|
||||
SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0);
|
||||
SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0);
|
||||
SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0);
|
||||
SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0);
|
||||
SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0);
|
||||
SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0);
|
||||
SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0);
|
||||
SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0);
|
||||
SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0);
|
||||
SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_handler, 3);
|
||||
|
||||
// Per-CPU setup
|
||||
trap_init_percpu();
|
||||
}
|
||||
|
||||
// Initialize and load the per-CPU TSS and IDT
|
||||
void
|
||||
trap_init_percpu(void)
|
||||
{
|
||||
// The example code here sets up the Task State Segment (TSS) and
|
||||
// the TSS descriptor for CPU 0. But it is incorrect if we are
|
||||
// running on other CPUs because each CPU has its own kernel stack.
|
||||
// Fix the code so that it works for all CPUs.
|
||||
//
|
||||
// Hints:
|
||||
// - The macro "thiscpu" always refers to the current CPU's
|
||||
// struct CpuInfo;
|
||||
// - The ID of the current CPU is given by cpunum() or
|
||||
// thiscpu->cpu_id;
|
||||
// - Use "thiscpu->cpu_ts" as the TSS for the current CPU,
|
||||
// rather than the global "ts" variable;
|
||||
// - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor;
|
||||
// - You mapped the per-CPU kernel stacks in mem_init_mp()
|
||||
// - Initialize cpu_ts.ts_iomb to prevent unauthorized environments
|
||||
// from doing IO (0 is not the correct value!)
|
||||
//
|
||||
// ltr sets a 'busy' flag in the TSS selector, so if you
|
||||
// accidentally load the same TSS on more than one CPU, you'll
|
||||
// get a triple fault. If you set up an individual CPU's TSS
|
||||
// wrong, you may not get a fault until you try to return from
|
||||
// user space on that CPU.
|
||||
//
|
||||
// LAB 4: Your code here:
|
||||
thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE);
|
||||
thiscpu->cpu_ts.ts_ss0 = GD_KD;
|
||||
|
||||
// Initialize the TSS slot of the gdt.
|
||||
gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0);
|
||||
gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0;
|
||||
|
||||
// Load the TSS selector (like other segment selectors, the
|
||||
// bottom three bits are special; we leave them 0)
|
||||
ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum());
|
||||
|
||||
// Load the IDT
|
||||
lidt(&idt_pd);
|
||||
}
|
||||
|
||||
void
|
||||
print_trapframe(struct Trapframe *tf)
|
||||
{
|
||||
cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum());
|
||||
print_regs(&tf->tf_regs);
|
||||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
|
||||
// If this trap was a page fault that just happened
|
||||
// (so %cr2 is meaningful), print the faulting linear address.
|
||||
if (tf == last_tf && tf->tf_trapno == T_PGFLT)
|
||||
cprintf(" cr2 0x%08x\n", rcr2());
|
||||
cprintf(" err 0x%08x", tf->tf_err);
|
||||
// For page faults, print decoded fault error code:
|
||||
// U/K=fault occurred in user/kernel mode
|
||||
// W/R=a write/read caused the fault
|
||||
// PR=a protection violation caused the fault (NP=page not present).
|
||||
if (tf->tf_trapno == T_PGFLT)
|
||||
cprintf(" [%s, %s, %s]\n",
|
||||
tf->tf_err & 4 ? "user" : "kernel",
|
||||
tf->tf_err & 2 ? "write" : "read",
|
||||
tf->tf_err & 1 ? "protection" : "not-present");
|
||||
else
|
||||
cprintf("\n");
|
||||
cprintf(" eip 0x%08x\n", tf->tf_eip);
|
||||
cprintf(" cs 0x----%04x\n", tf->tf_cs);
|
||||
cprintf(" flag 0x%08x\n", tf->tf_eflags);
|
||||
if ((tf->tf_cs & 3) != 0) {
|
||||
cprintf(" esp 0x%08x\n", tf->tf_esp);
|
||||
cprintf(" ss 0x----%04x\n", tf->tf_ss);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_regs(struct PushRegs *regs)
|
||||
{
|
||||
cprintf(" edi 0x%08x\n", regs->reg_edi);
|
||||
cprintf(" esi 0x%08x\n", regs->reg_esi);
|
||||
cprintf(" ebp 0x%08x\n", regs->reg_ebp);
|
||||
cprintf(" oesp 0x%08x\n", regs->reg_oesp);
|
||||
cprintf(" ebx 0x%08x\n", regs->reg_ebx);
|
||||
cprintf(" edx 0x%08x\n", regs->reg_edx);
|
||||
cprintf(" ecx 0x%08x\n", regs->reg_ecx);
|
||||
cprintf(" eax 0x%08x\n", regs->reg_eax);
|
||||
}
|
||||
|
||||
static void
|
||||
trap_dispatch(struct Trapframe *tf)
|
||||
{
|
||||
// Handle processor exceptions.
|
||||
// LAB 3: Your code here.
|
||||
switch(tf->tf_trapno) {
|
||||
case T_PGFLT:
|
||||
page_fault_handler(tf);
|
||||
break;
|
||||
case T_BRKPT:
|
||||
monitor(tf);
|
||||
break;
|
||||
case T_SYSCALL:
|
||||
|
||||
tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax,
|
||||
tf->tf_regs.reg_edx,
|
||||
tf->tf_regs.reg_ecx,
|
||||
tf->tf_regs.reg_ebx,
|
||||
tf->tf_regs.reg_edi,
|
||||
tf->tf_regs.reg_esi);
|
||||
break;
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unexpected trap: The user process or the kernel has a bug.
|
||||
print_trapframe(tf);
|
||||
if (tf->tf_cs == GD_KT)
|
||||
panic("unhandled trap in kernel");
|
||||
else {
|
||||
env_destroy(curenv);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Handle clock interrupts. Don't forget to acknowledge the
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
trap(struct Trapframe *tf)
|
||||
{
|
||||
// The environment may have set DF and some versions
|
||||
// of GCC rely on DF being clear
|
||||
asm volatile("cld" ::: "cc");
|
||||
|
||||
// Halt the CPU if some other CPU has called panic()
|
||||
extern char *panicstr;
|
||||
if (panicstr)
|
||||
asm volatile("hlt");
|
||||
|
||||
// Re-acqurie the big kernel lock if we were halted in
|
||||
// sched_yield()
|
||||
if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED)
|
||||
lock_kernel();
|
||||
// Check that interrupts are disabled. If this assertion
|
||||
// fails, DO NOT be tempted to fix it by inserting a "cli" in
|
||||
// the interrupt path.
|
||||
assert(!(read_eflags() & FL_IF));
|
||||
|
||||
if ((tf->tf_cs & 3) == 3) {
|
||||
// Trapped from user mode.
|
||||
// Acquire the big kernel lock before doing any
|
||||
// serious kernel work.
|
||||
// LAB 4: Your code here.
|
||||
lock_kernel();
|
||||
assert(curenv);
|
||||
|
||||
// Garbage collect if current enviroment is a zombie
|
||||
if (curenv->env_status == ENV_DYING) {
|
||||
env_free(curenv);
|
||||
curenv = NULL;
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// Copy trap frame (which is currently on the stack)
|
||||
// into 'curenv->env_tf', so that running the environment
|
||||
// will restart at the trap point.
|
||||
curenv->env_tf = *tf;
|
||||
// The trapframe on the stack should be ignored from here on.
|
||||
tf = &curenv->env_tf;
|
||||
}
|
||||
|
||||
// Record that tf is the last real trapframe so
|
||||
// print_trapframe can print some additional information.
|
||||
last_tf = tf;
|
||||
|
||||
// Dispatch based on what type of trap occurred
|
||||
trap_dispatch(tf);
|
||||
|
||||
// If we made it to this point, then no other environment was
|
||||
// scheduled, so we should return to the current environment
|
||||
// if doing so makes sense.
|
||||
if (curenv && curenv->env_status == ENV_RUNNING)
|
||||
env_run(curenv);
|
||||
else
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
page_fault_handler(struct Trapframe *tf)
|
||||
{
|
||||
uint32_t fault_va;
|
||||
|
||||
// Read processor's CR2 register to find the faulting address
|
||||
fault_va = rcr2();
|
||||
|
||||
// Handle kernel-mode page faults.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
|
||||
// 怎么判断是内核模式, CPL位
|
||||
|
||||
if(tf->tf_cs && 3 == 0) {
|
||||
panic("page_fault in kernel mode, fault address %d\n", fault_va);
|
||||
}
|
||||
|
||||
// We've already handled kernel-mode exceptions, so if we get here,
|
||||
// the page fault happened in user mode.
|
||||
|
||||
|
||||
// Call the environment's page fault upcall, if one exists. Set up a
|
||||
// page fault stack frame on the user exception stack (below
|
||||
// UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
|
||||
//
|
||||
// The page fault upcall might cause another page fault, in which case
|
||||
// we branch to the page fault upcall recursively, pushing another
|
||||
// page fault stack frame on top of the user exception stack.
|
||||
//
|
||||
// It is convenient for our code which returns from a page fault
|
||||
// (lib/pfentry.S) to have one word of scratch space at the top of the
|
||||
// trap-time stack; it allows us to more easily restore the eip/esp. In
|
||||
// the non-recursive case, we don't have to worry about this because
|
||||
// the top of the regular user stack is free. In the recursive case,
|
||||
// this means we have to leave an extra word between the current top of
|
||||
// the exception stack and the new stack frame because the exception
|
||||
// stack _is_ the trap-time stack.
|
||||
//
|
||||
// If there's no page fault upcall, the environment didn't allocate a
|
||||
// page for its exception stack or can't write to it, or the exception
|
||||
// stack overflows, then destroy the environment that caused the fault.
|
||||
// Note that the grade script assumes you will first check for the page
|
||||
// fault upcall and print the "user fault va" message below if there is
|
||||
// none. The remaining three checks can be combined into a single test.
|
||||
//
|
||||
// Hints:
|
||||
// user_mem_assert() and env_run() are useful here.
|
||||
// To change what the user environment runs, modify 'curenv->env_tf'
|
||||
// (the 'tf' variable points at 'curenv->env_tf').
|
||||
|
||||
// LAB 4: Your code here.
|
||||
struct UTrapframe *utf;
|
||||
// cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va);
|
||||
if (curenv->env_pgfault_upcall) {
|
||||
|
||||
if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) {
|
||||
// 异常模式下陷入
|
||||
utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4);
|
||||
|
||||
}
|
||||
else {
|
||||
// 非异常模式下陷入
|
||||
utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe));
|
||||
}
|
||||
// 检查异常栈是否溢出
|
||||
user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W);
|
||||
|
||||
utf->utf_fault_va = fault_va;
|
||||
utf->utf_err = tf->tf_trapno;
|
||||
utf->utf_regs = tf->tf_regs;
|
||||
utf->utf_eflags = tf->tf_eflags;
|
||||
// 保存陷入时现场,用于返回
|
||||
utf->utf_eip = tf->tf_eip;
|
||||
utf->utf_esp = tf->tf_esp;
|
||||
// 再次转向执行
|
||||
curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall;
|
||||
// 异常栈
|
||||
curenv->env_tf.tf_esp = (uint32_t) utf;
|
||||
env_run(curenv);
|
||||
}
|
||||
else {
|
||||
// Destroy the environment that caused the fault.
|
||||
cprintf("[%08x] user fault va %08x ip %08x\n",
|
||||
curenv->env_id, fault_va, tf->tf_eip);
|
||||
print_trapframe(tf);
|
||||
env_destroy(curenv);
|
||||
}
|
||||
}
|
||||
|
||||
23
lab/kern/trap.h
Normal file
23
lab/kern/trap.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_TRAP_H
|
||||
#define JOS_KERN_TRAP_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/trap.h>
|
||||
#include <inc/mmu.h>
|
||||
|
||||
/* The kernel's interrupt descriptor table */
|
||||
extern struct Gatedesc idt[];
|
||||
extern struct Pseudodesc idt_pd;
|
||||
|
||||
void trap_init(void);
|
||||
void trap_init_percpu(void);
|
||||
void print_regs(struct PushRegs *regs);
|
||||
void print_trapframe(struct Trapframe *tf);
|
||||
void page_fault_handler(struct Trapframe *);
|
||||
void backtrace(struct Trapframe *);
|
||||
|
||||
#endif /* JOS_KERN_TRAP_H */
|
||||
109
lab/kern/trapentry.S
Normal file
109
lab/kern/trapentry.S
Normal file
@@ -0,0 +1,109 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#include <kern/picirq.h>
|
||||
|
||||
|
||||
###################################################################
|
||||
# exceptions/interrupts
|
||||
###################################################################
|
||||
|
||||
/* TRAPHANDLER defines a globally-visible function for handling a trap.
|
||||
* It pushes a trap number onto the stack, then jumps to _alltraps.
|
||||
* Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
|
||||
*
|
||||
* You shouldn't call a TRAPHANDLER function from C, but you may
|
||||
* need to _declare_ one in C (for instance, to get a function pointer
|
||||
* during IDT setup). You can declare the function with
|
||||
* void NAME();
|
||||
* where NAME is the argument passed to TRAPHANDLER.
|
||||
*/
|
||||
#define TRAPHANDLER(name, num) \
|
||||
.globl name; /* define global symbol for 'name' */ \
|
||||
.type name, @function; /* symbol type is function */ \
|
||||
.align 2; /* align function definition */ \
|
||||
name: /* function starts here */ \
|
||||
pushl $(num); \
|
||||
jmp _alltraps
|
||||
|
||||
/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code.
|
||||
* It pushes a 0 in place of the error code, so the trap frame has the same
|
||||
* format in either case.
|
||||
|
||||
* tarp handler no error code
|
||||
*/
|
||||
|
||||
#define TRAPHANDLER_NOEC(name, num) \
|
||||
.globl name; \
|
||||
.type name, @function; \
|
||||
.align 2; \
|
||||
name: \
|
||||
pushl $0; \
|
||||
pushl $(num); \
|
||||
jmp _alltraps
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* 我们知道哪些trap是有错误代码的? https://wiki.osdev.org/Exceptions
|
||||
* Lab 3: Your code here for generating entry points for the different traps.
|
||||
*/
|
||||
TRAPHANDLER_NOEC(divide_handler, T_DIVIDE);
|
||||
TRAPHANDLER_NOEC(debug_handler, T_DEBUG);
|
||||
TRAPHANDLER_NOEC(nmi_handler, T_NMI);
|
||||
TRAPHANDLER_NOEC(brkpt_handler, T_BRKPT);
|
||||
TRAPHANDLER_NOEC(oflow_handler, T_OFLOW);
|
||||
TRAPHANDLER_NOEC(bound_handler, T_BOUND);
|
||||
TRAPHANDLER_NOEC(illop_handler, T_ILLOP);
|
||||
TRAPHANDLER_NOEC(device_handler, T_DEVICE);
|
||||
TRAPHANDLER(dblflt_handler, T_DBLFLT);
|
||||
TRAPHANDLER(tss_handler, T_TSS);
|
||||
TRAPHANDLER(segnp_handler, T_SEGNP);
|
||||
TRAPHANDLER(stack_handler, T_STACK);
|
||||
TRAPHANDLER(gpflt_handler, T_GPFLT);
|
||||
TRAPHANDLER(pgflt_handler, T_PGFLT);
|
||||
TRAPHANDLER_NOEC(fperr_handler, T_FPERR);
|
||||
TRAPHANDLER(align_handler, T_ALIGN);
|
||||
TRAPHANDLER_NOEC(mchk_handler, T_MCHK);
|
||||
TRAPHANDLER_NOEC(simderr_handler, T_SIMDERR);
|
||||
TRAPHANDLER_NOEC(syscall_handler, T_SYSCALL);
|
||||
|
||||
// IRQs
|
||||
TRAPHANDLER_NOEC(timer_handler, IRQ_OFFSET + IRQ_TIMER);
|
||||
TRAPHANDLER_NOEC(kbd_handler, IRQ_OFFSET + IRQ_KBD);
|
||||
TRAPHANDLER_NOEC(serial_handler, IRQ_OFFSET + IRQ_SERIAL);
|
||||
TRAPHANDLER_NOEC(spurious_handler, IRQ_OFFSET + IRQ_SPURIOUS);
|
||||
TRAPHANDLER_NOEC(ide_handler, IRQ_OFFSET + IRQ_IDE);
|
||||
TRAPHANDLER_NOEC(error_handler, IRQ_OFFSET + IRQ_ERROR);
|
||||
|
||||
|
||||
/*
|
||||
Your _alltraps should:
|
||||
1. push values to make the stack look like a struct Trapframe
|
||||
2. load GD_KD into %ds and %es
|
||||
3. pushl %esp to pass a pointer to the Trapframe as an argument to trap()
|
||||
4. call trap (can trap ever return?)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Lab 3: Your code here for _alltraps
|
||||
*what is padding ? 填充 pushl %ds;小端模式,是指数据的高字节保存在内存的高地址中
|
||||
*/
|
||||
.globl _alltraps
|
||||
_alltraps:
|
||||
|
||||
pushl %ds;
|
||||
pushl %es;
|
||||
pushal;
|
||||
|
||||
movw $GD_KD, %ax;
|
||||
movw %ax, %ds;
|
||||
movw %ax, %es;
|
||||
|
||||
/*push esp, trap 能自己读esp, 我有点觉得是赋值*/
|
||||
pushl %esp;
|
||||
call trap
|
||||
|
||||
36
lab/lib/Makefrag
Normal file
36
lab/lib/Makefrag
Normal file
@@ -0,0 +1,36 @@
|
||||
OBJDIRS += lib
|
||||
|
||||
LIB_SRCFILES := lib/console.c \
|
||||
lib/libmain.c \
|
||||
lib/exit.c \
|
||||
lib/panic.c \
|
||||
lib/printf.c \
|
||||
lib/printfmt.c \
|
||||
lib/readline.c \
|
||||
lib/string.c \
|
||||
lib/syscall.c
|
||||
|
||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
||||
lib/pgfault.c \
|
||||
lib/pfentry.S \
|
||||
lib/fork.c \
|
||||
lib/ipc.c
|
||||
|
||||
|
||||
|
||||
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
|
||||
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
|
||||
|
||||
$(OBJDIR)/lib/%.o: lib/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + cc[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/lib/%.o: lib/%.S $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + as[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/lib/libjos.a: $(LIB_OBJFILES)
|
||||
@echo + ar $@
|
||||
$(V)$(AR) r $@ $(LIB_OBJFILES)
|
||||
25
lab/lib/console.c
Normal file
25
lab/lib/console.c
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
cputchar(int ch)
|
||||
{
|
||||
char c = ch;
|
||||
|
||||
// Unlike standard Unix's putchar,
|
||||
// the cputchar function _always_ outputs to the system console.
|
||||
sys_cputs(&c, 1);
|
||||
}
|
||||
|
||||
int
|
||||
getchar(void)
|
||||
{
|
||||
int r;
|
||||
// sys_cgetc does not block, but getchar should.
|
||||
while ((r = sys_cgetc()) == 0)
|
||||
sys_yield();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
36
lab/lib/entry.S
Normal file
36
lab/lib/entry.S
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
.data
|
||||
// Define the global symbols 'envs', 'pages', 'uvpt', and 'uvpd'
|
||||
// so that they can be used in C as if they were ordinary global arrays.
|
||||
.globl envs
|
||||
.set envs, UENVS
|
||||
.globl pages
|
||||
.set pages, UPAGES
|
||||
.globl uvpt
|
||||
.set uvpt, UVPT
|
||||
.globl uvpd
|
||||
// 为什么不直接写 >>10 ?
|
||||
.set uvpd, (UVPT+(UVPT>>12)*4)
|
||||
|
||||
|
||||
// Entrypoint - this is where the kernel (or our parent environment)
|
||||
// starts us running when we are initially loaded into a new environment.
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
// See if we were started with arguments on the stack
|
||||
cmpl $USTACKTOP, %esp
|
||||
jne args_exist
|
||||
|
||||
// If not, push dummy argc/argv arguments.
|
||||
// This happens when we are loaded by the kernel,
|
||||
// because the kernel does not know about passing arguments.
|
||||
pushl $0
|
||||
pushl $0
|
||||
|
||||
args_exist:
|
||||
call libmain
|
||||
1: jmp 1b
|
||||
|
||||
9
lab/lib/exit.c
Normal file
9
lab/lib/exit.c
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
exit(void)
|
||||
{
|
||||
sys_env_destroy(0);
|
||||
}
|
||||
|
||||
167
lab/lib/fork.c
Normal file
167
lab/lib/fork.c
Normal file
@@ -0,0 +1,167 @@
|
||||
// implement fork from user space
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
// PTE_COW marks copy-on-write page table entries.
|
||||
// It is one of the bits explicitly allocated to user processes (PTE_AVAIL).
|
||||
#define PTE_COW 0x800
|
||||
|
||||
//
|
||||
// Custom page fault handler - if faulting page is copy-on-write,
|
||||
// map in our own private writable copy.
|
||||
//
|
||||
static void
|
||||
pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void *) utf->utf_fault_va;
|
||||
uint32_t err = utf->utf_err;
|
||||
int r;
|
||||
|
||||
// Check that the faulting access was (1) a write, and (2) to a
|
||||
// copy-on-write page. If not, panic.
|
||||
// Hint:
|
||||
// Use the read-only page table mappings at uvpt
|
||||
// (see <inc/memlayout.h>).
|
||||
|
||||
// LAB 4: Your code here.
|
||||
if (! ( (err & FEC_WR) && (uvpd[PDX(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_COW)))
|
||||
panic("Neither the fault is a write nor COW page. \n");
|
||||
// Allocate a new page, map it at a temporary location (PFTEMP),
|
||||
// copy the data from the old page to the new page, then move the new
|
||||
// page to the old page's address.
|
||||
// Hint:
|
||||
// You should make three system calls.
|
||||
|
||||
// LAB 4: Your code here.
|
||||
envid_t envid = sys_getenvid();
|
||||
// cprintf("pgfault: envid: %d\n", ENVX(envid));
|
||||
// 临时页暂存
|
||||
if ((r = sys_page_alloc(envid, (void *)PFTEMP, PTE_P| PTE_W|PTE_U)) < 0)
|
||||
panic("pgfault: page allocation fault:%e\n", r);
|
||||
addr = ROUNDDOWN(addr, PGSIZE);
|
||||
memcpy((void *) PFTEMP, (const void *) addr, PGSIZE);
|
||||
if ((r = sys_page_map(envid, (void *) PFTEMP, envid, addr , PTE_P|PTE_W|PTE_U)) < 0 )
|
||||
panic("pgfault: page map failed %e\n", r);
|
||||
|
||||
if ((r = sys_page_unmap(envid, (void *) PFTEMP)) < 0)
|
||||
panic("pgfault: page unmap failed %e\n", r);
|
||||
|
||||
|
||||
|
||||
// panic("pgfault not implemented");
|
||||
}
|
||||
|
||||
//
|
||||
// Map our virtual page pn (address pn*PGSIZE) into the target envid
|
||||
// at the same virtual address. If the page is writable or copy-on-write,
|
||||
// the new mapping must be created copy-on-write, and then our mapping must be
|
||||
// marked copy-on-write as well. (Exercise: Why do we need to mark ours
|
||||
// copy-on-write again if it was already copy-on-write at the beginning of
|
||||
// this function?)
|
||||
//
|
||||
// Returns: 0 on success, < 0 on error.
|
||||
// It is also OK to panic on error.
|
||||
//
|
||||
|
||||
static int
|
||||
duppage(envid_t envid, unsigned pn)
|
||||
{
|
||||
|
||||
// LAB 4: Your code here.
|
||||
pte_t *pte;
|
||||
int ret;
|
||||
// 用户空间的地址较低
|
||||
uint32_t va = pn * PGSIZE;
|
||||
|
||||
|
||||
if ( (uvpt[pn] & PTE_W) || (uvpt[pn] & PTE_COW)) {
|
||||
|
||||
// 子进程标记
|
||||
if ((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void *) va, PTE_P|PTE_U|PTE_COW)) < 0)
|
||||
return ret;
|
||||
// 父进程标记
|
||||
if ((ret = sys_page_map(thisenv->env_id, (void *)va, thisenv->env_id, (void *)va, PTE_P|PTE_U|PTE_COW)) < 0)
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
// 简单映射
|
||||
if((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void * )va, PTE_P|PTE_U)) <0 )
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// panic("duppage not implemented");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// User-level fork with copy-on-write.
|
||||
// Set up our page fault handler appropriately.
|
||||
// Create a child.
|
||||
// Copy our address space and page fault handler setup to the child.
|
||||
// Then mark the child as runnable and return.
|
||||
//
|
||||
// Returns: child's envid to the parent, 0 to the child, < 0 on error.
|
||||
// It is also OK to panic on error.
|
||||
//
|
||||
// Hint:
|
||||
// Use uvpd, uvpt, and duppage.
|
||||
// Remember to fix "thisenv" in the child process.
|
||||
// Neither user exception stack should ever be marked copy-on-write,
|
||||
// so you must allocate a new page for the child's user exception stack.
|
||||
//
|
||||
envid_t
|
||||
fork(void)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
envid_t envid;
|
||||
int r;
|
||||
size_t i, j, pn;
|
||||
// Set up our page fault handler
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
envid = sys_exofork();
|
||||
|
||||
if (envid < 0) {
|
||||
panic("sys_exofork failed: %e", envid);
|
||||
}
|
||||
|
||||
if (envid == 0) {
|
||||
// child
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
return 0;
|
||||
}
|
||||
// here is parent !
|
||||
// Copy our address space and page fault handler setup to the child.
|
||||
|
||||
for (pn = PGNUM(UTEXT); pn < PGNUM(USTACKTOP); pn++) {
|
||||
if ( (uvpd[pn >> 10] & PTE_P) && (uvpt[pn] & PTE_P)) {
|
||||
// 页表
|
||||
if ( (r = duppage(envid, pn)) < 0)
|
||||
return r;
|
||||
|
||||
}
|
||||
}
|
||||
// alloc a page and map child exception stack
|
||||
if ((r = sys_page_alloc(envid, (void *)(UXSTACKTOP-PGSIZE), PTE_U | PTE_P | PTE_W)) < 0)
|
||||
return r;
|
||||
extern void _pgfault_upcall(void);
|
||||
if ((r = sys_env_set_pgfault_upcall(envid, _pgfault_upcall)) < 0)
|
||||
return r;
|
||||
|
||||
// Start the child environment running
|
||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return envid;
|
||||
// panic("fork not implemented");
|
||||
}
|
||||
|
||||
// Challenge!
|
||||
int
|
||||
sfork(void)
|
||||
{
|
||||
panic("sfork not implemented");
|
||||
return -E_INVAL;
|
||||
}
|
||||
85
lab/lib/ipc.c
Normal file
85
lab/lib/ipc.c
Normal file
@@ -0,0 +1,85 @@
|
||||
// User-level IPC library routines
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
// Receive a value via IPC and return it.
|
||||
// If 'pg' is nonnull, then any page sent by the sender will be mapped at
|
||||
// that address.
|
||||
// If 'from_env_store' is nonnull, then store the IPC sender's envid in
|
||||
// *from_env_store.
|
||||
// If 'perm_store' is nonnull, then store the IPC sender's page permission
|
||||
// in *perm_store (this is nonzero iff a page was successfully
|
||||
// transferred to 'pg').
|
||||
// If the system call fails, then store 0 in *fromenv and *perm (if
|
||||
// they're nonnull) and return the error.
|
||||
// Otherwise, return the value sent by the sender
|
||||
//
|
||||
// Hint:
|
||||
// Use 'thisenv' to discover the value and who sent it.
|
||||
// If 'pg' is null, pass sys_ipc_recv a value that it will understand
|
||||
// as meaning "no page". (Zero is not the right value, since that's
|
||||
// a perfectly valid place to map a page.)
|
||||
int32_t
|
||||
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
//panic("ipc_recv not implemented");
|
||||
int r;
|
||||
if (!pg)
|
||||
pg = (void *)UTOP;
|
||||
r = sys_ipc_recv(pg);
|
||||
|
||||
|
||||
if (from_env_store)
|
||||
*from_env_store = r < 0? 0:thisenv->env_ipc_from;
|
||||
if (perm_store)
|
||||
*perm_store = r<0? 0:thisenv->env_ipc_perm;
|
||||
if (r < 0)
|
||||
return r;
|
||||
else
|
||||
return thisenv->env_ipc_value;
|
||||
}
|
||||
|
||||
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
|
||||
// This function keeps trying until it succeeds.
|
||||
// It should panic() on any error other than -E_IPC_NOT_RECV.
|
||||
//
|
||||
// Hint:
|
||||
// Use sys_yield() to be CPU-friendly.
|
||||
// If 'pg' is null, pass sys_ipc_try_send a value that it will understand
|
||||
// as meaning "no page". (Zero is not the right value.)
|
||||
void
|
||||
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
|
||||
{
|
||||
// LAB 4: Your code here.
|
||||
int r;
|
||||
if (!pg)
|
||||
pg = (void *) UTOP;
|
||||
|
||||
do {
|
||||
r = sys_ipc_try_send(to_env, val, pg, perm);
|
||||
if (r == -E_IPC_NOT_RECV) {
|
||||
// 用户级程序不能直接调用 sched_yeild();
|
||||
sys_yield();
|
||||
|
||||
}
|
||||
else if( (r != -E_IPC_NOT_RECV)&& (r < 0 )) {
|
||||
panic("ipc_send failed %e\n", r);
|
||||
}
|
||||
} while ( r < 0) ;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Find the first environment of the given type. We'll use this to
|
||||
// find special environments.
|
||||
// Returns 0 if no such environment exists.
|
||||
envid_t
|
||||
ipc_find_env(enum EnvType type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NENV; i++)
|
||||
if (envs[i].env_type == type)
|
||||
return envs[i].env_id;
|
||||
return 0;
|
||||
}
|
||||
28
lab/lib/libmain.c
Normal file
28
lab/lib/libmain.c
Normal file
@@ -0,0 +1,28 @@
|
||||
// Called from entry.S to get us going.
|
||||
// entry.S already took care of defining envs, pages, uvpd, and uvpt.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
extern void umain(int argc, char **argv);
|
||||
|
||||
const volatile struct Env *thisenv;
|
||||
const char *binaryname = "<unknown>";
|
||||
|
||||
void
|
||||
libmain(int argc, char **argv)
|
||||
{
|
||||
// set thisenv to point at our Env structure in envs[].
|
||||
// LAB 3: Your code here.
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
|
||||
// save the name of the program so that panic() can use it
|
||||
if (argc > 0)
|
||||
binaryname = argv[0];
|
||||
|
||||
// call user main routine
|
||||
umain(argc, argv);
|
||||
|
||||
// exit gracefully
|
||||
exit();
|
||||
}
|
||||
|
||||
26
lab/lib/panic.c
Normal file
26
lab/lib/panic.c
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors.
|
||||
* It prints "panic: <message>", then causes a breakpoint exception,
|
||||
* which causes JOS to enter the JOS kernel monitor.
|
||||
*/
|
||||
void
|
||||
_panic(const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
// Print the panic message
|
||||
cprintf("[%08x] user panic in %s at %s:%d: ",
|
||||
sys_getenvid(), binaryname, file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
|
||||
// Cause a breakpoint exception
|
||||
while (1)
|
||||
asm volatile("int3");
|
||||
}
|
||||
|
||||
90
lab/lib/pfentry.S
Normal file
90
lab/lib/pfentry.S
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
// Page fault upcall entrypoint.
|
||||
|
||||
// This is where we ask the kernel to redirect us to whenever we cause
|
||||
// a page fault in user space (see the call to sys_set_pgfault_handler
|
||||
// in pgfault.c).
|
||||
//
|
||||
// When a page fault actually occurs, the kernel switches our ESP to
|
||||
// point to the user exception stack if we're not already on the user
|
||||
// exception stack, and then it pushes a UTrapframe onto our user
|
||||
// exception stack:
|
||||
//
|
||||
// trap-time esp
|
||||
// trap-time eflags
|
||||
// trap-time eip
|
||||
// utf_regs.reg_eax
|
||||
// ...
|
||||
// utf_regs.reg_esi
|
||||
// utf_regs.reg_edi
|
||||
// utf_err (error code)
|
||||
// utf_fault_va <-- %esp
|
||||
//
|
||||
// If this is a recursive fault, the kernel will reserve for us a
|
||||
// blank word above the trap-time esp for scratch work when we unwind
|
||||
// the recursive call.
|
||||
//
|
||||
// We then have call up to the appropriate page fault handler in C
|
||||
// code, pointed to by the global variable '_pgfault_handler'.
|
||||
|
||||
.text
|
||||
.globl _pgfault_upcall
|
||||
_pgfault_upcall:
|
||||
// Call the C page fault handler.
|
||||
pushl %esp // function argument: pointer to UTF
|
||||
movl _pgfault_handler, %eax
|
||||
call *%eax
|
||||
addl $4, %esp // pop function argument 恢复栈
|
||||
|
||||
// Now the C page fault handler has returned and you must return
|
||||
// to the trap time state.
|
||||
// Push trap-time %eip onto the trap-time stack.
|
||||
//
|
||||
// Explanation:
|
||||
// We must prepare the trap-time stack for our eventual return to
|
||||
// re-execute the instruction that faulted.
|
||||
// Unfortunately, we can't return directly from the exception stack:
|
||||
// We can't call 'jmp', since that requires that we load the address
|
||||
// into a register, and all registers must have their trap-time
|
||||
// values after the return.
|
||||
// We can't call 'ret' from the exception stack either, since if we
|
||||
// did, %esp would have the wrong value.
|
||||
// So instead, we push the trap-time %eip onto the *trap-time* stack!
|
||||
// Below we'll switch to that stack and call 'ret', which will
|
||||
// restore %eip to its pre-fault value.
|
||||
//
|
||||
// In the case of a recursive fault on the exception stack,
|
||||
// note that the word we're pushing now will fit in the
|
||||
// blank word that the kernel reserved for us.
|
||||
//
|
||||
// Throughout the remaining code, think carefully about what
|
||||
// registers are available for intermediate calculations. You
|
||||
// may find that you have to rearrange your code in non-obvious
|
||||
// ways as registers become unavailable as scratch space.
|
||||
//
|
||||
// LAB 4: Your code here.
|
||||
// Struct PushRegs size = 32
|
||||
addl $8, %esp // esp+8 -> PushRegs over utf_fault_va utf_err
|
||||
movl 0x20(%esp), %eax // eax = (esp+0x20 -> utf_eip )
|
||||
subl $4, 0x28(%esp) // for trap time eip 保留32bit, esp+48 = utf_esp
|
||||
movl 0x28(%esp), %edx // %edx = utf_esp-4
|
||||
movl %eax, (%edx) // %eax = eip ----> esp-4 以至于ret可以直接读取其继续执行的地址
|
||||
|
||||
// Restore the trap-time registers. After you do this, you
|
||||
// can no longer modify any general-purpose registers.
|
||||
// LAB 4: Your code here.
|
||||
popal // after popal esp->utf_eip
|
||||
// Restore eflags from the stack. After you do this, you can
|
||||
// no longer use arithmetic operations or anything else that
|
||||
// modifies eflags.
|
||||
// LAB 4: Your code here.
|
||||
addl $4, %esp // esp+4 -> utf_eflags
|
||||
popfl
|
||||
// Switch back to the adjusted trap-time stack.
|
||||
// LAB 4: Your code here.
|
||||
popl %esp
|
||||
// Return to re-execute the instruction that faulted.
|
||||
// LAB 4: Your code here.
|
||||
ret // 这里十分巧妙, ret会读取esp指向的第一个内容, 也就是我们第一步写入的eip
|
||||
40
lab/lib/pgfault.c
Normal file
40
lab/lib/pgfault.c
Normal file
@@ -0,0 +1,40 @@
|
||||
// User-level page fault handler support.
|
||||
// Rather than register the C page fault handler directly with the
|
||||
// kernel as the page fault handler, we register the assembly language
|
||||
// wrapper in pfentry.S, which in turns calls the registered C
|
||||
// function.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
|
||||
// Assembly language pgfault entrypoint defined in lib/pfentry.S.
|
||||
extern void _pgfault_upcall(void);
|
||||
|
||||
// Pointer to currently installed C-language pgfault handler.
|
||||
void (*_pgfault_handler)(struct UTrapframe *utf);
|
||||
|
||||
//
|
||||
// Set the page fault handler function.
|
||||
// If there isn't one yet, _pgfault_handler will be 0.
|
||||
// The first time we register a handler, we need to
|
||||
// allocate an exception stack (one page of memory with its top
|
||||
// at UXSTACKTOP), and tell the kernel to call the assembly-language
|
||||
// _pgfault_upcall routine when a page fault occurs.
|
||||
//
|
||||
void
|
||||
set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
|
||||
{
|
||||
int r;
|
||||
|
||||
if (_pgfault_handler == 0) {
|
||||
// First time through!
|
||||
// LAB 4: Your code here.
|
||||
|
||||
sys_page_alloc(sys_getenvid(), (void *) (UXSTACKTOP - PGSIZE), PTE_SYSCALL);
|
||||
sys_env_set_pgfault_upcall(sys_getenvid(), _pgfault_upcall);
|
||||
// panic("set_pgfault_handler not implemented");
|
||||
}
|
||||
|
||||
// Save handler pointer for assembly to call.
|
||||
_pgfault_handler = handler;
|
||||
}
|
||||
62
lab/lib/printf.c
Normal file
62
lab/lib/printf.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// Implementation of cprintf console output for user environments,
|
||||
// based on printfmt() and the sys_cputs() system call.
|
||||
//
|
||||
// cprintf is a debugging statement, not a generic output statement.
|
||||
// It is very important that it always go to the console, especially when
|
||||
// debugging file descriptor code!
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
|
||||
// Collect up to 256 characters into a buffer
|
||||
// and perform ONE system call to print all of them,
|
||||
// in order to make the lines output to the console atomic
|
||||
// and prevent interrupts from causing context switches
|
||||
// in the middle of a console output line and such.
|
||||
struct printbuf {
|
||||
int idx; // current buffer index
|
||||
int cnt; // total bytes printed so far
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
putch(int ch, struct printbuf *b)
|
||||
{
|
||||
b->buf[b->idx++] = ch;
|
||||
if (b->idx == 256-1) {
|
||||
sys_cputs(b->buf, b->idx);
|
||||
b->idx = 0;
|
||||
}
|
||||
b->cnt++;
|
||||
}
|
||||
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
struct printbuf b;
|
||||
|
||||
b.idx = 0;
|
||||
b.cnt = 0;
|
||||
vprintfmt((void*)putch, &b, fmt, ap);
|
||||
sys_cputs(b.buf, b.idx);
|
||||
|
||||
return b.cnt;
|
||||
}
|
||||
|
||||
int
|
||||
cprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
302
lab/lib/printfmt.c
Normal file
302
lab/lib/printfmt.c
Normal file
@@ -0,0 +1,302 @@
|
||||
// Stripped-down primitive printf-style formatting routines,
|
||||
// used in common by printf, sprintf, fprintf, etc.
|
||||
// This code is also used by both the kernel and user programs.
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/stdarg.h>
|
||||
#include <inc/error.h>
|
||||
|
||||
/*
|
||||
* Space or zero padding and a field width are supported for the numeric
|
||||
* formats only.
|
||||
*
|
||||
* The special format %e takes an integer error code
|
||||
* and prints a string describing the error.
|
||||
* The integer may be positive or negative,
|
||||
* so that -E_NO_MEM and E_NO_MEM are equivalent.
|
||||
*/
|
||||
|
||||
static const char * const error_string[MAXERROR] =
|
||||
{
|
||||
[E_UNSPECIFIED] = "unspecified error",
|
||||
[E_BAD_ENV] = "bad environment",
|
||||
[E_INVAL] = "invalid parameter",
|
||||
[E_NO_MEM] = "out of memory",
|
||||
[E_NO_FREE_ENV] = "out of environments",
|
||||
[E_FAULT] = "segmentation fault",
|
||||
[E_IPC_NOT_RECV]= "env is not recving",
|
||||
[E_EOF] = "unexpected end of file",
|
||||
};
|
||||
|
||||
/*
|
||||
* Print a number (base <= 16) in reverse order,
|
||||
* using specified putch function and associated pointer putdat.
|
||||
*/
|
||||
static void
|
||||
printnum(void (*putch)(int, void*), void *putdat,
|
||||
unsigned long long num, unsigned base, int width, int padc)
|
||||
{
|
||||
// first recursively print all preceding (more significant) digits
|
||||
if (num >= base) {
|
||||
printnum(putch, putdat, num / base, base, width - 1, padc);
|
||||
} else {
|
||||
// print any needed pad characters before first digit
|
||||
while (--width > 0)
|
||||
putch(padc, putdat);
|
||||
}
|
||||
|
||||
// then print this (the least significant) digit
|
||||
putch("0123456789abcdef"[num % base], putdat);
|
||||
}
|
||||
|
||||
// Get an unsigned int of various possible sizes from a varargs list,
|
||||
// depending on the lflag parameter.
|
||||
static unsigned long long
|
||||
getuint(va_list *ap, int lflag)
|
||||
{
|
||||
if (lflag >= 2)
|
||||
return va_arg(*ap, unsigned long long);
|
||||
else if (lflag)
|
||||
return va_arg(*ap, unsigned long);
|
||||
else
|
||||
return va_arg(*ap, unsigned int);
|
||||
}
|
||||
|
||||
// Same as getuint but signed - can't use getuint
|
||||
// because of sign extension
|
||||
static long long
|
||||
getint(va_list *ap, int lflag)
|
||||
{
|
||||
if (lflag >= 2)
|
||||
return va_arg(*ap, long long);
|
||||
else if (lflag)
|
||||
return va_arg(*ap, long);
|
||||
else
|
||||
return va_arg(*ap, int);
|
||||
}
|
||||
|
||||
|
||||
// Main function to format and print a string.
|
||||
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
|
||||
|
||||
void
|
||||
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
|
||||
{
|
||||
register const char *p;
|
||||
register int ch, err;
|
||||
unsigned long long num;
|
||||
int base, lflag, width, precision, altflag;
|
||||
char padc;
|
||||
|
||||
while (1) {
|
||||
while ((ch = *(unsigned char *) fmt++) != '%') {
|
||||
if (ch == '\0')
|
||||
return;
|
||||
putch(ch, putdat);
|
||||
}
|
||||
|
||||
// Process a %-escape sequence
|
||||
padc = ' ';
|
||||
width = -1;
|
||||
precision = -1;
|
||||
lflag = 0;
|
||||
altflag = 0;
|
||||
reswitch:
|
||||
switch (ch = *(unsigned char *) fmt++) {
|
||||
|
||||
// flag to pad on the right
|
||||
case '-':
|
||||
padc = '-';
|
||||
goto reswitch;
|
||||
|
||||
// flag to pad with 0's instead of spaces
|
||||
case '0':
|
||||
padc = '0';
|
||||
goto reswitch;
|
||||
|
||||
// width field
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
for (precision = 0; ; ++fmt) {
|
||||
precision = precision * 10 + ch - '0';
|
||||
ch = *fmt;
|
||||
if (ch < '0' || ch > '9')
|
||||
break;
|
||||
}
|
||||
goto process_precision;
|
||||
|
||||
case '*':
|
||||
precision = va_arg(ap, int);
|
||||
goto process_precision;
|
||||
|
||||
case '.':
|
||||
if (width < 0)
|
||||
width = 0;
|
||||
goto reswitch;
|
||||
|
||||
case '#':
|
||||
altflag = 1;
|
||||
goto reswitch;
|
||||
|
||||
process_precision:
|
||||
if (width < 0)
|
||||
width = precision, precision = -1;
|
||||
goto reswitch;
|
||||
|
||||
// long flag (doubled for long long)
|
||||
case 'l':
|
||||
lflag++;
|
||||
goto reswitch;
|
||||
|
||||
// character
|
||||
case 'c':
|
||||
putch(va_arg(ap, int), putdat);
|
||||
break;
|
||||
|
||||
// error message
|
||||
case 'e':
|
||||
err = va_arg(ap, int);
|
||||
if (err < 0)
|
||||
err = -err;
|
||||
if (err >= MAXERROR || (p = error_string[err]) == NULL)
|
||||
printfmt(putch, putdat, "error %d", err);
|
||||
else
|
||||
printfmt(putch, putdat, "%s", p);
|
||||
break;
|
||||
|
||||
// string
|
||||
case 's':
|
||||
if ((p = va_arg(ap, char *)) == NULL)
|
||||
p = "(null)";
|
||||
if (width > 0 && padc != '-')
|
||||
for (width -= strnlen(p, precision); width > 0; width--)
|
||||
putch(padc, putdat);
|
||||
for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
|
||||
if (altflag && (ch < ' ' || ch > '~'))
|
||||
putch('?', putdat);
|
||||
else
|
||||
putch(ch, putdat);
|
||||
for (; width > 0; width--)
|
||||
putch(' ', putdat);
|
||||
break;
|
||||
|
||||
// (signed) decimal
|
||||
case 'd':
|
||||
num = getint(&ap, lflag);
|
||||
if ((long long) num < 0) {
|
||||
putch('-', putdat);
|
||||
num = -(long long) num;
|
||||
}
|
||||
base = 10;
|
||||
goto number;
|
||||
|
||||
// unsigned decimal
|
||||
case 'u':
|
||||
num = getuint(&ap, lflag);
|
||||
base = 10;
|
||||
goto number;
|
||||
|
||||
// (unsigned) octal
|
||||
case 'o':
|
||||
// Replace this with your code.
|
||||
putch('X', putdat);
|
||||
putch('X', putdat);
|
||||
putch('X', putdat);
|
||||
break;
|
||||
|
||||
// pointer
|
||||
case 'p':
|
||||
putch('0', putdat);
|
||||
putch('x', putdat);
|
||||
num = (unsigned long long)
|
||||
(uintptr_t) va_arg(ap, void *);
|
||||
base = 16;
|
||||
goto number;
|
||||
|
||||
// (unsigned) hexadecimal
|
||||
case 'x':
|
||||
num = getuint(&ap, lflag);
|
||||
base = 16;
|
||||
number:
|
||||
printnum(putch, putdat, num, base, width, padc);
|
||||
break;
|
||||
|
||||
// escaped '%' character
|
||||
case '%':
|
||||
putch(ch, putdat);
|
||||
break;
|
||||
|
||||
// unrecognized escape sequence - just print it literally
|
||||
default:
|
||||
putch('%', putdat);
|
||||
for (fmt--; fmt[-1] != '%'; fmt--)
|
||||
/* do nothing */;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintfmt(putch, putdat, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
struct sprintbuf {
|
||||
char *buf;
|
||||
char *ebuf;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
static void
|
||||
sprintputch(int ch, struct sprintbuf *b)
|
||||
{
|
||||
b->cnt++;
|
||||
if (b->buf < b->ebuf)
|
||||
*b->buf++ = ch;
|
||||
}
|
||||
|
||||
int
|
||||
vsnprintf(char *buf, int n, const char *fmt, va_list ap)
|
||||
{
|
||||
struct sprintbuf b = {buf, buf+n-1, 0};
|
||||
|
||||
if (buf == NULL || n < 1)
|
||||
return -E_INVAL;
|
||||
|
||||
// print the string to the buffer
|
||||
vprintfmt((void*)sprintputch, &b, fmt, ap);
|
||||
|
||||
// null terminate the buffer
|
||||
*b.buf = '\0';
|
||||
|
||||
return b.cnt;
|
||||
}
|
||||
|
||||
int
|
||||
snprintf(char *buf, int n, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, fmt);
|
||||
rc = vsnprintf(buf, n, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
38
lab/lib/readline.c
Normal file
38
lab/lib/readline.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/error.h>
|
||||
|
||||
#define BUFLEN 1024
|
||||
static char buf[BUFLEN];
|
||||
|
||||
char *
|
||||
readline(const char *prompt)
|
||||
{
|
||||
int i, c, echoing;
|
||||
|
||||
if (prompt != NULL)
|
||||
cprintf("%s", prompt);
|
||||
|
||||
i = 0;
|
||||
echoing = iscons(0);
|
||||
while (1) {
|
||||
c = getchar();
|
||||
if (c < 0) {
|
||||
cprintf("read error: %e\n", c);
|
||||
return NULL;
|
||||
} else if ((c == '\b' || c == '\x7f') && i > 0) {
|
||||
if (echoing)
|
||||
cputchar('\b');
|
||||
i--;
|
||||
} else if (c >= ' ' && i < BUFLEN-1) {
|
||||
if (echoing)
|
||||
cputchar(c);
|
||||
buf[i++] = c;
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
if (echoing)
|
||||
cputchar('\n');
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
285
lab/lib/string.c
Normal file
285
lab/lib/string.c
Normal file
@@ -0,0 +1,285 @@
|
||||
// Basic string routines. Not hardware optimized, but not shabby.
|
||||
|
||||
#include <inc/string.h>
|
||||
|
||||
// Using assembly for memset/memmove
|
||||
// makes some difference on real hardware,
|
||||
// but it makes an even bigger difference on bochs.
|
||||
// Primespipe runs 3x faster this way.
|
||||
#define ASM 1
|
||||
|
||||
int
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; *s != '\0'; s++)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
strnlen(const char *s, size_t size)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; size > 0 && *s != '\0'; s++, size--)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
char *
|
||||
strcpy(char *dst, const char *src)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
ret = dst;
|
||||
while ((*dst++ = *src++) != '\0')
|
||||
/* do nothing */;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
strcat(char *dst, const char *src)
|
||||
{
|
||||
int len = strlen(dst);
|
||||
strcpy(dst + len, src);
|
||||
return dst;
|
||||
}
|
||||
|
||||
char *
|
||||
strncpy(char *dst, const char *src, size_t size) {
|
||||
size_t i;
|
||||
char *ret;
|
||||
|
||||
ret = dst;
|
||||
for (i = 0; i < size; i++) {
|
||||
*dst++ = *src;
|
||||
// If strlen(src) < size, null-pad 'dst' out to 'size' chars
|
||||
if (*src != '\0')
|
||||
src++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t size)
|
||||
{
|
||||
char *dst_in;
|
||||
|
||||
dst_in = dst;
|
||||
if (size > 0) {
|
||||
while (--size > 0 && *src != '\0')
|
||||
*dst++ = *src++;
|
||||
*dst = '\0';
|
||||
}
|
||||
return dst - dst_in;
|
||||
}
|
||||
|
||||
int
|
||||
strcmp(const char *p, const char *q)
|
||||
{
|
||||
while (*p && *p == *q)
|
||||
p++, q++;
|
||||
return (int) ((unsigned char) *p - (unsigned char) *q);
|
||||
}
|
||||
|
||||
int
|
||||
strncmp(const char *p, const char *q, size_t n)
|
||||
{
|
||||
while (n > 0 && *p && *p == *q)
|
||||
n--, p++, q++;
|
||||
if (n == 0)
|
||||
return 0;
|
||||
else
|
||||
return (int) ((unsigned char) *p - (unsigned char) *q);
|
||||
}
|
||||
|
||||
// Return a pointer to the first occurrence of 'c' in 's',
|
||||
// or a null pointer if the string has no 'c'.
|
||||
char *
|
||||
strchr(const char *s, char c)
|
||||
{
|
||||
for (; *s; s++)
|
||||
if (*s == c)
|
||||
return (char *) s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return a pointer to the first occurrence of 'c' in 's',
|
||||
// or a pointer to the string-ending null character if the string has no 'c'.
|
||||
char *
|
||||
strfind(const char *s, char c)
|
||||
{
|
||||
for (; *s; s++)
|
||||
if (*s == c)
|
||||
break;
|
||||
return (char *) s;
|
||||
}
|
||||
|
||||
#if ASM
|
||||
void *
|
||||
memset(void *v, int c, size_t n)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (n == 0)
|
||||
return v;
|
||||
if ((int)v%4 == 0 && n%4 == 0) {
|
||||
c &= 0xFF;
|
||||
c = (c<<24)|(c<<16)|(c<<8)|c;
|
||||
asm volatile("cld; rep stosl\n"
|
||||
:: "D" (v), "a" (c), "c" (n/4)
|
||||
: "cc", "memory");
|
||||
} else
|
||||
asm volatile("cld; rep stosb\n"
|
||||
:: "D" (v), "a" (c), "c" (n)
|
||||
: "cc", "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
void *
|
||||
memmove(void *dst, const void *src, size_t n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if (s < d && s + n > d) {
|
||||
s += n;
|
||||
d += n;
|
||||
if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0)
|
||||
asm volatile("std; rep movsl\n"
|
||||
:: "D" (d-4), "S" (s-4), "c" (n/4) : "cc", "memory");
|
||||
else
|
||||
asm volatile("std; rep movsb\n"
|
||||
:: "D" (d-1), "S" (s-1), "c" (n) : "cc", "memory");
|
||||
// Some versions of GCC rely on DF being clear
|
||||
asm volatile("cld" ::: "cc");
|
||||
} else {
|
||||
if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0)
|
||||
asm volatile("cld; rep movsl\n"
|
||||
:: "D" (d), "S" (s), "c" (n/4) : "cc", "memory");
|
||||
else
|
||||
asm volatile("cld; rep movsb\n"
|
||||
:: "D" (d), "S" (s), "c" (n) : "cc", "memory");
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *
|
||||
memset(void *v, int c, size_t n)
|
||||
{
|
||||
char *p;
|
||||
int m;
|
||||
|
||||
p = v;
|
||||
m = n;
|
||||
while (--m >= 0)
|
||||
*p++ = c;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void *
|
||||
memmove(void *dst, const void *src, size_t n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if (s < d && s + n > d) {
|
||||
s += n;
|
||||
d += n;
|
||||
while (n-- > 0)
|
||||
*--d = *--s;
|
||||
} else
|
||||
while (n-- > 0)
|
||||
*d++ = *s++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *
|
||||
memcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
return memmove(dst, src, n);
|
||||
}
|
||||
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, size_t n)
|
||||
{
|
||||
const uint8_t *s1 = (const uint8_t *) v1;
|
||||
const uint8_t *s2 = (const uint8_t *) v2;
|
||||
|
||||
while (n-- > 0) {
|
||||
if (*s1 != *s2)
|
||||
return (int) *s1 - (int) *s2;
|
||||
s1++, s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
memfind(const void *s, int c, size_t n)
|
||||
{
|
||||
const void *ends = (const char *) s + n;
|
||||
for (; s < ends; s++)
|
||||
if (*(const unsigned char *) s == (unsigned char) c)
|
||||
break;
|
||||
return (void *) s;
|
||||
}
|
||||
|
||||
long
|
||||
strtol(const char *s, char **endptr, int base)
|
||||
{
|
||||
int neg = 0;
|
||||
long val = 0;
|
||||
|
||||
// gobble initial whitespace
|
||||
while (*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
|
||||
// plus/minus sign
|
||||
if (*s == '+')
|
||||
s++;
|
||||
else if (*s == '-')
|
||||
s++, neg = 1;
|
||||
|
||||
// hex or octal base prefix
|
||||
if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x'))
|
||||
s += 2, base = 16;
|
||||
else if (base == 0 && s[0] == '0')
|
||||
s++, base = 8;
|
||||
else if (base == 0)
|
||||
base = 10;
|
||||
|
||||
// digits
|
||||
while (1) {
|
||||
int dig;
|
||||
|
||||
if (*s >= '0' && *s <= '9')
|
||||
dig = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'z')
|
||||
dig = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'Z')
|
||||
dig = *s - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
if (dig >= base)
|
||||
break;
|
||||
s++, val = (val * base) + dig;
|
||||
// we don't properly detect overflow!
|
||||
}
|
||||
|
||||
if (endptr)
|
||||
*endptr = (char *) s;
|
||||
return (neg ? -val : val);
|
||||
}
|
||||
|
||||
113
lab/lib/syscall.c
Normal file
113
lab/lib/syscall.c
Normal file
@@ -0,0 +1,113 @@
|
||||
// System call stubs.
|
||||
|
||||
#include <inc/syscall.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
static inline int32_t
|
||||
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
// Generic system call: pass system call number in AX,
|
||||
// up to five parameters in DX, CX, BX, DI, SI.
|
||||
// Interrupt kernel with T_SYSCALL.
|
||||
//
|
||||
// The "volatile" tells the assembler not to optimize
|
||||
// this instruction away just because we don't use the
|
||||
// return value.
|
||||
//
|
||||
// The last clause tells the assembler that this can
|
||||
// potentially change the condition codes and arbitrary
|
||||
// memory locations.
|
||||
|
||||
asm volatile("int %1\n"
|
||||
: "=a" (ret)
|
||||
: "i" (T_SYSCALL),
|
||||
"a" (num),
|
||||
"d" (a1),
|
||||
"c" (a2),
|
||||
"b" (a3),
|
||||
"D" (a4),
|
||||
"S" (a5)
|
||||
: "cc", "memory");
|
||||
|
||||
if(check && ret > 0)
|
||||
panic("syscall %d returned %d (> 0)", num, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
sys_cputs(const char *s, size_t len)
|
||||
{
|
||||
syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_cgetc(void)
|
||||
{
|
||||
return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_destroy(envid_t envid)
|
||||
{
|
||||
return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
envid_t
|
||||
sys_getenvid(void)
|
||||
{
|
||||
return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
sys_yield(void)
|
||||
{
|
||||
syscall(SYS_yield, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
||||
{
|
||||
return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm)
|
||||
{
|
||||
return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_unmap(envid_t envid, void *va)
|
||||
{
|
||||
return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0);
|
||||
}
|
||||
|
||||
// sys_exofork is inlined in lib.h
|
||||
|
||||
int
|
||||
sys_env_set_status(envid_t envid, int status)
|
||||
{
|
||||
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
||||
{
|
||||
return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm)
|
||||
{
|
||||
return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_ipc_recv(void *dstva)
|
||||
{
|
||||
return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
16
lab/user/Makefrag
Normal file
16
lab/user/Makefrag
Normal file
@@ -0,0 +1,16 @@
|
||||
OBJDIRS += user
|
||||
|
||||
|
||||
USERLIBS += jos
|
||||
|
||||
$(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + cc[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
|
||||
@echo + ld $@
|
||||
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||
$(V)$(OBJDUMP) -S $@ > $@.asm
|
||||
$(V)$(NM) -n $@ > $@.sym
|
||||
|
||||
11
lab/user/badsegment.c
Normal file
11
lab/user/badsegment.c
Normal file
@@ -0,0 +1,11 @@
|
||||
// program to cause a general protection exception
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
// Try to load the kernel's TSS selector into the DS register.
|
||||
asm volatile("movw $0x28,%ax; movw %ax,%ds");
|
||||
}
|
||||
|
||||
10
lab/user/breakpoint.c
Normal file
10
lab/user/breakpoint.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// program to cause a breakpoint trap
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
asm volatile("int $3");
|
||||
}
|
||||
|
||||
11
lab/user/buggyhello.c
Normal file
11
lab/user/buggyhello.c
Normal file
@@ -0,0 +1,11 @@
|
||||
// buggy hello world -- unmapped pointer passed to kernel
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_cputs((char*)1, 1);
|
||||
}
|
||||
|
||||
13
lab/user/buggyhello2.c
Normal file
13
lab/user/buggyhello2.c
Normal file
@@ -0,0 +1,13 @@
|
||||
// buggy hello world 2 -- pointed-to region extends into unmapped memory
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
const char *hello = "hello, world\n";
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_cputs(hello, 1024*1024);
|
||||
}
|
||||
|
||||
13
lab/user/divzero.c
Normal file
13
lab/user/divzero.c
Normal file
@@ -0,0 +1,13 @@
|
||||
// buggy program - causes a divide by zero exception
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
int zero;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
zero = 0;
|
||||
cprintf("1/0 is %08x!\n", 1/zero);
|
||||
}
|
||||
|
||||
90
lab/user/dumbfork.c
Normal file
90
lab/user/dumbfork.c
Normal file
@@ -0,0 +1,90 @@
|
||||
// Ping-pong a counter between two processes.
|
||||
// Only need to start one of these -- splits into two, crudely.
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
envid_t dumbfork(void);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
int i;
|
||||
|
||||
// fork a child process
|
||||
who = dumbfork();
|
||||
|
||||
// print a message and yield to the other a few times
|
||||
for (i = 0; i < (who ? 10 : 20); i++) {
|
||||
cprintf("%d: I am the %s!\n", i, who ? "parent" : "child");
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
duppage(envid_t dstenv, void *addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
// This is NOT what you should do in your fork.
|
||||
if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
// dstenv 的 addr 映射到 envs[0]的UTEMP,
|
||||
if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_map: %e", r);
|
||||
|
||||
memmove(UTEMP, addr, PGSIZE);
|
||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
||||
panic("sys_page_unmap: %e", r);
|
||||
}
|
||||
|
||||
envid_t
|
||||
dumbfork(void)
|
||||
{
|
||||
envid_t envid;
|
||||
uint8_t *addr;
|
||||
int r;
|
||||
extern unsigned char end[];
|
||||
|
||||
// Allocate a new child environment.
|
||||
// The kernel will initialize it with a copy of our register state,
|
||||
// so that the child will appear to have called sys_exofork() too -
|
||||
// except that in the child, this "fake" call to sys_exofork()
|
||||
// will return 0 instead of the envid of the child.
|
||||
envid = sys_exofork();
|
||||
cprintf("envid: %d\n",envid);
|
||||
if (envid < 0)
|
||||
panic("sys_exofork: %e", envid);
|
||||
if (envid == 0) {
|
||||
// We're the child.
|
||||
// The copied value of the global variable 'thisenv'
|
||||
// is no longer valid (it refers to the parent!).
|
||||
// Fix it and return 0.
|
||||
// "child, sysgetenvid() : 1
|
||||
// cprintf("child, sysgetenvid() :%d\n", ENVX(sys_getenvid()));
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We're the parent.
|
||||
// Eagerly copy our entire address space into the child.
|
||||
// This is NOT what you should do in your fork implementation.
|
||||
// 用户程序通常从 UTEXT处开始。
|
||||
for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE) {
|
||||
// copying addr :00800000
|
||||
// copying addr :00801000
|
||||
// copying addr :00802000
|
||||
cprintf("copying addr :%08x\n", (uint32_t)addr);
|
||||
duppage(envid, addr);
|
||||
}
|
||||
// Also copy the stack we are currently running on.
|
||||
duppage(envid, ROUNDDOWN(&addr, PGSIZE));
|
||||
|
||||
// Start the child environment running
|
||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return envid;
|
||||
}
|
||||
|
||||
12
lab/user/evilhello.c
Normal file
12
lab/user/evilhello.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// evil hello world -- kernel pointer passed to kernel
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
// try to print the kernel entry point as a string! mua ha ha!
|
||||
sys_cputs((char*)0xf010000c, 100);
|
||||
}
|
||||
|
||||
25
lab/user/fairness.c
Normal file
25
lab/user/fairness.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// Demonstrate lack of fairness in IPC.
|
||||
// Start three instances of this program as envs 1, 2, and 3.
|
||||
// (user/idle is env 0).
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who, id;
|
||||
|
||||
id = sys_getenvid();
|
||||
|
||||
if (thisenv == &envs[1]) {
|
||||
while (1) {
|
||||
ipc_recv(&who, 0, 0);
|
||||
cprintf("%x recv from %x\n", id, who);
|
||||
}
|
||||
} else {
|
||||
cprintf("%x loop sending to %x\n", id, envs[1].env_id);
|
||||
while (1)
|
||||
ipc_send(envs[1].env_id, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
24
lab/user/faultalloc.c
Normal file
24
lab/user/faultalloc.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
cprintf("%s\n", (char*)0xDeadBeef);
|
||||
cprintf("%s\n", (char*)0xCafeBffe);
|
||||
}
|
||||
24
lab/user/faultallocbad.c
Normal file
24
lab/user/faultallocbad.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
// doesn't work because we sys_cputs instead of cprintf (exercise: why?)
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
sys_cputs((char*)0xDEADBEEF, 4);
|
||||
}
|
||||
14
lab/user/faultbadhandler.c
Normal file
14
lab/user/faultbadhandler.c
Normal file
@@ -0,0 +1,14 @@
|
||||
// test bad pointer for user-level fault handler
|
||||
// this is going to fault in the fault handler accessing eip (always!)
|
||||
// so eventually the kernel kills it (PFM_KILL) because
|
||||
// we outrun the stack with invocations of the user-level handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
19
lab/user/faultdie.c
Normal file
19
lab/user/faultdie.c
Normal file
@@ -0,0 +1,19 @@
|
||||
// test user-level fault handler -- just exit when we fault
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
uint32_t err = utf->utf_err;
|
||||
cprintf("i faulted at va %x, err %x\n", addr, err & 7);
|
||||
sys_env_destroy(sys_getenvid());
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
*(int*)0xDeadBeef = 0;
|
||||
}
|
||||
11
lab/user/faultevilhandler.c
Normal file
11
lab/user/faultevilhandler.c
Normal file
@@ -0,0 +1,11 @@
|
||||
// test evil pointer for user-level fault handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xF0100020);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
12
lab/user/faultnostack.c
Normal file
12
lab/user/faultnostack.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// test user fault handler being called with no exception stack mapped
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void _pgfault_upcall();
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
10
lab/user/faultread.c
Normal file
10
lab/user/faultread.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// buggy program - faults with a read from location zero
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
cprintf("I read %08x from location 0!\n", *(unsigned*)0);
|
||||
}
|
||||
|
||||
10
lab/user/faultreadkernel.c
Normal file
10
lab/user/faultreadkernel.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// buggy program - faults with a read from kernel space
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
cprintf("I read %08x from location 0xf0100000!\n", *(unsigned*)0xf0100000);
|
||||
}
|
||||
|
||||
146
lab/user/faultregs.c
Normal file
146
lab/user/faultregs.c
Normal file
@@ -0,0 +1,146 @@
|
||||
// test register restore on user-level page fault return
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
struct regs
|
||||
{
|
||||
struct PushRegs regs;
|
||||
uintptr_t eip;
|
||||
uint32_t eflags;
|
||||
uintptr_t esp;
|
||||
};
|
||||
|
||||
#define SAVE_REGS(base) \
|
||||
"\tmovl %%edi, "base"+0x00\n" \
|
||||
"\tmovl %%esi, "base"+0x04\n" \
|
||||
"\tmovl %%ebp, "base"+0x08\n" \
|
||||
"\tmovl %%ebx, "base"+0x10\n" \
|
||||
"\tmovl %%edx, "base"+0x14\n" \
|
||||
"\tmovl %%ecx, "base"+0x18\n" \
|
||||
"\tmovl %%eax, "base"+0x1c\n" \
|
||||
"\tmovl %%esp, "base"+0x28\n"
|
||||
|
||||
#define LOAD_REGS(base) \
|
||||
"\tmovl "base"+0x00, %%edi\n" \
|
||||
"\tmovl "base"+0x04, %%esi\n" \
|
||||
"\tmovl "base"+0x08, %%ebp\n" \
|
||||
"\tmovl "base"+0x10, %%ebx\n" \
|
||||
"\tmovl "base"+0x14, %%edx\n" \
|
||||
"\tmovl "base"+0x18, %%ecx\n" \
|
||||
"\tmovl "base"+0x1c, %%eax\n" \
|
||||
"\tmovl "base"+0x28, %%esp\n"
|
||||
|
||||
static struct regs before, during, after;
|
||||
|
||||
static void
|
||||
check_regs(struct regs* a, const char *an, struct regs* b, const char *bn,
|
||||
const char *testname)
|
||||
{
|
||||
int mismatch = 0;
|
||||
|
||||
cprintf("%-6s %-8s %-8s\n", "", an, bn);
|
||||
|
||||
#define CHECK(name, field) \
|
||||
do { \
|
||||
cprintf("%-6s %08x %08x ", #name, a->field, b->field); \
|
||||
if (a->field == b->field) \
|
||||
cprintf("OK\n"); \
|
||||
else { \
|
||||
cprintf("MISMATCH\n"); \
|
||||
mismatch = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
CHECK(edi, regs.reg_edi);
|
||||
CHECK(esi, regs.reg_esi);
|
||||
CHECK(ebp, regs.reg_ebp);
|
||||
CHECK(ebx, regs.reg_ebx);
|
||||
CHECK(edx, regs.reg_edx);
|
||||
CHECK(ecx, regs.reg_ecx);
|
||||
CHECK(eax, regs.reg_eax);
|
||||
CHECK(eip, eip);
|
||||
CHECK(eflags, eflags);
|
||||
CHECK(esp, esp);
|
||||
|
||||
#undef CHECK
|
||||
|
||||
cprintf("Registers %s ", testname);
|
||||
if (!mismatch)
|
||||
cprintf("OK\n");
|
||||
else
|
||||
cprintf("MISMATCH\n");
|
||||
}
|
||||
|
||||
static void
|
||||
pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (utf->utf_fault_va != (uint32_t)UTEMP)
|
||||
panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)",
|
||||
utf->utf_fault_va, utf->utf_eip);
|
||||
|
||||
// Check registers in UTrapframe
|
||||
during.regs = utf->utf_regs;
|
||||
during.eip = utf->utf_eip;
|
||||
during.eflags = utf->utf_eflags & ~FL_RF;
|
||||
during.esp = utf->utf_esp;
|
||||
check_regs(&before, "before", &during, "during", "in UTrapframe");
|
||||
|
||||
// Map UTEMP so the write succeeds
|
||||
if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
asm volatile(
|
||||
// Light up eflags to catch more errors
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\torl $0x8d5, %%eax\n"
|
||||
"\tpushl %%eax\n"
|
||||
"\tpopfl\n"
|
||||
|
||||
// Save before registers
|
||||
// eflags
|
||||
"\tmov %%eax, %0+0x24\n"
|
||||
// eip
|
||||
"\tleal 0f, %%eax\n"
|
||||
"\tmovl %%eax, %0+0x20\n"
|
||||
"\tpopl %%eax\n"
|
||||
// others
|
||||
SAVE_REGS("%0")
|
||||
|
||||
// Fault at UTEMP
|
||||
"\t0: movl $42, 0x400000\n"
|
||||
|
||||
// Save after registers (except eip and eflags)
|
||||
SAVE_REGS("%1")
|
||||
// Restore registers (except eip and eflags). This
|
||||
// way, the test will run even if EIP is the *only*
|
||||
// thing restored correctly.
|
||||
LOAD_REGS("%0")
|
||||
// Save after eflags (now that stack is back); note
|
||||
// that we were very careful not to modify eflags in
|
||||
// since we saved it
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\tmov %%eax, %1+0x24\n"
|
||||
"\tpopl %%eax\n"
|
||||
: : "m" (before), "m" (after) : "memory", "cc");
|
||||
|
||||
// Check UTEMP to roughly determine that EIP was restored
|
||||
// correctly (of course, we probably wouldn't get this far if
|
||||
// it weren't)
|
||||
if (*(int*)UTEMP != 42)
|
||||
cprintf("EIP after page-fault MISMATCH\n");
|
||||
after.eip = before.eip;
|
||||
|
||||
check_regs(&before, "before", &after, "after", "after page-fault");
|
||||
}
|
||||
10
lab/user/faultwrite.c
Normal file
10
lab/user/faultwrite.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// buggy program - faults with a write to location zero
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
*(unsigned*)0 = 0;
|
||||
}
|
||||
|
||||
10
lab/user/faultwritekernel.c
Normal file
10
lab/user/faultwritekernel.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// buggy program - faults with a write to a kernel location
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
*(unsigned*)0xf0100000 = 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user