From 1a83673424f310fdf7dc6c8b9e03af2bab3af7e0 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Thu, 30 Aug 2018 15:17:20 -0400 Subject: [PATCH] Lab 1 --- .dir-locals.el | 12 + .gdbinit.tmpl | 30 ++ .gitignore | 18 + CODING | 37 ++ GNUmakefile | 315 ++++++++++++++ boot/Makefrag | 32 ++ boot/boot.S | 85 ++++ boot/main.c | 125 ++++++ boot/sign.pl | 23 + conf/env.mk | 20 + conf/lab.mk | 2 + fs/test.c | 67 +++ fs/testshell.key | 70 +++ grade-lab1 | 44 ++ gradelib.py | 547 +++++++++++++++++++++++ inc/COPYRIGHT | 154 +++++++ inc/assert.h | 20 + inc/elf.h | 65 +++ inc/error.h | 20 + inc/kbdreg.h | 83 ++++ inc/memlayout.h | 147 +++++++ inc/mmu.h | 317 ++++++++++++++ inc/stab.h | 51 +++ inc/stdarg.h | 14 + inc/stdio.h | 33 ++ inc/string.h | 25 ++ inc/types.h | 75 ++++ inc/x86.h | 264 +++++++++++ kern/COPYRIGHT | 155 +++++++ kern/Makefrag | 89 ++++ kern/console.c | 476 ++++++++++++++++++++ kern/console.h | 26 ++ kern/entry.S | 96 ++++ kern/entrypgdir.c | 1059 +++++++++++++++++++++++++++++++++++++++++++++ kern/init.c | 92 ++++ kern/kdebug.c | 206 +++++++++ kern/kdebug.h | 20 + kern/kernel.ld | 61 +++ kern/monitor.c | 125 ++++++ kern/monitor.h | 19 + kern/printf.c | 37 ++ lib/printfmt.c | 300 +++++++++++++ lib/readline.c | 38 ++ lib/string.c | 285 ++++++++++++ mergedep.pl | 86 ++++ user/sendpage.c | 39 ++ 46 files changed, 5904 insertions(+) create mode 100644 .dir-locals.el create mode 100644 .gdbinit.tmpl create mode 100644 .gitignore create mode 100644 CODING create mode 100644 GNUmakefile create mode 100644 boot/Makefrag create mode 100644 boot/boot.S create mode 100644 boot/main.c create mode 100644 boot/sign.pl create mode 100644 conf/env.mk create mode 100644 conf/lab.mk create mode 100644 fs/test.c create mode 100644 fs/testshell.key create mode 100755 grade-lab1 create mode 100644 gradelib.py create mode 100644 inc/COPYRIGHT create mode 100644 inc/assert.h create mode 100644 inc/elf.h create mode 100644 inc/error.h create mode 100644 inc/kbdreg.h create mode 100644 inc/memlayout.h create mode 100644 inc/mmu.h create mode 100644 inc/stab.h create mode 100644 inc/stdarg.h create mode 100644 inc/stdio.h create mode 100644 inc/string.h create mode 100644 inc/types.h create mode 100644 inc/x86.h create mode 100644 kern/COPYRIGHT create mode 100644 kern/Makefrag create mode 100644 kern/console.c create mode 100644 kern/console.h create mode 100644 kern/entry.S create mode 100644 kern/entrypgdir.c create mode 100644 kern/init.c create mode 100644 kern/kdebug.c create mode 100644 kern/kdebug.h create mode 100644 kern/kernel.ld create mode 100644 kern/monitor.c create mode 100644 kern/monitor.h create mode 100644 kern/printf.c create mode 100644 lib/printfmt.c create mode 100644 lib/readline.c create mode 100644 lib/string.c create mode 100644 mergedep.pl create mode 100644 user/sendpage.c diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..1b46b0b --- /dev/null +++ b/.dir-locals.el @@ -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)) + ) diff --git a/.gdbinit.tmpl b/.gdbinit.tmpl new file mode 100644 index 0000000..234bb85 --- /dev/null +++ b/.gdbinit.tmpl @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85c53b8 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CODING b/CODING new file mode 100644 index 0000000..898097e --- /dev/null +++ b/CODING @@ -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. diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..7bd9e14 --- /dev/null +++ b/GNUmakefile @@ -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=\ /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 diff --git a/boot/Makefrag b/boot/Makefrag new file mode 100644 index 0000000..a2f49f8 --- /dev/null +++ b/boot/Makefrag @@ -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 + diff --git a/boot/boot.S b/boot/boot.S new file mode 100644 index 0000000..7a91ab1 --- /dev/null +++ b/boot/boot.S @@ -0,0 +1,85 @@ +#include + +# 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 + diff --git a/boot/main.c b/boot/main.c new file mode 100644 index 0000000..c230272 --- /dev/null +++ b/boot/main.c @@ -0,0 +1,125 @@ +#include +#include + +/********************************************************************** + * 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); +} + diff --git a/boot/sign.pl b/boot/sign.pl new file mode 100644 index 0000000..0bf46cb --- /dev/null +++ b/boot/sign.pl @@ -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; diff --git a/conf/env.mk b/conf/env.mk new file mode 100644 index 0000000..a603f9e --- /dev/null +++ b/conf/env.mk @@ -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= diff --git a/conf/lab.mk b/conf/lab.mk new file mode 100644 index 0000000..af4714d --- /dev/null +++ b/conf/lab.mk @@ -0,0 +1,2 @@ +LAB=1 +PACKAGEDATE=Thu Aug 30 15:16:04 EDT 2018 diff --git a/fs/test.c b/fs/test.c new file mode 100644 index 0000000..c2b743e --- /dev/null +++ b/fs/test.c @@ -0,0 +1,67 @@ +#include +#include + +#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"); +} diff --git a/fs/testshell.key b/fs/testshell.key new file mode 100644 index 0000000..c8208d2 --- /dev/null +++ b/fs/testshell.key @@ -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 isdir 0 size 32 dev pipe +fd 3: name 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