commit 3d9b2dcfc601b3f035020b1bf61cc9c60592add4 Author: Freya Murphy Date: Wed Sep 25 15:00:19 2024 -0400 inital checkout from try 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/Makefile b/Makefile new file mode 100644 index 0000000..33cd4aa --- /dev/null +++ b/Makefile @@ -0,0 +1,170 @@ +# +# 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/env.mk + +LABSETUP ?= ./ + +TOP = . + +# 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 + +QEMU = /home/course/csci352/bin/qemu-system-i386 + +# Native commands +NCC := gcc $(CC_VER) -pipe +NATIVE_CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -I$(TOP) -MD -Wall +TAR := tar +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 + +# prevent relative addressing and dynamic linking +CFLAGS += -fno-pic -fno-pie + +# 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 +# this can cause some strange behavior with GNU ld for some reason +#LDFLAGS += -no-pie + +# 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 -q -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 + +distclean: realclean + rm -rf conf/gcc.mk + +# 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 \ + clean realclean distclean 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..03b5ae2 --- /dev/null +++ b/conf/env.mk @@ -0,0 +1,16 @@ +# 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 not ELF-compatible, uncomment +# and edit the following line to use those tools. The value should be the +# prefix portion of the command names (e.g., /usr/bin/x86_64-linux-gnu-gcc +# would define GCCPREFIX=x86_64-linux-gnu-) +# +# GCCPREFIX= 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