LAB 4 IS DONE.

This commit is contained in:
winPond
2019-07-06 10:39:40 +08:00
parent 21dd0e1a51
commit e78b336ba8
113 changed files with 11622 additions and 0 deletions

37
lab/CODING Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
LAB=4
PACKAGEDATE=Mon Oct 8 21:31:51 PDT 2018

67
lab/fs/test.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

572
lab/kern/env.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

93
lab/kern/pmap.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,9 @@
#include <inc/lib.h>
void
exit(void)
{
sys_env_destroy(0);
}

167
lab/lib/fork.c Normal file
View 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
View 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
View 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
View 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
View 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 // retesp eip

40
lab/lib/pgfault.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}

View 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
View 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;
}

View 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
View 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
View 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);
}

View 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
View 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
View 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;
}

View 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