Lab 1
This commit is contained in:
commit
1a83673424
12
.dir-locals.el
Normal file
12
.dir-locals.el
Normal file
|
@ -0,0 +1,12 @@
|
|||
((nil
|
||||
(indent-tabs-mode . t)
|
||||
(tab-width . 8))
|
||||
(c-mode
|
||||
(c-file-style . "bsd")
|
||||
(c-basic-offset . 8))
|
||||
(shell-mode
|
||||
(sh-basic-offset . 8)
|
||||
(sh-indentation . 8))
|
||||
(python-mode
|
||||
(indent-tabs-mode . nil))
|
||||
)
|
30
.gdbinit.tmpl
Normal file
30
.gdbinit.tmpl
Normal file
|
@ -0,0 +1,30 @@
|
|||
set $lastcs = -1
|
||||
|
||||
define hook-stop
|
||||
# There doesn't seem to be a good way to detect if we're in 16- or
|
||||
# 32-bit mode, but we always run with CS == 8 in 32-bit mode.
|
||||
if $cs == 8 || $cs == 27
|
||||
if $lastcs != 8 && $lastcs != 27
|
||||
set architecture i386
|
||||
end
|
||||
x/i $pc
|
||||
else
|
||||
if $lastcs == -1 || $lastcs == 8 || $lastcs == 27
|
||||
set architecture i8086
|
||||
end
|
||||
# Translate the segment:offset into a physical address
|
||||
printf "[%4x:%4x] ", $cs, $eip
|
||||
x/i $cs*16+$eip
|
||||
end
|
||||
set $lastcs = $cs
|
||||
end
|
||||
|
||||
echo + target remote localhost:1234\n
|
||||
target remote localhost:1234
|
||||
|
||||
# If this fails, it's probably because your GDB doesn't support ELF.
|
||||
# Look at the tools page at
|
||||
# http://pdos.csail.mit.edu/6.828/2009/tools.html
|
||||
# for instructions on building GDB with ELF support.
|
||||
echo + symbol-file obj/kern/kernel\n
|
||||
symbol-file obj/kern/kernel
|
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/obj
|
||||
/jos.in
|
||||
/jos.log
|
||||
/jos.out
|
||||
/jos.out.*
|
||||
/jos.cmd
|
||||
/.gdbinit
|
||||
/wget.log
|
||||
/qemu.pcap
|
||||
/qemu.pcap.*
|
||||
/qemu.out
|
||||
/qemu.log
|
||||
/gradelib.pyc
|
||||
/lab*-handin.tar.gz
|
||||
/lab?/
|
||||
/sol?/
|
||||
/myapi.key
|
||||
/.suf
|
37
CODING
Normal file
37
CODING
Normal file
|
@ -0,0 +1,37 @@
|
|||
JOS CODING STANDARDS
|
||||
|
||||
It's easier on everyone if all authors working on a shared
|
||||
code base are consistent in the way they write their programs.
|
||||
We have the following conventions in our code:
|
||||
|
||||
* No space after the name of a function in a call
|
||||
For example, printf("hello") not printf ("hello").
|
||||
|
||||
* One space after keywords "if", "for", "while", "switch".
|
||||
For example, if (x) not if(x).
|
||||
|
||||
* Space before braces.
|
||||
For example, if (x) { not if (x){.
|
||||
|
||||
* Function names are all lower-case separated by underscores.
|
||||
|
||||
* Beginning-of-line indentation via tabs, not spaces.
|
||||
|
||||
* Preprocessor macros are always UPPERCASE.
|
||||
There are a few grandfathered exceptions: assert, panic,
|
||||
static_assert, offsetof.
|
||||
|
||||
* Pointer types have spaces: (uint16_t *) not (uint16_t*).
|
||||
|
||||
* Multi-word names are lower_case_with_underscores.
|
||||
|
||||
* Comments in imported code are usually C /* ... */ comments.
|
||||
Comments in new code are C++ style //.
|
||||
|
||||
* In a function definition, the function name starts a new line.
|
||||
Then you can grep -n '^foo' */*.c to find the definition of foo.
|
||||
|
||||
* Functions that take no arguments are declared f(void) not f().
|
||||
|
||||
The included .dir-locals.el file will automatically set up the basic
|
||||
indentation style in Emacs.
|
315
GNUmakefile
Normal file
315
GNUmakefile
Normal file
|
@ -0,0 +1,315 @@
|
|||
#
|
||||
# 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
|
||||
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 += -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
|
||||
|
||||
|
||||
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 += $(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
|
||||
|
||||
|
||||
# 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
boot/Makefrag
Normal file
32
boot/Makefrag
Normal file
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Makefile fragment for the JOS kernel.
|
||||
# This is NOT a complete makefile;
|
||||
# you must run GNU make in the top-level directory
|
||||
# where the GNUmakefile is located.
|
||||
#
|
||||
|
||||
OBJDIRS += boot
|
||||
|
||||
BOOT_OBJS := $(OBJDIR)/boot/boot.o $(OBJDIR)/boot/main.o
|
||||
|
||||
$(OBJDIR)/boot/%.o: boot/%.c
|
||||
@echo + cc -Os $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/boot/%.o: boot/%.S
|
||||
@echo + as $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/boot/main.o: boot/main.c
|
||||
@echo + cc -Os $<
|
||||
$(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $(OBJDIR)/boot/main.o boot/main.c
|
||||
|
||||
$(OBJDIR)/boot/boot: $(BOOT_OBJS)
|
||||
@echo + ld boot/boot
|
||||
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^
|
||||
$(V)$(OBJDUMP) -S $@.out >$@.asm
|
||||
$(V)$(OBJCOPY) -S -O binary -j .text $@.out $@
|
||||
$(V)perl boot/sign.pl $(OBJDIR)/boot/boot
|
||||
|
85
boot/boot.S
Normal file
85
boot/boot.S
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <inc/mmu.h>
|
||||
|
||||
# Start the CPU: switch to 32-bit protected mode, jump into C.
|
||||
# The BIOS loads this code from the first sector of the hard disk into
|
||||
# memory at physical address 0x7c00 and starts executing in real mode
|
||||
# with %cs=0 %ip=7c00.
|
||||
|
||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
||||
.set CR0_PE_ON, 0x1 # protected mode enable flag
|
||||
|
||||
.globl start
|
||||
start:
|
||||
.code16 # Assemble for 16-bit mode
|
||||
cli # Disable interrupts
|
||||
cld # String operations increment
|
||||
|
||||
# Set up the important data segment registers (DS, ES, SS).
|
||||
xorw %ax,%ax # Segment number zero
|
||||
movw %ax,%ds # -> Data Segment
|
||||
movw %ax,%es # -> Extra Segment
|
||||
movw %ax,%ss # -> Stack Segment
|
||||
|
||||
# Enable A20:
|
||||
# For backwards compatibility with the earliest PCs, physical
|
||||
# address line 20 is tied low, so that addresses higher than
|
||||
# 1MB wrap around to zero by default. This code undoes this.
|
||||
seta20.1:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.1
|
||||
|
||||
movb $0xd1,%al # 0xd1 -> port 0x64
|
||||
outb %al,$0x64
|
||||
|
||||
seta20.2:
|
||||
inb $0x64,%al # Wait for not busy
|
||||
testb $0x2,%al
|
||||
jnz seta20.2
|
||||
|
||||
movb $0xdf,%al # 0xdf -> port 0x60
|
||||
outb %al,$0x60
|
||||
|
||||
# Switch from real to protected mode, using a bootstrap GDT
|
||||
# and segment translation that makes virtual addresses
|
||||
# identical to their physical addresses, so that the
|
||||
# effective memory map does not change during the switch.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE_ON, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Jump to next instruction, but in 32-bit code segment.
|
||||
# Switches processor into 32-bit mode.
|
||||
ljmp $PROT_MODE_CSEG, $protcseg
|
||||
|
||||
.code32 # Assemble for 32-bit mode
|
||||
protcseg:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $PROT_MODE_DSEG, %ax # Our data segment selector
|
||||
movw %ax, %ds # -> DS: Data Segment
|
||||
movw %ax, %es # -> ES: Extra Segment
|
||||
movw %ax, %fs # -> FS
|
||||
movw %ax, %gs # -> GS
|
||||
movw %ax, %ss # -> SS: Stack Segment
|
||||
|
||||
# Set up the stack pointer and call into C.
|
||||
movl $start, %esp
|
||||
call bootmain
|
||||
|
||||
# If bootmain returns (it shouldn't), loop.
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
gdt:
|
||||
SEG_NULL # null seg
|
||||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||
SEG(STA_W, 0x0, 0xffffffff) # data seg
|
||||
|
||||
gdtdesc:
|
||||
.word 0x17 # sizeof(gdt) - 1
|
||||
.long gdt # address gdt
|
||||
|
125
boot/main.c
Normal file
125
boot/main.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
#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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
boot/sign.pl
Normal file
23
boot/sign.pl
Normal file
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
open(BB, $ARGV[0]) || die "open $ARGV[0]: $!";
|
||||
|
||||
binmode BB;
|
||||
my $buf;
|
||||
read(BB, $buf, 1000);
|
||||
$n = length($buf);
|
||||
|
||||
if($n > 510){
|
||||
print STDERR "boot block too large: $n bytes (max 510)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print STDERR "boot block is $n bytes (max 510)\n";
|
||||
|
||||
$buf .= "\0" x (510-$n);
|
||||
$buf .= "\x55\xAA";
|
||||
|
||||
open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
|
||||
binmode BB;
|
||||
print BB $buf;
|
||||
close BB;
|
20
conf/env.mk
Normal file
20
conf/env.mk
Normal file
|
@ -0,0 +1,20 @@
|
|||
# env.mk - configuration variables for the JOS lab
|
||||
|
||||
# '$(V)' controls whether the lab makefiles print verbose commands (the
|
||||
# actual shell commands run by Make), as well as the "overview" commands
|
||||
# (such as '+ cc lib/readline.c').
|
||||
#
|
||||
# For overview commands only, the line should read 'V = @'.
|
||||
# For overview and verbose commands, the line should read 'V ='.
|
||||
V = @
|
||||
|
||||
# If your system-standard GNU toolchain is ELF-compatible, then comment
|
||||
# out the following line to use those tools (as opposed to the i386-jos-elf
|
||||
# tools that the 6.828 make system looks for by default).
|
||||
#
|
||||
# GCCPREFIX=''
|
||||
|
||||
# If the makefile cannot find your QEMU binary, uncomment the
|
||||
# following line and set it to the full path to QEMU.
|
||||
#
|
||||
# QEMU=
|
2
conf/lab.mk
Normal file
2
conf/lab.mk
Normal file
|
@ -0,0 +1,2 @@
|
|||
LAB=1
|
||||
PACKAGEDATE=Thu Aug 30 15:16:04 EDT 2018
|
67
fs/test.c
Normal file
67
fs/test.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include <inc/x86.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
static char *msg = "This is the NEW message of the day!\n\n";
|
||||
|
||||
void
|
||||
fs_test(void)
|
||||
{
|
||||
struct File *f;
|
||||
int r;
|
||||
char *blk;
|
||||
uint32_t *bits;
|
||||
|
||||
// back up bitmap
|
||||
if ((r = sys_page_alloc(0, (void*) PGSIZE, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
bits = (uint32_t*) PGSIZE;
|
||||
memmove(bits, bitmap, PGSIZE);
|
||||
// allocate block
|
||||
if ((r = alloc_block()) < 0)
|
||||
panic("alloc_block: %e", r);
|
||||
// check that block was free
|
||||
assert(bits[r/32] & (1 << (r%32)));
|
||||
// and is not free any more
|
||||
assert(!(bitmap[r/32] & (1 << (r%32))));
|
||||
cprintf("alloc_block is good\n");
|
||||
|
||||
if ((r = file_open("/not-found", &f)) < 0 && r != -E_NOT_FOUND)
|
||||
panic("file_open /not-found: %e", r);
|
||||
else if (r == 0)
|
||||
panic("file_open /not-found succeeded!");
|
||||
if ((r = file_open("/newmotd", &f)) < 0)
|
||||
panic("file_open /newmotd: %e", r);
|
||||
cprintf("file_open is good\n");
|
||||
|
||||
if ((r = file_get_block(f, 0, &blk)) < 0)
|
||||
panic("file_get_block: %e", r);
|
||||
if (strcmp(blk, msg) != 0)
|
||||
panic("file_get_block returned wrong data");
|
||||
cprintf("file_get_block is good\n");
|
||||
|
||||
*(volatile char*)blk = *(volatile char*)blk;
|
||||
assert((uvpt[PGNUM(blk)] & PTE_D));
|
||||
file_flush(f);
|
||||
assert(!(uvpt[PGNUM(blk)] & PTE_D));
|
||||
cprintf("file_flush is good\n");
|
||||
|
||||
if ((r = file_set_size(f, 0)) < 0)
|
||||
panic("file_set_size: %e", r);
|
||||
assert(f->f_direct[0] == 0);
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
cprintf("file_truncate is good\n");
|
||||
|
||||
if ((r = file_set_size(f, strlen(msg))) < 0)
|
||||
panic("file_set_size 2: %e", r);
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
if ((r = file_get_block(f, 0, &blk)) < 0)
|
||||
panic("file_get_block 2: %e", r);
|
||||
strcpy(blk, msg);
|
||||
assert((uvpt[PGNUM(blk)] & PTE_D));
|
||||
file_flush(f);
|
||||
assert(!(uvpt[PGNUM(blk)] & PTE_D));
|
||||
assert(!(uvpt[PGNUM(f)] & PTE_D));
|
||||
cprintf("file rewrite is good\n");
|
||||
}
|
70
fs/testshell.key
Normal file
70
fs/testshell.key
Normal file
|
@ -0,0 +1,70 @@
|
|||
# echo hello world | cat
|
||||
hello world
|
||||
# cat lorem
|
||||
Lorem ipsum dolor sit amet, consectetur
|
||||
adipisicing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit
|
||||
in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim
|
||||
id est laborum.
|
||||
# cat lorem |num
|
||||
1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 adipisicing elit, sed do eiusmod tempor
|
||||
3 incididunt ut labore et dolore magna
|
||||
4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 nostrud exercitation ullamco laboris
|
||||
6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 Duis aute irure dolor in reprehenderit
|
||||
8 in voluptate velit esse cillum dolore eu
|
||||
9 fugiat nulla pariatur. Excepteur sint
|
||||
10 occaecat cupidatat non proident, sunt in
|
||||
11 culpa qui officia deserunt mollit anim
|
||||
12 id est laborum.
|
||||
# cat lorem |num |num |num |num |num
|
||||
1 1 1 1 1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 2 2 2 2 adipisicing elit, sed do eiusmod tempor
|
||||
3 3 3 3 3 incididunt ut labore et dolore magna
|
||||
4 4 4 4 4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 5 5 5 5 nostrud exercitation ullamco laboris
|
||||
6 6 6 6 6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 7 7 7 7 Duis aute irure dolor in reprehenderit
|
||||
8 8 8 8 8 in voluptate velit esse cillum dolore eu
|
||||
9 9 9 9 9 fugiat nulla pariatur. Excepteur sint
|
||||
10 10 10 10 10 occaecat cupidatat non proident, sunt in
|
||||
11 11 11 11 11 culpa qui officia deserunt mollit anim
|
||||
12 12 12 12 12 id est laborum.
|
||||
# lsfd -1
|
||||
fd 0: name testshell.sh isdir 0 size 113 dev file
|
||||
fd 1: name <pipe> isdir 0 size 32 dev pipe
|
||||
fd 3: name <pipe> isdir 0 size 32 dev pipe
|
||||
# cat script
|
||||
echo This is from the script.
|
||||
cat lorem | num | cat
|
||||
echo These are my file descriptors.
|
||||
lsfd -1
|
||||
echo This is the end of the script.
|
||||
# sh <script
|
||||
This is from the script.
|
||||
1 Lorem ipsum dolor sit amet, consectetur
|
||||
2 adipisicing elit, sed do eiusmod tempor
|
||||
3 incididunt ut labore et dolore magna
|
||||
4 aliqua. Ut enim ad minim veniam, quis
|
||||
5 nostrud exercitation ullamco laboris
|
||||
6 nisi ut aliquip ex ea commodo consequat.
|
||||
7 Duis aute irure dolor in reprehenderit
|
||||
8 in voluptate velit esse cillum dolore eu
|
||||
9 fugiat nulla pariatur. Excepteur sint
|
||||
10 occaecat cupidatat non proident, sunt in
|
||||
11 culpa qui officia deserunt mollit anim
|
||||
12 id est laborum.
|
||||
These are my file descriptors.
|
||||
fd 0: name script isdir 0 size 132 dev file
|
||||
fd 1: name <pipe> isdir 0 size 32 dev pipe
|
||||
fd 3: name <pipe> isdir 0 size 32 dev pipe
|
||||
This is the end of the script.
|
44
grade-lab1
Executable file
44
grade-lab1
Executable 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()
|
547
gradelib.py
Normal file
547
gradelib.py
Normal file
|
@ -0,0 +1,547 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import sys, os, re, time, socket, select, subprocess, errno, shutil, traceback
|
||||
from subprocess import check_call, Popen
|
||||
from optparse import OptionParser
|
||||
|
||||
__all__ = []
|
||||
|
||||
##################################################################
|
||||
# Test structure
|
||||
#
|
||||
|
||||
__all__ += ["test", "end_part", "run_tests", "get_current_test"]
|
||||
|
||||
TESTS = []
|
||||
TOTAL = POSSIBLE = 0
|
||||
PART_TOTAL = PART_POSSIBLE = 0
|
||||
CURRENT_TEST = None
|
||||
|
||||
def test(points, title=None, parent=None):
|
||||
"""Decorator for declaring test functions. If title is None, the
|
||||
title of the test will be derived from the function name by
|
||||
stripping the leading "test_" and replacing underscores with
|
||||
spaces."""
|
||||
|
||||
def register_test(fn, title=title):
|
||||
if not title:
|
||||
assert fn.__name__.startswith("test_")
|
||||
title = fn.__name__[5:].replace("_", " ")
|
||||
if parent:
|
||||
title = " " + title
|
||||
|
||||
def run_test():
|
||||
global TOTAL, POSSIBLE, CURRENT_TEST
|
||||
|
||||
# Handle test dependencies
|
||||
if run_test.complete:
|
||||
return
|
||||
run_test.complete = True
|
||||
if parent:
|
||||
parent()
|
||||
|
||||
# Run the test
|
||||
fail = None
|
||||
start = time.time()
|
||||
CURRENT_TEST = run_test
|
||||
sys.stdout.write("%s: " % title)
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
fn()
|
||||
except AssertionError as e:
|
||||
fail = "".join(traceback.format_exception_only(type(e), e))
|
||||
|
||||
# Display and handle test result
|
||||
POSSIBLE += points
|
||||
if points:
|
||||
print("%s" % \
|
||||
(color("red", "FAIL") if fail else color("green", "OK")), end=' ')
|
||||
if time.time() - start > 0.1:
|
||||
print("(%.1fs)" % (time.time() - start), end=' ')
|
||||
print()
|
||||
if fail:
|
||||
print(" %s" % fail.replace("\n", "\n "))
|
||||
else:
|
||||
TOTAL += points
|
||||
for callback in run_test.on_finish:
|
||||
callback(fail)
|
||||
CURRENT_TEST = None
|
||||
|
||||
# Record test metadata on the test wrapper function
|
||||
run_test.__name__ = fn.__name__
|
||||
run_test.title = title
|
||||
run_test.complete = False
|
||||
run_test.on_finish = []
|
||||
TESTS.append(run_test)
|
||||
return run_test
|
||||
return register_test
|
||||
|
||||
def end_part(name):
|
||||
def show_part():
|
||||
global PART_TOTAL, PART_POSSIBLE
|
||||
print("Part %s score: %d/%d" % \
|
||||
(name, TOTAL - PART_TOTAL, POSSIBLE - PART_POSSIBLE))
|
||||
print()
|
||||
PART_TOTAL, PART_POSSIBLE = TOTAL, POSSIBLE
|
||||
show_part.title = ""
|
||||
TESTS.append(show_part)
|
||||
|
||||
def run_tests():
|
||||
"""Set up for testing and run the registered test functions."""
|
||||
|
||||
# Handle command line
|
||||
global options
|
||||
parser = OptionParser(usage="usage: %prog [-v] [filters...]")
|
||||
parser.add_option("-v", "--verbose", action="store_true",
|
||||
help="print commands")
|
||||
parser.add_option("--color", choices=["never", "always", "auto"],
|
||||
default="auto", help="never, always, or auto")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
# Start with a full build to catch build errors
|
||||
make()
|
||||
|
||||
# Clean the file system if there is one
|
||||
reset_fs()
|
||||
|
||||
# Run tests
|
||||
limit = list(map(str.lower, args))
|
||||
try:
|
||||
for test in TESTS:
|
||||
if not limit or any(l in test.title.lower() for l in limit):
|
||||
test()
|
||||
if not limit:
|
||||
print("Score: %d/%d" % (TOTAL, POSSIBLE))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
if TOTAL < POSSIBLE:
|
||||
sys.exit(1)
|
||||
|
||||
def get_current_test():
|
||||
if not CURRENT_TEST:
|
||||
raise RuntimeError("No test is running")
|
||||
return CURRENT_TEST
|
||||
|
||||
##################################################################
|
||||
# Assertions
|
||||
#
|
||||
|
||||
__all__ += ["assert_equal", "assert_lines_match"]
|
||||
|
||||
def assert_equal(got, expect, msg=""):
|
||||
if got == expect:
|
||||
return
|
||||
if msg:
|
||||
msg += "\n"
|
||||
raise AssertionError("%sgot:\n %s\nexpected:\n %s" %
|
||||
(msg, str(got).replace("\n", "\n "),
|
||||
str(expect).replace("\n", "\n ")))
|
||||
|
||||
def assert_lines_match(text, *regexps, **kw):
|
||||
"""Assert that all of regexps match some line in text. If a 'no'
|
||||
keyword argument is given, it must be a list of regexps that must
|
||||
*not* match any line in text."""
|
||||
|
||||
def assert_lines_match_kw(no=[]):
|
||||
return no
|
||||
no = assert_lines_match_kw(**kw)
|
||||
|
||||
# Check text against regexps
|
||||
lines = text.splitlines()
|
||||
good = set()
|
||||
bad = set()
|
||||
for i, line in enumerate(lines):
|
||||
if any(re.match(r, line) for r in regexps):
|
||||
good.add(i)
|
||||
regexps = [r for r in regexps if not re.match(r, line)]
|
||||
if any(re.match(r, line) for r in no):
|
||||
bad.add(i)
|
||||
|
||||
if not regexps and not bad:
|
||||
return
|
||||
|
||||
# We failed; construct an informative failure message
|
||||
show = set()
|
||||
for lineno in good.union(bad):
|
||||
for offset in range(-2, 3):
|
||||
show.add(lineno + offset)
|
||||
if regexps:
|
||||
show.update(n for n in range(len(lines) - 5, len(lines)))
|
||||
|
||||
msg = []
|
||||
last = -1
|
||||
for lineno in sorted(show):
|
||||
if 0 <= lineno < len(lines):
|
||||
if lineno != last + 1:
|
||||
msg.append("...")
|
||||
last = lineno
|
||||
msg.append("%s %s" % (color("red", "BAD ") if lineno in bad else
|
||||
color("green", "GOOD") if lineno in good
|
||||
else " ",
|
||||
lines[lineno]))
|
||||
if last != len(lines) - 1:
|
||||
msg.append("...")
|
||||
if bad:
|
||||
msg.append("unexpected lines in output")
|
||||
for r in regexps:
|
||||
msg.append(color("red", "MISSING") + " '%s'" % r)
|
||||
raise AssertionError("\n".join(msg))
|
||||
|
||||
##################################################################
|
||||
# Utilities
|
||||
#
|
||||
|
||||
__all__ += ["make", "maybe_unlink", "reset_fs", "color"]
|
||||
|
||||
MAKE_TIMESTAMP = 0
|
||||
|
||||
def pre_make():
|
||||
"""Delay prior to running make to ensure file mtimes change."""
|
||||
while int(time.time()) == MAKE_TIMESTAMP:
|
||||
time.sleep(0.1)
|
||||
|
||||
def post_make():
|
||||
"""Record the time after make completes so that the next run of
|
||||
make can be delayed if needed."""
|
||||
global MAKE_TIMESTAMP
|
||||
MAKE_TIMESTAMP = int(time.time())
|
||||
|
||||
def make(*target):
|
||||
pre_make()
|
||||
if Popen(("make",) + target).wait():
|
||||
sys.exit(1)
|
||||
post_make()
|
||||
|
||||
def show_command(cmd):
|
||||
from pipes import quote
|
||||
print("\n$", " ".join(map(quote, cmd)))
|
||||
|
||||
def maybe_unlink(*paths):
|
||||
for path in paths:
|
||||
try:
|
||||
os.unlink(path)
|
||||
except EnvironmentError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
COLORS = {"default": "\033[0m", "red": "\033[31m", "green": "\033[32m"}
|
||||
|
||||
def color(name, text):
|
||||
if options.color == "always" or (options.color == "auto" and os.isatty(1)):
|
||||
return COLORS[name] + text + COLORS["default"]
|
||||
return text
|
||||
|
||||
def reset_fs():
|
||||
if os.path.exists("obj/fs/clean-fs.img"):
|
||||
shutil.copyfile("obj/fs/clean-fs.img", "obj/fs/fs.img")
|
||||
|
||||
##################################################################
|
||||
# Controllers
|
||||
#
|
||||
|
||||
__all__ += ["QEMU", "GDBClient"]
|
||||
|
||||
class QEMU(object):
|
||||
_GDBPORT = None
|
||||
|
||||
def __init__(self, *make_args):
|
||||
# Check that QEMU is not currently running
|
||||
try:
|
||||
GDBClient(self.get_gdb_port(), timeout=0).close()
|
||||
except socket.error:
|
||||
pass
|
||||
else:
|
||||
print("""\
|
||||
GDB stub found on port %d.
|
||||
QEMU appears to already be running. Please exit it if possible or use
|
||||
'killall qemu' or 'killall qemu.real'.""" % self.get_gdb_port(), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if options.verbose:
|
||||
show_command(("make",) + make_args)
|
||||
cmd = ("make", "-s", "--no-print-directory") + make_args
|
||||
self.proc = Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdin=subprocess.PIPE)
|
||||
# Accumulated output as a string
|
||||
self.output = ""
|
||||
# Accumulated output as a bytearray
|
||||
self.outbytes = bytearray()
|
||||
self.on_output = []
|
||||
|
||||
@staticmethod
|
||||
def get_gdb_port():
|
||||
if QEMU._GDBPORT is None:
|
||||
p = Popen(["make", "-s", "--no-print-directory", "print-gdbport"],
|
||||
stdout=subprocess.PIPE)
|
||||
(out, _) = p.communicate()
|
||||
if p.returncode:
|
||||
raise RuntimeError(
|
||||
"Failed to get gdbport: make exited with %d" %
|
||||
p.returncode)
|
||||
QEMU._GDBPORT = int(out)
|
||||
return QEMU._GDBPORT
|
||||
|
||||
def fileno(self):
|
||||
if self.proc:
|
||||
return self.proc.stdout.fileno()
|
||||
|
||||
def handle_read(self):
|
||||
buf = os.read(self.proc.stdout.fileno(), 4096)
|
||||
self.outbytes.extend(buf)
|
||||
self.output = self.outbytes.decode("utf-8", "replace")
|
||||
for callback in self.on_output:
|
||||
callback(buf)
|
||||
if buf == b"":
|
||||
self.wait()
|
||||
return
|
||||
|
||||
def wait(self):
|
||||
if self.proc:
|
||||
self.proc.wait()
|
||||
self.proc = None
|
||||
|
||||
def kill(self):
|
||||
if self.proc:
|
||||
self.proc.terminate()
|
||||
|
||||
class GDBClient(object):
|
||||
def __init__(self, port, timeout=15):
|
||||
start = time.time()
|
||||
while True:
|
||||
self.sock = socket.socket()
|
||||
try:
|
||||
self.sock.settimeout(1)
|
||||
self.sock.connect(("localhost", port))
|
||||
break
|
||||
except socket.error:
|
||||
if time.time() >= start + timeout:
|
||||
raise
|
||||
self.__buf = ""
|
||||
|
||||
def fileno(self):
|
||||
if self.sock:
|
||||
return self.sock.fileno()
|
||||
|
||||
def handle_read(self):
|
||||
try:
|
||||
data = self.sock.recv(4096).decode("ascii", "replace")
|
||||
except socket.error:
|
||||
data = ""
|
||||
if data == "":
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
return
|
||||
self.__buf += data
|
||||
|
||||
while True:
|
||||
m = re.search(r"\$([^#]*)#[0-9a-zA-Z]{2}", self.__buf)
|
||||
if not m:
|
||||
break
|
||||
pkt = m.group(1)
|
||||
self.__buf = self.__buf[m.end():]
|
||||
|
||||
if pkt.startswith("T05"):
|
||||
# Breakpoint
|
||||
raise TerminateTest
|
||||
|
||||
def __send(self, cmd):
|
||||
packet = "$%s#%02x" % (cmd, sum(map(ord, cmd)) % 256)
|
||||
self.sock.sendall(packet.encode("ascii"))
|
||||
|
||||
def __send_break(self):
|
||||
self.sock.sendall(b"\x03")
|
||||
|
||||
def close(self):
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def cont(self):
|
||||
self.__send("c")
|
||||
|
||||
def breakpoint(self, addr):
|
||||
self.__send("Z1,%x,1" % addr)
|
||||
|
||||
|
||||
##################################################################
|
||||
# QEMU test runner
|
||||
#
|
||||
|
||||
__all__ += ["TerminateTest", "Runner"]
|
||||
|
||||
class TerminateTest(Exception):
|
||||
pass
|
||||
|
||||
class Runner():
|
||||
def __init__(self, *default_monitors):
|
||||
self.__default_monitors = default_monitors
|
||||
|
||||
def run_qemu(self, *monitors, **kw):
|
||||
"""Run a QEMU-based test. monitors should functions that will
|
||||
be called with this Runner instance once QEMU and GDB are
|
||||
started. Typically, they should register callbacks that throw
|
||||
TerminateTest when stop events occur. The target_base
|
||||
argument gives the make target to run. The make_args argument
|
||||
should be a list of additional arguments to pass to make. The
|
||||
timeout argument bounds how long to run before returning."""
|
||||
|
||||
def run_qemu_kw(target_base="qemu", make_args=[], timeout=30):
|
||||
return target_base, make_args, timeout
|
||||
target_base, make_args, timeout = run_qemu_kw(**kw)
|
||||
|
||||
# Start QEMU
|
||||
pre_make()
|
||||
self.qemu = QEMU(target_base + "-nox-gdb", *make_args)
|
||||
self.gdb = None
|
||||
|
||||
try:
|
||||
# Wait for QEMU to start or make to fail. This will set
|
||||
# self.gdb if QEMU starts.
|
||||
self.qemu.on_output = [self.__monitor_start]
|
||||
self.__react([self.qemu], timeout=30)
|
||||
self.qemu.on_output = []
|
||||
if self.gdb is None:
|
||||
print("Failed to connect to QEMU; output:")
|
||||
print(self.qemu.output)
|
||||
sys.exit(1)
|
||||
post_make()
|
||||
|
||||
# QEMU and GDB are up
|
||||
self.reactors = [self.qemu, self.gdb]
|
||||
|
||||
# Start monitoring
|
||||
for m in self.__default_monitors + monitors:
|
||||
m(self)
|
||||
|
||||
# Run and react
|
||||
self.gdb.cont()
|
||||
self.__react(self.reactors, timeout)
|
||||
finally:
|
||||
# Shutdown QEMU
|
||||
try:
|
||||
if self.gdb is None:
|
||||
sys.exit(1)
|
||||
self.qemu.kill()
|
||||
self.__react(self.reactors, 5)
|
||||
self.gdb.close()
|
||||
self.qemu.wait()
|
||||
except:
|
||||
print("""\
|
||||
Failed to shutdown QEMU. You might need to 'killall qemu' or
|
||||
'killall qemu.real'.
|
||||
""")
|
||||
raise
|
||||
|
||||
def __monitor_start(self, output):
|
||||
if b"\n" in output:
|
||||
try:
|
||||
self.gdb = GDBClient(self.qemu.get_gdb_port(), timeout=30)
|
||||
raise TerminateTest
|
||||
except socket.error:
|
||||
pass
|
||||
if not len(output):
|
||||
raise TerminateTest
|
||||
|
||||
def __react(self, reactors, timeout):
|
||||
deadline = time.time() + timeout
|
||||
try:
|
||||
while True:
|
||||
timeleft = deadline - time.time()
|
||||
if timeleft < 0:
|
||||
sys.stdout.write("Timeout! ")
|
||||
sys.stdout.flush()
|
||||
return
|
||||
|
||||
rset = [r for r in reactors if r.fileno() is not None]
|
||||
if not rset:
|
||||
return
|
||||
|
||||
rset, _, _ = select.select(rset, [], [], timeleft)
|
||||
for reactor in rset:
|
||||
reactor.handle_read()
|
||||
except TerminateTest:
|
||||
pass
|
||||
|
||||
def user_test(self, binary, *monitors, **kw):
|
||||
"""Run a user test using the specified binary. Monitors and
|
||||
keyword arguments are as for run_qemu. This runs on a disk
|
||||
snapshot unless the keyword argument 'snapshot' is False."""
|
||||
|
||||
maybe_unlink("obj/kern/init.o", "obj/kern/kernel")
|
||||
if kw.pop("snapshot", True):
|
||||
kw.setdefault("make_args", []).append("QEMUEXTRA+=-snapshot")
|
||||
self.run_qemu(target_base="run-%s" % binary, *monitors, **kw)
|
||||
|
||||
def match(self, *args, **kwargs):
|
||||
"""Shortcut to call assert_lines_match on the most recent QEMU
|
||||
output."""
|
||||
|
||||
assert_lines_match(self.qemu.output, *args, **kwargs)
|
||||
|
||||
##################################################################
|
||||
# Monitors
|
||||
#
|
||||
|
||||
__all__ += ["save", "stop_breakpoint", "call_on_line", "stop_on_line"]
|
||||
|
||||
def save(path):
|
||||
"""Return a monitor that writes QEMU's output to path. If the
|
||||
test fails, copy the output to path.test-name."""
|
||||
|
||||
def setup_save(runner):
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
runner.qemu.on_output.append(f.write)
|
||||
get_current_test().on_finish.append(save_on_finish)
|
||||
|
||||
def save_on_finish(fail):
|
||||
f.flush()
|
||||
save_path = path + "." + get_current_test().__name__[5:]
|
||||
if fail:
|
||||
shutil.copyfile(path, save_path)
|
||||
print(" QEMU output saved to %s" % save_path)
|
||||
elif os.path.exists(save_path):
|
||||
os.unlink(save_path)
|
||||
print(" (Old %s failure log removed)" % save_path)
|
||||
|
||||
f = open(path, "wb")
|
||||
return setup_save
|
||||
|
||||
def stop_breakpoint(addr):
|
||||
"""Returns a monitor that stops when addr is reached. addr may be
|
||||
a number or the name of a symbol."""
|
||||
|
||||
def setup_breakpoint(runner):
|
||||
if isinstance(addr, str):
|
||||
addrs = [int(sym[:8], 16) for sym in open("obj/kern/kernel.sym")
|
||||
if sym[11:].strip() == addr]
|
||||
assert len(addrs), "Symbol %s not found" % addr
|
||||
runner.gdb.breakpoint(addrs[0])
|
||||
else:
|
||||
runner.gdb.breakpoint(addr)
|
||||
return setup_breakpoint
|
||||
|
||||
def call_on_line(regexp, callback):
|
||||
"""Returns a monitor that calls 'callback' when QEMU prints a line
|
||||
matching 'regexp'."""
|
||||
|
||||
def setup_call_on_line(runner):
|
||||
buf = bytearray()
|
||||
def handle_output(output):
|
||||
buf.extend(output)
|
||||
while b"\n" in buf:
|
||||
line, buf[:] = buf.split(b"\n", 1)
|
||||
line = line.decode("utf-8", "replace")
|
||||
if re.match(regexp, line):
|
||||
callback(line)
|
||||
runner.qemu.on_output.append(handle_output)
|
||||
return setup_call_on_line
|
||||
|
||||
def stop_on_line(regexp):
|
||||
"""Returns a monitor that stops when QEMU prints a line matching
|
||||
'regexp'."""
|
||||
|
||||
def stop(line):
|
||||
raise TerminateTest
|
||||
return call_on_line(regexp, stop)
|
154
inc/COPYRIGHT
Normal file
154
inc/COPYRIGHT
Normal file
|
@ -0,0 +1,154 @@
|
|||
The files in this directory are:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders. See the file AUTHORS which should have accompanied this software
|
||||
* for a list of all copyright holders.
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by the copyright
|
||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
||||
* the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
queue.h is:
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
stdarg.h is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)stdarg.h 8.1 (Berkeley) 6/10/93
|
||||
*/
|
||||
|
||||
types.h is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1982, 1986, 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
* All or some portions of this file are derived from material licensed
|
||||
* to the University of California by American Telephone and Telegraph
|
||||
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||
* the permission of UNIX System Laboratories, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)types.h 8.4 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
20
inc/assert.h
Normal file
20
inc/assert.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_INC_ASSERT_H
|
||||
#define JOS_INC_ASSERT_H
|
||||
|
||||
#include <inc/stdio.h>
|
||||
|
||||
void _warn(const char*, int, const char*, ...);
|
||||
void _panic(const char*, int, const char*, ...) __attribute__((noreturn));
|
||||
|
||||
#define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define assert(x) \
|
||||
do { if (!(x)) panic("assertion failed: %s", #x); } while (0)
|
||||
|
||||
// static_assert(x) will generate a compile-time error if 'x' is false.
|
||||
#define static_assert(x) switch (x) case 0: case (x):
|
||||
|
||||
#endif /* !JOS_INC_ASSERT_H */
|
65
inc/elf.h
Normal file
65
inc/elf.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef JOS_INC_ELF_H
|
||||
#define JOS_INC_ELF_H
|
||||
|
||||
#define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */
|
||||
|
||||
struct Elf {
|
||||
uint32_t e_magic; // must equal ELF_MAGIC
|
||||
uint8_t e_elf[12];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry;
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct Proghdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_va;
|
||||
uint32_t p_pa;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
struct Secthdr {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint32_t sh_flags;
|
||||
uint32_t sh_addr;
|
||||
uint32_t sh_offset;
|
||||
uint32_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint32_t sh_addralign;
|
||||
uint32_t sh_entsize;
|
||||
};
|
||||
|
||||
// Values for Proghdr::p_type
|
||||
#define ELF_PROG_LOAD 1
|
||||
|
||||
// Flag bits for Proghdr::p_flags
|
||||
#define ELF_PROG_FLAG_EXEC 1
|
||||
#define ELF_PROG_FLAG_WRITE 2
|
||||
#define ELF_PROG_FLAG_READ 4
|
||||
|
||||
// Values for Secthdr::sh_type
|
||||
#define ELF_SHT_NULL 0
|
||||
#define ELF_SHT_PROGBITS 1
|
||||
#define ELF_SHT_SYMTAB 2
|
||||
#define ELF_SHT_STRTAB 3
|
||||
|
||||
// Values for Secthdr::sh_name
|
||||
#define ELF_SHN_UNDEF 0
|
||||
|
||||
#endif /* !JOS_INC_ELF_H */
|
20
inc/error.h
Normal file
20
inc/error.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* 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
|
||||
|
||||
MAXERROR
|
||||
};
|
||||
|
||||
#endif // !JOS_INC_ERROR_H */
|
83
inc/kbdreg.h
Normal file
83
inc/kbdreg.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef JOS_KBDREG_H
|
||||
#define JOS_KBDREG_H
|
||||
|
||||
// Special keycodes
|
||||
#define KEY_HOME 0xE0
|
||||
#define KEY_END 0xE1
|
||||
#define KEY_UP 0xE2
|
||||
#define KEY_DN 0xE3
|
||||
#define KEY_LF 0xE4
|
||||
#define KEY_RT 0xE5
|
||||
#define KEY_PGUP 0xE6
|
||||
#define KEY_PGDN 0xE7
|
||||
#define KEY_INS 0xE8
|
||||
#define KEY_DEL 0xE9
|
||||
|
||||
|
||||
/* This is i8042reg.h + kbdreg.h from NetBSD. */
|
||||
|
||||
#define KBSTATP 0x64 /* kbd controller status port(I) */
|
||||
#define KBS_DIB 0x01 /* kbd data in buffer */
|
||||
#define KBS_IBF 0x02 /* kbd input buffer low */
|
||||
#define KBS_WARM 0x04 /* kbd input buffer low */
|
||||
#define KBS_OCMD 0x08 /* kbd output buffer has command */
|
||||
#define KBS_NOSEC 0x10 /* kbd security lock not engaged */
|
||||
#define KBS_TERR 0x20 /* kbd transmission error or from mouse */
|
||||
#define KBS_RERR 0x40 /* kbd receive error */
|
||||
#define KBS_PERR 0x80 /* kbd parity error */
|
||||
|
||||
#define KBCMDP 0x64 /* kbd controller port(O) */
|
||||
#define KBC_RAMREAD 0x20 /* read from RAM */
|
||||
#define KBC_RAMWRITE 0x60 /* write to RAM */
|
||||
#define KBC_AUXDISABLE 0xa7 /* disable auxiliary port */
|
||||
#define KBC_AUXENABLE 0xa8 /* enable auxiliary port */
|
||||
#define KBC_AUXTEST 0xa9 /* test auxiliary port */
|
||||
#define KBC_KBDECHO 0xd2 /* echo to keyboard port */
|
||||
#define KBC_AUXECHO 0xd3 /* echo to auxiliary port */
|
||||
#define KBC_AUXWRITE 0xd4 /* write to auxiliary port */
|
||||
#define KBC_SELFTEST 0xaa /* start self-test */
|
||||
#define KBC_KBDTEST 0xab /* test keyboard port */
|
||||
#define KBC_KBDDISABLE 0xad /* disable keyboard port */
|
||||
#define KBC_KBDENABLE 0xae /* enable keyboard port */
|
||||
#define KBC_PULSE0 0xfe /* pulse output bit 0 */
|
||||
#define KBC_PULSE1 0xfd /* pulse output bit 1 */
|
||||
#define KBC_PULSE2 0xfb /* pulse output bit 2 */
|
||||
#define KBC_PULSE3 0xf7 /* pulse output bit 3 */
|
||||
|
||||
#define KBDATAP 0x60 /* kbd data port(I) */
|
||||
#define KBOUTP 0x60 /* kbd data port(O) */
|
||||
|
||||
#define K_RDCMDBYTE 0x20
|
||||
#define K_LDCMDBYTE 0x60
|
||||
|
||||
#define KC8_TRANS 0x40 /* convert to old scan codes */
|
||||
#define KC8_MDISABLE 0x20 /* disable mouse */
|
||||
#define KC8_KDISABLE 0x10 /* disable keyboard */
|
||||
#define KC8_IGNSEC 0x08 /* ignore security lock */
|
||||
#define KC8_CPU 0x04 /* exit from protected mode reset */
|
||||
#define KC8_MENABLE 0x02 /* enable mouse interrupt */
|
||||
#define KC8_KENABLE 0x01 /* enable keyboard interrupt */
|
||||
#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE)
|
||||
|
||||
/* keyboard commands */
|
||||
#define KBC_RESET 0xFF /* reset the keyboard */
|
||||
#define KBC_RESEND 0xFE /* request the keyboard resend the last byte */
|
||||
#define KBC_SETDEFAULT 0xF6 /* resets keyboard to its power-on defaults */
|
||||
#define KBC_DISABLE 0xF5 /* as per KBC_SETDEFAULT, but also disable key scanning */
|
||||
#define KBC_ENABLE 0xF4 /* enable key scanning */
|
||||
#define KBC_TYPEMATIC 0xF3 /* set typematic rate and delay */
|
||||
#define KBC_SETTABLE 0xF0 /* set scancode translation table */
|
||||
#define KBC_MODEIND 0xED /* set mode indicators(i.e. LEDs) */
|
||||
#define KBC_ECHO 0xEE /* request an echo from the keyboard */
|
||||
|
||||
/* keyboard responses */
|
||||
#define KBR_EXTENDED 0xE0 /* extended key sequence */
|
||||
#define KBR_RESEND 0xFE /* needs resend of command */
|
||||
#define KBR_ACK 0xFA /* received a valid command */
|
||||
#define KBR_OVERRUN 0x00 /* flooded */
|
||||
#define KBR_FAILURE 0xFD /* diagnosic failure */
|
||||
#define KBR_BREAK 0xF0 /* break code prefix - sent on key release */
|
||||
#define KBR_RSTDONE 0xAA /* reset complete */
|
||||
#define KBR_ECHO 0xEE /* echo response */
|
||||
|
||||
#endif /* !JOS_KBDREG_H */
|
147
inc/memlayout.h
Normal file
147
inc/memlayout.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
#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)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef uint32_t pte_t;
|
||||
typedef uint32_t pde_t;
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* !JOS_INC_MEMLAYOUT_H */
|
317
inc/mmu.h
Normal file
317
inc/mmu.h
Normal file
|
@ -0,0 +1,317 @@
|
|||
#ifndef JOS_INC_MMU_H
|
||||
#define JOS_INC_MMU_H
|
||||
|
||||
/*
|
||||
* This file contains definitions for the x86 memory management unit (MMU),
|
||||
* including paging- and segmentation-related data structures and constants,
|
||||
* the %cr0, %cr4, and %eflags registers, and traps.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 1. Paging data structures and constants.
|
||||
*
|
||||
*/
|
||||
|
||||
// A linear address 'la' has a three-part structure as follows:
|
||||
//
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
// | Page Directory | Page Table | Offset within Page |
|
||||
// | Index | Index | |
|
||||
// +----------------+----------------+---------------------+
|
||||
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
|
||||
// \---------- PGNUM(la) ----------/
|
||||
//
|
||||
// The PDX, PTX, PGOFF, and PGNUM macros decompose linear addresses as shown.
|
||||
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
|
||||
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
|
||||
|
||||
// page number field of address
|
||||
#define PGNUM(la) (((uintptr_t) (la)) >> PTXSHIFT)
|
||||
|
||||
// page directory index
|
||||
#define PDX(la) ((((uintptr_t) (la)) >> PDXSHIFT) & 0x3FF)
|
||||
|
||||
// page table index
|
||||
#define PTX(la) ((((uintptr_t) (la)) >> PTXSHIFT) & 0x3FF)
|
||||
|
||||
// offset in page
|
||||
#define PGOFF(la) (((uintptr_t) (la)) & 0xFFF)
|
||||
|
||||
// construct linear address from indexes and offset
|
||||
#define PGADDR(d, t, o) ((void*) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
|
||||
// Page directory and page table constants.
|
||||
#define NPDENTRIES 1024 // page directory entries per page directory
|
||||
#define NPTENTRIES 1024 // page table entries per page table
|
||||
|
||||
#define PGSIZE 4096 // bytes mapped by a page
|
||||
#define PGSHIFT 12 // log2(PGSIZE)
|
||||
|
||||
#define PTSIZE (PGSIZE*NPTENTRIES) // bytes mapped by a page directory entry
|
||||
#define PTSHIFT 22 // log2(PTSIZE)
|
||||
|
||||
#define PTXSHIFT 12 // offset of PTX in a linear address
|
||||
#define PDXSHIFT 22 // offset of PDX in a linear address
|
||||
|
||||
// Page table/directory entry flags.
|
||||
#define PTE_P 0x001 // Present
|
||||
#define PTE_W 0x002 // Writeable
|
||||
#define PTE_U 0x004 // User
|
||||
#define PTE_PWT 0x008 // Write-Through
|
||||
#define PTE_PCD 0x010 // Cache-Disable
|
||||
#define PTE_A 0x020 // Accessed
|
||||
#define PTE_D 0x040 // Dirty
|
||||
#define PTE_PS 0x080 // Page Size
|
||||
#define PTE_G 0x100 // Global
|
||||
|
||||
// The PTE_AVAIL bits aren't used by the kernel or interpreted by the
|
||||
// hardware, so user processes are allowed to set them arbitrarily.
|
||||
#define PTE_AVAIL 0xE00 // Available for software use
|
||||
|
||||
// Flags in PTE_SYSCALL may be used in system calls. (Others may not.)
|
||||
#define PTE_SYSCALL (PTE_AVAIL | PTE_P | PTE_W | PTE_U)
|
||||
|
||||
// Address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((physaddr_t) (pte) & ~0xFFF)
|
||||
|
||||
// Control Register flags
|
||||
#define CR0_PE 0x00000001 // Protection Enable
|
||||
#define CR0_MP 0x00000002 // Monitor coProcessor
|
||||
#define CR0_EM 0x00000004 // Emulation
|
||||
#define CR0_TS 0x00000008 // Task Switched
|
||||
#define CR0_ET 0x00000010 // Extension Type
|
||||
#define CR0_NE 0x00000020 // Numeric Errror
|
||||
#define CR0_WP 0x00010000 // Write Protect
|
||||
#define CR0_AM 0x00040000 // Alignment Mask
|
||||
#define CR0_NW 0x20000000 // Not Writethrough
|
||||
#define CR0_CD 0x40000000 // Cache Disable
|
||||
#define CR0_PG 0x80000000 // Paging
|
||||
|
||||
#define CR4_PCE 0x00000100 // Performance counter enable
|
||||
#define CR4_MCE 0x00000040 // Machine Check Enable
|
||||
#define CR4_PSE 0x00000010 // Page Size Extensions
|
||||
#define CR4_DE 0x00000008 // Debugging Extensions
|
||||
#define CR4_TSD 0x00000004 // Time Stamp Disable
|
||||
#define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts
|
||||
#define CR4_VME 0x00000001 // V86 Mode Extensions
|
||||
|
||||
// Eflags register
|
||||
#define FL_CF 0x00000001 // Carry Flag
|
||||
#define FL_PF 0x00000004 // Parity Flag
|
||||
#define FL_AF 0x00000010 // Auxiliary carry Flag
|
||||
#define FL_ZF 0x00000040 // Zero Flag
|
||||
#define FL_SF 0x00000080 // Sign Flag
|
||||
#define FL_TF 0x00000100 // Trap Flag
|
||||
#define FL_IF 0x00000200 // Interrupt Flag
|
||||
#define FL_DF 0x00000400 // Direction Flag
|
||||
#define FL_OF 0x00000800 // Overflow Flag
|
||||
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
|
||||
#define FL_IOPL_0 0x00000000 // IOPL == 0
|
||||
#define FL_IOPL_1 0x00001000 // IOPL == 1
|
||||
#define FL_IOPL_2 0x00002000 // IOPL == 2
|
||||
#define FL_IOPL_3 0x00003000 // IOPL == 3
|
||||
#define FL_NT 0x00004000 // Nested Task
|
||||
#define FL_RF 0x00010000 // Resume Flag
|
||||
#define FL_VM 0x00020000 // Virtual 8086 mode
|
||||
#define FL_AC 0x00040000 // Alignment Check
|
||||
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
|
||||
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
|
||||
#define FL_ID 0x00200000 // ID flag
|
||||
|
||||
// Page fault error codes
|
||||
#define FEC_PR 0x1 // Page fault caused by protection violation
|
||||
#define FEC_WR 0x2 // Page fault caused by a write
|
||||
#define FEC_U 0x4 // Page fault occured while in user mode
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 2. Segmentation data structures and constants.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __ASSEMBLER__
|
||||
|
||||
/*
|
||||
* Macros to build GDT entries in assembly.
|
||||
*/
|
||||
#define SEG_NULL \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
#define SEG(type,base,lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
#else // not __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Segment Descriptors
|
||||
struct Segdesc {
|
||||
unsigned sd_lim_15_0 : 16; // Low bits of segment limit
|
||||
unsigned sd_base_15_0 : 16; // Low bits of segment base address
|
||||
unsigned sd_base_23_16 : 8; // Middle bits of segment base address
|
||||
unsigned sd_type : 4; // Segment type (see STS_ constants)
|
||||
unsigned sd_s : 1; // 0 = system, 1 = application
|
||||
unsigned sd_dpl : 2; // Descriptor Privilege Level
|
||||
unsigned sd_p : 1; // Present
|
||||
unsigned sd_lim_19_16 : 4; // High bits of segment limit
|
||||
unsigned sd_avl : 1; // Unused (available for software use)
|
||||
unsigned sd_rsv1 : 1; // Reserved
|
||||
unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||
unsigned sd_g : 1; // Granularity: limit scaled by 4K when set
|
||||
unsigned sd_base_31_24 : 8; // High bits of segment base address
|
||||
};
|
||||
// Null segment
|
||||
#define SEG_NULL { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
// Segment that is loadable but faults when used
|
||||
#define SEG_FAULT { 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }
|
||||
// Normal segment
|
||||
#define SEG(type, base, lim, dpl) \
|
||||
{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \
|
||||
type, 1, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1, \
|
||||
(unsigned) (base) >> 24 }
|
||||
#define SEG16(type, base, lim, dpl) (struct Segdesc) \
|
||||
{ (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \
|
||||
type, 1, dpl, 1, (unsigned) (lim) >> 16, 0, 0, 1, 0, \
|
||||
(unsigned) (base) >> 24 }
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
// Application segment type bits
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_E 0x4 // Expand down (non-executable segments)
|
||||
#define STA_C 0x4 // Conforming code segment (executable only)
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
// System segment type bits
|
||||
#define STS_T16A 0x1 // Available 16-bit TSS
|
||||
#define STS_LDT 0x2 // Local Descriptor Table
|
||||
#define STS_T16B 0x3 // Busy 16-bit TSS
|
||||
#define STS_CG16 0x4 // 16-bit Call Gate
|
||||
#define STS_TG 0x5 // Task Gate / Coum Transmitions
|
||||
#define STS_IG16 0x6 // 16-bit Interrupt Gate
|
||||
#define STS_TG16 0x7 // 16-bit Trap Gate
|
||||
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||
#define STS_T32B 0xB // Busy 32-bit TSS
|
||||
#define STS_CG32 0xC // 32-bit Call Gate
|
||||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Part 3. Traps.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
// Task state segment format (as described by the Pentium architecture book)
|
||||
struct Taskstate {
|
||||
uint32_t ts_link; // Old ts selector
|
||||
uintptr_t ts_esp0; // Stack pointers and segment selectors
|
||||
uint16_t ts_ss0; // after an increase in privilege level
|
||||
uint16_t ts_padding1;
|
||||
uintptr_t ts_esp1;
|
||||
uint16_t ts_ss1;
|
||||
uint16_t ts_padding2;
|
||||
uintptr_t ts_esp2;
|
||||
uint16_t ts_ss2;
|
||||
uint16_t ts_padding3;
|
||||
physaddr_t ts_cr3; // Page directory base
|
||||
uintptr_t ts_eip; // Saved state from last task switch
|
||||
uint32_t ts_eflags;
|
||||
uint32_t ts_eax; // More saved state (registers)
|
||||
uint32_t ts_ecx;
|
||||
uint32_t ts_edx;
|
||||
uint32_t ts_ebx;
|
||||
uintptr_t ts_esp;
|
||||
uintptr_t ts_ebp;
|
||||
uint32_t ts_esi;
|
||||
uint32_t ts_edi;
|
||||
uint16_t ts_es; // Even more saved state (segment selectors)
|
||||
uint16_t ts_padding4;
|
||||
uint16_t ts_cs;
|
||||
uint16_t ts_padding5;
|
||||
uint16_t ts_ss;
|
||||
uint16_t ts_padding6;
|
||||
uint16_t ts_ds;
|
||||
uint16_t ts_padding7;
|
||||
uint16_t ts_fs;
|
||||
uint16_t ts_padding8;
|
||||
uint16_t ts_gs;
|
||||
uint16_t ts_padding9;
|
||||
uint16_t ts_ldt;
|
||||
uint16_t ts_padding10;
|
||||
uint16_t ts_t; // Trap on task switch
|
||||
uint16_t ts_iomb; // I/O map base address
|
||||
};
|
||||
|
||||
// Gate descriptors for interrupts and traps
|
||||
struct Gatedesc {
|
||||
unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment
|
||||
unsigned gd_sel : 16; // segment selector
|
||||
unsigned gd_args : 5; // # args, 0 for interrupt/trap gates
|
||||
unsigned gd_rsv1 : 3; // reserved(should be zero I guess)
|
||||
unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})
|
||||
unsigned gd_s : 1; // must be 0 (system)
|
||||
unsigned gd_dpl : 2; // descriptor(meaning new) privilege level
|
||||
unsigned gd_p : 1; // Present
|
||||
unsigned gd_off_31_16 : 16; // high bits of offset in segment
|
||||
};
|
||||
|
||||
// Set up a normal interrupt/trap gate descriptor.
|
||||
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
|
||||
// see section 9.6.1.3 of the i386 reference: "The difference between
|
||||
// an interrupt gate and a trap gate is in the effect on IF (the
|
||||
// interrupt-enable flag). An interrupt that vectors through an
|
||||
// interrupt gate resets IF, thereby preventing other interrupts from
|
||||
// interfering with the current interrupt handler. A subsequent IRET
|
||||
// instruction restores IF to the value in the EFLAGS image on the
|
||||
// stack. An interrupt through a trap gate does not change IF."
|
||||
// - sel: Code segment selector for interrupt/trap handler
|
||||
// - off: Offset in code segment for interrupt/trap handler
|
||||
// - dpl: Descriptor Privilege Level -
|
||||
// the privilege level required for software to invoke
|
||||
// this interrupt/trap gate explicitly using an int instruction.
|
||||
#define SETGATE(gate, istrap, sel, off, dpl) \
|
||||
{ \
|
||||
(gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \
|
||||
(gate).gd_sel = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t) (off) >> 16; \
|
||||
}
|
||||
|
||||
// Set up a call gate descriptor.
|
||||
#define SETCALLGATE(gate, sel, off, dpl) \
|
||||
{ \
|
||||
(gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \
|
||||
(gate).gd_sel = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = STS_CG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t) (off) >> 16; \
|
||||
}
|
||||
|
||||
// Pseudo-descriptors used for LGDT, LLDT and LIDT instructions.
|
||||
struct Pseudodesc {
|
||||
uint16_t pd_lim; // Limit
|
||||
uint32_t pd_base; // Base address
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !JOS_INC_MMU_H */
|
51
inc/stab.h
Normal file
51
inc/stab.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#ifndef JOS_STAB_H
|
||||
#define JOS_STAB_H
|
||||
#include <inc/types.h>
|
||||
|
||||
// <inc/stab.h>
|
||||
// STABS debugging info
|
||||
|
||||
// The JOS kernel debugger can understand some debugging information
|
||||
// in the STABS format. For more information on this format, see
|
||||
// http://sourceware.org/gdb/onlinedocs/stabs.html
|
||||
|
||||
// The constants below define some symbol types used by various debuggers
|
||||
// and compilers. JOS uses the N_SO, N_SOL, N_FUN, and N_SLINE types.
|
||||
|
||||
#define N_GSYM 0x20 // global symbol
|
||||
#define N_FNAME 0x22 // F77 function name
|
||||
#define N_FUN 0x24 // procedure name
|
||||
#define N_STSYM 0x26 // data segment variable
|
||||
#define N_LCSYM 0x28 // bss segment variable
|
||||
#define N_MAIN 0x2a // main function name
|
||||
#define N_PC 0x30 // global Pascal symbol
|
||||
#define N_RSYM 0x40 // register variable
|
||||
#define N_SLINE 0x44 // text segment line number
|
||||
#define N_DSLINE 0x46 // data segment line number
|
||||
#define N_BSLINE 0x48 // bss segment line number
|
||||
#define N_SSYM 0x60 // structure/union element
|
||||
#define N_SO 0x64 // main source file name
|
||||
#define N_LSYM 0x80 // stack variable
|
||||
#define N_BINCL 0x82 // include file beginning
|
||||
#define N_SOL 0x84 // included source file name
|
||||
#define N_PSYM 0xa0 // parameter variable
|
||||
#define N_EINCL 0xa2 // include file end
|
||||
#define N_ENTRY 0xa4 // alternate entry point
|
||||
#define N_LBRAC 0xc0 // left bracket
|
||||
#define N_EXCL 0xc2 // deleted include file
|
||||
#define N_RBRAC 0xe0 // right bracket
|
||||
#define N_BCOMM 0xe2 // begin common
|
||||
#define N_ECOMM 0xe4 // end common
|
||||
#define N_ECOML 0xe8 // end common (local name)
|
||||
#define N_LENG 0xfe // length of preceding entry
|
||||
|
||||
// Entries in the STABS table are formatted as follows.
|
||||
struct Stab {
|
||||
uint32_t n_strx; // index into string table of name
|
||||
uint8_t n_type; // type of symbol
|
||||
uint8_t n_other; // misc info (usually empty)
|
||||
uint16_t n_desc; // description field
|
||||
uintptr_t n_value; // value of symbol
|
||||
};
|
||||
|
||||
#endif /* !JOS_STAB_H */
|
14
inc/stdarg.h
Normal file
14
inc/stdarg.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* $NetBSD: stdarg.h,v 1.12 1995/12/25 23:15:31 mycroft Exp $ */
|
||||
|
||||
#ifndef JOS_INC_STDARG_H
|
||||
#define JOS_INC_STDARG_H
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(ap, last) __builtin_va_start(ap, last)
|
||||
|
||||
#define va_arg(ap, type) __builtin_va_arg(ap, type)
|
||||
|
||||
#define va_end(ap) __builtin_va_end(ap)
|
||||
|
||||
#endif /* !JOS_INC_STDARG_H */
|
33
inc/stdio.h
Normal file
33
inc/stdio.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef JOS_INC_STDIO_H
|
||||
#define JOS_INC_STDIO_H
|
||||
|
||||
#include <inc/stdarg.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *) 0)
|
||||
#endif /* !NULL */
|
||||
|
||||
// lib/console.c
|
||||
void cputchar(int c);
|
||||
int getchar(void);
|
||||
int iscons(int fd);
|
||||
|
||||
// lib/printfmt.c
|
||||
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
|
||||
void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list);
|
||||
int snprintf(char *str, int size, const char *fmt, ...);
|
||||
int vsnprintf(char *str, int size, const char *fmt, va_list);
|
||||
|
||||
// lib/printf.c
|
||||
int cprintf(const char *fmt, ...);
|
||||
int vcprintf(const char *fmt, va_list);
|
||||
|
||||
// lib/fprintf.c
|
||||
int printf(const char *fmt, ...);
|
||||
int fprintf(int fd, const char *fmt, ...);
|
||||
int vfprintf(int fd, const char *fmt, va_list);
|
||||
|
||||
// lib/readline.c
|
||||
char* readline(const char *prompt);
|
||||
|
||||
#endif /* !JOS_INC_STDIO_H */
|
25
inc/string.h
Normal file
25
inc/string.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef JOS_INC_STRING_H
|
||||
#define JOS_INC_STRING_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
int strlen(const char *s);
|
||||
int strnlen(const char *s, size_t size);
|
||||
char * strcpy(char *dst, const char *src);
|
||||
char * strncpy(char *dst, const char *src, size_t size);
|
||||
char * strcat(char *dst, const char *src);
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
int strncmp(const char *s1, const char *s2, size_t size);
|
||||
char * strchr(const char *s, char c);
|
||||
char * strfind(const char *s, char c);
|
||||
|
||||
void * memset(void *dst, int c, size_t len);
|
||||
void * memcpy(void *dst, const void *src, size_t len);
|
||||
void * memmove(void *dst, const void *src, size_t len);
|
||||
int memcmp(const void *s1, const void *s2, size_t len);
|
||||
void * memfind(const void *s, int c, size_t len);
|
||||
|
||||
long strtol(const char *s, char **endptr, int base);
|
||||
|
||||
#endif /* not JOS_INC_STRING_H */
|
75
inc/types.h
Normal file
75
inc/types.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef JOS_INC_TYPES_H
|
||||
#define JOS_INC_TYPES_H
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*) 0)
|
||||
#endif
|
||||
|
||||
// Represents true-or-false values
|
||||
typedef _Bool bool;
|
||||
enum { false, true };
|
||||
|
||||
// Explicitly-sized versions of integer types
|
||||
typedef __signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
// Pointers and addresses are 32 bits long.
|
||||
// We use pointer types to represent virtual addresses,
|
||||
// uintptr_t to represent the numerical values of virtual addresses,
|
||||
// and physaddr_t to represent physical addresses.
|
||||
typedef int32_t intptr_t;
|
||||
typedef uint32_t uintptr_t;
|
||||
typedef uint32_t physaddr_t;
|
||||
|
||||
// Page numbers are 32 bits long.
|
||||
typedef uint32_t ppn_t;
|
||||
|
||||
// size_t is used for memory object sizes.
|
||||
typedef uint32_t size_t;
|
||||
// ssize_t is a signed version of ssize_t, used in case there might be an
|
||||
// error return.
|
||||
typedef int32_t ssize_t;
|
||||
|
||||
// off_t is used for file offsets and lengths.
|
||||
typedef int32_t off_t;
|
||||
|
||||
// Efficient min and max operations
|
||||
#define MIN(_a, _b) \
|
||||
({ \
|
||||
typeof(_a) __a = (_a); \
|
||||
typeof(_b) __b = (_b); \
|
||||
__a <= __b ? __a : __b; \
|
||||
})
|
||||
#define MAX(_a, _b) \
|
||||
({ \
|
||||
typeof(_a) __a = (_a); \
|
||||
typeof(_b) __b = (_b); \
|
||||
__a >= __b ? __a : __b; \
|
||||
})
|
||||
|
||||
// Rounding operations (efficient when n is a power of 2)
|
||||
// Round down to the nearest multiple of n
|
||||
#define ROUNDDOWN(a, n) \
|
||||
({ \
|
||||
uint32_t __a = (uint32_t) (a); \
|
||||
(typeof(a)) (__a - __a % (n)); \
|
||||
})
|
||||
// Round up to the nearest multiple of n
|
||||
#define ROUNDUP(a, n) \
|
||||
({ \
|
||||
uint32_t __n = (uint32_t) (n); \
|
||||
(typeof(a)) (ROUNDDOWN((uint32_t) (a) + __n - 1, __n)); \
|
||||
})
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
// Return the offset of 'member' relative to the beginning of a struct type
|
||||
#define offsetof(type, member) ((size_t) (&((type*)0)->member))
|
||||
|
||||
#endif /* !JOS_INC_TYPES_H */
|
264
inc/x86.h
Normal file
264
inc/x86.h
Normal file
|
@ -0,0 +1,264 @@
|
|||
#ifndef JOS_INC_X86_H
|
||||
#define JOS_INC_X86_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
static inline void
|
||||
breakpoint(void)
|
||||
{
|
||||
asm volatile("int3");
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
inb(int port)
|
||||
{
|
||||
uint8_t data;
|
||||
asm volatile("inb %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insb(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsb"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
inw(int port)
|
||||
{
|
||||
uint16_t data;
|
||||
asm volatile("inw %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insw(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsw"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
inl(int port)
|
||||
{
|
||||
uint32_t data;
|
||||
asm volatile("inl %w1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insl(int port, void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\tinsl"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outb(int port, uint8_t data)
|
||||
{
|
||||
asm volatile("outb %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsb(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsb"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outw(int port, uint16_t data)
|
||||
{
|
||||
asm volatile("outw %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsw(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsw"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsl(int port, const void *addr, int cnt)
|
||||
{
|
||||
asm volatile("cld\n\trepne\n\toutsl"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outl(int port, uint32_t data)
|
||||
{
|
||||
asm volatile("outl %0,%w1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
invlpg(void *addr)
|
||||
{
|
||||
asm volatile("invlpg (%0)" : : "r" (addr) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
lidt(void *p)
|
||||
{
|
||||
asm volatile("lidt (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lgdt(void *p)
|
||||
{
|
||||
asm volatile("lgdt (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lldt(uint16_t sel)
|
||||
{
|
||||
asm volatile("lldt %0" : : "r" (sel));
|
||||
}
|
||||
|
||||
static inline void
|
||||
ltr(uint16_t sel)
|
||||
{
|
||||
asm volatile("ltr %0" : : "r" (sel));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr0(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr0" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr0(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr0,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr2(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr2,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr3(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr3" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr3(void)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("movl %%cr3,%0" : "=r" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr4(uint32_t val)
|
||||
{
|
||||
asm volatile("movl %0,%%cr4" : : "r" (val));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rcr4(void)
|
||||
{
|
||||
uint32_t cr4;
|
||||
asm volatile("movl %%cr4,%0" : "=r" (cr4));
|
||||
return cr4;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tlbflush(void)
|
||||
{
|
||||
uint32_t cr3;
|
||||
asm volatile("movl %%cr3,%0" : "=r" (cr3));
|
||||
asm volatile("movl %0,%%cr3" : : "r" (cr3));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_eflags(void)
|
||||
{
|
||||
uint32_t eflags;
|
||||
asm volatile("pushfl; popl %0" : "=r" (eflags));
|
||||
return eflags;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_eflags(uint32_t eflags)
|
||||
{
|
||||
asm volatile("pushl %0; popfl" : : "r" (eflags));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_ebp(void)
|
||||
{
|
||||
uint32_t ebp;
|
||||
asm volatile("movl %%ebp,%0" : "=r" (ebp));
|
||||
return ebp;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_esp(void)
|
||||
{
|
||||
uint32_t esp;
|
||||
asm volatile("movl %%esp,%0" : "=r" (esp));
|
||||
return esp;
|
||||
}
|
||||
|
||||
static inline void
|
||||
cpuid(uint32_t info, uint32_t *eaxp, uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
asm volatile("cpuid"
|
||||
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
||||
: "a" (info));
|
||||
if (eaxp)
|
||||
*eaxp = eax;
|
||||
if (ebxp)
|
||||
*ebxp = ebx;
|
||||
if (ecxp)
|
||||
*ecxp = ecx;
|
||||
if (edxp)
|
||||
*edxp = edx;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
read_tsc(void)
|
||||
{
|
||||
uint64_t tsc;
|
||||
asm volatile("rdtsc" : "=A" (tsc));
|
||||
return tsc;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
xchg(volatile uint32_t *addr, uint32_t newval)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
// The + in "+m" denotes a read-modify-write operand.
|
||||
asm volatile("lock; xchgl %0, %1"
|
||||
: "+m" (*addr), "=a" (result)
|
||||
: "1" (newval)
|
||||
: "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* !JOS_INC_X86_H */
|
155
kern/COPYRIGHT
Normal file
155
kern/COPYRIGHT
Normal file
|
@ -0,0 +1,155 @@
|
|||
Most of the source files in this directory are derived from the Exokernel,
|
||||
which is:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders. See the file AUTHORS which should have accompanied this software
|
||||
* for a list of all copyright holders.
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by the copyright
|
||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
||||
* the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
Console.c was created consulting the NetBSD pccons driver which is:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved.
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* William Jolitz and Don Ahn.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
Kclock.h, sched.h, and printf.h are copyright:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1998 Exotec, Inc.
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with Exotec, Inc..
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by Exotec, Inc. The rest
|
||||
* of this file is covered by the copyright notices, if any, listed below.
|
||||
*/
|
||||
|
||||
Printf.c is copyright:
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1986, 1988, 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
* All or some portions of this file are derived from material licensed
|
||||
* to the University of California by American Telephone and Telegraph
|
||||
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||
* the permission of UNIX System Laboratories, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
89
kern/Makefrag
Normal file
89
kern/Makefrag
Normal file
|
@ -0,0 +1,89 @@
|
|||
#
|
||||
# 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
|
||||
|
||||
# Only build files if they exist.
|
||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
||||
|
||||
# Binary program images to embed within the kernel.
|
||||
KERN_BINFILES :=
|
||||
|
||||
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 $^ $@
|
476
kern/console.c
Normal file
476
kern/console.c
Normal file
|
@ -0,0 +1,476 @@
|
|||
/* 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>
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** 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
kern/console.h
Normal file
26
kern/console.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef _CONSOLE_H_
|
||||
#define _CONSOLE_H_
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
#define MONO_BASE 0x3B4
|
||||
#define MONO_BUF 0xB0000
|
||||
#define CGA_BASE 0x3D4
|
||||
#define CGA_BUF 0xB8000
|
||||
|
||||
#define CRT_ROWS 25
|
||||
#define CRT_COLS 80
|
||||
#define CRT_SIZE (CRT_ROWS * CRT_COLS)
|
||||
|
||||
void cons_init(void);
|
||||
int cons_getc(void);
|
||||
|
||||
void kbd_intr(void); // irq 1
|
||||
void serial_intr(void); // irq 4
|
||||
|
||||
#endif /* _CONSOLE_H_ */
|
96
kern/entry.S
Normal file
96
kern/entry.S
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.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
kern/entrypgdir.c
Normal file
1059
kern/entrypgdir.c
Normal file
File diff suppressed because it is too large
Load Diff
92
kern/init.c
Normal file
92
kern/init.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* 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>
|
||||
|
||||
// Test the stack backtrace function (lab 1 only)
|
||||
void
|
||||
test_backtrace(int x)
|
||||
{
|
||||
cprintf("entering test_backtrace %d\n", x);
|
||||
if (x > 0)
|
||||
test_backtrace(x-1);
|
||||
else
|
||||
mon_backtrace(0, 0, 0);
|
||||
cprintf("leaving test_backtrace %d\n", x);
|
||||
}
|
||||
|
||||
void
|
||||
i386_init(void)
|
||||
{
|
||||
extern char edata[], end[];
|
||||
|
||||
// Before doing anything else, complete the ELF loading process.
|
||||
// Clear the uninitialized global data (BSS) section of our program.
|
||||
// This ensures that all static/global variables start out zero.
|
||||
memset(edata, 0, end - edata);
|
||||
|
||||
// Initialize the console.
|
||||
// Can't call cprintf until after we do this!
|
||||
cons_init();
|
||||
|
||||
cprintf("6828 decimal is %o octal!\n", 6828);
|
||||
|
||||
// Test the stack backtrace function (lab 1 only)
|
||||
test_backtrace(5);
|
||||
|
||||
// Drop into the kernel monitor.
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 at %s:%d: ", 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);
|
||||
}
|
206
kern/kdebug.c
Normal file
206
kern/kdebug.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
#include <inc/stab.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/kdebug.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
|
||||
|
||||
|
||||
// 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 {
|
||||
// Can't search for user-level addresses yet!
|
||||
panic("User address");
|
||||
}
|
||||
|
||||
// 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
kern/kdebug.h
Normal file
20
kern/kdebug.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef JOS_KERN_KDEBUG_H
|
||||
#define JOS_KERN_KDEBUG_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Debug information about a particular instruction pointer
|
||||
struct Eipdebuginfo {
|
||||
const char *eip_file; // Source code filename for EIP
|
||||
int eip_line; // Source code linenumber for EIP
|
||||
|
||||
const char *eip_fn_name; // Name of function containing EIP
|
||||
// - Note: not null terminated!
|
||||
int eip_fn_namelen; // Length of function name
|
||||
uintptr_t eip_fn_addr; // Address of start of function
|
||||
int eip_fn_narg; // Number of function arguments
|
||||
};
|
||||
|
||||
int debuginfo_eip(uintptr_t eip, struct Eipdebuginfo *info);
|
||||
|
||||
#endif
|
61
kern/kernel.ld
Normal file
61
kern/kernel.ld
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* 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)
|
||||
}
|
||||
|
||||
PROVIDE(edata = .);
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack)
|
||||
}
|
||||
}
|
125
kern/monitor.c
Normal file
125
kern/monitor.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
// 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>
|
||||
|
||||
#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");
|
||||
|
||||
|
||||
while (1) {
|
||||
buf = readline("K> ");
|
||||
if (buf != NULL)
|
||||
if (runcmd(buf, tf) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
19
kern/monitor.h
Normal file
19
kern/monitor.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef JOS_KERN_MONITOR_H
|
||||
#define JOS_KERN_MONITOR_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
struct Trapframe;
|
||||
|
||||
// Activate the kernel monitor,
|
||||
// optionally providing a trap frame indicating the current state
|
||||
// (NULL if none).
|
||||
void monitor(struct Trapframe *tf);
|
||||
|
||||
// Functions implementing monitor commands.
|
||||
int mon_help(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_kerninfo(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
|
||||
|
||||
#endif // !JOS_KERN_MONITOR_H
|
37
kern/printf.c
Normal file
37
kern/printf.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Simple implementation of cprintf console output for the kernel,
|
||||
// based on printfmt() and the kernel console's cputchar().
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
|
||||
|
||||
static void
|
||||
putch(int ch, int *cnt)
|
||||
{
|
||||
cputchar(ch);
|
||||
*cnt++;
|
||||
}
|
||||
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
vprintfmt((void*)putch, &cnt, fmt, ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int
|
||||
cprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
300
lib/printfmt.c
Normal file
300
lib/printfmt.c
Normal file
|
@ -0,0 +1,300 @@
|
|||
// 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",
|
||||
};
|
||||
|
||||
/*
|
||||
* 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
lib/readline.c
Normal file
38
lib/readline.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <inc/stdio.h>
|
||||
#include <inc/error.h>
|
||||
|
||||
#define BUFLEN 1024
|
||||
static char buf[BUFLEN];
|
||||
|
||||
char *
|
||||
readline(const char *prompt)
|
||||
{
|
||||
int i, c, echoing;
|
||||
|
||||
if (prompt != NULL)
|
||||
cprintf("%s", prompt);
|
||||
|
||||
i = 0;
|
||||
echoing = iscons(0);
|
||||
while (1) {
|
||||
c = getchar();
|
||||
if (c < 0) {
|
||||
cprintf("read error: %e\n", c);
|
||||
return NULL;
|
||||
} else if ((c == '\b' || c == '\x7f') && i > 0) {
|
||||
if (echoing)
|
||||
cputchar('\b');
|
||||
i--;
|
||||
} else if (c >= ' ' && i < BUFLEN-1) {
|
||||
if (echoing)
|
||||
cputchar(c);
|
||||
buf[i++] = c;
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
if (echoing)
|
||||
cputchar('\n');
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
285
lib/string.c
Normal file
285
lib/string.c
Normal file
|
@ -0,0 +1,285 @@
|
|||
// Basic string routines. Not hardware optimized, but not shabby.
|
||||
|
||||
#include <inc/string.h>
|
||||
|
||||
// Using assembly for memset/memmove
|
||||
// makes some difference on real hardware,
|
||||
// but it makes an even bigger difference on bochs.
|
||||
// Primespipe runs 3x faster this way.
|
||||
#define ASM 1
|
||||
|
||||
int
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; *s != '\0'; s++)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
strnlen(const char *s, size_t size)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; size > 0 && *s != '\0'; s++, size--)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
char *
|
||||
strcpy(char *dst, const char *src)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
ret = dst;
|
||||
while ((*dst++ = *src++) != '\0')
|
||||
/* do nothing */;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
strcat(char *dst, const char *src)
|
||||
{
|
||||
int len = strlen(dst);
|
||||
strcpy(dst + len, src);
|
||||
return dst;
|
||||
}
|
||||
|
||||
char *
|
||||
strncpy(char *dst, const char *src, size_t size) {
|
||||
size_t i;
|
||||
char *ret;
|
||||
|
||||
ret = dst;
|
||||
for (i = 0; i < size; i++) {
|
||||
*dst++ = *src;
|
||||
// If strlen(src) < size, null-pad 'dst' out to 'size' chars
|
||||
if (*src != '\0')
|
||||
src++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t size)
|
||||
{
|
||||
char *dst_in;
|
||||
|
||||
dst_in = dst;
|
||||
if (size > 0) {
|
||||
while (--size > 0 && *src != '\0')
|
||||
*dst++ = *src++;
|
||||
*dst = '\0';
|
||||
}
|
||||
return dst - dst_in;
|
||||
}
|
||||
|
||||
int
|
||||
strcmp(const char *p, const char *q)
|
||||
{
|
||||
while (*p && *p == *q)
|
||||
p++, q++;
|
||||
return (int) ((unsigned char) *p - (unsigned char) *q);
|
||||
}
|
||||
|
||||
int
|
||||
strncmp(const char *p, const char *q, size_t n)
|
||||
{
|
||||
while (n > 0 && *p && *p == *q)
|
||||
n--, p++, q++;
|
||||
if (n == 0)
|
||||
return 0;
|
||||
else
|
||||
return (int) ((unsigned char) *p - (unsigned char) *q);
|
||||
}
|
||||
|
||||
// Return a pointer to the first occurrence of 'c' in 's',
|
||||
// or a null pointer if the string has no 'c'.
|
||||
char *
|
||||
strchr(const char *s, char c)
|
||||
{
|
||||
for (; *s; s++)
|
||||
if (*s == c)
|
||||
return (char *) s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return a pointer to the first occurrence of 'c' in 's',
|
||||
// or a pointer to the string-ending null character if the string has no 'c'.
|
||||
char *
|
||||
strfind(const char *s, char c)
|
||||
{
|
||||
for (; *s; s++)
|
||||
if (*s == c)
|
||||
break;
|
||||
return (char *) s;
|
||||
}
|
||||
|
||||
#if ASM
|
||||
void *
|
||||
memset(void *v, int c, size_t n)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (n == 0)
|
||||
return v;
|
||||
if ((int)v%4 == 0 && n%4 == 0) {
|
||||
c &= 0xFF;
|
||||
c = (c<<24)|(c<<16)|(c<<8)|c;
|
||||
asm volatile("cld; rep stosl\n"
|
||||
:: "D" (v), "a" (c), "c" (n/4)
|
||||
: "cc", "memory");
|
||||
} else
|
||||
asm volatile("cld; rep stosb\n"
|
||||
:: "D" (v), "a" (c), "c" (n)
|
||||
: "cc", "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
void *
|
||||
memmove(void *dst, const void *src, size_t n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if (s < d && s + n > d) {
|
||||
s += n;
|
||||
d += n;
|
||||
if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0)
|
||||
asm volatile("std; rep movsl\n"
|
||||
:: "D" (d-4), "S" (s-4), "c" (n/4) : "cc", "memory");
|
||||
else
|
||||
asm volatile("std; rep movsb\n"
|
||||
:: "D" (d-1), "S" (s-1), "c" (n) : "cc", "memory");
|
||||
// Some versions of GCC rely on DF being clear
|
||||
asm volatile("cld" ::: "cc");
|
||||
} else {
|
||||
if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0)
|
||||
asm volatile("cld; rep movsl\n"
|
||||
:: "D" (d), "S" (s), "c" (n/4) : "cc", "memory");
|
||||
else
|
||||
asm volatile("cld; rep movsb\n"
|
||||
:: "D" (d), "S" (s), "c" (n) : "cc", "memory");
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *
|
||||
memset(void *v, int c, size_t n)
|
||||
{
|
||||
char *p;
|
||||
int m;
|
||||
|
||||
p = v;
|
||||
m = n;
|
||||
while (--m >= 0)
|
||||
*p++ = c;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void *
|
||||
memmove(void *dst, const void *src, size_t n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if (s < d && s + n > d) {
|
||||
s += n;
|
||||
d += n;
|
||||
while (n-- > 0)
|
||||
*--d = *--s;
|
||||
} else
|
||||
while (n-- > 0)
|
||||
*d++ = *s++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *
|
||||
memcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
return memmove(dst, src, n);
|
||||
}
|
||||
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, size_t n)
|
||||
{
|
||||
const uint8_t *s1 = (const uint8_t *) v1;
|
||||
const uint8_t *s2 = (const uint8_t *) v2;
|
||||
|
||||
while (n-- > 0) {
|
||||
if (*s1 != *s2)
|
||||
return (int) *s1 - (int) *s2;
|
||||
s1++, s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
memfind(const void *s, int c, size_t n)
|
||||
{
|
||||
const void *ends = (const char *) s + n;
|
||||
for (; s < ends; s++)
|
||||
if (*(const unsigned char *) s == (unsigned char) c)
|
||||
break;
|
||||
return (void *) s;
|
||||
}
|
||||
|
||||
long
|
||||
strtol(const char *s, char **endptr, int base)
|
||||
{
|
||||
int neg = 0;
|
||||
long val = 0;
|
||||
|
||||
// gobble initial whitespace
|
||||
while (*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
|
||||
// plus/minus sign
|
||||
if (*s == '+')
|
||||
s++;
|
||||
else if (*s == '-')
|
||||
s++, neg = 1;
|
||||
|
||||
// hex or octal base prefix
|
||||
if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x'))
|
||||
s += 2, base = 16;
|
||||
else if (base == 0 && s[0] == '0')
|
||||
s++, base = 8;
|
||||
else if (base == 0)
|
||||
base = 10;
|
||||
|
||||
// digits
|
||||
while (1) {
|
||||
int dig;
|
||||
|
||||
if (*s >= '0' && *s <= '9')
|
||||
dig = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'z')
|
||||
dig = *s - 'a' + 10;
|
||||
else if (*s >= 'A' && *s <= 'Z')
|
||||
dig = *s - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
if (dig >= base)
|
||||
break;
|
||||
s++, val = (val * base) + dig;
|
||||
// we don't properly detect overflow!
|
||||
}
|
||||
|
||||
if (endptr)
|
||||
*endptr = (char *) s;
|
||||
return (neg ? -val : val);
|
||||
}
|
||||
|
86
mergedep.pl
Normal file
86
mergedep.pl
Normal file
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/perl
|
||||
# Copyright 2003 Bryan Ford
|
||||
# Distributed under the GNU General Public License.
|
||||
#
|
||||
# Usage: mergedep <main-depfile> [<new-depfiles> ...]
|
||||
#
|
||||
# This script merges the contents of all <new-depfiles> specified
|
||||
# on the command line into the single file <main-depfile>,
|
||||
# which may or may not previously exist.
|
||||
# Dependencies in the <new-depfiles> will override
|
||||
# any existing dependencies for the same targets in <main-depfile>.
|
||||
# The <new-depfiles> are deleted after <main-depfile> is updated.
|
||||
#
|
||||
# The <new-depfiles> are typically generated by GCC with the -MD option,
|
||||
# and the <main-depfile> is typically included from a Makefile,
|
||||
# as shown here for GNU 'make':
|
||||
#
|
||||
# .deps: $(wildcard *.d)
|
||||
# perl mergedep $@ $^
|
||||
# -include .deps
|
||||
#
|
||||
# This script properly handles multiple dependencies per <new-depfile>,
|
||||
# including dependencies having no target,
|
||||
# so it is compatible with GCC3's -MP option.
|
||||
#
|
||||
|
||||
sub readdeps {
|
||||
my $filename = shift;
|
||||
|
||||
open(DEPFILE, $filename) or return 0;
|
||||
while (<DEPFILE>) {
|
||||
if (/([^:]*):([^\\:]*)([\\]?)$/) {
|
||||
my $target = $1;
|
||||
my $deplines = $2;
|
||||
my $slash = $3;
|
||||
while ($slash ne '') {
|
||||
$_ = <DEPFILE>;
|
||||
defined($_) or die
|
||||
"Unterminated dependency in $filename";
|
||||
/(^[ \t][^\\]*)([\\]?)$/ or die
|
||||
"Bad continuation line in $filename";
|
||||
$deplines = "$deplines\\\n$1";
|
||||
$slash = $2;
|
||||
}
|
||||
#print "DEPENDENCY [[$target]]: [[$deplines]]\n";
|
||||
$dephash{$target} = $deplines;
|
||||
} elsif (/^[#]?[ \t]*$/) {
|
||||
# ignore blank lines and comments
|
||||
} else {
|
||||
die "Bad dependency line in $filename: $_";
|
||||
}
|
||||
}
|
||||
close DEPFILE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if ($#ARGV < 0) {
|
||||
print "Usage: mergedep <main-depfile> [<new-depfiles> ..]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
%dephash = ();
|
||||
|
||||
# Read the main dependency file
|
||||
$maindeps = $ARGV[0];
|
||||
readdeps($maindeps);
|
||||
|
||||
# Read and merge in the new dependency files
|
||||
foreach $i (1 .. $#ARGV) {
|
||||
readdeps($ARGV[$i]) or die "Can't open $ARGV[$i]";
|
||||
}
|
||||
|
||||
# Update the main dependency file
|
||||
open(DEPFILE, ">$maindeps.tmp") or die "Can't open output file $maindeps.tmp";
|
||||
foreach $target (keys %dephash) {
|
||||
print DEPFILE "$target:$dephash{$target}";
|
||||
}
|
||||
close DEPFILE;
|
||||
rename("$maindeps.tmp", "$maindeps") or die "Can't overwrite $maindeps";
|
||||
|
||||
# Finally, delete the new dependency files
|
||||
foreach $i (1 .. $#ARGV) {
|
||||
unlink($ARGV[$i]) or print "Error removing $ARGV[$i]\n";
|
||||
}
|
||||
|
39
user/sendpage.c
Normal file
39
user/sendpage.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Test Conversation between parent and child environment
|
||||
// Contributed by Varun Agrawal at Stony Brook
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
const char *str1 = "hello child environment! how are you?";
|
||||
const char *str2 = "hello parent environment! I'm good.";
|
||||
|
||||
#define TEMP_ADDR ((char*)0xa00000)
|
||||
#define TEMP_ADDR_CHILD ((char*)0xb00000)
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
|
||||
if ((who = fork()) == 0) {
|
||||
// Child
|
||||
ipc_recv(&who, TEMP_ADDR_CHILD, 0);
|
||||
cprintf("%x got message: %s\n", who, TEMP_ADDR_CHILD);
|
||||
if (strncmp(TEMP_ADDR_CHILD, str1, strlen(str1)) == 0)
|
||||
cprintf("child received correct message\n");
|
||||
|
||||
memcpy(TEMP_ADDR_CHILD, str2, strlen(str2) + 1);
|
||||
ipc_send(who, 0, TEMP_ADDR_CHILD, PTE_P | PTE_W | PTE_U);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parent
|
||||
sys_page_alloc(thisenv->env_id, TEMP_ADDR, PTE_P | PTE_W | PTE_U);
|
||||
memcpy(TEMP_ADDR, str1, strlen(str1) + 1);
|
||||
ipc_send(who, 0, TEMP_ADDR, PTE_P | PTE_W | PTE_U);
|
||||
|
||||
ipc_recv(&who, TEMP_ADDR, 0);
|
||||
cprintf("%x got message: %s\n", who, TEMP_ADDR);
|
||||
if (strncmp(TEMP_ADDR, str2, strlen(str2)) == 0)
|
||||
cprintf("parent received correct message\n");
|
||||
return;
|
||||
}
|
Loading…
Reference in New Issue
Block a user